converting bitmap to 8bpp grayscale outputs as 8bpp coloured index in c# - c#

I'm trying to convert bitmap to 8bpp grayscale with the code below
private Bitmap ConvertPixelformat(ref Bitmap Bmp)
{
Bitmap myBitmap = new Bitmap(Bmp);
// Clone a portion of the Bitmap object.
Rectangle cloneRect = new Rectangle(0, 0, Bmp.Width, Bmp.Height);
PixelFormat format = PixelFormat.Format8bppIndexed;
Bitmap cloneBitmap = myBitmap.Clone(cloneRect, format);
var pal = cloneBitmap.Palette;
for (i = 0; i < cloneBitmap.Palette.Entries.Length; ++i)
{
var entry = cloneBitmap.Palette.Entries[i];
var gray = (int)(0.30 * entry.R + 0.59 * entry.G + 0.11 * entry.B);
pal.Entries[i] = Color.FromArgb(gray, gray, gray);
}
cloneBitmap.Palette = pal;
cloneBitmap.SetResolution(500.0F, 500.0F);
return cloneBitmap;
}
Checking the properties of the bitmap image shows that the bit depth is correctly set to 8bpp but not as a grayscale rather it is a coloured index 8bpp. Kindly guide on what to do.

Examine the following code:
public static unsafe Bitmap ToGrayscale(Bitmap colorBitmap)
{
int Width = colorBitmap.Width;
int Height = colorBitmap.Height;
Bitmap grayscaleBitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
grayscaleBitmap.SetResolution(colorBitmap.HorizontalResolution,
colorBitmap.VerticalResolution);
///////////////////////////////////////
// Set grayscale palette
///////////////////////////////////////
ColorPalette colorPalette = grayscaleBitmap.Palette;
for (int i = 0; i < colorPalette.Entries.Length; i++)
{
colorPalette.Entries[i] = Color.FromArgb(i, i, i);
}
grayscaleBitmap.Palette = colorPalette;
///////////////////////////////////////
// Set grayscale palette
///////////////////////////////////////
BitmapData bitmapData = grayscaleBitmap.LockBits(
new Rectangle(Point.Empty, grayscaleBitmap.Size),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
Byte* pPixel = (Byte*)bitmapData.Scan0;
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
Color clr = colorBitmap.GetPixel(x, y);
Byte byPixel = (byte)((30 * clr.R + 59 * clr.G + 11 * clr.B) / 100);
pPixel[x] = byPixel;
}
pPixel += bitmapData.Stride;
}
grayscaleBitmap.UnlockBits(bitmapData);
return grayscaleBitmap;
}
this code converts a color image to a grayscale image.

Related

How to add the different pixels to a new empty bitmap and how to set the different pixels to any chosen color?

