Find the rectangle area of image to Crop

2019-03-03 08:20发布

问题:

I've image with the size of (352 x 240)

And I want to crop the white area (the selected area as show below).

I use this code to crop image ,

private static Image cropImage(Image img, Rectangle cropArea)
{
   Bitmap bmpImage = new Bitmap(img);
   Bitmap bmpCrop = bmpImage.Clone(cropArea,
   bmpImage.PixelFormat);
   return (Image)(bmpCrop);
}

But I can't find the exact rectangle area for my need !
How can I find Rectangle value of selected area ?
I just want to crop this area !! Thanks !

回答1:

If you want to do it yourself you certainly can. But the details of how to do it best will depend a lot on exactly how much you know about the source image.

Here are a few questions and decisions to consider:

  • do you know something about the dimension of the image
  • do you know something about the position of the image, will it always be in the bottom right?
  • do you know something about the sizes of the cut corners?
  • do you know the colors?
  • would you rather cut losing some pixels on the inside or including some pixels on the outside or a middle way?

Looking at your example you could for example try this strategy:

  • First you traverse the image horizontally at some distances, say in slices of 10 or 20.
  • On each transversal of a line you observe and collect all the points where the pixels change color
  • then you compare the resulting lists to see if there are some in the middle (!) that agree
  • this gets you the left and right boundaries
  • it also gets you an area where you will can do the vertical scans
  • then you repeat for vertical scans to get the top and bottom coordinates.
  • the vertical scans don't have to search for the rectangle, they can work from the middle of the results of the horizontal scans

For this to work you must know a little about the color changes you expect: Will there be smoothing involved? If so what level of change do you need to compare the result of the GetPixel colors..?

Also the dimensions should either be known approximately or else you may need to repeat the whole process with a finer mesh..

Edit: Using the info from your comment one can make a few assumptions (*) and use this piece of code to determine the target rectangle. The assumptions are used to :

  • determine two scanline ranges where the scans can be assumed to hit the body of the target
  • determine two pixels with the inside and the outside color

I use a colordistance function and 4 lists of Points and average over a few hits. The variable blur works with values 2-6 for your image.

List<Point> xPoints1 = new List<Point>();
List<Point> xPoints2 = new List<Point>();
List<Point> yPoints1 = new List<Point>();
List<Point> yPoints2 = new List<Point>();

Rectangle findRectangle()
{
    int xMax = pictureBox1.Image.Width; 
    int yMax = pictureBox1.Image.Height;

    // * we can asume that these lines hit the body
    int x0 = xMax * 3 / 4;
    int y0 = yMax * 3 / 4;

    using (Bitmap bmp = new Bitmap(pictureBox1.Image) )
    {
        // we can assume that these are the out- and inside colors
        Color col0 = bmp.GetPixel(9, 9);
        Color col1 = bmp.GetPixel(x0, y0);

        int blur = 4;
        int diff = colordistance(col0, col1) / blur;

        bool outside = true;
        // a few horizontal scans..
        for (int y = y0 - 20; y < y0 + 20; y += 4)
        {
          outside = true;
          for (int x = 0; x < xMax; x++)
          {
            Color c = bmp.GetPixel(x, y);

            if ( outside && colordistance(c, col0) > diff)
               { outside = !outside; xPoints1.Add(new Point(x, y)); }
            else if (!outside && colordistance(c, col1) > diff) 
               { outside = !outside; xPoints2.Add(new Point(x, y)); }
            }
        }              

        // a few vertical scans..
        for (int x = x0 - 20; x < x0 + 20; x += 4)
        {
           outside = true;
           for (int y = 0; y < yMax; y++)
           {
              Color c = bmp.GetPixel(x, y);
              if (outside && colordistance(c, col0) > diff) 
                  { outside = !outside; yPoints1.Add(new Point(x, y)); }
              else if (!outside && colordistance(c, col1) > diff) 
                  { outside = !outside; yPoints2.Add(new Point(x, y)); }
           }
        }

        int left   = (int)xPoints1.Average(p => p.X);
        int right  = (int)xPoints2.Average(p => p.X);
        int top    = (int)yPoints1.Average(p => p.Y);
        int bottom = (int)yPoints2.Average(p => p.Y);
        // if the target sits at the bottom we didn't find the edge
        if (bottom == 0) bottom = yMax;

        return = new Rectangle(left, top, right - left, bottom - top);

    }
}

int colordistance(Color c1, Color c2)
{
    return (int) Math.Sqrt((c1.R - c2.R) * (c1.R - c2.R) +
        (c1.G - c2.G) * (c1.G - c2.G) +
        (c1.B - c2.B) * (c1.B - c2.B));

}

Note: The top and the left values hit the inside of the Target. The bottom (and possibly the bottom) hit the outside. So you should decrease either the former or the latter by 1 pixel, depending on your needs..!

Edit The 1st version had the outside variable set at the wrong spot. Corrected.