export transparent images in c#? - c#

I've edited an bitmap in c# and for every pixel i've changed it to a certain color if a condition was true else i've set the color to Color.Transparent ( the operations were done with getPixel/setPixel ) . I've exported the image in .png format but the image isn't transparent. Any ideas why or how should i do it ?
Regards,
Alexandru Badescu
here is the code :
-- here i load the image and convert to PixelFormat.Format24bppRgb if png
m_Bitmap = (Bitmap)Bitmap.FromFile(openFileDialog.FileName, false);
if(openFileDialog.FilterIndex==3) //3 is png
m_Bitmap=ConvertTo24(m_Bitmap);
-- this is for changing the pixels after a certain position in an matrix
for (int i = startX; i < endX; i++)
for (int j = startY; j < endY; j++)
{
if (indexMatrix[i][j] == matrixFillNumber)
m_Bitmap.SetPixel(j, i, selectedColor);
else
m_Bitmap.SetPixel(j, i, Color.Transparent);
}

Its because pixelformat.
Here is a sample code for you:
Bitmap inp = new Bitmap("path of the image to edit");
Bitmap outImg = new Bitmap(inp.Width, inp.Height, PixelFormat.Format32bppArgb);
outImg.SetResolution(inp.HorizontalResolution, inp.VerticalResolution);
Graphics g = Graphics.FromImage(outImg);
g.DrawImage(inp, Point.Empty);
g.Dispose();
inp.Dispose();
////
// Check your condition and set pixel here (outImg.GetPixel, outImg.SetPixel)
////
outImg.Save("out file path", ImageFormat.Png);
outImg.Dispose();
this is the code that requires minimum change to your current code.
But i would recommend you to check out LockBits Method for a better performance:
http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx

I will need more code to verify, but my best guess is that you first write something on the bitmap to clear it (like fill it with White color), then to make the transparent pixels you draw with Color.Transparent on top. This will simply not work, since White (or anything else) with Transparent on top, is still White.

If you have created a bitmap in code, it will be most likely 24 bit and would not support alpha blending/transparent.
provide the code for creating and we should be able to help.

Related

C# cropping bitmaps depending on bitmap location

