C# Crop transparent parts in an image - c#

I have this image:
and I wrote a code that supposed to crop only the part with the black dots(the code is built for 1-color images only),
without all of the transparent pixels around the dots,
and then return the image after the crop,
but for some reason, when it gets to a black pixel,
it does not recognize that this is a black pixel,
and because of that, it skips the 'if' statement.
Here is the code:
private Image cropTransparency(Image image)
{
Bitmap imageCrop = new Bitmap(image);
imageCrop.Save(#"C:\Users\Nehoray\Desktop\Test.png");
Point min = new Point(imageCrop.Width, imageCrop.Height);
Point max = new Point(imageCrop.Width, imageCrop.Height);
for (int w = 0; w < imageCrop.Width; w++)
{
//'w' stands for Width
for (int h = 0; h < imageCrop.Height; h++)
{
//'h' stands for Height
Color check = imageCrop.GetPixel(w, h);
if (check == Color.Black)
{
MessageBox.Show("Found a white pixel!");
if (w < min.X)
{
min.X = w;
}
if (h < min.Y)
{
min.Y = h;
}
if (w > max.X)
{
max.X = w;
}
if (h > max.Y)
{
max.Y = h;
}
}
}
}
imageCrop = new Bitmap(max.X - min.X, max.Y - min.Y);
Graphics g = Graphics.FromImage(imageCrop);
Rectangle cropRect = new Rectangle(new Point(0, 0), new Size(max.X - min.X, max.Y - min.Y));
g.DrawImage(image, new Rectangle(0, 0, max.X - min.X, max.Y - min.Y), cropRect, GraphicsUnit.Pixel);
g.Save();
return imageCrop;
}
If you find out why it does not recognize when there is a black pixel, please let me know..
thanks anyway :)

There are quite a few issues with this code:
Point max = new Point(imageCrop.Width, imageCrop.Height);
How will a point ever be greater than the max, when the max is initialised to the maximum value? This should be (0,0)
Color check = imageCrop.GetPixel(w, h);
if (check == Color.Black)
I'm not sure this does what you think it will. You have a 32-bit image, with an alpha channel, so you need to take the alpha values into account. Also, you're comparing against a predefined colour which has a reference that won't match your pixel even if all 4 channels are a match. You possibly just want to check for the alpha component being non-zero. If you only compare the colour channels, be aware that transparent pixels may well have a matching colour, producing unexpected results.
Rectangle cropRect = new Rectangle(new Point(0, 0), new Size(max.X - min.X, max.Y - min.Y));
Why are you cropping from 0,0? Your rectangle should begin at min.X, min.Y
g.Save();
This doesn't save the image, you know that right? You save the image, unmodified at the start of your code, and then never re-save it once you've cropped it (I assume this stuff, including the hard-coded path, is for debug, but even then it seems you probably meant to write the image here)

You are comparing: (check == Color.Black) which means: is the reference check pointing to the same instance as the reference Color.Black --> this will never be true.
you have to compare the actual color:
(check.ToArgb() == Color.Black.ToArgb())

private Image cropTransparency(Image image)
{
Bitmap imageCrop = new Bitmap(image); // aca paso la imagen original a Bitmap
//imageCrop.Save(#"tmp.png");
Point min = new Point(imageCrop.Width, imageCrop.Height);
Point max = new Point(0, 0);
for (int w = 0; w < imageCrop.Width; w++)
{
//'w' stands for Width
for (int h = 0; h < imageCrop.Height; h++)
{
//'h' stands for Height
Color check = imageCrop.GetPixel(w, h);
if (check == Color.FromArgb(255, 0, 0, 0))
{
Console.WriteLine("Found a white pixel!");
if (w < min.X)
{
min.X = w;
}
if (h < min.Y)
{
min.Y = h;
}
if (w > max.X)
{
max.X = w;
}
if (h > max.Y)
{
max.Y = h;
}
}
}
}
imageCrop = new Bitmap(max.X - min.X, max.Y - min.Y);
Graphics g = Graphics.FromImage(imageCrop);
Rectangle cropRect = new Rectangle(new Point(min.X,min.Y), new Size(max.X - min.X, max.Y - min.Y));
g.DrawImage(image, new Rectangle(0, 0, max.X - min.X, max.Y - min.Y), cropRect, GraphicsUnit.Pixel);
g.Save();
return imageCrop;
}

Related

How to center this image

