Difference between bitmap and bitmapdata - c#
What is the difference between System.Drawing.bitmap and System.Drawing.Imaging.bitmapdata in C#?
How to convert them to each other?
System.Drawing.Bitmap is an actual bitmap object. You can use it to draw to using a Graphics instance obtained from it, you can display it on the screen, you can save the data to a file, etc.
The System.Drawing.Imaging.BitmapData class is a helper object used when calling the Bitmap.LockBits() method. It contains information about the locked bitmap, which you can use to inspect the pixel data within the bitmap.
You can't really "convert" between the two per se, as they don't represent the same information. You can obtain a BitmapData object from a Bitmap object simply by calling LockBits(). If you have a BitmapData object from some other Bitmap object, you can copy that data to a new Bitmap object by allocating one with the same format as the original, calling LockBits on that one too, and then just copying the bytes from one to the other.
Convert bitmap to bitmap data. Also refer this link
Private void LockUnlockBitsExample(PaintEventArgs e) {
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// 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.ReadWrite, bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3) rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}
Related
Access Violation Exception saving Bitmap
I am receiving the following exception: Exception thrown: 'System.AccessViolationException' in System.Drawing.dll When calling the Save function of a Bitmap. The procedure works fine the first time around, but subsequent calls throw this exception. My application takes a single long image and vertically tiles it out into several separate images. I do this by first breaking out the whole image into bytes, then in a Parallel.For loop I generate the bitmap from a subset byte array. // Generate Bitmap from width, height and bytes private Bitmap GenerateBitmap(int width, int height, byte[] bytes) { Bitmap bmp = new Bitmap(width, height, Stride(width), PixelFormat.Format8bppIndexed, Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0)); bmp.SetPalette(); return bmp; } That is the bitmap generation routine. Here is the loop body that calls it. Parallel.For(0, tileCount, i => { byte[] bytes = new byte[imageWidth * tileHeight]; for (int j = 0; j < bytes.Length; j++) { bytes[j] = imageBytes[j + (imageWidth * (tileHeight * i))]; } arr[i] = GenerateBitmap(imageWidth, tileHeight, bytes); }); And here is the code elsewhere that the exception is thrown. foreach(Bitmap tile in pattern.Tiles) { Console.WriteLine("Creating Tile " + count); using (Bitmap bmp = new Bitmap(tile)) { bmp.Save(Globals.patternOutputPath + "tile_" + count + ".png"); } count += 1; } Where Tiles is a property of the pattern that calls the for loop function (which returns a list of Bitmaps). I am assuming I'm missing some clean up somewhere in here. Additional info: all images (input and output) are 256 (index) color format. Edit: The comments below address the problem at hand, and I THINK I've solved the problem. I changed the GenerateBitmap routine to the following and am no longer getting this exception, but I have some more testing to do. private Bitmap GenerateBitmap(int width, int height, byte[] bytes) { Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed); BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); Marshal.Copy(bytes, 0, bmpData.Scan0, bytes.Length); bmp.UnlockBits(bmpData); return bmp; /*Bitmap bmp = new Bitmap(width, height, Stride(width), PixelFormat.Format8bppIndexed, Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0)); bmp.SetPalette(); return bmp;*/ }
Reading an pixel array to Bitmap returns black image
I have an pixel array which i want to open as a Bitmap. The method i use is the following: http://www.tek-tips.com/viewthread.cfm?qid=1264492: /// <summary> /// function CopyDataToBitmap /// Purpose: Given the pixel data return a bitmap of size [352,288],PixelFormat=24RGB /// </summary> /// <param name="data">Byte array with pixel data</param> public Bitmap CopyDataToBitmap(byte[] data) { //Here create the Bitmap to the know height, width and format Bitmap bmp = new Bitmap( 352, 288, PixelFormat.Format24bppRgb); //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); //Return the bitmap return bmp; } I dont know much about the array, other than the image was black/white. The problem might be in the line: PixelFormat.Format24bppRgb. How can I tell the format without trying them all ? Array looks like this: "0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,255,255,255,0,255,255,0,0,0,0,0,0,0,255,255,255,255,255,255,0,255,255,255,0,0,0,0,255,255,255,255,255,0,0,0,0,0,255,255,0,255,255,255,255,255,255,0,0,0,0,0,0,0,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" I am saving the image to disk like this: var img = CopyDataToBitmap(result.Pixels); img.Save("img.jpg", ImageFormat.Jpeg);
Reading QR Code in an image with zxing
I'm using a C# library for reading of QRCodes. A lot of the samples I've found are based off the old version of zxing where the RGBLuminanceSource constructor still takes in bitmap. In the latest version RGBLuminanceSource only takes byte[]. I've tried to convert bitmap to byte[], but the decode result is always null. Here's the code I used for conversion: private byte[] GetRGBValues(Bitmap bmp) { // Lock the bitmap's bits. System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height); System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat); // Get the address of the first line. IntPtr ptr = bmpData.Scan0; // Declare an array to hold the bytes of the bitmap. int bytes = bmpData.Stride * bmp.Height; byte[] rgbValues = new byte[bytes]; // Copy the RGB values into the array. System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); bmp.UnlockBits(bmpData); return rgbValues; } and for decode: Bitmap bitmap = Bitmap.FromFile(#"C:\QRimages.jpg") as Bitmap; LuminanceSource source = new RGBLuminanceSource(GetRGBValues(bitmap), bitmap.Width, bitmap.Height); var binarizer = new HybridBinarizer(source); var binBitmap = new BinaryBitmap(binarizer); QRCodeReader reader = new QRCodeReader(); var result = reader.decode(binBitmap); Somehow the result is always null. Also, our requirement is that we have to use image taken by camera. I've tried this: Bitmap bitmap = Bitmap.FromFile(#"C:\QRimages.jpg") as Bitmap; BarcodeReader reader = new BarcodeReader { AutoRotate = true, TryHarder = true }; Result result = reader.Decode(bitmap); It only works for the QR image I download online, but if I print out that image and take a picture of that with my phone, then try to process that image, the result is back to null. Any suggestions would be appreciated. Here's the image I'm using:
How do I grab an OpenGL render as a bitmap using OpenTK in Xamarin for Android?
I've attempted two different methods to achieve this, the first being an Android-style method and the second being an OpenGL style method. From my activity, I create a view which contains the OpenGL (1.1) code. The first method (android): Bitmap b = gameView.GetDrawingCache (true); // this is always null And the second method (opengl): public Bitmap GrabScreenshot() { int size = Width * Height * 4; byte[] bytes = new byte[size]; GL.ReadPixels<byte>(0, 0, Width, Height, All.Rgba, All.UnsignedByte, bytes); Bitmap bmp = BitmapFactory.DecodeByteArray (bytes, 0, size); return bmp; }
I have not tested this code. I was thinking you might be able to use it as a guide. How about trying something like this (derived from: OpenTK Forums): public Bitmap GrabScreenshot() { Bitmap bmp = new Bitmap(Width, Height); System.Drawing.Imaging.BitmapData data = bmp.LockBits(otkViewport.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb); GL.Finish(); GL.ReadPixels(0, 0, this.otkViewport.Width, this.otkViewport.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0); bmp.UnlockBits(data); bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); return bmp; } I believe a problem might occur due to the formatting of the bytes. In the example, they explicitly state the beginning to the array of data with data.Scan0 However, you just sends in a byte array.
Here a version that works on Xamarin.Android: private static Bitmap GraphicsContextToBitmap(int width, int height) { GL.Flush(); GL.PixelStore (PixelStoreParameter.PackAlignment, 1); var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888); var data = bitmap.LockPixels(); GL.ReadPixels(0, 0, width, height, PixelFormat.Rgba, PixelType.UnsignedByte, data); GL.Finish(); bitmap.UnlockPixels(); return bitmap; }
Copy a channel from one image to another using safe code
I'm trying to refactor this unsafe code to copy a single ARGB channel from one image to another using System.Runtime.InteropServices.Marshal.Copy as per this example on MSDN but I'm totally lost. Could anyone walk me through how I would go about it? public enum ChannelARGB { Blue = 0, Green = 1, Red = 2, Alpha = 3 } public static void transferOneARGBChannelFromOneBitmapToAnother( Bitmap source, Bitmap dest, ChannelARGB sourceChannel, ChannelARGB destChannel ) { if ( source.Size!=dest.Size ) throw new ArgumentException(); Rectangle r = new Rectangle( Point.Empty, source.Size ); BitmapData bdSrc = source.LockBits( r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb ); BitmapData bdDst = dest.LockBits( r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb ); unsafe { byte* bpSrc = (byte*)bdSrc.Scan0.ToPointer(); byte* bpDst = (byte*)bdDst.Scan0.ToPointer(); bpSrc += (int)sourceChannel; bpDst += (int)destChannel; for ( int i = r.Height * r.Width; i > 0; i-- ) { *bpDst = *bpSrc; bpSrc += 4; bpDst += 4; } } source.UnlockBits( bdSrc ); dest.UnlockBits( bdDst ); } Edit In an attempt to work through #Ben Voigt walk though I have come up with this so far. Unfortunately I am now getting the following error: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. private static void TransferOneArgbChannelFromOneBitmapToAnother( Bitmap source, Bitmap destination, ChannelARGB sourceChannel, ChannelARGB destinationChannel) { if (source.Size != destination.Size) { throw new ArgumentException(); } Rectangle rectangle = new Rectangle(Point.Empty, source.Size); // Lockbits the source. BitmapData bitmapDataSource = source.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); // Declare an array to hold the bytes of the bitmap. int bytes = bitmapDataSource.Stride * bitmapDataSource.Height; // Allocate a buffer for the source image byte[] sourceRgbValues = new byte[bytes]; // Get the address of the first line. IntPtr ptrSource = bitmapDataSource.Scan0; // Copy the RGB values into the array. System.Runtime.InteropServices.Marshal.Copy(ptrSource, sourceRgbValues, 0, bytes); // Unlockbits the source. source.UnlockBits(bitmapDataSource); // Lockbits the destination. BitmapData bitmapDataDestination = destination.LockBits(rectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); // Allocate a buffer for image byte[] destinationRgbValues = new byte[bytes]; IntPtr ptrDestination = bitmapDataDestination.Scan0; // Copy the RGB values into the array. System.Runtime.InteropServices.Marshal.Copy(ptrDestination, destinationRgbValues, 0, bytes); ptrSource += (int)sourceChannel; ptrDestination += (int)destinationChannel; for (int i = rectangle.Height * rectangle.Width; i > 0; i--) { destinationRgbValues[i] = sourceRgbValues[i]; ptrSource += 4; ptrDestination += 4; } // Copy the RGB values back to the bitmap // ******This is where I am getting the exception*******. System.Runtime.InteropServices.Marshal.Copy(destinationRgbValues, 0, ptrDestination, bytes); // Unlock bits the destination. destination.UnlockBits(bitmapDataDestination); } Can anyone see what I have done wrong? This is all a bit over my head to be honest. I think I should buy some books.
LockBits the source. Marshal.Copy the source BitmapData to a byte[] buffer. UnlockBits the source. LockBits the destination. Marshal.Copy the destination BitmapData to a byte[] buffer. Loop through and copy that channel from the source byte[] to the destination byte[] (note, use arithmetic on indexes instead of on pointers) Marshal.Copy the destination byte[] back to the BitmapData. UnlockBits the destination. I'm not sure what the point is, though. Code that uses Marshal.Copy is just as dangerous as code that uses the unsafe keyword, and should require similar code security permission. A potentially more efficient way would be to use ImageAttributes.SetColorMatrix to remove the desired channel from the destination image, remove all other channels from the source image, and then blend. See the example for ColorMatrix Or use DirectX (or OpenGL) and a shader that just transfers the one channel.
You could use my simple LINQ based image processing framework from Nuget or Codeplex and write a simple query that swaps the channels around. You could also use a ColorMatrix to perform the channel swap like in this code.
Unfortunately, a ColorMatrix won't work if you want to combine channels from two separate images. You would need an additive (or bitwise or) blending method, and the only blending provided by GDI+ is Over and Copy. It also looks to me like any methods that would allow you to access the bits directly, including LockBits, are locked down. I think the only option is to use GetPixel and SetPixel on each pixel, something like this: Color dstColor = bpDst.GetPixel(x, y); Color srcColor = bpSrc.GetPixel(x, y); int srcValue = (srcColor.ToArgb() >> (sourceChannel * 8)) & 0xff; int dstArgb = (dstColor.ToArgb() & ~(0xff << (destChannel * 8))) | (srcValue << (destChannel * 8)); bpDst.SetPixel(x, y, Color.FromArgb(dstArgb));