Greyscale darkening problem with Bitmap image in C# - c#

I'm loading a 8bppIndexed greyscale image into memory and reading the pixel values. The problem is that the values I am getting from the pixels do not seem to match the actual image, they are always darker. My image is a simple grey gradient like this:
The bottom right pixel is returning 191 and the top left 0. The top left is actually 64 and bottom right is 255.
Here is how I am loading my image:
Bitmap threshImg = new Bitmap(#"C:\grey.bmp");
Checking the PixelFormat confirms it is in Format8bppIndexed. So I read the bottom right pixel and top left like so:
BitmapData data = threshImg.LockBits(new Rectangle(0, 0, rectWidth, rectHeight), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
unsafe
{
byte* pixel = (byte*)data.Scan0.ToPointer();
int topVal = (int)(byte)pixel[0];
int bottomVal = (int)(byte)pixel[((threshImg.Height * threshImg.Width)) - 1];
}
threshImg.UnlockBits(data);
If I convert the image to 24bppRbg (and adjust the code accordingly) I see the correct colour values in the respective corners.
Anyone know why I'm getting darker values when using an 8bppIndexed image?

The value in the 8bpp indexed image isn't the color itself (or gray value) but the index. Try to look up the color value in the palette.

With an indexed image there are only a certain number of colours (or in this case shades of grey) available - usually 256. It's probable that there aren't enough to get the full range of shades in the original image.
As having the exact shades is important I'd shift to a 24bpp image.

Related

C# WinForms | Picturebox white bleeding right & bottom edges

Hello Stackoverflow Community.
I currently work on an application which has a cursor region magnifyer feature, for the user to pick a color.
However, i've the problem that the Picturebox has white edges on the right and bottom, even though the image captured is only one dark color.
The captured screen is 10x10 pixel and the Picturebox is 80x80 pixel and it's SizeMode set to StretchImage.
I checked if the raw captured image maybe contains this edges already by saving it and checking it in Photoshop. But the raw captured image is fine, so it must be something weird with the Picturebox.
Here you can see how it looks (The mousecursor and the capture-region[green rectangle] are just painted on the screenshot for demonstration, as i was not able to screen my cursor, and yes - the green region is painted way too large, it should be just 10x10 pixel ^^)
Has it maybe something to do with the way the Image get's stretched internally? If so, is there any, not too complicated way around it?
I appreciate any help in advance :)
//Edit: I think it's actually about the stretching
I found this topic Image after resize has white border but i don't know what the GetSize() Method is, or where it is (from)
Have a great day!
I found a solution for the edges here, as you can see in the first screen. However, in the second screen you can see a 2x2 pixel capture, stretched to 80x80 pixel with the found "solution", will mix colors as it's actually 80x80 pixel in the end, while i want to have it displaying the raw pixels, so, 3 black ones and one white one in this case. Now i am even more stuck ^^
I guess i should instead read the pixel colors of the single pixels from captured image and set the colors in the preview picturebox, or make 4 panels in the case of just 2x2 pixels
public new Image Resize(Image image, int targetWidth, int targetHeight)
{
var resizedImage = new Bitmap(targetWidth, targetHeight);
using (var graphics = Graphics.FromImage(resizedImage))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
var attributes = new ImageAttributes();
attributes.SetWrapMode(WrapMode.TileFlipXY);
var destination = new Rectangle(0, 0, targetWidth, targetHeight);
graphics.DrawImage(image, destination, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
}
return resizedImage;
}
//Edit:
Okay i think i'm gonna use Get/SetPixel or Panels(for small captures), as i did here on the right, the left is still the 80x80 Picturebox

Application C# image quality [duplicate]

This question already exists:
Application about C# image quality [closed]
Closed 2 years ago.
I am developing an application with C#. In the application I developed, I take the picture file with the user selection and transfer the picture onto the form.
After this step, I want to make changes to the bitmap of the picture. For example resizing the image in small sizes and converting to 1bpp color format.
I can do these now, I can resize the image and convert it to 1bpp color format, but at this point I think I have quality problems.
For example, when I take a screenshot of a text and send it to the program, I see that the letters in the text are unclearly bad when I view it in the resized 1bpp color format.
I show the algorithms I used and the screenshot of the application:
Bitmap bmp = new Bitmap(ResizeImage.filePath);
Bitmap bmpOriginalRGB = Helper.ImageResize(bmp, 512, 384);
pcbox1.Image = bmpOriginalRGB;
Bitmap bmpResizeRGB = Helper.ImageResize(bmp, ResizeWidth, ResizeHeight);
pcbox2.Image = bmpResizeRGB;
Bitmap bmpResize1BPP_1 = Helper.ConvertTo1BppImage(Helper.ImageResize(bmp, ResizeWidth, ResizeHeight));
pcbox3.Image = bmpResize1BPP_1;
Bitmap bmpResize1BPP_2 = bmpResizeRGB.Clone(new Rectangle(0, 0, ResizeWidth, ResizeHeight), PixelFormat.Format1bppIndexed);
pcbox4.Image = bmpResize1BPP_2;
Bitmap oledBitmap = bmpResize1BPP_2;
and
public static Bitmap ImageResize(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;
}
Here's a first screenshoot
Here's a second screenshoot
Here's a third screenshoot
For example, as in the second screen image, the shadow of the woman appears as black. And I don't want that. I want to see a clearer black and white image.
Do you think it is possible to further improve this picture quality?
Edit: Sorry for translate..
I don't believe your problem is the quality on resizing, it's rather the conversion from greyscale to black and white.
When resizing, the color of the adjacent pixels are averaged together to get a new color approximating the set of pixels. So going from a 200x200 pixel image to a 100x100 pixel image, a set of 4 pixels becomes a single pixel. It will still look fairly good, but the solid black text will become a series of gray pixels. The exact formula will vary by the interpolation method used. But when resizing a nice piece of black and white text, the text will end up lighter or more grey (which will be important later)
However, when going from greyscale (or full color) to black and white each pixel ends up being either black and white, there are no other options, but there are different algorithms used to decide which pixels end up black or white, often called dithering. (https://en.wikipedia.org/wiki/Dither)
Your first b/w image appears to use some form of error diffusion, quite possibly Floyd-Steinberg. It tends to work fairly well on real wold images and turns grey areas into spaced out black pixels visually approximating the greyness of the area they fall in.
Your second b/w image appears to be a simple threshold algorithm. Basically pixels darker than a certain color end up black, and all the rest end up white. You can adjust the image by simply setting which color is used as the threshold. Often this works well with text, but you will need to adjust the value used as the threshold, but I don't know if the libraries you are using allow for this or not. I have found what works well for programmatically selecting the threshold is to total up how many pixels there are of each color, and then assume some percent will be black (text tends to be mostly white space), then choose the threshold that gives you that number of black pixels.
And there are many other dithering algorithms that you can try, as well as edge detection algorithms. You can also try adjusting the contrast of the image before converting to b/w.
However, at the end of the day, when resolution is low (pixels per character), it may not be possible to easily convert them to b/w and have them still be readable (just try to fax small text on a fax machine in standard mode). Remember your resize removed a lot of information and the conversion from 8 bit to 1 bit removed another 87% of the information in the resized picture.

Converting 8bpp Indexed Bitmap to 24bpp and back again with the same custom color palette

I'm making a tool that does some graphical editing to some bitmaps. I have some 8bppIndexed bitmaps with an existing color palette that I have to convert to 24bppRgb to allow me to draw on it and edit colors of pixels based on some options the user selected. Bitmap graphic is the original 8bpp .bmp file:
//Get Bitmap from file
Bitmap graphic = new Bitmap(source);
//Get palette and pixel format
ColorPalette pal = graphic.Palette;
PixelFormat pix = graphic.PixelFormat; //Format8bppIndexed
//Create datagraphic in 24bppRgb to allow for editing
PixelFormat datapix = PixelFormat.Format24bppRgb;
Bitmap datagraphic = graphic.Clone(new Rectangle(0, 0, graphic.Width, graphic.Height), datapix);
After drawing and editing Bitmap datagraphic, I'm trying to convert it back to 8bppIndexed with the same color palette as Bitmap graphic. I can do that like this:
//Convert back to graphic pixel format
datagraphic = datagraphic.Clone(new Rectangle(0, 0, datagraphic.Width, datagraphic.Height), pix); //Format8bppIndexed
//Apply color palette
datagraphic.Palette = pal;
However, after cloning datagraphic with the same pixel format as graphic, it creates an entirely new color palette. The colors in datagraphic are then all incorrect after applying ColorPalette pal. They only match by their index number between the two palettes. Is there another way of doing this that preserves the colors?
And yes, the bitmaps need to be 8bppIndexed with the custom palette. I'm trying to avoid the need to go through Photoshop to change the color index of all the end result data graphics with the correct palette.
I should say that I'm still fairly new to C#. I hope I was clear as to what I'm trying to do. I appreciate any help here. Thanks in advance.
Converting an 8-bit image to 24bpp is trivial: you just paint it on a new image of the same dimensions and the preferred pixel format.
public static Bitmap PaintOn24bpp(Image image)
{
Bitmap bp = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(bp))
gr.DrawImage(image, new Rectangle(0, 0, bp.Width, bp.Height));
return bp;
}
This works with any source image no matter its pixel format, though for images containing transparency you may want to preprocess them to fill any transparent areas, because I don't know if painting with transparency taken into account will work if the image you're drawing on doesn't support alpha.
The reverse, to convert a high-colour image to indexed, is a lot trickier, but if you have the palette, it is certainly possible. The main problem in this case is that using Graphics doesn't work, since indexed graphics don't have coloured pixels; they have a coloured palette, and pixels that just reference those colours.
Editing 8-bit data generally boils down to building a byte array containing the image data, and converting that to an image with the palette slapped onto it. The actual conversion is done by making a new 8bpp Bitmap, opening its backing byte array with the LockBits function, and copying your new data into it with Marshal.Copy.
After that, the final missing link is a method to match your image's coloured pixels to their closest palette match, so you can then take the array of resulting matches and bake it into an 8bpp image. This is generally done by calculating the Pythagorean distance (in 3D RGB colour space) of your colour to each of the palette colours, and then taking the index of the colour for which that distance is the smallest.
The whole process, from start to end, is explained in detail in this answer:
A: How to convert a colored image to a image that has only two predefined colors?
This deals with 2 colours, but the method for dealing with 2 or with 256 colours is completely identical, since the end result in both cases was an 8bpp image.
It could be optimised for dealing with 24bpp images directly instead of converting the input to 32bpp, but that's probably not worth the effort, especially since the method posted there works with absolutely any input.

C# resize indexed colour bitmap and maintain colours

I have a Bitmap which is in 256-colour indexed format, I need to resize it so I create a new Bitmap in 24-bit RGB format and draw it using a Graphics object as I cannot create a graphics object from an indexed colour bitmap. I then need to save the resized image back as an indexed colour format so I use FormatConvertedBitmap to convert to indexed colour like this:
BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
FormatConvertedBitmap converted = new FormatConvertedBitmap();
converted.BeginInit();
converted.Source = bitmapSource;
converted.DestinationFormat = System.Windows.Media.PixelFormats.Indexed8;
converted.DestinationPalette = new BitmapPalette(bitmapSource, 256);
converted.EndInit();
This works but the solid colours are now grainy and contain other colour pixels. Is there a better method to either resize an indexed colour image or to maintain the solid colours?
Yes, this is pretty inevitable. The core property you want to control to influence this is Graphics.InterpolationMode. A high quality selection like Bicubic will add a lot of colors to the resized image. Trying to whack it back to 256 colors is going to produce a grainy image. You could use NearestNeighbor instead to reduce the number of added colors but you'll end up with a blocky image.
You could try the GIF encoder, it is forced to resample the image back to 256 colors as well. It uses a dithering algorithm. But tends to produce flecky artifacts that become noticeable when the image has large areas of similar colors, like a blue sky. There is no magical cure beyond simply not resampling back. There is just no point in doing that with the wonderful hardware we have these days.

Need to know the difference between the Bitmap's pixel format Format32bppArgb and Format24bppRgb

I have an issue with .net controls while saving them as images. when i export the Panel control as image with bitmap's Pixel format 'Format24bppRgb' then a Gray border is shown in the image but it is not shown when i export it using the pixelformat 'Format32bppArgb'. I dont know why the gray border is visible in the image. Can any one help me on this?
Here is the code which is used to export image:
using (Bitmap bmp = new Bitmap((int)panel1.Width, (int)panel1.Height,PixelFormat.Format24bppRgb))
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
using (Brush brushFill = diagram1.Model.BackgroundStyle.CreateBrush(g, rect))
{
g.FillRectangle(brushFill, Geometry.ConvertRectangle(rect));
}
bmp.Save(#"..//..//bitmap.png", ImageFormat.Png);
}
}
Here is the image i have created with format Format24bppRgb
Here is the image i have created with format Format32bppArgb
Thanks is advance........................
Format24bppRgb is just the Red, Green and Blue values of each pixel. At 8 bits per colour you get 24 bits per pixel.
Format32bppArgb include an Alpha (or transparency) value for each pixel. This is an extra 8 bits per pixel so you get a total of 32 bits per pixel.
There's a border on the second image as well - it's seems to be a dotted and a lot fainter than on the first. This must be an artefact of the saving process and must be fainter in the second case because of the alpha channel.
Format24bppRgb is Red, Green and Blue values of each pixel. At 8 bits per color (0 ~ 255)
Format32bppArgb is Alpha, Red, Green and Blue values of each pixel. At 8 bits per color (0 ~ 255). Alpha is for transparent effect. You can set Graphics.CompositingMode = CompositingMode.SourceOver; to get the transparent effect, show as the following pic:

Categories