BitmapSource.Create switches RGB values - c#

I am loading pixels from a file onto an array of bytes using this code:
Bitmap bmp = new Bitmap(filename);
var rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr ptr = bmpData.Scan0;
int numBytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[numBytes];
Marshal.Copy(ptr, rgbValues, 0, numBytes);
I am pretty sure that this is not the problem.
After I have loaded the file, I want to display manipulate the colors and then display in a WPF window.
So I create a bitmap source with the following line:
BitmapSource img = BitmapSource.Create(width, height, 96, 96, PixelFormats.Rgb24, null, pixels, stride);
The problem is that the red bytes are switched with the green bytes.
It is similar to this - Why color changes when save a BitmapSource to Bitmap for PixelFormat.Format48bppRgb? -
but i don't know how to fix it.

Then you may use PixelFormats.Bgr24 instead of PixelFormats.Rgb24 to create the BitmapSource.
var img = BitmapSource.Create(
width, height, 96, 96, PixelFormats.Bgr24, null, pixels, stride);

Related

Display RAW RGB byte array in PictureBox in WinForms app

I have an .rgb file that I attached. It is a valid rgb file that I verified through other software. As far as I understand this is a raw RGB data file.
I need to load the file into byte[] and display it in PictureBox.
From all my searches here I came to this solution that seems correct and that works for others. Unfortunately it displays nothing for me:
string imgPath = Path.Combine(mInstallPath, "squirrel-720x576x50.rgb\\squirrel-720x576x50.rgb");
byte[] imageData = File.ReadAllBytes(imgPath);
using (var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
Marshal.Copy(imageData, 0, bmpData.Scan0, width * height * 3);//imageData.Length);
//bmpSource.CopyPixels((Int32Rect.Empty, bmpData.Scan0, bmpData.Height * bmpData.Stride, bmpData.Stride);
bmp.UnlockBits(bmpData);
pictureBox.Image = bmp;
}
Any ideas why nothing is displayed in my pictureBox ?

