Why BitmapData Stride have alternating sign? - c#

I have the following code
var rect = new Rectangle(x, y, w, h);
BitmapData data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
BitmapSource cropppedImage;
if (data.Stride < 0)
{
cropppedImage = BitmapSource.Create(data.Width, data.Height, dpiX, dpiY,
GetPixelFormat(bitmap.PixelFormat), palette, data.Scan0 + data.Stride * data.Height, Math.Abs(data.Stride) * data.Height, Math.Abs(data.Stride));
}
else
{
cropppedImage = BitmapSource.Create(data.Width, data.Height, dpiX, dpiY,
GetPixelFormat(bitmap.PixelFormat), palette, data.Scan0, Math.Abs(data.Stride) * data.Height, Math.Abs(data.Stride));
}
Where rect is completely inside the image boundaries.
According to C# documentation positive Stride means top-down image, while negative means bottom-up.
How and why can the Stride member of BitmapData has different sign on different parts of the image?
As far as i understand(according to this, and this) one image can be top-down or bottom-up, but only one of them.
In my case i have an image with top-down AND bottom-up parts at the same time.
But how could this happen?

It's certainly possible (permissible) in terms of the specification of API that each time you call LockBits, the memory aperture you get could be presented differently. You have no access to the bits whatsoever until you lock them, so it's totally up to the implementation how to present those bits to you after you lock them. The API permits a signed Stride, so the implementation could take advantage of that to the extent it wants to. When you unlock the bits and then lock them again, the API is permitted to present the bits to you at a different address, with a different stride.
So you should probably be prepared for the eventuality that the stride is different between different calls to LockBits of the same bitmap. (Write your code in a way that supports positive and negative strides.) Honestly, I can't see the advantage of assuming that the memory is arranged with the same stride sign on subsequent LockBits calls.
As for whether or not that actually happens in the field with a particular implementation is less interesting a question to me. Even if it doesn't happen right now, it could happen with an update that arrives tomorrow because, like I said, it's up to the implementation. But having said that, typically once the original bitmap memory layout is determined, it will likely stay the same for reasons of efficiency. Changing the row order would involve, minimally, copying the bits to a different region of memory, so it's more efficient to leave it alone if possible.
Here's a case where you can observe different strides on the same bitmap. Suppose you have a bitmap stored in a bottom-up format natively on disk. After being read from disk, but being locked in a non-native format, the implementation flips it around after transcoding it to a different (e.g. wider) pixel format. Locking pixels in PixelFormat.Format16bppRgb555 natively might give you bottom up, but then locking them in PixelFormat.Format32bppRgb might flip the row order and present a buffer with positive stride because that's the way the internal transcoder might work: to always allocate a top-down destination for the new format even if the source format is bottom-up.
See this source code as evidence that this can happen in practice, where it always chooses a positive stride for bitmaps locked in non-native formats even if the source format had a negative stride.
As another example, an implementation may choose to perform a copy/transcode when the locked rectangle doesn't meet some criteria (e.g. multiple of 4 bytes width) resulting in the stride of the buffer allocated for the copied bits, which is potentially different from the original.

Related

Max dimensions of a bitmap file?

