Cropping a cross rectangle from image using c#

2019-02-27 16:10发布

问题:

What I want to do is basically cropping a rectangle from an image. However, it should satisfy some special cases:

  1. I want to crop an angled rectangle on image.
  2. I don't want to rotate the image and crop a rectangle :)
  3. If cropping exceeds the image size, I don't want to crop an empty background color.

I want to crop from back of the starting point, that will end at starting point when rectangle size completed. I know I couldn't explain well so if I show what I want visually:

The blue dot is the starting point there, and the arrow shows cropping direction. When cropping exceeds image borders, it will go back to the back of the starting point as much as, when the rectangle width and height finished the end of the rectangle will be at starting point.

Besides this is the previous question I asked:

  • How to crop a cross rectangle from an image using c#?

In this question, I couldn't predict that a problem can occur about image dimensions so I didn't ask for it. But now there is case 3. Except case three, this is exactly same question. How can I do this, any suggestions?

回答1:

What needs to be done is to add offsets to the matrix alignment. In this case I am taking one extra length of the rectangle from each side (total 9 rectangles) and offsetting the matrix each time.

Notice that it is necessary to place offset 0 (the original crop) last, otherwise you will get the wrong result.

Also note that if you specify a rectangle that is bigger than the rotated picture you will still get empty areas.

public static Bitmap CropRotatedRect(Bitmap source, Rectangle rect, float angle, bool HighQuality)
{
    int[] offsets = { -1, 1, 0 }; //place 0 last!
    Bitmap result = new Bitmap(rect.Width, rect.Height);
    using (Graphics g = Graphics.FromImage(result))
    {
        g.InterpolationMode = HighQuality ? InterpolationMode.HighQualityBicubic : InterpolationMode.Default;
        foreach (int x in offsets)
        {
            foreach (int y in offsets)
            {
                using (Matrix mat = new Matrix())
                {
                    //create the appropriate filler offset according to x,y
                    //resulting in offsets (-1,-1), (-1, 0), (-1,1) ... (0,0)
                    mat.Translate(-rect.Location.X - rect.Width * x, -rect.Location.Y - rect.Height * y);
                    mat.RotateAt(angle, rect.Location);
                    g.Transform = mat;
                    g.DrawImage(source, new Point(0, 0));
                }
            }
        }
    }
    return result;
}

To recreate your example:

Bitmap source = new Bitmap("C:\\mjexample.jpg");
Bitmap dest = CropRotatedRect(source, new Rectangle(86, 182, 87, 228), -45, true);