I have an image that looks like this. I have the pointy image which looks good and then I have to draw the green image on top of the pointy image. The idea is to draw the green image in the center of the pointy image. I am having some problems with that.
var shipImageOffset = new PointF(0, 0);
using (var bmp = new Bitmap(_shipImage))
using (var image = ImageUtilities.RotateImage(
bmp,
(float)ShipHeading,
new PointF(this.Width / 2, this.Height / 2),
shipImageOffset,
this.Width,
this.Height))
{
e.Graphics.DrawImage(image, new PointF(0,0));
}
//paint the green pointer image
if (_windImageRepo.TryGetValue(needleSpeed, out var windImage))
{
//need to figure out the origin point - the origin point is the center of the long rectangle
//at the bottom
var center = new PointF(windImage.HorizontalResolution, windImage.VerticalResolution);
using (var bmp = new Bitmap(windImage))
{
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
var color = bmp.GetPixel(x, y);
if (color != Color.FromArgb(0))
{
bmp.SetPixel(x, y, System.Drawing.Color.LimeGreen);
}
}
}
var windImageOffset = new PointF(0,0);
using (var image = ImageUtilities.RotateImage(
bmp,
(float)WindHeading,
new PointF(this.Width / 2, this.Height / 2),
windImageOffset,
this.Width,
this.Height))
{
//This is your image from the pictureBox1
Bitmap img = new Bitmap(_shipImage);
// I am targeting the middle of the image, Your case would be the binarized image
Point index = new Point(img.Size.Width / 2, img.Size.Height / 2);
e.Graphics.DrawImage(image,Center(_shipImage));
}
}
}
base.OnPaint(e);
}
The objective is to get the green pointer in the middle of other image. Not having luck on that.

pictureBox1.Image is null even after drawing on a PictureBox

I am drawing on a PictureBox control a grid with a small image on it.
When pressing a Button, I need to update the small image position on the grid, drawing it again on a different position.
I am drawing first time with:
Bitmap ime = new Bitmap(Properties.Resources.ime);
Image imge= ime;
Graphics g = e.Graphics;
using (Pen pen = new Pen(Color.Black, 2))
{
int rows = matrix.GetUpperBound(0) + 1 - matrix.GetLowerBound(0); // = 3, this value is not used
int columns = matrix.GetUpperBound(1) + 1 - matrix.GetLowerBound(1); // = 4
for (int index = 0; index < matrix.Length; index++)
{
int i = index / columns;
int j = index % columns;
if (matrix[i, j] == 0)
{
Rectangle rect = new Rectangle(new Point(5 + step * j, 5 + step * i), new Size(width, height));
g.DrawRectangle(pen, rect);
g.FillRectangle(Brushes.Black, rect);
}
}
Rectangle rect1 = new Rectangle(new Point(5 + step * 10, 5 + step * 10), new Size(width, height));
g.DrawImage(imge, rect1);
}
and the second time, when updating the PictureBox, I am using:
using (var g = Graphics.FromImage(matrixPictureBox.Image))
but I am getting the error saying that matrixPictureBox.Image is null
Does anybody know the problem?

Resize a Bitmap in C#

