I have a method to copy the data out of a System.Drawing.Bitmap which looks like this:
var readLock = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
byte[] data = new byte[3 * image.Width * image.Height];
if (data.Length != readLock.Stride * readLock.Height)
throw new InvalidOperationException("Incorrect number of bytes");
Marshal.Copy(readLock.Scan0, data , 0, data.Length);
image.UnlockBits(readLock);
Pretty simple, and it works for most of my images. However for a very small image (14x14) it hits the exception. In the failing case Stride is 44, not 42 (14 * 3) as expected.
The pixel format is Format24bppRgb, so there should be three bytes for every pixel in the image. Where are these extra bytes coming from, and how can I deal with them when processing the image data?
For anyone interested, I'm generating Normal data from a heightmap, so I need to be able to get each pixel and its neighbours accurately).
Every pixel line of Bitmap must be aligned, that's why stride is not always width * bytes-per-pixel. You should ignore any extra bytes. It means that if you are working with byte arrays with unaligned data, you might not always be able to copy all image data in a single Marshal.Copy() call. Every line of pixels starts at readLock.Scan0 + y * readLock.Stride and contains readLock.Width * bytes-per-pixel meaningful bytes.
Solution:
const int BYTES_PER_PIXEL = 3;
var data = new byte[readLock.Width * readLock.Height * BYTES_PER_PIXEL];
if(readLock.Stride == readLock.Width * BYTES_PER_PIXEL)
{
Marshal.Copy(readLock.Scan0, data, 0, data.Length);
}
else
{
for(int y = 0; y < readLock.Height; ++y)
{
IntPtr startOfLine = (IntPtr)((long)readLock.Scan0 + (readLock.Stride * y));
int dataOffset = y * readLock.Width * BYTES_PER_PIXEL;
Marshal.Copy(startOfLine, data, dataOffset, readLock.Width * BYTES_PER_PIXEL);
}
}
Related
I have a 32bit .tif file which is displayed in the image below by first ImageJ, and secondly my program. As you can guess, the way ImageJ displays the picture is correct.
I am converting the file to a Bitmap like this:
private Bitmap TiffToBmp()
{
Bitmap bmp;
int width;
int height;
float[] scanline32Bit;
float[] buffer;
using (Tiff original = Tiff.Open(file, "r"))
{
width = original.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
height = original.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
byte[] scanline = new byte[original.ScanlineSize()];
scanline32Bit = new float[original.ScanlineSize() / 4];
buffer = new float[width * height];
for (int i = 0; i < height; i++) //loading the data into a buffer
{
original.ReadScanline(scanline, i);
Buffer.BlockCopy(scanline, 0, scanline32Bit, 0, scanline.Length);
for (int j = 0; j < width; j++)
{
buffer[i * width + j] = scanline32Bit[j];
}
}
}
bmp = new Bitmap(width, height);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat);
byte[] bytes = new byte[data.Height * data.Stride];
for (int y = 0; y < data.Height; y++) //creating Bitmap from buffer
{
for (int x = 0; x < data.Stride; x += 4)
{
bytes[y * data.Stride + x] = (byte)buffer[(y * data.Stride + x) / 4];
bytes[y * data.Stride + x + 1] = (byte)buffer[(y * data.Stride + x) / 4];
bytes[y * data.Stride + x + 2] = (byte)buffer[(y * data.Stride + x) / 4];
bytes[y * data.Stride + x + 3] = 255;
}
}
Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
bmp.UnlockBits(data);
return bmp;
}
Using a RGBA Bitmap might seem a bit naive but it's necessary in further steps of the program. Also worth mentioning: XnView displays the file similar to mine, just the negative (from white to black instead of black to white). When opening the file it notices me that it converts the image to RGB with 8bits per channel (the same thing I'm doing) and falsely claims the .tif is 16bit instead of 32bit.
Has someone an idea of what I am doing wrong?
It seems to me that the error comes purely from some kind of misuse of structs or wrongly converting between them (from float to byte and so on).
I am using BitMiracle.LibTiff.Classic from the the .NET version of original libtiff library
edit: after some research I found out that original.ReadScanline(scanline, i) returns seemingly weird values and converts them via Buffer.BlockCopy(...) to the stripes appearing in the image. For example the 4 bytes (read from scanline) of the pixel (x = 0, y = 0) are 0, 0, 200, 6, the corresponding 8bit pixel value (read from scanline32Bit) turns out to be 110. Of Pixel (x = width, y = 0) the 4 bytes are 0, 128, 111, 68 and the 8bit value displayed is 958 corresponding to 190. So now I think there's something wrong with one of those steps but I actually have no idea what's going on.
Assuming you don't mind using existing code, there is this: https://www.codeproject.com/Articles/8279/ImageConverter-Converts-images-to-a-specific-image
The author (not me, BTW) provides both the C# source and an executable. I used the executable to convert a TIF (created using paint.net) to a BMP, so I presume the source will be useful to you. I was able to open the author's solution using VS 2017 (VS upgraded both the solution and project to work in the current VS environment). The conversion is based on the System.Drawing.Imaging.ImageFormat class
Some of my resulting images are slanted, some are not.
Expected Result: (529x22)
Actual Result: (529x22)
Don't mind the different image sizes, these are screenshots. They are both 529x22.
The code I am using, I just got this from an answer on a question here at SO.
// some other method
byte[] pixels = new byte[size - 16];
Array.Copy(this.data, offset, pixels, 0, pixels.Length);
this.ByteToImage(w, h, pixels);
// builds the pixels to a image
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
bmp.PixelFormat);
// bytes => not using this because it gives error
// eg. pixel.Length = 16032, bytes = 16064
int bytes = bmpData.Stride * bmp.Height;
Marshal.Copy(pixels, 0, bmpData.Scan0, pixels.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
I'm confused because some works ok, not slanted. But others are slanted. What did I miss?
Update
As stated in the comments and answers, the problem is how I'm calculating stride. I'm still confused on how to do it but I tried this:
public static void RemovePadding(this Bitmap bitmap)
{
int bytesPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
var pixels = new byte[bitmapData.Width * bitmapData.Height * bytesPerPixel];
for (int row = 0; row < bitmapData.Height; row++)
{
var dataBeginPointer = IntPtr.Add(bitmapData.Scan0, row * bitmapData.Stride);
Marshal.Copy(dataBeginPointer, pixels, row * bitmapData.Width * bytesPerPixel, bitmapData.Width * bytesPerPixel);
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
bitmap.UnlockBits(bitmapData);
}
But the result is (more slanted):
This seems to work here:
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
byte bpp = 2;
var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
bmp.PixelFormat);
// copy line by line:
for (int y = 0; y < h; y++ )
Marshal.Copy(pixels, y * w * bpp, bmpData.Scan0 + bmpData.Stride * y, w * bpp);
bmp.UnlockBits(bmpData);
return bmp;
}
I use a loop to place each row of data at the right spot. The data do not include the padding, but the target address must do so.
Therefore we need to multiply the data access by the actual width * bytePerPixel but the target adress by the Stride, i.e. the length of the scanline, padded to the next multiple of four bytes. For width=300 it is stride=300, for width=301 it is stride=304..
Moving all pixel data in one step can only work when there is no padding, i.e. when the width is a multiple of 4.
This expects the stride to correspond to the width, without padding. There can be padding. The padding would "eat" some of the next line, which will therefore appear to shift left.
Since the padding breaks up the lines, the only real way to deal with it (other than using the same padding everywhere) is copying line by line. You can calculate the starting address of a line with bmpData.Scan0 + y * bmpData.Stride. Copy starting there, for every y.
// bytes => not using this because it gives error
Yes, because your array does not have padding. So you were telling it to copy more data than the array held.
I have the following code which takes an array of bytes which i generated and writes them out to this bitmap. If i set the pixel format to Format4bppIndexed, then i get a readable image repeating width wise 4 times, if i set it to Format1bppIndexed(which is the correct setting) then i get one big unreadable image.
The image was a decoded Jbig2 image , i know the bytes are correct i can't seem to figure out how to get it into a 1bpp readable format.
Does anyone have any advice on that matter
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format1bppIndexed);
//Create a BitmapData and Lock all pixels to be written
BitmapData bmpData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly, bitmap.PixelFormat);
//Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(newarray, 0, bmpData.Scan0, newarray.Length);
//Unlock the pixels
bitmap.UnlockBits(bmpData);
The following may work although, if I remember correctly, Stride sometimes has an effect and a simple block-copy won't suffice (line by line must be used instead).
Bitmap bitmap = new Bitmap(
width,
height,
System.Drawing.PixelFormat.Format16bppGrayScale
);
To handle the Stride you'd want:
BitmapData^ data = bitmap->LockBits(oSize,
ImageLockMode::ReadOnly, bitmap->PixelFormat);
try {
unsigned char *pData = (unsigned char *)data->Scan0.ToPointer();
for( int x = 0; x < bmpImage->Width; ++x )
{
for( int y = 0; y < bmpImage->Height; ++y )
{
// Note: Stride is data width of scan line rounded up
// to 4 byte boundary.
// Requires use of Stride, not (width * pixelWidth)
int ps = y*bmpImage->Width*(nBitsPerPixel / 8)
+ x * (nBitsPerPixel / 8);
int p = y * data->Stride + x * (nBitsPerPixel / 8);
Byte lo = newarray[ps + 1];
Byte hi = newarray[ps + 0];
pData[p + 1] = lo;
pData[p + 0] = hi;
}
}
} finally {
bmpImage->UnlockBits(data);
}
Note: This was written in C++/CLI. Let me know if you need C# equivalents for any of the operations here. (Also, I pulled it from a read from bitmap rather than a write to bitmap so it may yet be a bit rough, but should hopefully give you the idea...)
I figured this out Although i'm still not sure why it should matter.
Based on this stackoverflow posting How can I load the raw data of a 48bpp image into a Bitmap?
I used the WPF classes instead of the GDI and wrote the code like this
var bitmap = new WriteableBitmap(width, height, 96, 96, System.Windows.Media.PixelFormats.BlackWhite, null);
bitmap.WritePixels(new System.Windows.Int32Rect(0, 0, width, height), newarray, stride, 0);
MemoryStream stream3 = new MemoryStream();
var encoder = new TiffBitmapEncoder ();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(stream3);
This correctly creates the image.
If anyone has any insight into why this might be the case please comment below
The port which now mostly works(lots of cleanup code) was based on a java implementation of JPedal Big2 Decoder to .NET. If anyone knows anyone interested send them here
https://github.com/devteamexpress/JBig2Decoder.NET
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I'm experiencing this error in the Marshal.Copy portion of my code. I do believe that my data is not corrupted nor protected.
I was wondering in what case does this occur.
I have a List<> of bitmaps. This only occurs when I process the first index [0].
So here's how I did it :
- First, I used this code [This code gets the pixel data of a bitmap] :
Bitmap tmp_bitmap = BitmapFromFile[0];
Rectangle rect = new Rectangle(0, 0, tmp_bitmap.Width, tmp_bitmap.Height);
System.Drawing.Imaging.BitmapData bmpData =
tmp_bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
int length = bmpData.Stride * bmpData.Height;
byte[] bytes = new byte[length];
// Copy bitmap to byte[]
Marshal.Copy(bmpData.Scan0, bytes, 0, length);
tmp_bitmap.UnlockBits(bmpData);
It works fine, no errors occur.
Then, I apply this code [ This will remove the pixel data line scan padding ]:
byte[] bytes = new byte[bmpData.Width * bmpData.Height * 3];
for (int y = 0; y < bmpData.Height; ++y) {
IntPtr mem = (IntPtr)((long)bmpData.Scan0 + y * bmpData.Stride * 3);
Marshal.Copy(mem, bytes, y * bmpData.Width * 3, bmpData.Width * 3); //This is where the exception is pointed.
}
It gives me that error whenever I'm processing the first image -- second to last, no problem at all.
I hope you can help me with this.
Thank you in advance.
You seem to be considering 3 times the stride for every row; your code will only work for the first third of the image; after that you have indeed gone outside of your allowed range. Basically:
bmpData.Scan0 + y * bmpData.Stride * 3
looks really dodgy. The "stride" is the number of bytes (including padding) used by every line. Typically, that would be just:
bmpData.Scan0 + y * bmpData.Stride
How do I calculate the required buffer size for the WriteableBitmap.WritePixels method?
I am using the overload taking four parameters, the first is an Int32Rect, the next is a byte array containing the RGBA numbers for the colour, the third is the stride (which is the width of my writeable bitmap multiplied by the bits per pixel divided by 8), and the last is the buffer (referred to as the offset in Intellisense).
I am getting the Buffer size is not sufficient runtime error in the below code:
byte[] colourData = { 0, 0, 0, 0 };
var xCoordinate = 1;
var yCoordinate = 1;
var width = 2;
var height = 2;
var rect = new Int32Rect(xCoordinate, yCoordinate, width, height);
var writeableBitmap = new WriteableBitmap(MyImage.Source as BitmapSource);
var stride = width*writeableBitmap.Format.BitsPerPixel/8;
writeableBitmap.WritePixels(rect, colourData, stride,0);
What is the formula I need to use to calculate the buffer value needed in the above code?
The stride value is calculated as the number of bytes per "pixel line" in the write rectangle:
var stride = (rect.Width * bitmap.Format.BitsPerPixel + 7) / 8;
The required buffer size is the number of bytes per line multiplied by the number of lines:
var bufferSize = rect.Height * stride;
Provided that you have a 2x2 write rectangle and a 32-bits-per-pixel format, e.g. PixelFormats.Pbgra32, you get stride as 8 and bufferSize as 16.
The stride is simply the width in bytes of your input buffer. It is called stride, because sometimes there is extra memory behind each line of an image, which makes it impossible to use the width of the image to read each line of an image.
So in your example, this is 2. You do not need to calculate anything with the bits per pixel of the bitmap, the WritePixels method knows all this information. You need to provide the information about how your input data is structured.
However, as mentioned in the other answer, your example won't work if the bitmap is also 2x2. Then the starting coordinate would be 0,0.
EDIT:
When I look closer at your example, I see the mistake. You say the colourData is the input color. But this is input per pixel. So if you want to change a rect of 2x2, you need the following inputdata:
byte[] colourData = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
And then the bytes per pixel is equal to that of the bitmap, so that is 4, times the width of each line (2), makes total 8.
Here's Microsoft's reflected code that performs the check within CopyPixels
int num = ((sourceRect.Width * this.Format.BitsPerPixel) + 7) / 8;
if (stride < num)
{
throw new ArgumentOutOfRangeException("stride", MS.Internal.PresentationCore.SR.Get("ParameterCannotBeLessThan", new object[] { num }));
}
int num2 = (stride * (sourceRect.Height - 1)) + num;
if (bufferSize < num2)
{
throw new ArgumentOutOfRangeException("buffer", MS.Internal.PresentationCore.SR.Get("ParameterCannotBeLessThan", new object[] { num2 }));
}
Although user Clemens answered the question concerning the buffer size, the questioner was not aware that he calculated the buffer size already correct and the problem was somewhere else.
While details are given and discussed in comments there is lacking one comprehensive snippet (and complete usage example of .WritePixels (without .CopyPixels) as well).
Here it is (I scanned similar questions, but this has been the best place):
var dpiX = 96;
var writeableBitmap = new WriteableBitmap(width, height, dpiX, dpiX, PixelFormats.Bgra32, null); // Pixelformat of Bgra32 results always in 4 bytes per pixel
int bytesPerPixel = (writeableBitmap.Format.BitsPerPixel + 7) / 8; // general formula
int stride = bytesPerPixel * width; // general formula valid for all PixelFormats
byte[] pixelByteArrayOfColors = new byte[stride * height]; // General calculation of buffer size
// The numbers in the array are indices to the used BitmapPalette,
// since we initialized it with null in the writeableBitmap init, they refer directly to RGBA, but only in this case.
// Choose a light green color for whole bitmap (for not easy to find commented MSDN example with random colors, see https://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap(VS.85).aspx
for (int pixel = 0; pixel < pixelByteArrayOfColors.Length; pixel += bytesPerPixel)
{
pixelByteArrayOfColors[pixel] = 0; // blue (depends normally on BitmapPalette)
pixelByteArrayOfColors[pixel + 1] = 255; // green (depends normally on BitmapPalette)
pixelByteArrayOfColors[pixel + 2] = 0; // red (depends normally on BitmapPalette)
pixelByteArrayOfColors[pixel + 3] = 50; // alpha (depends normally on BitmapPalette)
}
writeableBitmap.WritePixels(new Int32Rect(0, 0, width, height), pixelByteArrayOfColors, stride, 0);
I am work with this.
60z fs
this.playerOpacityMaskImage.WritePixels(
new Int32Rect(0, 0, this.depthWidth, this.depthHeight),
this.greenScreenPixelData,
this.depthWidth * ((this.playerOpacityMaskImage.Format.BitsPerPixel + 7) / 8),
0);
I am not sure but try this works for 24 bit rgb
{
//your code
var stride = width * 3;
WriteableBitmap bmp = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr24, null);
bmp.WritePixels(new System.Windows.Int32Rect(0, 0, width , height),byte[],stride,0));
}