When using DiffSetPixel it's adding the different pixels to the new bitmap diffBM when i'm using the second method DiffLockBits it's adding the different pixels to the existing bitmap.
what i want to do is in the first method the DiffSetPixel to be able to add the different pixels to existing bitmap any existing bitmap and not only to the diffBM and in the DiffLockBits i want to add it also to the diffBM.
and in both methods i want to be able to change the different pixels colors for example to red or green or any color and not only grayscale.
DiffSetPixel method
public static Bitmap DiffSetPixel(Bitmap src1, Bitmap src2, int x1, int y1, int x2, int y2, int width, int height)
{
Bitmap diffBM = new Bitmap(width, height, PixelFormat.Format24bppRgb);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
//Get Both Colours at the pixel point
Color col1 = src1.GetPixel(x1 + x, y1 + y);
Color col2 = src2.GetPixel(x2 + x, y2 + y);
// Get the difference RGB
int r = 0, g = 0, b = 0;
r = Math.Abs(col1.R - col2.R);
g = Math.Abs(col1.G - col2.G);
b = Math.Abs(col1.B - col2.B);
// Invert the difference average
int dif = 255 - ((r + g + b) / 3);
// Create new grayscale RGB colour
Color newcol = Color.FromArgb(dif,dif,dif);
diffBM.SetPixel(x, y, newcol);
}
}
return diffBM;
}
DiffLockBits method
public static Bitmap DiffLockBits(Bitmap bmp1 , Bitmap bmp2)
{
Bitmap diffBM = new Bitmap(bmp1.Width, bmp1.Height, PixelFormat.Format24bppRgb);
//Lock bitmap's bits to system memory
Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
BitmapData bmpData = bmp1.LockBits(rect, ImageLockMode.ReadWrite, bmp1.PixelFormat);
Rectangle rect1 = new Rectangle(0, 0, bmp2.Width, bmp2.Height);
BitmapData bmpData1 = bmp2.LockBits(rect1, ImageLockMode.ReadWrite, bmp2.PixelFormat);
//Scan for the first line
IntPtr ptr = bmpData.Scan0;
IntPtr ptr1 = bmpData1.Scan0;
//Declare an array in which your RGB values will be stored
int bytes = Math.Abs(bmpData.Stride) * bmp1.Height;
byte[] rgbValues = new byte[bytes];
int bytes1 = Math.Abs(bmpData1.Stride) * bmp2.Height;
byte[] rgbValues1 = new byte[bytes1];
//Copy RGB values in that array
Marshal.Copy(ptr, rgbValues, 0, bytes);
Marshal.Copy(ptr1, rgbValues1, 0, bytes1);
for (int i = 0; i < rgbValues.Length; i += 3)
{
if (rgbValues[i] != rgbValues1[i])
{
//Set RGB values in a Array where all RGB values are stored
byte gray = (byte)(rgbValues[i] * .21 + rgbValues[i + 1] * .71 + rgbValues[i + 2] * .071);
rgbValues[i] = rgbValues[i + 1] = rgbValues[i + 2] = gray;
}
}
//Copy changed RGB values back to bitmap
Marshal.Copy(rgbValues, 0, ptr, bytes);
//Unlock the bits
bmp1.UnlockBits(bmpData);
bmp2.UnlockBits(bmpData1);
return bmp1;
}

How to change value of red/green/blue/alpha values from pixels

I'm working on a processing image software and now i want to change white balance and add some saturation on my photos, so i have to work with each pixel value.
I've tried to transform my BitmapImage to WritableBitmap and then I've tried this code. But if i run it, my image turns to green.
I want to take pixels from that image and then set them to new values.
I've searched many solutions but found none, so maybe someone can help me with my code. Thanks.
public static WriteableBitmap Colors(Bitmap Image)
{
int width = Image.Width;
int height = Image.Width;
byte[,,] pixels = new byte[height, width, 4];
WriteableBitmap wbitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
var target = new Bitmap(Image.Width, Image.Height);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
pixels[i, j, 1] -= 30;
pixels[i, j, 3] -= 30;
}
}
byte[] pixels1d = new byte[height * width * 4];
int index = 0;
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
for (int i = 0; i < 4; i++)
pixels1d[index++] = pixels[row, col, i];
}
}
Int32Rect rect = new Int32Rect(0, 0, width, height);
int stride = 4 * width;
wbitmap.WritePixels(rect, pixels1d, stride, 0);
return wbitmap;
}
And then I'm running it like this:
private void run_OnClick(object sender, RoutedEventArgs e)
{
// Contrastt(BitmapImage2Bitmap(bitmap), 10);
// MedianFilter(BitmapImage2Bitmap(bitmap),5);
WriteableBitmap writeableBitmap = new WriteableBitmap(imageX);
BitmapImage colored = ConvertWriteableBitmapToBitmapImage(Colors((BitmapImage2Bitmap(bitmap))));
ImageBrush brush = new ImageBrush();
brush.ImageSource = imageX ;
CanvasProcessedImage.Background = brush;
}
Assuming that you already have a BitmapSource (or an instance of a derived class like BitmapImage) in a source variable, you would first create a BitmapSource with a known pixel format, and then create a WritabelBitmap from that:
// source is a BitmapSource with whatever PixelFormat
var format = PixelFormats.Bgra32;
var temp = new FormatConvertedBitmap(source, format, null, 0); // force BGRA
var bitmap = new WriteableBitmap(temp);
Now you could either directly manipulate the data in the WriteableBitmap's BackBuffer property, or first do it a bit simpler and create a copy of the buffer like this:
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var stride = bitmap.BackBufferStride;
var buffer = new byte[stride * height];
bitmap.CopyPixels(buffer, stride, 0);
Then manipulate the buffer and write it back to the WriteableBitmap. Since you have specified the PixelFormat, you know which byte of each 4-byte pixel value is B, G, R and A respectively.
for (int i = 0; i < buffer.Length; i += 4)
{
// Blue
//buffer[i + 0] = ...
// Green
buffer[i + 1] = (byte)Math.Max(buffer[i + 1] - 30, 0);
// Red
buffer[i + 2] = (byte)Math.Max(buffer[i + 2] - 30, 0);
// Alpha
//buffer[i + 3] = ...
}
bitmap.WritePixels(new Int32Rect(0, 0, width, height), buffer, stride, 0);
When you have assigned the WriteableBitmap to the Source property of an Image element, or the ImageSource property of an ImageBrush, you can call WritePixels later (and as often as necessary) without needing to reassign it it to the Source/ImageSource property. It is changed "in place".

