Image Ushort array to bitmap format8bppindexed - c#

I have as input a Ushort array of image data. Other inputs are gathered here, such as the 'Width, Height'. The ushort array also carries a Min and Max values, that I want to use, those are stored in 'io_current'.
I want to return a Format8ppIndexed Bitmap, and I have this code but what am I doing wrong?:
private Bitmap CreateBitmap(ushort[,] pixels16)
{
int width = pixels16.GetLength(1);
int height = pixels16.GetLength(0);
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, width, height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
// This 'unsafe' part of the code populates the bitmap bmp with data stored in pixel16.
// It does so using pointers, and therefore the need for 'unsafe'.
unsafe
{
//int pixelSize = 4;
int i, j; //, j1; //, i1;
byte b;
ushort sVal;
double lPixval;
//The array has max and min constraints
int distance = io_current.MaximumValue - io_current.MinimumValue;
for (i = 0; i < bmd.Height; ++i)
{
byte* row = (byte*)bmd.Scan0 + (i * bmd.Stride);
//i1 = i * bmd.Height;
for (j = 0; j < bmd.Width; ++j)
{
sVal = (ushort)(pixels16[i, j]);
lPixval = ((sVal - io_current.MinimumValue) * 255) / distance; // Convert to a 255 value range
//lPixval = ((sVal - io_current.MinimumValue) / distance) * 255;
//lPixval = 255 - lPixval; //invert the value
if (lPixval > 255) lPixval = 255;
if (lPixval < 0) lPixval = 0;
b = (byte)(lPixval);
//j1 = j * pixelSize; //Pixelsize is one
row[j] = b; // Just one in 8bpp
//Not necessary for format8bppindexed
//row[j1] = b; // Red
//row[j1 + 1] = b; // Green
//row[j1 + 2] = b; // Blue
//row[j1 + 3] = 255; //No Alpha channel in 24bit
}
}
}
bmp.UnlockBits(bmd);
return bmp;
}
I'm getting a Black screen, or a one color screen. Basically no usable data is returned. Obviously from the comments. I tried to convert this from 24bit bitmap code and thought it would be easy.

Related

RenderTargetBitmap with format rgba32

I am not very familiar with bitmaps and I need to save a FrameworkElement (specificaly Grid) as bitmap and copy it to buffer. The problem is I need to save it in Rgba format, not Pgrba, which isn't supported in RenderTargetBitmap. Relevant code is here:
_targetBitmap = new RenderTargetBitmap(xres, yres, 96, 96, PixelFormats.Pbgra32);
_targetBitmap.Clear();
// Child is grid
_targetBitmap.Render(Child);
// copy the pixels into the buffer
_targetBitmap.CopyPixels(new Int32Rect(0, 0, xres, yres), bufferPtr, _bufferSize, _stride);
I tried using WriteableBitmap, but I didn't how to render the Child. Any suggestions?
The CopyPixels function is already giving you direct access to the pixel data, so all you need to do is convert between formats. In this case, you need to swap the channel orders around and undo the premultiplication of alpha values.
NOTE: This code assumes your bufferPtr is a byte array or a byte pointer.
for (int y = 0; y < yres; y++)
{
for (int x = 0; x < xres; x++)
{
// Calculate array offset for this pixel
int offset = y * _stride + x * 4;
// Extract individual color channels from pixel value
int pb = bufferPtr[offset];
int pg = bufferPtr[offset + 1];
int pr = bufferPtr[offset + 2];
int alpha = bufferPtr[offset + 3];
// Remove premultiplication
int r = 0, g = 0, b = 0;
if (alpha > 0)
{
r = pr * 255 / alpha;
g = pg * 255 / alpha;
b = pb * 255 / alpha;
}
// Write color channels in desired order
bufferPtr[offset] = (byte)r;
bufferPtr[offset + 1] = (byte)g;
bufferPtr[offset + 2] = (byte)b;
bufferPtr[offset + 3] = (byte)alpha;
}
}

Extracting RGB from a pixel without color object in C#