I'm curious to know what is the maximum bitmap width and height independently of each other. I did find that the maximum size is 32768x32768, but is that just referencing a perfect square? Is 32768x32768 = 1,073,741,824 the total amount of pixels I can play with and I can rearrange those pixels among the width and height as long as the total doesn't exceed?
I don't get any error if I do this:
Dim theBitmap as Bitmap = New Bitmap(450, 100000)
Even though I am unable to open the image after I save it (which I don't need to do), I am still able to work with the bitmap BUT I believe there is something not quite right... The final result does not yield the expected result...
The purpose of what I am doing is irrelevant. All I care about is answers to the questions I stated in the first paragraph. If the answer is that I am limited to 32768 for the height, then I'll change my code accordingly. Thanks!
I was able to figure out the answer to my initial questions. You are indeed able to work with any width and height as long as the total dimension stays within the maximum size specification. You may experience problem saving awkward dimensions (1 by 1,000,000), but if you only need to manipulate a bitmap, you can indeed work with such scenarios.
Cheers to everyone that contributed in the comment section!
.bmps size is constrained by the max size of a uint32_t, which is 4GB.
Any dimensions are acceptable as long as the .bmp remains under 4GB.
However, not all bitmaps are created equal. Monochrome bitmaps only need 1 bit per pixel, and also use a slightly smaller color pallet (8 bytes total) so can have a little more than 4x the total number of pixels a 16 color bitmap needs (which uses 4 bits per pixel, and 64 bytes for the color pallet).
This does not take into account compression, as bmps allow for compression for all non monochrome bmps.
PNG and JPEG have no explicit limit on file size, whereas BMP has a limit of 32K by 32K pixels, which I believe is your problem here (some places state that it can also hold 2Gx2G, but I couldn't find anything related to those claims).

Is there a benefit to using Graphics.DrawImage(Image, RectangleF destRect, RectangleF srcRect, GraphicsUnit srcUnit) when the image is a Bitmap?

Unless I miss something bitmaps are quantized (pixel-oriented). So what happens when someone tries the following:
public void Foo(Bitmap image)
{
var destinationRect = new Rectangle(0, 0, 50, 50);
var resultingSubimage = new Bitmap(destinationRect.Width, destinationRect.Height, PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(resultingSubimage))
{
graphics.DrawImage(image, destinationRect, new RectangleF(30.3245F /*x*/, 23.234234F /*y*/, 50F, 50F), GraphicsUnit.Pixel);
// vs graphics.DrawImage(image, destinationRect, new Rectangle(30 /*x*/, 23 /*y*/, 50, 50), GraphicsUnit.Pixel);
}
}
notice that the x and y fields are decimals which point to a sub-pixel point. But is there even such a thing as a sub-pixel point for bitmaps? What's going on under the hood? Sorry if this has been answered elsewhere but the online documentation for both Graphics.DrawImage() and for the underlying p/invoke function 'GdipDrawImageRectRect' do not shed any light in this.
It is not well known, that if you draw an image, e.g.:
graphics.DrawImage(image, top, left);
the image will be scaled. This is because DrawImage looks at the dpi setting of the image (e.g. 72dpi from photoshop), and scales the image to match the destination display (typically 96dpi).
If you want to draw an image without any scaling, you must explicitly give DrawImage the size of the image:
graphics.DrawImage(img, top, left, img.Width, img.Height);
By calling, DrawImage with the the destination rectangle that matches the original image's pixel size, you are avoiding a resampling/rescaling.
Bonus Reading
.NET: What does Graphics.DrawImageUnscaled do?
Define "benefit". What overload would you use instead? I.e. "benefit" as compared to what?
It is most assuredly not the case that the overload is completely useless when dealing with Bitmap objects. Firstly, the GraphicsUnit value determines how the coordinates passed to the method are interpreted and of course one might pass something other than GraphicsUnit.Pixel. For example, suppose you are using GraphicsUnit.Inch and the resolution of the image is 120 dpi. Then each pixel is only 1/120th of an inch, and for per-pixel precision, your floating point values would be multiples of that (i.e. multiples of 0.0083333333333333), and not integer values.
Secondly, the Graphics object can be configured to do sub-pixel sampling in a variety of ways, and in such cases, a fractional pixel value could have meaning, even if the units being described were pixels.
You ask "what's going on under the hood", but I'm afraid that part is too broad a question for Stack Overflow. The Graphics object uses GDI+ as the underlying mechanism when using it on a Windows platform; the answer to what specifically happens with different configurations of the Graphics object would require a lengthy treatise.
If you want that level of detail, the right place to start would be the MSDN documentation for the GDI+ in the native Windows API. For most parts of Graphics, there's a one-for-one correspondence between the .NET API and the native one.
By the way, to be clear: your coordinates in this scenario are float values. I would be cautious about using the word "decimal" here, because .NET/C# has an actual decimal type, and you're definitely not using that. :)

Is it safe (pun intended) to copy bitmap regions using lockbits this way?

I think I've found a MUCH faster way to copy bitmaps in c#. (If it's valid, I'm sure I wasn't the first but I haven't seen it anywhere yet.)
The simplest way I can think to ask this is to assert what I based my idea on and if no one shoots holes in it, assume the idea is sound:
void FastCopyRegion(Bitmap CopyMe, ref Bitmap IntoMe, Rectangle CopyArea)
{
//`IntoMe` need not be declared `ref` but it brings
// attention to the fact it will be modified
Debug.Assert(CopyMe.PixelFormat == IntoMe.PixelFormat,
"PixelFormat mismatch, we could have a problem");
Debug.Assert(CopyMe.Width == IntoMe.Width, //This check does not verify
"Stride mismatch, we could have a problem");// sign of `stride` match
BitmapData copyData = CopyMe.LockBits(CopyArea,
ImageLockMode.ReadWrite, CopyMe.PixelFormat);
IntoMe.UnlockBits(copyData);
}
1) LockBits simply copies a block of pixel data out of a bitmap to fixed memory to be edited and copied back in using UnlockBits
2) Using LockBits does not affect the copied memory block so it should have no effect on the image copied from.
3) Since you never enter unsafe code, there should be no risk of corrupting memory.
Possible holes I see:
1) If the PixelFormats of the two bitmaps are different, this method may not always copy correctly. However, since LockBits requires specifying a pixelformat, it seems this is handled. (If so, hooray for that overhead the other 99.9% of the time we're not switching pixelformats! /EndSarcasm)
2) If the stride of the two bitmaps doesn't match, there could be a problem (because stride is the outer for loop's incrementor in the copy operation.) This problem would limit copying to bitmaps with equal stride.
Edit: I think assertion #2 must be wrong... I just found an error when trying to later access the bitmap passed through CopyMe. Workaround below, but I'm not sure if it leaves a block of fixed memory lying around. (memory leak alert!)
void FastCopyRegion(Bitmap CopyMe, ref Bitmap IntoMe, Rectangle CopyArea)
{
//`IntoMe` need not be declared `ref` but it brings attention to the fact it will be modified
Debug.Assert(CopyMe.PixelFormat == IntoMe.PixelFormat, "PixelFormat mismatch, we could have a problem");
Debug.Assert(CopyMe.Width == IntoMe.Width, "Width mismatch, we could have a problem");
BitmapData copyD = IntoMe.LockBits(CopyArea, ImageLockMode.ReadWrite, CopyMe.PixelFormat);
BitmapData copyData = CopyMe.LockBits(CopyArea, ImageLockMode.ReadWrite, CopyMe.PixelFormat);
CopyMe.UnlockBits(copyData);
IntoMe.UnlockBits(copyData);
}
Use Bitmap.Clone() instead. GDI+ tends to not report exceptions and the resulting bugs will be very hard to track.
A very fast way to copy images into a bitmap is with Graphics.DrawImage() as long as you don't convert pixel format or scale the image.

