C# wrong color preview image - c#

Im new in building apps with C#. Im working with images and doing some analysis with them. After clicking on image i get RGB value of current pixel. All values are next stored in listview with preview image. And there is my problem. The preview image is not the same, as RGB value of the current pixel. If i did this same with pictureboxes, then the colors were correct. But i dont know how to implement pictureboxes into listView or listBox.
There is my source code, which create bitmap for my color preview .
private Image createImage(Color col)
{
PixelFormat px_format = PixelFormat.Format32bppRgb;
int pixelFormatSize = Image.GetPixelFormatSize(px_format) / 8;
int stride = 16 * pixelFormatSize;
int padding = (stride % 4);
stride += padding == 0 ? 0 : 4 - padding; //pad out to multiple of 4
SharedPinnedByteArray byteArray = new SharedPinnedByteArray(stride * 16);
Bitmap bmp = new Bitmap(16, 16, stride, px_format, byteArray.bitPtr);
Graphics gpx = Graphics.FromImage(bmp);
SolidBrush brush = new SolidBrush(col);
gpx.FillRectangle(brush, 0, 0, 16, 16);
gpx.Dispose();
return bmp;
}
And here is how im calling function and adding items to listView
listView1.SmallImageList.Images.Add(createImage(color));
listView1.Items.Add(color.R.ToString() + "," + color.G.ToString() + "," + color.B.ToString() + ",", listView1.SmallImageList.Images.Count - 1);
Best regards
Gabriel

Since the default list view color depth is 8 bit, you need to change it to 24 or 32 bit:
listView1.SmallImageList.ColorDepth = ColorDepth.Depth24Bit;

Related

Bitmap.GetPixel only returning 0s for R, G and B

I am loading an image inside a Console application using Image.FromFile.
After that, I am casting it to a Bitmap to be able to use the Bitmap.GetPixel() method.
Surprisingly, while looping through all the pixels, all what I am getting from the GetPixel is 0,0,0 for R,G,B.
To make sure that the image is well read from the file, I added a reference to System.Windows.Forms and I loaded the image inside a PictureBox to see the result, and the image is well seen.
Original Image:
Here's how I am loading the image and showing it into a PictureBox:
Bitmap img = (Bitmap)Image.FromFile("image.png");
PictureBox pb = new PictureBox
{
Image = img
};
Form frm = new Form
{
Controls = { pb }
};
frm.ShowDialog();
Which shows the image as it is:
And after that, I am getting the pixels like:
byte[] input = new byte[784];
for (int x = 0; x < 28; x++)
{
for (int y = 0; y < 28; y++)
{
Color color = img.GetPixel(x, y);
byte r = color.R;
byte g = color.G;
byte b = color.B;
Console.Write($"{color.R},{color.G},{color.B}|||");
input[x * 28 + y] = (byte)((((r + g + b) / 3)));
}
Console.WriteLine();
}
Note that the image size is 28x28px, and I have tried other images and I've got the same result.
What I expected that the results shows the real color values since I've used that method before and it worked perfectly, but now all the results printed to the console are zeros which I am finding it difficult to understand.
Edit:
Since the PictureBox is showing the real image representation, I tried to get the pixels from the PictureBox.Image like:
Color color = ((Bitmap)pb.Image).GetPixel(x, y);
This also didn't work and the results came back as zeros.
What could be the reason behind this and how to fix it?
In your image all color channels of all pixels are 0, so you code actually works fine.
And most of the pixels also have an alpha of 0, so they are transparent. Some are semi-transparent, anti-aliased pixels and a few are fully opaque.
Where transparent or semi-transprent pixels are, the backcolor will shine through.
If you want to recognize the really, non-transparent black pixels you need to test the alpha channel as well.
Here is a function that will create a composite color of a pixel's color and a given background color..:
Color Composite(Color color, Color backcolor)
{
byte r = (byte)(((255 - color.A) * backcolor.R + color.R * color.A) / 256);
byte g = (byte)(((255 - color.A) * backcolor.G + color.G * color.A) / 256);
byte b = (byte)(((255 - color.A) * backcolor.B + color.B * color.A) / 256);
Color c2 = Color.FromArgb(255, r, g, b);
return c2;
}
If you want the brightness of one of your pixels you could create a composite of its color with white. But, given the image you have, you could just as well use color.A directly..