I have a PDF file containing numerous pages of hand-written surveys. My C# application currently breaks each PDF page down into single Bitmap objects (each PDF page is a Bitmap object) and then uses various APIs to read the hand-written data from each Bitmap object and then enters the extracted data into a database.
My problem is, in order for my API extraction to work, each checkbox and each answer box needs to be in the exact same XY pixel location in every Bitmap. Because these PDF files are scanned images, each PDF page may be a few pixels off in any direction e.g. some PDF pages are a few pixels off to the left, right, top or bottom
Does anybody know if it is possible to crop a Bitmap based on some constant in each Bitmap? For example (please see Bitmap image below), if I could crop each Bitmap starting at the "S" in Secondary School Study at the top left of each page, then each Bitmap would be cropped at the exact same location and this would solve my problem of each checkbox and answer box being in the same XY locations.
Any advice would be appreciated
EDIT: the only possible solution I can think of is looping over each pixel, starting at the top left hand corner until it hits a black pixel (which would be the first "S" in Secondary School Study). Could I then crop the Bitmap from this location?
I came up with a solution which was similar to the one I mentioned above. I scan over each pixel and until it reaches the first pixel in the "S" in Secondary School Study. I use this pixel X Y location to then crop a rectangle of a fixed height and width, starting at that location. I used bm.GetPixel().GetBrightness() to find out when the pixel reached the "S".
Bitmap bm = new Bitmap(#"C:\IronPDFDoc\2.png", true);
bool cropFlag = false;
int cropX = 0;
int cropY = 0;
for (int y = 0; y < 155; y++)
{
for (int x = 0; x < 115; x++)
{
float pixelBrightness = bm.GetPixel(x, y).GetBrightness();
if (pixelBrightness < 0.8 && cropFlag == false)
{
cropFlag = true;
cropX = x;
cropY = y;
}
}
}
Rectangle crop = new Rectangle(cropX, cropY, 648, 915);
Bitmap croppedSurvey = new Bitmap(crop.Width, crop.Height);
using (Graphics g = Graphics.FromImage(croppedSurvey))
{
g.DrawImage(bm, new Rectangle(0, 0, croppedSurvey.Width, croppedSurvey.Height),
crop,
GraphicsUnit.Pixel);
}
croppedSurvey.Save(#"C:\IronPDFDoc\croppedSurvey.png", ImageFormat.Png);

C# Remove gradient background from image

I have an image loaded into a Bitmap in C# with a gradient background from a document i scanned in.
An example of it could be like the picture below:
My goal in C# is now to remove the background so that I have a solid white background. Now I myself can't seem to find a way to do this. Is there a way to achieve this in a way?
Thanks in advance.
Here is a version using LockBits.
The premise is if it's not black then change it to white.
It will be magnitudes faster the GetPixel and SetPixel
It works with the raw data in memory using pointers
iterates through every pixel
Checks the color and changes it to white if needed
Saves the image
Note : obviously this will destroy any antialiasing and smoothing, it will fail for certain image types, and other assorted issues.
using (var bmp = new Bitmap(#"D:\Test.png"))
{
var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
var white = Color.White.ToArgb();
var black = Color.Black.ToArgb();
try
{
var length = (int*)data.Scan0 + bmp.Height * bmp.Width;
for (var p = (int*)data.Scan0; p < length; p++)
if (*p != black) *p = white;
}
finally
{
// unlock the bitmap
bmp.UnlockBits(data);
bmp.Save(#"D:\Output.Bmp", ImageFormat.Bmp);
}
}
Output
If you know the gradient colors (e.g. only part of RGB color responsible for red changes) or at least the color of text (e.g. if it is always black) then you can iterate through all of image's pixels and then:
Use GetPixel() to get pixel color.
Check if it is text (black).
If it is, then move to the next pixel.
If it isn't, then change color to white with SetPixel().
For gradient it should be enough. For more complex backgrounds it would need a more complex algorithm.

Remove the black background color of a bitmap

I need to remove the black background color of a bitmap in C# VS2013.
It is just like that I draw some points on a canvas. The canvas is black and I just need to change the canvas to be transparent meanwhile keeping colorful points on it without any changes.
I got the solution at:
How to remove white background color from Bitmap
Bitmap capcha = new Bitmap(#"C:/image.png");
capcha.MakeTransparent(Color.Black);
But, the background still have a gray color like a fog covering the points on the image.
How to remove it ?
UPDATE
I used the code:
ImageAttribute imageAttribute = new ImageAttribute();
imageAttribute.SetGamma(0.5f, ColorAdjustType.Bitmap);
gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height),
0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imageAttribute );
I got same thing.
More update of C# code to draw an image :
System.Drawing.Bitmap canvasImage = new System.Drawing.Bitmap(xSize, ySize, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
canvasImage.MakeTransparent(Color.Black);
Graphics g = Graphics.FromImage(canvasImage);
System.Drawing.Bitmap tempImage = myDrawImageFunction(myPoints);
g.Clear(Color.Transparent); // this only give me an empty image without any points on it. But, if I uncomment it, I will have an image with black background.
// my understanding about g.DrawImage() is to draw points on tempImage
// after clearing the background. But, the image still have a foggy covering on the image.
g.DrawImage(tempImage, new System.Drawing.PointF(x_position, y_position));
I want to have a transparent background for "tempImage" before any points are drawn on it.
The example image has a back ground that needs to be removed but the colorful points on the image need to be kept without any changes.
This will do the job:
public Color MakeTransparent(Color c, int threshold)
{ // calculate the weighed brightness:
byte val = (byte)((c.R * 0.299f + c.G * 0.587f + c.B * 0.114f));
return val < threshold ? Color.FromArgb(0, c.R, c.G, c.B) : c;
}
You could use it in a double loop over the pixels, but for fast results you should call it from the code in this post (second part!) which uses LockBits.
Change this
ModifyHue hueChanger = new ModifyHue(MaxChannel);
to the new function:
ModifyHue hueChanger = new ModifyHue(MakeTransparent);
and call it with a suitable threshold, maybe 10 or 20..:
c = hueChanger(c, 20);
The function skips the call to the system's MakeTransparent function and directly sets the alpha channel of each pixel to 0.
If you want to create a uniform color instead of a transparent one it should be easy to modify (e.g. by returning Color.FromArgb(255, 0, 0, 0) for solid black)
Do note that, while the code in the linked post takes both 24 and 32 bbp formats you should definitely not save as JPG, as this will re-introduce artifacts and the result will not work well with e.g. a TransparencyKey color..
Instead do save it as PNG, as Hans suggests!
I hope you can modify the button code to a function :-)

How can I read total number of the pixels in foreground and background of my image in C#?

Basically I have written a code that creates image of a character (randomly generated). I have a problem how to scan the image line by line and read the total number of the foreground (text) and background (white) pixels in my bmp image and display the results to the user. here is part of my code:
Image bmp = new Bitmap(100, 100);
Graphics g = Graphics.FromImage(bmp);
g.Clear(Color.White);
g.DrawString(randomString, myFont, new SolidBrush(Color.Black), new PointF(0, 0));
pictureBox1.Image = bmp;
bmp.Save(#"CAPTCHA.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
The basic idea is simple - iterate over all the pixels in the bitmap, and if the pixel is white, increment your background pixel counter. After you're done, you can simply get the total amount of pixels (width * height) and do whatever you want with the background pixel counter value.
A simple (and very slow) code snippet that does this:
var backgroundPixels = 0;
for (int x = 0; x < bmp.Width; x++)
for (int y = 0; y < bmp.Height; y++)
if (bmp.GetPixel(x, y) == Color.White)
backgroundPixels++;
The notion of foreground and background is only there in your head. The resulting bitmap only has an array of pixels, each with a position and colour. You can assign a specific meaning to some color (white in your case) and say that it means the background - but that's it.
A good alternative would be to use a transparent bitmap, where there indeed is a special meaning for what you call background - transparency. In that case, apart from the colour, the pixel also has a notion of the degree of transparency (Color.A), which you can exploit. In that case, you'd do g.Clear(Color.Transparent); instead of using white, and when iterating over the pixels, you'd check if bmp.GetPixel(x, y).A > 0 or whatever threshold you'd have for saying "this is the background". When you want to add the actual background colour, you'd paint this bitmap over a bitmap that's completely white and save that.

Bad text rendering using DrawString on top of transparent pixels

When rendering text into a bitmap, I find that text looks very bad when rendered on top of an area with non-opaque alpha. The problem is progressively worse as the underlying pixels become more transparent. If I had to guess I'd say that when underlying pixels are transparent, the text renderer draws any anti-aliased 'gray' pixels as solid black.
Here are some screenshots:
Text drawn on top of transparent pixels:
Text drawn on top of semi-transparent pixels:
Text drawn on opaque pixels:
Here is the code used to render the text:
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawString("Press the spacebar", Font, Brushes.Black, textLeft, textTop);
The option I used to workaround this problem was:
Graphics graphics = new Graphics();
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
There are some others useful options in TextRenderingHint
Hope it helps
There is a very simple answer to this...
g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit
If you set this before you render your text, it will come out clear. In addition, this methods supports more font sizes (The default only goes up to size 56).
Thanks for reading this post.
The first output is what you get when you draw black text on a black background, probably Color.Transparent. The 2nd was drawn on an almost-black background. The 3rd was drawn on the same background it is being displayed with.
Anti-aliasing cannot work when on a transparent background. The colors used for the anti-aliasing pixels will not blend the letter shape into the background when the text is displayed with a different background. Those pixels will now become very noticeable and make the text look very bad.
Note that SmoothingMode doesn't affect text output. It will look slightly less bad if you use a lower quality TextRenderingHint and a background color that's grayish with a alpha of zero. Only TextRenderingHint.SingleBitPerPixelGridFit avoids all anti-aliasing troubles.
Getting a perfect fix for this is very difficult. Vista's glass effect on the window title bar uses very subtle shading to give the text a well defined background color. You'd need SysInternals' ZoomIt tool to really see it. DrawThemeTextEx() function with a non-zero iGlowSize.
If you're looking for something that preserves antialiasing a bit better than GDI+ does by default, you can call Graphics.Clear with a chroma key, then manually remove the chroma artifacts that result. (See Why does DrawString look so crappy? and Ugly looking text problem.)
Here's how I ultimately ended up solving a similar problem:
static Bitmap TextToBitmap(string text, Font font, Color foregroundColor)
{
SizeF textSize;
using ( var g = Graphics.FromHwndInternal(IntPtr.Zero) )
textSize = g.MeasureString(text, font);
var image = new Bitmap((int)Math.Ceiling(textSize.Width), (int)Math.Ceiling(textSize.Height));
var brush = new SolidBrush(foregroundColor);
using ( var g = Graphics.FromImage(image) )
{
g.Clear(Color.Magenta);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawString(text, font, brush, 0, 0);
g.Flush();
}
image.MakeTransparent(Color.Magenta);
// The image now has a transparent background, but around each letter are antialiasing artifacts still keyed to magenta. We need to remove those.
RemoveChroma(image, foregroundColor, Color.Magenta);
return image;
}
static unsafe void RemoveChroma(Bitmap image, Color foregroundColor, Color chroma)
{
if (image == null) throw new ArgumentNullException("image");
BitmapData data = null;
try
{
data = image.LockBits(new Rectangle(Point.Empty, image.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
for ( int y = data.Height - 1; y >= 0; --y )
{
int* row = (int*)(data.Scan0 + (y * data.Stride));
for ( int x = data.Width - 1; x >= 0; --x )
{
if ( row[x] == 0 ) continue;
Color pixel = Color.FromArgb(row[x]);
if ( (pixel != foregroundColor) &&
((pixel.B >= foregroundColor.B) && (pixel.B <= chroma.B)) &&
((pixel.G >= foregroundColor.G) && (pixel.G <= chroma.G)) &&
((pixel.R >= foregroundColor.R) && (pixel.R <= chroma.R)) )
{
row[x] = Color.FromArgb(
255 - ((int)
((Math.Abs(pixel.B - foregroundColor.B) +
Math.Abs(pixel.G - foregroundColor.G) +
Math.Abs(pixel.R - foregroundColor.R)) / 3)),
foregroundColor).ToArgb();
}
}
}
}
finally
{
if (data != null) image.UnlockBits(data);
}
}
It's a shame GDI/GDI+ doesn't do this already, but that would be sensible, wouldn't it? :)
If you aren't able to use an unsafe context, you could easily use the same logic with Bitmap.GetPixel and Bitmap.SetPixel, though it will be significantly slower.

Categories

Resources