coverting grey8 (8bit) image into a rgb888(24bit) image

my problem is when is take a 8bit greyscaled image and convert it into an 24bit rgb24bpp my image gets zoomed in and the colorscale get mixed up( see 3).
My plan is to display a GigE-Cam with my program through a client-server implementation. Therfore i need it the picture to get grayscaled because of daterate issues, but i only get this wrongly sized and collored picture. This happens only if i covert them into the myImage/myImageFormat. My problem is that i need the picture for measurements afterwards.
The code i use to convert the image is as follows:
public myImage(Bitmap bmp, myImageFormat format)
{
if (bmp == null)
{
throw new ArgumentNullException("bmp");
}
this.Height = bmp.Height;
this.Width = bmp.Width;
this.Format = format;
// copy bmp matrix without padding to rgb or grey array
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
//if (format == myImageFormat.Grey8)
//{ return; }
//else
//{
int factor = 3;
if (format == myImageFormat.Grey8)
{
factor = 1;
// save grey scale palette
m_colorPaletteColorData = new Color[bmp.Palette.Entries.Length];
for (int i = 0; i < bmp.Palette.Entries.Length; i++)
{
m_colorPaletteColorData[i] = bmp.Palette.Entries[i];
}
//return;
}
else if (format == myImageFormat.Rgb888
&& bmp.PixelFormat == PixelFormat.Format8bppIndexed)
{
// convert all type of bitmaps to 24-Bit bitmaps by using GDI+
Bitmap bmp24 = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb);
bmp24.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution); // prevent resolution conversion
using (Graphics g = Graphics.FromImage(bmp24))
{
g.PageUnit = GraphicsUnit.Pixel; // prevent DPI conversion
g.DrawImageUnscaled(bmp, 0, 0, bmp.Width, bmp.Height);
}
bmp = bmp24;
}
BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat); // LOCKED
int bytes = bmp.Height * bmp.Width * factor;
int byteIndex = 0;
m_imageData = new byte[bytes];
unsafe
{
for (int y = 0; y < bmpData.Height; y++)
{
byte* row = (byte*)bmpData.Scan0 + (y * bmpData.Stride);
for (int x = 0; x < bmpData.Width; x++)
{
m_imageData[byteIndex] = row[x * factor];
byteIndex++;
if (format == myImageFormat.Rgb888)
{
m_imageData[byteIndex] = row[x * factor + 1];
byteIndex++;
m_imageData[byteIndex] = row[x * factor + 2];
byteIndex++;
}
}
}
}
bmp.UnlockBits(bmpData); // UNLOCKED
//}
}
/// <summary>
/// returns a 8bppGrey or 24bppRgb Bitmap
/// </summary>
public Bitmap ToBitmap()
{
if (m_imageData == null)
{
throw new InvalidOperationException("Internal image data does not exist!");
}
PixelFormat pixelFormat;
int factor = 3;
if (this.Format == myImageFormat.Grey8)
{
pixelFormat = PixelFormat.Format8bppIndexed;
factor = 1;
}
else
{
pixelFormat = PixelFormat.Format24bppRgb;
}
m_bmp = new Bitmap(this.Width, this.Height, pixelFormat);
Rectangle rect = new Rectangle(0, 0, this.Width, this.Height);
BitmapData bmpData = m_bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly, pixelFormat); // LOCKED
int counter = 0;
unsafe
{
for (int y = 0; y < bmpData.Height; y++)
{
byte* row = (byte*)bmpData.Scan0 + (y * bmpData.Stride);
for (int x = 0; x < bmpData.Width; x++)
{
byte color = m_imageData[counter];
row[x * factor] = color;
counter++;
if (this.Format == myImageFormat.Rgb888)
{
row[x * factor + 1] = m_imageData[counter];
counter++;
row[x * factor + 2] = m_imageData[counter];
counter++;
}
}
}
}
// copy original grayscale color palette, otherwise it will be a 8bit RGB bitmap
if (this.Format == myImageFormat.Grey8)
{
ColorPalette palette = m_bmp.Palette;
Color[] entries = palette.Entries;
if (m_colorPaletteColorData.Length >= entries.Length)
{
for (int i = 0; i < entries.Length; i++)
{
entries[i] = m_colorPaletteColorData[i];
}
}
m_bmp.Palette = palette;
}
m_bmp.UnlockBits(bmpData); // UNLOCKED
return m_bmp;
}
Can anyone help me on this problem?
The Grey8 image converted into an RGB24
If you pass in a 24-bit bitmap to be converted to 8-bit gray, you won't have a valid source palette to copy, since 24-bit formats aren't indexed. So, when you save the palette into m_colorPaletteColorData[] from the 24-bit source image (in your constructor) and then set it on the 8-bit gray image (in ToBitmap()), what does that palette actually contain? In the constructor, when your source is 24-bit (and therefore has no valid palette), you need to create a new gray-scale palette rather than try to copy it out of a non-indexed image.
Another lesser point, when converting in both directions (8 to 24, 24 to 8), you assign the bytes from one image to the other directly; so, if one is intended to be a palettized 8-bit image, and the other is a 24-bit image, you are assigning a palette index as each of the R, G, and B values of the color component, or assigning one color component as the index.
This should work (sort of) if-and-only-if the palette is guaranteed to be a linear gradient where each index matches the shade of gray-scale, but palette-based images rarely use a perfectly-organized palette, so this is a dangerous assumption. To make this work properly, you should really translate the 8-bit index values into the Color values from your palette before assigning to a 24-bit image, and look up the closest match in your color palette from the 24-bit value when assigning to an 8-bit gray image.