Unsafe C# bitmap floodfill

**
How to make 'GetPixel2' work for finding the color at a point
**
So I have a bitmap with lots of single colored shapes.
I have a list of x,y points for those shapes. Then, a second list
with the expected color at those points.
Finally have an algorithm using bitmap.Getpixel and SetPixel working.
Which was definitely slow.
http://csharpexamples.com/fast-image-processing-c/
Suggests using direct memory access to solve this. I'd like to use their sample without looping through the entire image, and hit a single x,y point.
Bitmap bmp2 = (Bitmap)Bitmap.FromFile(Environment.CurrentDirectory + #"\Content\map\provinces.bmp");
BitmapData bitmapData = bmp2.LockBits(new System.Drawing.Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadWrite, bmp2.PixelFormat);
int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(bmp2.PixelFormat) / 8;
int heightInPixels = bitmapData.Height;
int widthInBytes = bitmapData.Width * bytesPerPixel;
System.Drawing.Point pt = new System.Drawing.Point((int)provpos2[0].X, (int)provpos2[0].Y);
System.Drawing.Color targetColor = System.Drawing.Color.FromArgb(255, provcolors[0].R, provcolors[0].G, provcolors[0].B);
if (!ColorMatch(GetPixel2(pt.X, pt.Y, bytesPerPixel, bitmapData), targetColor)){
// This hits the completely wrong area.
}
public System.Drawing.Color GetPixel2(int x, int y, int bytesPerPixel, BitmapData bitmapData)
{
unsafe
{
byte* ptrFirstPixel = (byte*)bitmapData.Scan0;
byte* currentLine = ptrFirstPixel + (y * bitmapData.Stride);
x = x + bytesPerPixel;
System.Drawing.Color a = System.Drawing.Color.FromArgb(255, currentLine[x + 2], currentLine[x + 1], currentLine[x]);
return a;
}
}
public static bool ColorMatch(System.Drawing.Color a,System.Drawing.Color b)
{
return (a.ToArgb() & 0xffffff) == (b.ToArgb() & 0xffffff);
}
bytesPerPixel comes out at 3. Tried changing it to 4 just hits another undesired location on the bitmap.
It seems to hit around 1023x,351y instead of the desired 3084x,319y on a 5632x2048 bitmap.
Not entirely sure why it doesnt workout fo you, but keep in mind this:
Bits per pixel comes from colour format used there are a few formats some are handier then others, and sometimes you need to convert them to a strict RGB format. ea 8 bits per colour channel, there also exists RGBA, and there is RGB in bitwise 565 notation as used in some camera's, and there is 24bits per colour. Some formats are not supported in winforms, but are supported in wpf based applications, like 16bit gray formats. (since wpf is more new age like design friendly)
maybe try this it works great for me:
http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp?msg=5136670
if its 565 maybe do something like
private Bitmap Convert565bppTo24bpp(Bitmap ConvertMe)
{
Bitmap clone = new Bitmap(ConvertMe.Width, ConvertMe.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);//.Format32bppPArgb);
using (Graphics gr = Graphics.FromImage(clone))
{ gr.DrawImage(ConvertMe, new Rectangle(0, 0, clone.Width, clone.Height)); }
return clone;
}

WritableBitmap.WritePixels showing in wrong location

Here is a code snippet where I attempt to put a blue dot icon (Bgr32; 169x169) on a graphic (Bgra32 3985x3443) in a WPF Image control.
string s = new FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\lg_dot.jpg";
BitmapImage icon = new BitmapImage(new Uri(s));
var baseLayer = img_BackgroundLayer.Source as BitmapImage;
WriteableBitmap composite = new WriteableBitmap(baseLayer);
int bytesPerPixel = (int)(icon.Format.BitsPerPixel + 7) / 8;
int stride = (int)(bytesPerPixel * icon.Width);
int size = stride * (int)icon.Height;
byte[] pixels = new byte[size];
icon.CopyPixels(pixels, stride, 0);
composite.WritePixels(new Int32Rect(0, 0, (int)icon.Width, (int)icon.Height), pixels, stride, 1000, 1000);
Notice that I am placing the dot (icon) at (1000, 1000) on the image. When viewed however, the upper left of the dot is not at (1000, 1000) it is at about (760,760). If I try to place the icon at (2000,2000) it appears at about (1520,1520). The place the icon appears is always approx (0.76 * X, 0.76 * Y) where X and Y are the target coordinates.
I think I know what is happening here... something to do with putting a Bgr32 image onto a Bgra32 background... but I am not clever enough to see how to solve it.
Bytes per pixel formula :
(bits per pixel + 7) / 8
Stride formula :
bytes per pixel * image width
Notice bytes and bits and use integers for calculation !
So for a 1024 pixels wide image :
Bgr32
bytes per pixel : (24 + 7) / 8 = 3
stride : 3 * 1024 = 3072
Bgra32
bytes per pixel : (32 + 7) / 8 = 4
stride : 4 * 1024 = 4096
By looking at your code :
the stride should be 3, not 4 :D
you are needlessly using stride for an int[], remember sizeof(int) == 4
byte offset of the pixel in your image array of byte[] : stride * y + x * bytes per pixel
int offset of the pixel in your image array of int[] : y * width + x
By the way, did you check that your Image.Stretch is set to None ?
Finally, beware of the components order :
BGRA is represented as ARGB (you might see nothing if A is 0 and you think you've set B)
Last tip : https://writeablebitmapex.codeplex.com/ might be easier for you in the end.
EDIT
This is really bad, in my case it is null as BitmapFrameDecode cannot be casted to BitmapImage, it's better to keep a reference to the source instead :
var baseLayer = Image1.Source as BitmapImage;
The formulas I gave you are correct, I only had to change destX and destY as my background was not as large as yours.
var icon = new BitmapImage(new Uri("..\\..\\avatar92.jpg", UriKind.Relative));
var background = new BitmapImage(new Uri("..\\..\\canary_1024_600_a_0.jpg", UriKind.Relative));
var writeableBitmap = new WriteableBitmap(background);
int bytesPerPixel = (icon.Format.BitsPerPixel + 7) / 8;
int stride = bytesPerPixel * icon.PixelWidth;
int size = stride * icon.PixelHeight;
var pixels = new byte[size];
icon.CopyPixels(pixels, stride, 0);
writeableBitmap.WritePixels(new Int32Rect(0, 0, icon.PixelWidth, icon.PixelHeight), pixels, stride, 500, 500);
Image1.Source = writeableBitmap;
Again,
Is Image.Stretch == None ?
Are the image dimensions correct ? they should be I guess :D
Do bitmaps have the same format ? Not that it won't fail if they are different but you'll get all sort of funny surprises, try using a .GIF and a .JPEG file for instance.
I really suggest you to use WriteableBitmapEx instead, you can draw pixels, lines, rectangles, shapes etc ...
Here's the same result with WriteableBitmapEx without having to deal with all the details such as stride, array, bpp, etc ...
var icon = new BitmapImage(new Uri("..\\..\\avatar92.jpg", UriKind.Relative));
var background = new BitmapImage(new Uri("..\\..\\canary_1024_600_a_0.jpg", UriKind.Relative));
var img1 = BitmapFactory.ConvertToPbgra32Format(icon);
var img2 = BitmapFactory.ConvertToPbgra32Format(background);
var img1Size = new Size(img1.PixelWidth, img1.PixelHeight);
img2.Blit(new Rect(new Point(500, 500), img1Size), img1, new Rect(img1Size));
Image1.Source = img2;
If you prefer the hard way then make sure your bitmaps have the same format : FormatConvertedBitmap
If you still cannot get it working, post a Short, Self Contained, Correct (Compilable), Example and links to the two images so people can really help you. I found nothing on your code so I guess there's something not right but as you haven't pasted all the code we can't really tell.

convert 8 bit color bmp image to 8 bit grayscale bmp

This is my bitmap object
Bitmap b = new Bitmap(columns, rows, PixelFormat.Format8bppIndexed);
BitmapData bmd = b.LockBits(new Rectangle(0, 0, columns, rows), ImageLockMode.ReadWrite, b.PixelFormat);
How do i convert this into a 8 bit grayscale bitmap ?
Yes, no need to change the pixels, just the palette is fine. ColorPalette is a flaky type, this sample code worked well:
var bmp = Image.FromFile("c:/temp/8bpp.bmp");
if (bmp.PixelFormat != System.Drawing.Imaging.PixelFormat.Format8bppIndexed) throw new InvalidOperationException();
var newPalette = bmp.Palette;
for (int index = 0; index < bmp.Palette.Entries.Length; ++index) {
var entry = bmp.Palette.Entries[index];
var gray = (int)(0.30 * entry.R + 0.59 * entry.G + 0.11 * entry.B);
newPalette.Entries[index] = Color.FromArgb(gray, gray, gray);
}
bmp.Palette = newPalette; // Yes, assignment to self is intended
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
pictureBox1.Image = bmp;
I don't actually recommend you use this code, indexed pixel formats are a pita to deal with. You'll find a fast and more general color-to-grayscale conversion in this answer.
Something like:
Bitmap b = new Bitmap(columns, rows, PixelFormat.Format8bppIndexed);
for (int i = 0; i < columns; i++)
{
for (int x = 0; x < rows; x++)
{
Color oc = b.GetPixel(i, x);
int grayScale = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
Color nc = Color.FromArgb(oc.A, grayScale, grayScale, grayScale);
b.SetPixel(i, x, nc);
}
}
BitmapData bmd = b.LockBits(new Rectangle(0, 0, columns, rows), ImageLockMode.ReadWrite, b.PixelFormat);
hi you could change the color palette to a grayscale one
although the following code is in Vb.net. You could easily convert it to C#
Private Function GetGrayScalePalette() As ColorPalette
Dim bmp As Bitmap = New Bitmap(1, 1, Imaging.PixelFormat.Format8bppIndexed)
Dim monoPalette As ColorPalette = bmp.Palette
Dim entries() As Color = monoPalette.Entries
Dim i As Integer
For i = 0 To 256 - 1 Step i + 1
entries(i) = Color.FromArgb(i, i, i)
Next
Return monoPalette
End Function
Original Source -> http://social.msdn.microsoft.com/Forums/en-us/vblanguage/thread/500f7827-06cf-4646-a4a1-e075c16bbb38
Note that if you want to do the same conversion as modern HDTVs, you'll want to use the Rec. 709 coefficients for the conversion. The ones provided above (.3, .59, .11) are (almost) the Rec. 601 (standard def) coefficients. The Rec. 709 coefficients are gray = 0.2126 R' + 0.7152 G' + 0.0722 B', where R', G', and B' are the gamma adjusted red, green, and blue components.
Check this link out. We did this at university and it works.
It is all you need with input and output.

Using Alpha in image manipulation

I'm trying to get an image to display with one of the colors replaced with a white alpha so that I can layer it on top of other images. I've got it so that I can change colors easily enough, but changing it to be transparent is eluding me. Here's my code, using C# and WPF.
private void SetAlpha(string location)
{
//bmp is a bitmap source that I load from an image
bmp = new BitmapImage(new Uri(location));
int[] pixels = new int[(int)bmp.Width * (int)bmp.Height];
//still not sure what 'stride' is. Got this part from a tutorial
int stride = (bmp.PixelWidth * bmp.Format.BitsPerPixel + 7)/8;
bmp.CopyPixels(pixels, stride, 0);
int oldColor = pixels[0];
int red = 255;
int green = 255;
int blue = 255;
int alpha = 0;
int color = (alpha << 24) + (red << 16) + (green << 8) + blue;
for (int i = 0; i < (int)bmp.Width * (int)bmp.Height; i++)
{
if (pixels[i] == oldColor)
{
pixels[i] = color;
}
}
//remake the bitmap source with these pixels
bmp = BitmapSource.Create(bmp.PixelWidth, bmp.PixelHeight, bmp.DpiX, bmp.DpiY, bmp.Format, bmp.Palette, pixels, stride);
}
}
I've got two images that I'm testing this with. Image1 is like what I am going to be working on, no transparency in the original image. Image2 already has transparency. I thought it would be easy to just grab the value from image2 (0x00ffffff) but that just makes it white and covers up any images behind.
Both images are png, and the format for both is Bgr32.
Does anyone know how to get the image to be transparent?
How about using Bgra32?
Also make sure you understand how the color is represented in memory and what alpha means.

Categories