I am trying to extract R G B values from a pixel in the following code:
for ( int i=0; i < pixeldata.length; i++)
{
IntPtr ptr = bmd.Scan0+i;
byte* pixel = (byte*)ptr;
//here is the problem :O
float r = pixel[1];
float g = pixel[2];
float b = pixel[3];
}
....
where bmd is an array of pixels data:
BitmapData bmd = source.LockBits(rect, ImageLockMode.ReadOnly, source.PixelFormat);
and source is the Bitmap of my input, which is an image.
I am trying to avoid the use of Color object. I have already done that and it works, I want to use this other way, but the issue is that ptr is a number and I have to extract the R G B from it.
This is the solution that gives you the right answer.
Bitmap source = new Bitmap(image);
Rectangle rect = new Rectangle(0, 0, source.Width, source.Height);
BitmapData bmd = source.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int totalPixels = rect.Height * rect.Width;
int[] pixelData = new int[totalPixels];
for (int i = 0; i < totalPixels; i++)
{
byte* pixel = (byte*)bmd.Scan0;
pixel = pixel + (i * 4);
byte b = pixel[0];
byte g = pixel[1];
byte r = pixel[2];
int luma = (int)(r * 0.3 + g * 0.59 + b * 0.11);
pixelData[i] = luma;
}
If you have a format that stores R, G, and B as one byte each linearly in memory in that order, the code to extract the RGB values should look like
byte r = pixel[0];
byte g = pixel[1];
byte b = pixel[2];
Note that the index offset begins at 0, and that the values returned are byte not float (though you can certainly cast if you wish).
Additionally you would have to increment i by 3 rather than 1 because 3 adjacent bytes represent a single pixel.
You would be wise to test that source.PixelFormat indeed uses the format you are assuming.
You also have to compile with the /unsafe switch in order to use pointers in C#.
UPDATE
Per #Don's comment as well as your own, the order in linear memory would be ABGR. That means the code would be:
for ( int i=0; i < pixeldata.length; i+=4)
{
IntPtr ptr = bmd.Scan0+i;
byte* pixel = (byte*)ptr;
byte a = pixel[0]; // You can ignore if you do not need alpha.
byte b = pixel[1];
byte g = pixel[2];
byte r = pixel[3];
}
Ok, this was interesting, and I have written some code to play with. Assuming that your image has pixels in format Format24bppRgb (more info about formats here: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat.aspx). This format store B, G, R values in 24 bits one after another.
Below code which will parse some d:\\24bits.bmp image from your hard drive and creates new identical one "d:\\24bits_1.bmp" using information B, G, R information from bytes array of first image data.
unsafe private static void TestBMP()
{
Bitmap bmp = new Bitmap("d:\\24bits.bmp");
// Ensure that format is Format24bppRgb.
Console.WriteLine(bmp.PixelFormat);
Bitmap copyBmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// Copy all pixels of initial image for verification.
int pixels = bmp.Height * bmp.Width;
Color[,] allPixels = new Color[bmp.Height, bmp.Width];
for (int i = 0; i < bmp.Height; i++)
for (int j = 0; j < bmp.Width; j++)
allPixels[i, j] = bmp.GetPixel(j, i);
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
byte* stream = (byte*)ptr;
for (int y = 0; y < bmp.Height; y++)
for (int x = 0; x < bmp.Width; x++)
{
int byteIndex = y * bmpData.Stride + x * 3;
byte r = stream[byteIndex + 2];
byte g = stream[byteIndex + 1];
byte b = stream[byteIndex];
Color c = allPixels[y, x];
if (r != c.R || g != c.G || b != c.B)
{
Console.WriteLine("This should never appear");
}
copyBmp.SetPixel(x, y, Color.FromArgb(255, r, g, b));
}
// Save new image. It should be the same as initial one.
copyBmp.Save("d:\\24bits_1.bmp");
}

Create Bitmap from double two dimentional array

I've a two-dimensional double[,] rawImage array representing a gray level image with each element in the array has a rational value from 0 ~ 1 , and I need
to convert it to Bitmap image, I've used the following code:
private Bitmap ToBitmap(double[,] rawImage)
{
int width = rawImage.GetLength(1);
int height = rawImage.GetLength(0);
Bitmap Image= new Bitmap(width, height);
for (int i = 0; i < height; i++)
for (int j = 0; j < YSize; j++)
{
double color = rawImage[j, i];
int rgb = color * 255;
Image.SetPixel(i, j, rgb , rgb , rgb);
}
return Image;
}
but it seems to be so slow.
I don't know if there is a way to do the above work using pointers of short data type.
How can I write a faster code using pointers to handle this function ?
This should be enough for you. The example is written according to this source code.
private unsafe Bitmap ToBitmap(double[,] rawImage)
{
int width = rawImage.GetLength(1);
int height = rawImage.GetLength(0);
Bitmap Image = new Bitmap(width, height);
BitmapData bitmapData = Image.LockBits(
new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb
);
ColorARGB* startingPosition = (ColorARGB*) bitmapData.Scan0;
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
{
double color = rawImage[i, j];
byte rgb = (byte)(color * 255);
ColorARGB* position = startingPosition + j + i * width;
position->A = 255;
position->R = rgb;
position->G = rgb;
position->B = rgb;
}
Image.UnlockBits(bitmapData);
return Image;
}
public struct ColorARGB
{
public byte B;
public byte G;
public byte R;
public byte A;
public ColorARGB(Color color)
{
A = color.A;
R = color.R;
G = color.G;
B = color.B;
}
public ColorARGB(byte a, byte r, byte g, byte b)
{
A = a;
R = r;
G = g;
B = b;
}
public Color ToColor()
{
return Color.FromArgb(A, R, G, B);
}
}