OpenCV create Mat from byte array

In my C++ dll I am creating Mat from byte array:
BYTE * ptrImageData; //Image data is in this array passed to this function
Mat newImg = Mat(nImageHeight, nImageWidth, CV_8UC3, ptrImageData);
The image is created with some gray shade not the original one.
Is this the proper way of creating Mat from byte array?
Please see code
ptrImageData is passed to the C++ dll from C# code.
C# code to pass the image data
System.Drawing.Image srcImage //Has the image
MemoryStream ms = new MemoryStream();
Marshal.FreeHGlobal(ptrImageData);
srcImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] imgArray = ms.ToArray();
ms.Dispose();
int size1 = Marshal.SizeOf(imgArray[0]) * imgArray.Length;
IntPtr ptrImageData = Marshal.AllocHGlobal(size1);
Marshal.Copy(imgArray, 0, ptrImageData, imgArray.Length);
//Calling C++ dll function
ProcessImage(ptrImageData, srcImage.Width, srcImage.Height);
Marshal.FreeHGlobal(ptrImageData);
The C++ code appears ok, in that this creates a matrix wrapper for the supplied image data, assuming the buffer is in the conventional RGB8 format. Note that this constructor does not copy the buffer, so the buffer must remain valid for the duration of this Mat instance (or be copied).
Mat newImg = Mat(nImageHeight, nImageWidth, CV_8UC3, ptrImageData);
It appears the problem lies in Your C# code. I am not a C# developer, but I will do my best to help. You are creating a memory stream and using the JPEG codec to write a compressed version of the image into the buffer as if it were a file. But that is not the data format that cv::Mat is expecting, so you will basically see garbage (compressed data interpreted as uncompressed).
Given a System.Image.Drawing.Image instance, you can create a wrapper Bitmap object directly (or maybe use as, since it is a simple downcast). Then you can just use the Bitmap.LockBits() method tog obtain a pointer to the underlying image data.
Bitmap bmp = new Bitmap(sourceImage);
// 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[] rgbBuffer = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbBuffer, 0, bytes);
// Do your OpenCV processing...
// ...
// Unlock the bits.
bmp.UnlockBits(bmpData);
and then you can pass the rgbBuffer to OpenCV.
I'm not convinced that the memory management in the original code is entirely correct either, but anyway the above will work provided the scope of the buffer ownership is within the lock and unlock method calls. If the image data is to outlive this code block, you will have to copy the buffer.
Be careful with your pixel formats too - you need to make sure the Image/Bitmap instance really contains RGB8 data. OpenCV's cv::Mat has various flags so you can work with a variety of in-memory image formats. But note that these are not the same as the on-disk (typically compressed) formats, such as PNG, TIFF, and so forth.
Yes, this is one way to create a Mat from a byte array. You just have to be careful that your array contains what you think it does.
The image is created with some gray shade not the original one.
So you are getting an image in newImg? What was the pixel format of the original data?
Maybe you've switched the red and blue channels. The following line will swap the channels:
cv::cvtColor(newImg,swappedImg,CV_RGB2BGR);
Here is link to docs: http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-mat
In general you should take care about two things:
When you pass external data into matrix constructor, the external data is not automatically deallocated, so you should take care of it. If you want OpenCV matrix to care about memory, then you should copy matrix (you can do it in many ways, e.g. using Mat::clone or Mat::copyTo methods.
External data may not be continuous, i.e. size of row may be bigger than width multiplied by number of channels multiplied by size of data element. So you may want specify "step" as last argument of constructor. If you allocate external data manually and 100% sure that it is continuous, then you may not pass step and rely on automatic step calculation.
I am not familiar with C#, but it seems to me that you release data right after ProcessImage call. So if ProcessImage is asynchronous or somehow caches your matrix (i.e. lifetime of matrix is longer that ProcessImage call), then you should care about memory management.

Is there any way to do Image Quantization safely and with no Marshalling?

I'm currently using Brendan Tompkins ImageQuantization dll.
http://codebetter.com/blogs/brendan.tompkins/archive/2007/06/14/gif-image-color-quantizer-now-with-safe-goodness.aspx
But it doesn't run in medium trust in asp.net.
Does anyone know of a Image Quantization library that does run in medium trust?
Update
I don't care if the solution is slow. I just need something that works.
You should be able to replace the code using Marshal with explicit reading of the underlying stream via something like BinaryReader. This may be slower since you must read the stream entirely into your managed memory or seek into it rather than relying on the copy already in unmanaged memory being quickly accessible but is fundamentally your only option.
You simply cannot go spelunking into unmanaged memory from a medium trust context, even if only performing read operations.
Having looked at the linked code there's a reason you're not allowed to do this sort of thing. For starters he's ignoring the 64/32bit aspect of the IntPtr!
The underlying BitMapData class he's using is utterly predicated on having unfettered read access to arbitrary memory, this is never happening under medium trust.
A significant rewrite of his base functionality will be required to either use BitMap's directly (with the slow GetPixel calls) or read the data directly via conventional stream apis, dropping it into an array(s) and then parse it out yourself. Neither of these are likely to be pleasant. The former will be much slower (I would expect order of magnitude due to the high overhead per pixel read), the later less slow (though still slower) but has much more associated effort in terms of rewriting the low level parsing of the image data.
Here's a rough guide to what you need to change based on the current code:
from Quantizer.cs
public Bitmap Quantize(Image source)
{
// Get the size of the source image
int height = source.Height;
int width = source.Width;
// And construct a rectangle from these dimensions
Rectangle bounds = new Rectangle(0, 0, width, height);
// First off take a 32bpp copy of the image
Bitmap copy = new Bitmap(width, height, PixelFormat.Format32bppArgb);
// And construct an 8bpp version
Bitmap output = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
// Now lock the bitmap into memory
using (Graphics g = Graphics.FromImage(copy))
{
g.PageUnit = GraphicsUnit.Pixel;
// Draw the source image onto the copy bitmap,
// which will effect a widening as appropriate.
g.DrawImage(source, bounds);
}
//!! BEGIN CHANGES - no locking here
//!! simply use copy not a pointer to it
//!! you could also simply write directly to a buffer then make the final immage in one go but I don't bother here
// Call the FirstPass function if not a single pass algorithm.
// For something like an octree quantizer, this will run through
// all image pixels, build a data structure, and create a palette.
if (!_singlePass)
FirstPass(copy, width, height);
// Then set the color palette on the output bitmap. I'm passing in the current palette
// as there's no way to construct a new, empty palette.
output.Palette = GetPalette(output.Palette);
// Then call the second pass which actually does the conversion
SecondPass(copy, output, width, height, bounds);
//!! END CHANGES
// Last but not least, return the output bitmap
return output;
}
//!! Completely changed, note that I assume all the code is changed to just use Color rather than Color32
protected virtual void FirstPass(Bitmap source, int width, int height)
{
// Loop through each row
for (int row = 0; row < height; row++)
{
// And loop through each column
for (int col = 0; col < width; col++)
{
InitialQuantizePixel(source.GetPixel(col, row));
} // Now I have the pixel, call the FirstPassQuantize function...
}
}
you would need to do roughly the same in the other functions.
This removes any need for Color32, the Bitmap class will deal with all that for you.
Bitmap.SetPixel() will deal with the second pass. Note that this is the easiest way to port things but absolutely not the fastest way to do it within a medium trust environment.

Categories