im looking for a way to resize a Bitmap, without losing its actually shape, while having in mind that the new size is musn't be proportional to the original image dimensions.
To be exact I want to resize a image of a upright eight (40x110) to a 29x29 Bitmap and im looking for a function to convert the original proportions relativ to its new image, while filling the new created space (in width) with white and having the same side clearance from the actually eight to its white baselines.
Unfortunately this hasn't satisfy my requirements:
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
Update 1:
New function for proportional resizing:
public static Bitmap ResizeImageProportional(Bitmap bitmap, int width, int height)
{
Bitmap destImage;
Rectangle destRect;
int destH, destW, destX, dextY;
if (bitmap.Height > bitmap.Width)
{
destH = height;
destW = bitmap.Width / bitmap.Height * height;
destX = (width - destW) / 2;
dextY = 0;
}
else if (bitmap.Height < bitmap.Width)
{
destH = bitmap.Height / bitmap.Width * width;
destW = width;
destX = 0;
dextY = (height - destH) / 2;
}
else
// if (bitmap.Width == bitmap.Height)
{
destH = height;
destW = width;
destX = 0;
dextY = 0;
}
destRect = new Rectangle(destX, dextY, destW, destH);
destImage = new Bitmap(width, height);
destImage.SetResolution(bitmap.HorizontalResolution, bitmap.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(bitmap, destRect, 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
Unfortunately I always get a empty bitmap with this solution. I first crop the original bitmap to a rect in which the element of my bitmap does precisely fit in. Otherwise put the left baseline of the bitmap itself is also the baseline of the graphics element in it, while the whole background is white.
Then I put this bitmap (approx. 40x80) in to the "ResizeImageProportional" (original post edit.) function with a width and height value of 29.
If you want to keep the proportion you need to calculate the destination rectnagle.
Here are the measures, off the top of my head:
destH = height;
destW = image.Width / image.Height * height;
destX = (width - destW) / 2;
dextY = 0;
You should clear the background to the color you want before: Graphics.Clear(someColor);
If you don't you use the bounds of the source and the destination images, thereby distorting the image. Which is what you seem to have.., no?
In that case there is no new space, of course.
When calculating, use float if necessary..
Update:
Maybe you want to analyze the original image and preserve or remove the spaces from the foreground to its edges. That means either looking into the pixels or maybe going one step back to how the original was created..
For the example I have added the bounds of the figure.
Here is a simple cropping function; it assumes the top left corner to hold the background color and returns a cropping rectangle:
public static Rectangle CropBounds(Image image)
{
Bitmap bmp = (Bitmap)image;
int x0 = 0;
int x1 = bmp.Width - 1;
int y0 = 0;
int y1 = bmp.Height - 1;
Color c = bmp.GetPixel(0, 0);
while (x0==0)
{ for (int x = 0; x < x1; x++)
if ((x0==0)) for (int y = 0; y < y1; y++)
if ( bmp.GetPixel(x,y)!= c) {x0 = x-1; break;} }
while (x1 == bmp.Width - 1)
{
for (int x = bmp.Width - 1; x > 0; x--)
if ((x1 == bmp.Width - 1)) for (int y = 0; y < y1; y++)
if (bmp.GetPixel(x, y) != c) { x1 = x + 1; break; } }
while (y0 == 0)
{
for (int y = 0; y < y1; y++)
if ((y0 == 0)) for (int x = 0; x < bmp.Width; x++)
if (bmp.GetPixel(x, y) != c) { y0 = y - 1; break; }
}
while (y1 == bmp.Height - 1)
{
for (int y = bmp.Height - 1; y > 0; y--)
if ((y1 == bmp.Height - 1)) for (int x = 0; x < bmp.Width; x++)
if (bmp.GetPixel(x, y) != c) { y1 = y + 1; break; }
}
return new Rectangle(x0, y0, x1 - x0, y1 - y0);
}
Now you can change your code to use it like this:
graphics.DrawImage(image, destRect, CropBounds(image) , GraphicsUnit.Pixel);
If you really need to flip, simply store the rectangle and change to the appropriate format..!
Update 2:
There was a reason why I wrote When calculating, use float if necessary..:
In your ResizeImageProportional function you need to avoid integer precision loss! Change the divisions to:
destW = (int) (1f * bitmap.Width / bitmap.Height * height);
and
destH = (int) (1f *bitmap.Height / bitmap.Width * width);

shadow getting darker drawn on bitmap

I'm trying to create a shadow to a rectangle drawn dynamically on a bitmap. The problem is the shadow gets darker each time I draw a new rectangle (please see screenshot). I suspect that the same bitmap is used to draw the new rectangles. I tried using Graphics.clear() but it cleans the screen which I don't want. How can solve this problem?
Here is the code which draws the shadow:
public void drawAll(Rectangle baseRect,Graphics g)
{
int shadWidth = 10;
Bitmap bm = new Bitmap(shadWidth, baseRect.Height+shadWidth);//baseRect is created dynamically
for (int y = 0; y < baseRect.Height + shadWidth; y++)
{
int factor = 255 / shadWidth;//255 is the alpha color divided over the shadow width
int alpha = 255;
for (int x = 0; x < shadWidth; x++)
{
alpha -= factor;
if (alpha < 0) alpha = 0;
Color transColr = Color.FromArgb(alpha, 0, 0, 0);
bm.SetPixel(x, y, transColr);
}
}
GraphicsPath path = new GraphicsPath();
PointF[] pts = new[] {new PointF(baseRect.Right, baseRect.Top),
new PointF(baseRect.Right+shadWidth, baseRect.Top+shadWidth),
new PointF(baseRect.Right+shadWidth, baseRect.Bottom+shadWidth),
new PointF(baseRect.Right, baseRect.Bottom),
new PointF(baseRect.Right, baseRect.Top)};
path.AddLines(pts);
SmoothingMode old = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawImageUnscaled(bm, baseRect.Right, baseRect.Y);
}

Bitmap cant find a pixel on itself!

Scenario:
1) Program going to draw a string (commonly a single character) on a bitmap:
protected void DrawCharacter(string character, Font font)
{
if(string.IsNullOrEmpty(character))
character = ".";
FontFamily f = new FontFamily(FontName);
bitmap = new Bitmap((int)(font.Size * 2f), (int)font.Height);
Graphics g = Graphics.FromImage(bitmap);
g.Clear(Color.White);
g.DrawString(character, font, Brushes.Black, DrawPoint);
}
2) Using following algorithm we get all black pixels position:
public Int16[] GetColoredPixcels(Bitmap bmp, bool useGeneric)
{
List<short> pixels = new List<short>();
int x = 0, y = 0;
do
{
Color c = bmp.GetPixel(x, y);
if (c.R == 0 && c.G == 0 && c.B == 0)
pixels.Add((Int16)(x + (y + 1) * bmp.Width));
if (x == bmp.Width - 1)
{
x = 0;
y++;
}
else
x++;
} while (y < bmp.Height);
return pixels.ToArray();
}
Problem occurs when input character is a single point (.). I don't know what's happening in bitmap object while processing function bmp.GetPixel(x, y), because it can't find point position! Output array claims bitmap has no black point! But when input string is (:) program can find pixels position properly!
Any suggestion or guid?
thanks in advance...
I suspect that anti-aliasing means that the pixel for "." isn't completely black. Why not change your condition to just pick "very dark" pixels?
private const int Threshold = 10;
...
if (c.R < Threshold && c.G < Threshold && c.B < Threshold)

Categories