C# fast img merging

Today I wanted to try sth new in image processing in C#.
Szenario as following: I have two black and white images. Now I want to get all white pixels (x,y) from both images and put them into a return image. So in the end my image3 contains all white pixels from image1 and image2.
I'm using unsafe pointers as they are faster.
So in the code I check if image1 in (x,y) == image2 in (x,y) as it is very unlikely that both pictures have a white pixel at the same spot
My approach right now:
private unsafe static Bitmap combine_img(Bitmap img1, Bitmap img2)
{
Bitmap retBitmap = img1;
int width = img1.Width;
int height = img1.Height;
BitmapData image1 = retBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData image2 = img2.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); // here an error
byte* scan1 = (byte*)image1.Scan0.ToPointer();
int stride1 = image1.Stride;
byte* scan2 = (byte*)image2.Scan0.ToPointer();
int stride2 = image2.Stride;
for (int y = 0; y < height; y++)
{
byte* row1 = scan1 + (y * stride1);
byte* row2 = scan2 + (y * stride2);
for (int x = 0; x < width; x++)
{
if (row1[x] == row2[x])
row1[x] = 255;
}
}
img1.UnlockBits(image1);
img2.UnlockBits(image2);
return retBitmap;
}
unfortunately it returns an error when trying to lock the second image, saying the image has already been locked!
The problem was, that strangely I did pass the same image, here the corrected Code:
private unsafe static void combine_img(Bitmap img1, Bitmap img2)
{
BitmapData image1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData image2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int bytesPerPixel = 3;
byte* scan1 = (byte*)image1.Scan0.ToPointer();
int stride1 = image1.Stride;
byte* scan2 = (byte*)image2.Scan0.ToPointer();
int stride2 = image2.Stride;
for (int y = 0; y < img1.Height; y++)
{
byte* row1 = scan1 + (y * stride1);
byte* row2 = scan2 + (y * stride2);
for (int x = 0; x < img1.Width; x++)
{
if (row2[x * bytesPerPixel] == 255)
row1[x * bytesPerPixel] = row1[x * bytesPerPixel - 1] = row1[x * bytesPerPixel-2] = 255;
}
}
img1.UnlockBits(image1);
img2.UnlockBits(image2);
}
I guess your second image is not 24 bit image. Mayber try something like:
BitmapData image2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), ImageLockMode.ReadWrite, img2.PixelFormat);
In this case you will always pass this line (I assume), but problem is you wont know you are actually dealing with 24 bits image or 32 bits image.

