3

Possible Duplicate:
Remove surrounding whitespace from an image

I'm looking for help to crop the whitespace on the top, bottom, left and right of an image.

I have found the following question/answer on SO, however the link with the answer is to a third party site, which does not appear to be up anymore.

Community
  • 1
  • 1
Richard West
  • 2,166
  • 4
  • 26
  • 40
  • Are you looking for a third party library or native code. It pretty easy to iterate through the edges checking for white space and then resizing the image but not really optimal. There are third party libraries that will include this sort of function for you in a far more optimal manor. – Graymatter Nov 08 '11 at 21:38
  • I don't have a problem with a third party library - perferably no cost since this is an internal project. – Richard West Nov 08 '11 at 21:40
  • Check out [this answer](http://stackoverflow.com/questions/4820212/automatically-trim-a-bitmap-to-minimum-size/4821100#4821100). It assumes transparent whitespace, but you can easily adjust the logic to your needs – Thomas Levesque Nov 08 '11 at 22:41

1 Answers1

16

Found a solution here, but modified the return code section to allow for an empty image to be input, in which case the original image will be returned.

class ImageCrop
{
    public static byte[][] GetRGB(Bitmap bmp)
    {
        BitmapData bmp_data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        IntPtr ptr = bmp_data.Scan0;
        int num_pixels = bmp.Width * bmp.Height, num_bytes = bmp_data.Stride * bmp.Height, padding = bmp_data.Stride - bmp.Width * 3, i = 0, ct = 1;
        byte[] r = new byte[num_pixels], g = new byte[num_pixels], b = new byte[num_pixels], rgb = new byte[num_bytes];
        System.Runtime.InteropServices.Marshal.Copy(ptr, rgb, 0, num_bytes);

        for (int x = 0; x < num_bytes - 3; x += 3)
        {
            if (x == (bmp_data.Stride * ct - padding)) { x += padding; ct++; };
            r[i] = rgb[x]; g[i] = rgb[x + 1]; b[i] = rgb[x + 2]; i++;
        }
        bmp.UnlockBits(bmp_data);
        return new byte[3][] { r, g, b };
    }
    public static Image AutoCrop(Bitmap bmp)
    {
        //Get an array containing the R,G,B components of each pixel
        var pixels = GetRGB(bmp);

        int h = bmp.Height - 1, w = bmp.Width, top = 0, bottom = h, left = bmp.Width, right = 0, white = 0;
        int tolerance = 95; // 95%

        bool prev_color = false;
        for (int i = 0; i < pixels[0].Length; i++)
        {
            int x = (i % (w)), y = (int)(Math.Floor((decimal)(i / w))), tol = 255 * tolerance / 100;
            if (pixels[0][i] >= tol && pixels[1][i] >= tol && pixels[2][i] >= tol) { white++; right = (x > right && white == 1) ? x : right; }
            else { left = (x < left && white >= 1) ? x : left; right = (x == w - 1 && white == 0) ? w - 1 : right; white = 0; }
            if (white == w) { top = (y - top < 3) ? y : top; bottom = (prev_color && x == w - 1 && y > top + 1) ? y : bottom; }
            left = (x == 0 && white == 0) ? 0 : left; bottom = (y == h && x == w - 1 && white != w && prev_color) ? h + 1 : bottom;
            if (x == w - 1) { prev_color = (white < w) ? true : false; white = 0; }
        }
        right = (right == 0) ? w : right; left = (left == w) ? 0 : left;

        //Crop the image
        if (bottom - top > 0)
        {
            Bitmap bmpCrop = bmp.Clone(new Rectangle(left, top, right - left + 1, bottom - top), bmp.PixelFormat);

            return (Bitmap)(bmpCrop);
        }
        else
        {
            return bmp;
        }
    }


}
Richard West
  • 2,166
  • 4
  • 26
  • 40
  • Great work, should have more up votes. – Max Favilli Jul 24 '14 at 13:25
  • I had issues with this throwing an "Out of Memory" exception related to invalid dimensions being passed into the .Clone() call. You can find more information at http://stackoverflow.com/questions/199468/c-sharp-image-clone-out-of-memory-exception. It appears to be an off by one error with the +1 in the "right - left + 1" when the entire image is supposed to be cropped. In the end, I wrote my own version of this method that was a bit clearer and uses less memory at the expense of conciseness. You can find it in the linked duplicate. – Brian Hasden Apr 22 '15 at 00:44
  • Doesn't work that well. I used this one https://stackoverflow.com/questions/4820212/automatically-trim-a-bitmap-to-minimum-size/4821100#4821100 and it works great!! Super fast too! – Shiroy Sep 06 '19 at 07:34