Can you do edge detection of a DepthFrame in Kinect SDK (WPF, C#)?

I've processed my depth frame to byte array which is a particular color for a user and black otherwise. This byte array (depthColorImage) is converted to a Bitmap using the following code:
public static Bitmap ToBitmap(this byte[] data, int width, int height, PixelFormat format)
{
var bitmap = new Bitmap(width, height, format);
var bitmapData = bitmap.LockBits(
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly,
bitmap.PixelFormat);
Marshal.Copy(data, 0, bitmapData.Scan0, data.Length);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
I've then tried to apply single matrix convolution using the code found here.
I convert the resultant bitmap back to bitmapsource using the code:
public static Media.Imaging.BitmapSource ToBitmapSource(this Bitmap bitmap)
{
if (bitmap == null) return null;
IntPtr ptr = bitmap.GetHbitmap();
var source =System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
ptr,
IntPtr.Zero,
Int32Rect.Empty,
Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
DeleteObject(ptr);
return source;
}
Finally I try to display this by using:
this.depthImage.Source = depthColorImage.ToBitmap(depthFrame.Width, depthFrame.Height,PixelFormats.Bgr32).ConvolutionFilter().ToBitmapSource();
But my Window just hangs. If I take out the convolution filter it works just fine. I'm a beginner to C# and I'm looking into openCV but I was wondering if there is something wrong with my code or is it something else all together?

Bitmap array format in C#

I have following code to create bitmap using array with data*
//Here create the Bitmap to the know height, width and format
Bitmap bmp = new Bitmap( 5,7,PixelFormat.Format1bppIndexed);
//Create a BitmapData and Lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
//Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
//Unlock the pixels
bmp.UnlockBits(bmpData);
bmp.Save("BitMapIcon.bmp",ImageFormat.Bmp);
My input array (data ) :
byte[5] data = {0xff,0xff,0xff,0xff,0xff}
Question :
Can array of data* for bitmap input have only valu of each pixel (in this
format it is 0 and 1 ) ?
Why passing array of 0xff value some of
pixel are not black ?
Can width size be less than 1 byte ?
You have two issues:
First, the default palette for the indexed format uses 0 for black and 1 for white. So your code is actually trying to initialize a white bitmap, not a black one
Second, and more to the point of your question, is that you are not fully initializing the bitmap. This relates to your third question: the width of the bitmap is allowed to have less than 1 byte's worth of pixels, but the bitmap data itself could require more than that for a single scan line of the bitmap.
Indeed, due to the alignment requirements of a bitmap, your bitmap's "stride" is 4 bytes. So you need 28 bytes total in order to fully initialize the bitmap.
This code will initialize the bitmap the way you want:
//Here create the Bitmap to the know height, width and format
Bitmap bmp = new Bitmap(5, 7, PixelFormat.Format1bppIndexed);
//Create a BitmapData and Lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
//Copy the data from the byte array into BitmapData.Scan0
byte[] data = new byte[bmpData.Stride * bmpData.Height];
for (int i = 0; i < data.Length; i++)
{
data[i] = 0xff;
}
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
//Unlock the pixels
bmp.UnlockBits(bmpData);
Use 0x00 instead of 0xff if you actually wanted black pixels.

Bitmap Resize, only crops the image and no resizing

I have this code to resize a bitmap, but all it does is to crop it instead of resizing, what I am doing wrong?
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
//a holder for the result
Bitmap result = new Bitmap(width, height);
// set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
and I call the function like this
bmPhoto = Imaging.ImageProcessing.ResizeImage(bmPhoto, scaledSize.Width, scaledSize.Height);
// Keeping Aspect Ratio
Image resizeImg(Image img, int width)
{
double targetHeight = Convert.ToDouble(width) / (img.Width / img.Height);
Bitmap bmp = new Bitmap(width, (int)targetHeight);
Graphics grp = Graphics.FromImage(bmp);
grp.DrawImage(img, new Rectangle(0, 0, bmp.Width, bmp.Height), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
return (Image)bmp;
}
// Without Keeping Aspect Ratio
Image resizeImg(Image img, int width, int height)
{
Bitmap bmp = new Bitmap(width, height);
Graphics grp = Graphics.FromImage(bmp);
grp.DrawImage(img, new Rectangle(0, 0, bmp.Width, bmp.Height), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
return (Image)bmp;
}
Try using a Rectangle object to specify the portion of the new image that you want to fill, like so:
graphics.DrawImage(image, new Rectangle(0, 0, result.Width, result.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, null);
As noted the Rectangle specifies that the image should be drawn between the top left and bottom right corner, and then you provide the coordinates of the original image that you want to scale into that area (0,0,image.Width,image.Height).

C# convert stride/buffer/width/height to bitmap

I have an image width/height/stride and buffer.
How do I convert this information to a System.Drawing.Bitmap? Can I get the original image back if I have these 4 things?
There is a Bitmap constructor overload, which requires everything you have (plus PixelFormat):
public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0);
This might work (if args.Buffer is an array of blittable type, like byte for example):
Bitmap bitmap;
var gch = System.Runtime.InteropServices.GCHandle.Alloc(args.Buffer, GCHandleType.Pinned);
try
{
bitmap = new Bitmap(
args.Width, args.Height, args.Stride,
System.Drawing.Imaging.PixelFormat.Format24bppRgb,
gch.AddrOfPinnedObject());
}
finally
{
gch.Free();
}
Update:
Probably it's better to copy image bytes to newly created Bitmap manually, because it seems like that constructors doesn't do that, and if byte[] array of image data gets garbage collected all sorts of bad things can happen.
var bitmap = new Bitmap(args.Width, args.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
var data = bitmap.LockBits(
new Rectangle(0, 0, args.Width, args.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
if(data.Stride == args.Stride)
{
Marshal.Copy(args.Buffer, 0, data.Scan0, args.Stride * args.Height);
}
else
{
int arrayOffset = 0;
int imageOffset = 0;
for(int y = 0; y < args.Height; ++y)
{
Marshal.Copy(args.Buffer, arrayOffset, (IntPtr)(((long)data.Scan0) + imageOffset), data.Stride);
arrayOffset += args.Stride;
imageOffset += data.Stride;
}
}
bitmap.UnlockBits(data);
This should work if you have the buffer as byte[], a width and the height + the pixelformat (stride)
public Bitmap CreateBitmapFromRawDataBuffer(int width, int height, PixelFormat imagePixelFormat, byte[] buffer)
{
Size imageSize = new Size(width, height);
Bitmap bitmap = new Bitmap(imageSize.Width, imageSize.Height, imagePixelFormat);
Rectangle wholeBitmap = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
// Lock all bitmap's pixels.
BitmapData bitmapData = bitmap.LockBits(wholeBitmap, ImageLockMode.WriteOnly, imagePixelFormat);
// Copy the buffer into bitmapData.
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, bitmapData.Scan0, buffer.Length);
// Unlock all bitmap's pixels.
bitmap.UnlockBits(bitmapData);
return bitmap;
}

Categories