Reducing Bitmap bit-size in C#

I am using C#, and having an image stored in the object Bitmap.
Now I would like to convert this image into 8bit greyscale, then into a 4-bit greyscale image.
Do you have any tips how this can be made?
In the .NET Bitmap formats, there are no such thing as a 8 or 4 bit grayscale image. The supported formats are enumerated by the PixelFormat enumeration. You can, however, create a 4 or 8 bit image by creating a indexed image (8bppIndexed or 4bppIndexed), where each entry in the palette is a greyscale value.
This code takes a Bitmap and creates a copy as a 8bpp indexed image with greyscale values:
public static Bitmap BitmapToGrayscale(Bitmap source)
{
// Create target image.
int width = source.Width;
int height = source.Height;
Bitmap target = new Bitmap(width,height,PixelFormat.Format8bppIndexed);
// Set the palette to discrete shades of gray
ColorPalette palette = target.Palette;
for(int i = 0 ; i < palette.Entries.Length ; i++)
{
palette.Entries[i] = Color.FromArgb(0,i,i,i);
}
target.Palette = palette;
// Lock bits so we have direct access to bitmap data
BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
unsafe
{
for(int r = 0 ; r < height ; r++)
{
byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
for(int c = 0 ; c < width ; c++)
{
byte colorIndex = (byte) (((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11));
*pTarget = colorIndex;
pTarget++;
pSource += 3;
}
}
}
target.UnlockBits(targetData);
source.UnlockBits(sourceData);
return target;
}
In order to make a 4Bpp image instead, you would need to create the target with PixelFormat.Format4bppIndexed, and then set the ColorPalette to 16 discrete shades of gray. Finally, in the loop you should normalize values 2 be between 0-15 and pack each 2 pixel values into a single byte.
This is the modified code to make a 4bpp greyscale image:
public static Bitmap BitmapToGrayscale4bpp(Bitmap source)
{
// Create target image.
int width = source.Width;
int height = source.Height;
Bitmap target = new Bitmap(width,height,PixelFormat.Format4bppIndexed);
// Set the palette to discrete shades of gray
ColorPalette palette = target.Palette;
for(int i = 0 ; i < palette.Entries.Length ; i++)
{
int cval = 17*i;
palette.Entries[i] = Color.FromArgb(0,cval,cval,cval);
}
target.Palette = palette;
// Lock bits so we have direct access to bitmap data
BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
ImageLockMode.ReadWrite, PixelFormat.Format4bppIndexed);
BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
unsafe
{
for(int r = 0 ; r < height ; r++)
{
byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
byte prevValue = 0;
for(int c = 0 ; c < width ; c++)
{
byte colorIndex = (byte) ((((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11)) / 16);
if (c % 2 == 0)
prevValue = colorIndex;
else
*(pTarget++) = (byte)(prevValue | colorIndex << 4);
pSource += 3;
}
}
}
target.UnlockBits(targetData);
source.UnlockBits(sourceData);
return target;
}

Categories

Resources