i would like to crop an image from a full image only at a specific part this is my code
private unsafe Bitmap GetDiffBitmap(Bitmap bmp)
{
BitmapData bmDataRes;
bmpRes = new Bitmap(bmp.Width, bmp.Height);
bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
bmDataRes = bmpRes.LockBits(new Rectangle(0, 0, bmpRes.Width, bmpRes.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
IntPtr scan0 = bmData.Scan0;
IntPtr scan0Res = bmDataRes.Scan0;
int stride = bmData.Stride;
int strideRes = bmDataRes.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
for(int y = 0; y < nHeight; y++)
{
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
byte* pRes = (byte*)scan0Res.ToPointer();
pRes += y * strideRes;
for (int x = 0; x < nWidth; x++)
{
//always get the complete pixel when differences are found
if (p[0] ==255)
{
pRes[0] = p[0];
pRes[1] = p[1];
pRes[2] = p[2];
//alpha (opacity)
pRes[3] = p[3];
}
else
{
bmpRes.UnlockBits(bmDataRes);
break;
}
p += 4;
pRes += 4;
}
}
return bmpRes;
}
as you can see i just want to copy the pixels when the red byte is 255 and if its not,immediently to stop and break. i want to find continuous red parts.
but im getting a weird exception on that line when i release the bitmap
bmpRes.UnlockBits(bmDataRes); the exception -A generic error occurred in GDI+ any idea why is it throwing that error?
You should break the outter for loop as well.
What happens is that you're trying to UnlockBits more than once, actually.
Or more simply you can change
bmpRes.UnlockBits(bmDataRes);
break;
into
bmpRes.UnlockBits(bmDataRes);
return bmpRes;
Related
I am trying to extract a specific area from a bitmap for further processing. In rare cases an error occurs when Marshal.Copy is called. This can be reproduced with the following example code:
Bitmap bitmap = new Bitmap(1741, 2141, PixelFormat.Format1bppIndexed);
int zoneWidth = 50;
int zoneHeight = 50;
int x = 168;
int y = bitmap.Height - zoneHeight;
Rectangle zone = new Rectangle(x, y, zoneWidth, zoneHeight);
BitmapData bitmapData = bitmap.LockBits(zone, ImageLockMode.ReadOnly, bitmap.PixelFormat);
int byteCount = Math.Abs(bitmapData.Stride) * bitmapData.Height;
byte[] pixels = new byte[byteCount];
Marshal.Copy(bitmapData.Scan0, pixels, 0, byteCount);
// some further processing
bitmap.UnlockBits(bitmapData);
In other posts I have read that Stride can be negative. That is not the case here.
Why does the error occur and how can I prevent it?
Edit 1:
I have implemented the second suggestion of JonasH. But that also fails with the AccessViolationException. Probably I did not do that correctly.
Bitmap bitmap = new Bitmap(1741, 2141, PixelFormat.Format1bppIndexed);
int zoneWidth = 50;
int zoneHeight = 50;
int zoneX = 168;
int zoneY = bitmap.Height - zoneHeight;
Rectangle zone = new Rectangle(zoneX, zoneY, zoneWidth, zoneHeight);
BitmapData bitmapData = bitmap.LockBits(zone, ImageLockMode.ReadOnly, bitmap.PixelFormat);
int rowSize = Math.Abs(bitmapData.Stride);
byte[] pixels = new byte[bitmapData.Height * rowSize];
IntPtr iptr = bitmapData.Scan0;
for (int y = 0; y < bitmapData.Height; y++)
{
Marshal.Copy(IntPtr.Add(iptr, y * rowSize),
pixels,
y * rowSize,
rowSize);
}
bitmap.UnlockBits(bitmapData);
This is probably because you are only locking part of the bitmap. Replace the zone with new Rectangle(0, 0, bitmap.Width , bitmap.Height); and I would expect your problem to disappear.
The alternative would be to restrict the copy-operation to the locked part of the bitmap, but that would require copying row by row and not the entire bitmap at once.
Copying row by row needs careful usage of offsets, you need to keep track of both the horizontal and vertical offsets of both the source and target data. I think something like this should work:
for (int y = 0; y < zoneHeight ; y++)
{
Marshal.Copy(
IntPtr.Add(iptr,(y + zoneY ) * bitmapData.Stride + zoneX)
pixels,
y * zoneWidth,
zoneWidth );
}
I have to draw an image from integer values. They are stored in List<int>[].
The list has 5081 arrays with 2048 values each. Each value is between 0-1000 and one int is the color for one pixel, so it's grayscale.
I know how to do it with setpixel but this is too slow.
for (int y = 0; y < channelId.Length; y++) {
for (int x = 0; x < channelId[y].Count; x++) {
int myColor = (channelId[y].ElementAt(x) * 255) / 1000;
if (myColor > 255) {
myColor = 255;
} else if (myColor < 0) {
myColor = 0;
}
bmp.SetPixel(x, y, Color.FromArgb(myColor, myColor, myColor));
}
}
I know that there are similar questions here how to draw bitmaps faster but they already have a bitmap. I have to draw the image from my values.
If you don't want to do it unsafely, you could do this (assumes a 32bppArgb pixelformat):
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
for (var y = 0; y < channelId.Length; y++)
{
var scanLineSize = channelId[y].Count*4;
var rgb = new byte[scanLineSize];
int idx = 0;
// Convert the whole scanline
foreach (var id in channelId[y])
{
rgb[idx] = rgb[idx+1] = rgb[idx+2] = (byte)(id*255/1000);
rgb[idx+3] = 255;
idx += 4;
}
// And copy it in one pass
System.Runtime.InteropServices.Marshal.Copy(rgb, 0, ptr, scanLineSize);
ptr += bmpData.Stride;
}
bmp.UnlockBits(bmpData);
If it's not fast enough for you, you could do the whole conversion of the bitmap in-memory and copy the whole array in one pass. This would not be very memory-efficient though, and this should be "fast enough" for the amount of data you are moving.
Update
Just for fun, in one pass:
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int idx = 0;
var rgb = new byte[channelId[0].Count * 4 * channelId.Length];
foreach (var id in channelId.SelectMany(t => t))
{
rgb[idx] = rgb[idx+1] = rgb[idx+2] = (byte)(id*255/1000);
rgb[idx+3] = 255;
idx += 4;
}
System.Runtime.InteropServices.Marshal.Copy(rgb, 0, ptr, rgb.Length);
bmp.UnlockBits(bmpData);
Apart from the memory-efficiency, you'll need to make sure all List<int> in the array contain the same number of elements and that the Stride of the bitmap is the same as width*4. It should be for 2048 elements, but you never know.
BitmapData bitmapData = bmp.LockBits(
new Rectangle(0, 0, channelId[0].Count, channelId.Length),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb
);
unsafe{
ColorARGB* startingPosition = (ColorARGB*) bitmapData.Scan0;
for (int y = 0; y < channelId.Length; y++) {
for (int x = 0; x < channelId[y].Count; x++) {
int myColor = (channelId[y].ElementAt(x) * 255) / 1000;
if (myColor > 255) {
myColor = 255;
} else if (myColor < 0) {
myColor = 0;
}
ColorARGB* position = startingPosition + j + i * channelId[0].Count;
position->A = 255;
position->R = myColor;
position->G = myColor;
position->B = myColor;
}
}
bmp.UnlockBits(bitmapData);
}
Today I wanted to try sth new in image processing in C#.
Szenario as following: I have two black and white images. Now I want to get all white pixels (x,y) from both images and put them into a return image. So in the end my image3 contains all white pixels from image1 and image2.
I'm using unsafe pointers as they are faster.
So in the code I check if image1 in (x,y) == image2 in (x,y) as it is very unlikely that both pictures have a white pixel at the same spot
My approach right now:
private unsafe static Bitmap combine_img(Bitmap img1, Bitmap img2)
{
Bitmap retBitmap = img1;
int width = img1.Width;
int height = img1.Height;
BitmapData image1 = retBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData image2 = img2.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); // here an error
byte* scan1 = (byte*)image1.Scan0.ToPointer();
int stride1 = image1.Stride;
byte* scan2 = (byte*)image2.Scan0.ToPointer();
int stride2 = image2.Stride;
for (int y = 0; y < height; y++)
{
byte* row1 = scan1 + (y * stride1);
byte* row2 = scan2 + (y * stride2);
for (int x = 0; x < width; x++)
{
if (row1[x] == row2[x])
row1[x] = 255;
}
}
img1.UnlockBits(image1);
img2.UnlockBits(image2);
return retBitmap;
}
unfortunately it returns an error when trying to lock the second image, saying the image has already been locked!
The problem was, that strangely I did pass the same image, here the corrected Code:
private unsafe static void combine_img(Bitmap img1, Bitmap img2)
{
BitmapData image1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData image2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int bytesPerPixel = 3;
byte* scan1 = (byte*)image1.Scan0.ToPointer();
int stride1 = image1.Stride;
byte* scan2 = (byte*)image2.Scan0.ToPointer();
int stride2 = image2.Stride;
for (int y = 0; y < img1.Height; y++)
{
byte* row1 = scan1 + (y * stride1);
byte* row2 = scan2 + (y * stride2);
for (int x = 0; x < img1.Width; x++)
{
if (row2[x * bytesPerPixel] == 255)
row1[x * bytesPerPixel] = row1[x * bytesPerPixel - 1] = row1[x * bytesPerPixel-2] = 255;
}
}
img1.UnlockBits(image1);
img2.UnlockBits(image2);
}
I guess your second image is not 24 bit image. Mayber try something like:
BitmapData image2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), ImageLockMode.ReadWrite, img2.PixelFormat);
In this case you will always pass this line (I assume), but problem is you wont know you are actually dealing with 24 bits image or 32 bits image.
I'm trying to optimize the performance on a VNC-type app I'm writing, and I'm having a bit of difficulty getting it truly optimized.
Given two bitmaps, I'd like to return a third bitmap, of the smallest possible size, which fully contains the differences between the bitmaps.
I am able to return a bitmap with the same size as my Bitmap A / Bitmap B, where the differences have color, and the rest is transparent, but this doesn't save me any actual data space, and doesn't actually optimize anything (Unless Bitmaps do something special with completely transparent pixels, but I feel as though they do not.)
Anyway, here's what I hve now, and I could potentially keep track of the pointer where the first change is found, and then keep track of the pointer where the last change is found, but how could I convert these pointers into a rectangle? And how could I crop my full-size difference bitmap in to that rectangle?
Here's the code I'm using so far:
var a = previous.Screen;
var b = next.Screen;
Bitmap output = new Bitmap(
Math.Max(a.Width, b.Width),
Math.Max(a.Height, b.Height),
PixelFormat.Format32bppArgb);
Rectangle recta = new Rectangle(Point.Empty, a.Size);
Rectangle rectb = new Rectangle(Point.Empty, b.Size);
Rectangle rectOutput = new Rectangle(Point.Empty, output.Size);
BitmapData aData = a.LockBits(recta, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData bData = b.LockBits(rectb, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData outputData = output.LockBits(rectOutput, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
try
{
unsafe
{
byte* aPtr = (byte*) aData.Scan0;
byte* bPtr = (byte*) bData.Scan0;
byte* outputPtr = (byte*) outputData.Scan0;
int h = Math.Min(a.Height, b.Height);
int w = Math.Min(a.Width, b.Width);
for (int y = 0; y < h; y++)
{
aPtr = (byte*) aData.Scan0;
bPtr = (byte*) bData.Scan0;
outputPtr = (byte*) outputData.Scan0;
aPtr += y*aData.Stride;
bPtr += y*bData.Stride;
outputPtr += y*outputData.Stride;
for (int x = 0; x < w; x++)
{
bool bl = false;
//the three color channels
for (int j = 0; j < 3; j++)
{
if (*aPtr != *bPtr)
{
bl = true;
}
*outputPtr = (byte) *bPtr;
outputPtr++;
aPtr++;
bPtr++;
}
//alpha, when one or mre color channels are different
if (bl)
*outputPtr = (byte) ((*aPtr + *bPtr)/2);
outputPtr++;
aPtr++;
bPtr++;
}
}
}
}
catch
{
if (output != null)
{
output.UnlockBits(outputData);
output.Dispose();
output = null;
}
}
finally
{
a.UnlockBits(aData);
b.UnlockBits(bData);
if (output != null)
output.UnlockBits(outputData);
}
All the time the size is 1920x1080 but i want it to be for example 10x10 so the process in the function will be faster. I dont mind the size of the bitmap or its quality i just want it to be faster.
In this function im creating an histogram of each Bitmap. The problem is that the FOR loops take very long.
public static long[] GetHistogram(Bitmap b)
{
long[] myHistogram = new long[256]; // histogram סופר כמה יש מכל גוון
BitmapData bmData = null;
//b = new Bitmap(100, 100);
//b.SetPixel(0,0,Color.FromArgb(10,30,40,50)); //run and make it come to here
try
{
ResizeBitmap(b, 10, 10);
//Lock it fixed with 32bpp
bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int scanline = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nWidth = b.Width;
int nHeight = b.Height;
for (int y = 0; y < nHeight; y++)
{
for (int x = 0; x < nWidth; x++)
{
long Temp = 0;
Temp += p[0]; // p[0] - blue, p[1] - green , p[2]-red
Temp += p[1];
Temp += p[2];
Temp = (int)Temp / 3;
myHistogram[Temp]++;
//we do not need to use any offset, we always can increment by pixelsize when
//locking in 32bppArgb - mode
p += 4;
}
}
}
b.UnlockBits(bmData);
}
catch
{
try
{
b.UnlockBits(bmData);
}
catch
{
}
}
return myHistogram; // to save to a file the histogram of all the bitmaps/frames each line contain 0 to 256 values of a frame.
}
So im calling this function: ResizeBitmap(b, 10, 10);
And try to resize each bitmap but after this line i see that the bitmap size is still 1920x1080
This is the ResizeBitmap function:
public static Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
{
Bitmap result = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage((Image)result))
g.DrawImage(b, 0, 0, nWidth, nHeight);
return result;
}
Maybe there is another way to make the GetHistogram function to work faster ?
The method returns the new bitmap, so you should use the return value:
Bitmap newBitmap = ResizeBitmap(b, 10, 10);
b.Dispose();
b = newBitmap;
Side note: You are ignoring the stride of the image, which means that the pointer can go outside the image data. Consider that the stride is negative if the bitmap is stored upside down.