Using LockBits generates weird image

I'm trying to write out a grayscale image using Lockbits, my current code looks is
/// <summary>
/// Save the content of the FrameProc out to a bitmap
/// </summary>
public void Save(string path)
{
Bitmap bmp = new Bitmap(this.size.Width, this.size.Height
,PixelFormat.Format32bppRgb);
var data = bmp.LockBits(this.size, ImageLockMode.WriteOnly, bmp.PixelFormat);
unsafe
{
for (int y = 0; y < this.size.Height; y++)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
for (int x = 0; x < this.size.Width; x++)
{
byte value = (byte)this.buffer[y, x];
row[x*Bits+r] = value;
row[x*Bits+g] = value;
row[x*Bits+b] = value;
}
}
}
bmp.UnlockBits(data);
bmp.Save(path, ImageFormat.Bmp);
}
where
/// <summary>
/// The amount of Bytes per pixel in the image
/// </summary>
private const int Bits = 4;
/// <summary>
/// Image components
/// </summary>
private const int a=3, r = 2, g = 1, b = 0;
However the image i receive is not correct:
Maybe this is related to how i'm reading them in? So here's that code
public FrameProc(Bitmap bmp)
{
this.size=new Rectangle(new Point(0,0), bmp.Size);
var data = bmp.LockBits(this.size
,ImageLockMode.ReadOnly
,bmp.PixelFormat);
this.buffer = new Matrix(this.size.Height, this.size.Width);
unsafe
{
for (int y = 0; y < this.size.Height; y++)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
for (int x = 0; x < this.size.Width; x++)
{
this.buffer[y,x] = 0.299*row[x*Bytes+r]
+ 0.587*row[x*Bytes+g]
+ 0.114*row[x*Bytes+b];
}
}
}
bmp.UnlockBits(data);
}
From the results you're getting - it looks exactly as if each pixel is three bytes big and not four as you have declared it - and as one would expect. (Note: you called it Bits - but that's wrong - it should be namned Bytes, not Bits).
I'd experiment with any one of this:
change from 4 to 3 bytes
change from Format32bppRgb to Format32bppArgb and fill out the alpha with 255
change from 4 to 3 bytes and from Format32bppRgb to from Format24bppRgb
I would also rewrite the loop slightly for performance (sorry, I can't help myself):
for (int x = 0; x < this.size.Width; x++, row += Bits)
{
byte value = (byte)this.buffer[y, x];
row[r] = value;
row[g] = value;
row[b] = value;
}
But were you really would get more speed if you get a pointer to this.buffer using the fixed keyword. Yes, you're not having any performance problems, but I couldn't help myself from mentioning it!
Use this function indeed:
public Bitmap MakeGrayscale(Bitmap original)
{
unsafe
{
//create an empty bitmap the same size as original
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
//lock the original bitmap in memory
BitmapData originalData = original.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
//lock the new bitmap in memory
BitmapData newData = newBitmap.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
//set the number of bytes per pixel
// here is set to 3 because I use an Image with 24bpp
int pixelSize = 3;
for (int y = 0; y < original.Height; y++)
{
//get the data from the original image
byte* oRow = (byte*)originalData.Scan0 + (y * originalData.Stride);
//get the data from the new image
byte* nRow = (byte*)newData.Scan0 + (y * newData.Stride);
for (int x = 0; x < original.Width; x++)
{
//create the grayscale version
byte grayScale =
(byte)((oRow[x * pixelSize] * .11) + //B
(oRow[x * pixelSize + 1] * .59) + //G
(oRow[x * pixelSize + 2] * .3)); //R
//set the new image's pixel to the grayscale version
nRow[x * pixelSize] = grayScale; //B
nRow[x * pixelSize + 1] = grayScale; //G
nRow[x * pixelSize + 2] = grayScale; //R
}
}
//unlock the bitmaps
newBitmap.UnlockBits(newData);
original.UnlockBits(originalData);
return newBitmap;
}
}
Source and other interesting examples (with theory behind) could be taken from here

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