C# Crop white space from around the image
This code will crop the image based on all white and transparent pixels from around the image
public static Bitmap CropWhiteSpace(Bitmap bmp){int w = bmp.Width;
int h = bmp.Height;
int white = 0xffffff;
Func<int, bool> allWhiteRow = r =>{for (int i = 0; i < w; ++i)if ((bmp.GetPixel(i, r).ToArgb() & white) != white)
return false;return true;};Func<int, bool> allWhiteColumn = c =>{for (int i = 0; i < h; ++i)if ((bmp.GetPixel(c, i).ToArgb() & white) != white)
return false;return true;};int topmost = 0;
for (int row = 0; row < h; ++row){if (!allWhiteRow(row))
break;
topmost = row;}int bottommost = 0;
for (int row = h - 1; row >= 0; --row){if (!allWhiteRow(row))
break;
bottommost = row;}int leftmost = 0, rightmost = 0;
for (int col = 0; col < w; ++col){if (!allWhiteColumn(col))
break;
leftmost = col;}for (int col = w - 1; col >= 0; --col){if (!allWhiteColumn(col))
break;
rightmost = col;}if (rightmost == 0) rightmost = w; // As reached leftif (bottommost == 0) bottommost = h; // As reached top.int croppedWidth = rightmost - leftmost;
int croppedHeight = bottommost - topmost;
if (croppedWidth == 0) // No border on left or right{leftmost = 0;croppedWidth = w;}if (croppedHeight == 0) // No border on top or bottom{topmost = 0;croppedHeight = h;}try
{var target = new Bitmap(croppedWidth, croppedHeight);
using (Graphics g = Graphics.FromImage(target))
{g.DrawImage(bmp,new RectangleF(0, 0, croppedWidth, croppedHeight),
new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
GraphicsUnit.Pixel);}return target;
}catch (Exception ex)
{throw new Exception(string.Format("Values are topmost={0} btm={1} left={2} right={3} croppedWidth={4} croppedHeight={5}", topmost, bottommost, leftmost, rightmost, croppedWidth, croppedHeight),ex);}}
[Test]public void Test(){var inputPath = "image.png";
var outputPath = inputPath.Replace(".png", "-out.png");var bitmap = new Bitmap(inputPath);
var cropped = CropWhiteSpace(bitmap);cropped.Save(outputPath, ImageFormat.Png);}
I see that this procedure doesn't work if the image is not a rectangle, is it true?
ReplyDeleteSo, why don't look only for the first not white pixel (from beginning of the bitmap) and the last one (from the end)?
From their coordinates you can simply calculate the new rectangle avoiding a lot of code!
Hi Tedebus, this works fine for any shape. It finds the first non-white pixel from top/bottom/left/right and crops the image based on this information.
ReplyDeleteHi!
ReplyDeleteI tried your code and I saw that irregular images still contain white areas (like you show on your article, now).
If your idea is to create a PNG (that has alpha information inside) you may prefer a different approach: bitmap reconstruction based on transparent color substitution on external white areas and, then, image crop.
This may be an example of code that does it
(please note that it crop not only white color, but any bounding color)
public static System.Drawing.Bitmap ClearBitmapBorders(System.Drawing.Bitmap bitmap, int tollerance)
{
Color pixelColor;
Color referenceColor = bitmap.GetPixel(0, 0);
int X1 = bitmap.Width;
int Y1 = bitmap.Height;
int X2 = 0;
int Y2 = 0;
int row, column = 0;
for (row = 0; row < bitmap.Height; row++)
{
for (column = 0; column < bitmap.Width; column++)
{
pixelColor = bitmap.GetPixel(column, row);
if ((pixelColor.ToArgb() << 8 == referenceColor.ToArgb() << 8))
{ bitmap.SetPixel(column, row, Color.Transparent); }
else
{
X1 = column < X1 ? column : X1;
Y1 = row < Y1 ? row : Y1;
break;
}
}
for (column = bitmap.Width - 1; column >= 0; column--)
{
pixelColor = bitmap.GetPixel(column, row);
if ((pixelColor.ToArgb() << 8 == referenceColor.ToArgb() << 8))
{ bitmap.SetPixel(column, row, Color.Transparent); }
else
{
X2 = column > X2 ? column : X2;
System.Diagnostics.Debug.WriteLine(X2);
Y2 = row > Y2 ? row : Y2;
break;
}
}
}
try
{
var target = new Bitmap(X2, Y2);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(bitmap,
new RectangleF(0, 0, X2, Y2),
new RectangleF(X1, Y1, X2, Y2),
GraphicsUnit.Pixel);
}
return target;
}
catch
{ return null; }
}
If you try it on your image you can see that now only red area will be kept and it remains white inside.
I hope it should be useful
PS "tollerance" is not used, I posted a piece of code that I used somewhere else and I forgot to delete it.
ReplyDeleteHi, just to explain, the purpose of this code was to remove unnecessary white space from around the image to make the image boundaries (square) close to the actual image so I can send that image in the email. I've used it after creating image from html template described here: C# Convert html page to image You're trying to go a bit further to make the remaining space outside the actual image transparent. And yes I've uploaded an extra image to show how it works for non square images.
ReplyDeleteAnd thanks for positing your code!
ReplyDeleteOk! I understand now...
ReplyDeleteThe matter was that I found your article on Google and I was looking especially for "C# Crop white space from around the image" (there was a discussion with my colleague) and I saw it. But I saw that it wasn't the answer to my question so I decided to write to you and explain my code.
ReplyDeleteIt works good guys.
public void TrimImage() {
int threshhold = 250;
int topOffset = 0;
int bottomOffset = 0;
int leftOffset = 0;
int rightOffset = 0;
Bitmap img = new Bitmap(@"e:\Temp\Trim_Blank_Image.png");
bool foundColor = false;
// Get left bounds to crop
for (int x = 1; x < img.Width && foundColor == false; x++)
{
for (int y = 1; y < img.Height && foundColor == false; y++)
{
Color color = img.GetPixel(x, y);
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
leftOffset += 1;
}
foundColor = false;
// Get top bounds to crop
for (int y = 1; y < img.Height && foundColor == false; y++)
{
for (int x = 1; x < img.Width && foundColor == false; x++)
{
Color color = img.GetPixel(x, y);
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
topOffset += 1;
}
foundColor = false;
// Get right bounds to crop
for (int x = img.Width - 1; x >= 1 && foundColor == false; x--)
{
for (int y = 1; y < img.Height && foundColor == false; y++)
{
Color color = img.GetPixel(x, y);
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
rightOffset += 1;
}
foundColor = false;
// Get bottom bounds to crop
for (int y = img.Height - 1; y >= 1 && foundColor == false; y--)
{
for (int x = 1; x < img.Width && foundColor == false; x++)
{
Color color = img.GetPixel(x, y);
if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
foundColor = true;
}
bottomOffset += 1;
}
// Create a new image set to the size of the original minus the white space
//Bitmap newImg = new Bitmap(img.Width - leftOffset - rightOffset, img.Height - topOffset - bottomOffset);
Bitmap croppedBitmap = new Bitmap(img);
croppedBitmap = croppedBitmap.Clone(
new Rectangle(leftOffset - 3, topOffset - 3, img.Width - leftOffset - rightOffset + 6, img.Height - topOffset - bottomOffset + 6),
System.Drawing.Imaging.PixelFormat.DontCare);
// Get a graphics object for the new bitmap, and draw the original bitmap onto it, offsetting it do remove the whitespace
//Graphics g = Graphics.FromImage(croppedBitmap);
//g.DrawImage(img, 1 - leftOffset, 1 - rightOffset);
croppedBitmap.Save(@"e:\Temp\Trim_Blank_Image-crop.png", ImageFormat.Png);
}
You have given great content here. I am glad to discover this post as I found lots of valuable data in your article. Thanks for sharing an article like this. Remove Background From Image
ReplyDeletethe most effective way to invest your savings is
ReplyDeleteproperties for rent in dubai
Properties For Sale In Dubai
property for sale in downtown dubai
property for rent in palm jumeirah
apartments for rent in palm jumeirah
property for rent in downtown dubai
villas for rent in dubai
villas for sale in dubai
property for sale in burj khalifa dubai
apartments for rent in palm jumeirah
apartments for sale in burj khalifa dubai
Apartments for sale in Dubai Hills Estate