I have a drawing application developed in winforms C# which uses many System.Drawing.Bitmap object throughout the code.
Now I am writing it into WPF with c#. I have done almost 90% of the conversion.
Coming to the problem... I have the following code which is used to traverse the image pixel by pixel
Bitmap result = new Bitmap(img); // img is of System.Drawing.Image
result.SetResolution(img.HorizontalResolution, img.VerticalResolution);
BitmapData bmpData = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.ReadWrite, img.PixelFormat);
int pixelBytes = System.Drawing.Image.GetPixelFormatSize(img.PixelFormat) / 8;
System.IntPtr ptr = bmpData.Scan0;
int size = bmpData.Stride * result.Height;
byte[] pixels = new byte[size];
int index = 0;
double R = 0;
double G = 0;
double B = 0;
System.Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, size);
for (int row = 0; row <= result.Height - 1; row++)
{
for (int col = 0; col <= result.Width - 1; col++)
{
index = (row * bmpData.Stride) + (col * pixelBytes);
R = pixels[index + 2];
G = pixels[index + 1];
B = pixels[index + 0];
.
.// logic code
.
}
}
result.UnlockBits(bmpData);
It uses System.Drawing's for the purpose.
Is it possible to achieve this thing in wpf as well keeping it simple as it is?
In addtion to Chris's anwser you might want to look at WriteableBitmap. It's another way to manipulate images pixels.
Example
You can use BitmapImage.CopyPixels to copy the image your pixel buffer.
BitmapImage img= new BitmapImage(...); // This is your image
int bytePerPixel = (img.Format.BitsPerPixel + 7) / 8;
int stride = img.PixelWidth * bytesPerPixel;
int size = img.PixelHeight * stride;
byte[] pixels = new byte[size];
img.CopyPixels(pixels, stride, 0);
// Now you can access 'pixels' to perform your logic
for (int row = 0; row < img.PixelHeight; row++)
{
for (int col = 0; col < img.PixelWidth; col++)
{
index = (row * stride) + (col * bytePerPixel );
...
}
}
Related
I am capturing data from some camera (array of RAW data).
Then I'm mapping this data to RGB values according to color palette.
I need to map it as fast as possible, so I use BitmapDdata and edit pixels in unsafe piece of code using pointers.
public void dataAcquired(int[] data)
{
Bitmap bmp = new Bitmap(width, height);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
for (int i = 0; i < data.Length; i++)
{
int x = i % bmp.Width;
int y = i / bmp.Width;
Rgb rgb = mapColors[data[i]];
unsafe
{
byte* ptr = (byte*)data.Scan0;
ptr[(x * 3) + y * data.Stride] = rgb.b;
ptr[(x * 3) + y * data.Stride + 1] = rgb.g;
ptr[(x * 3) + y * data.Stride + 2] = rgb.r;
}
}
bmp.UnlockBits(data);
}
And I'm doing this for every incoming frame. It works fine, but it still takes something like 30ms for each frame for 320x240 pixels.
Is it possible to make it even more faster? Maybe I couldlock/unlock data in memory only once, but I'm not sure about this.
Instead of calculating x and y for each pixel, you could make them loop counters, like this:
for( y = 0; y < bmp.Height; y++ )
for( x = 0; x < bmp.Width; x++ )
Better yet, ditch x and y altogether and just keep incrementing the ptr pointer instead of recalculating an offset from the ptr pointer three times per pixel.
Try this (warning: I have not checked it.)
public void dataAcquired()
{
Bitmap bmp = new Bitmap(width, height);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
int i = 0;
byte* ptr = (byte*)data.Scan0;
for( int y = 0; y < bmp.Height; y++ )
{
byte* ptr2 = ptr;
for( int x = 0; x < bmp.Width; x++ )
{
Rgb rgb = mapColors[data[i++]];
*(ptr2++) = rgb.b;
*(ptr2++) = rgb.g;
*(ptr2++) = rgb.r;
}
ptr += data.Stride;
}
}
bmp.UnlockBits(data);
}
Try run code in parallel: change
for (int i = 0; i < data.Length; i++) {
...
}
into
Parallel.For(0, data.Length, (i) => {
int x = i % bmp.Width;
int y = i / bmp.Width;
Rgb rgb = mapColors[data[i]];
unsafe {
byte* ptr = (byte*) data.Scan0;
ptr[(x * 3) + y * data.Stride] = rgb.b;
ptr[(x * 3) + y * data.Stride + 1] = rgb.g;
ptr[(x * 3) + y * data.Stride + 2] = rgb.r;
}
});
How do I go about setting a Bitmap with a Color of the pixels. I created a program with LockBits and it is very fast but now I need to set a PictureBox with that image I ran through the LockBits I do not want to use SetPixels My current code is:
Bitmap imageFile = new Bitmap(bmpPath);
BitmapData imageData = imageFile.LockBits(new Rectangle(0, 0, imageFile.Width, imageFile.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
IntPtr Pointer = imageData.Scan0;
int ArraySize = Math.Abs(imageData.Stride) * imageFile.Height;
byte[] PixelArray = new byte[ArraySize];
Marshal.Copy(Pointer, PixelArray, 0, ArraySize);
int PixelAmount = 4; //ArGb
Color ArGBformat;
Bitmap RenderedImage = new Bitmap(imageFile.Width, imageFile.Height);
byte NewAlpha;
byte NewRed;
byte NewGreen;
byte NewBlue;
unsafe
{
for (int y = 0; y < imageData.Height; y++)
{
byte* row = (byte*)imageData.Scan0 + (y * imageData.Stride);
for (int x = 0; x < imageData.Width; x++)
{
int offSet = x * PixelAmount;
// read pixels
byte blue = row[offSet];
byte green = row[offSet + 1];
byte red = row[offSet + 2];
byte alpha = row[offSet + 3];
//Manipulates pixels
NewAlpha = Convert.ToByte(Math.Abs(alpha - _Alpha));
NewRed = Convert.ToByte(Math.Abs(red - _Red));
NewBlue = Convert.ToByte(Math.Abs(blue - _Blue));
NewGreen = Convert.ToByte(Math.Abs(green - _Green));
ArGBformat = Color.FromArgb(NewAlpha, NewRed, NewGreen, NewBlue);
RenderedImage.SetPixel(x, y, ArGBformat); //Slow and want something else
}
}
}
I would like to set my PictureBox1 to the pixels that get ran through the program.
Found the answer. I needed to set the pixels back.
//Sets image
row[offSet] = NewBlue;
row[offSet + 1] = NewGreen;
row[offSet + 2] = NewRed;
row[offSet + 3] = NewAlpha;
Suppose i had two nearly identical images and i wanted to locate and highlight the differences between them and produce the diff image. the routine works but this routine ask to supply color which i do not want. here is my code.
public class ImageTool
{
public static unsafe Bitmap GetDifferenceImage(Bitmap image1, Bitmap image2, Color matchColor)
{
if (image1 == null | image2 == null)
return null;
if (image1.Height != image2.Height || image1.Width != image2.Width)
return null;
Bitmap diffImage = image2.Clone() as Bitmap;
int height = image1.Height;
int width = image1.Width;
BitmapData data1 = image1.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
BitmapData data2 = image2.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
BitmapData diffData = diffImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
byte* data1Ptr = (byte*)data1.Scan0;
byte* data2Ptr = (byte*)data2.Scan0;
byte* diffPtr = (byte*)diffData.Scan0;
byte[] swapColor = new byte[3];
swapColor[0] = matchColor.B;
swapColor[1] = matchColor.G;
swapColor[2] = matchColor.R;
int rowPadding = data1.Stride - (image1.Width * 3);
// iterate over height (rows)
for (int i = 0; i < height; i++)
{
// iterate over width (columns)
for (int j = 0; j < width; j++)
{
int same = 0;
byte[] tmp = new byte[3];
// compare pixels and copy new values into temporary array
for (int x = 0; x < 3; x++)
{
tmp[x] = data2Ptr[0];
if (data1Ptr[0] == data2Ptr[0])
{
same++;
}
data1Ptr++; // advance image1 ptr
data2Ptr++; // advance image2 ptr
}
// swap color or add new values
for (int x = 0; x < 3; x++)
{
diffPtr[0] = (same == 3) ? swapColor[x] : tmp[x];
diffPtr++; // advance diff image ptr
}
}
// at the end of each column, skip extra padding
if (rowPadding > 0)
{
data1Ptr += rowPadding;
data2Ptr += rowPadding;
diffPtr += rowPadding;
}
}
image1.UnlockBits(data1);
image2.UnlockBits(data2);
diffImage.UnlockBits(diffData);
return diffImage;
}
}
calling like this way:
Bitmap diff = ImageTool.GetDifferenceImage(image1, image2, Color.Pink);
diff.MakeTransparent(Color.Pink);
diff.Save("C:\\test-diff.png",ImageFormat.Png);
some one just guide me how to change this routine as a result we do not have to pass color when i will call GetDifferenceImage() method.
this way image comparison is best technique if not then guide me how to develop a routine which can be more faster to get the diff image.
after getting the diff image how can i merge the diff image with image1. help me to develop a faster merge routine.
The diff image is black if two images are identical and has an increasing brightness for pixels with larger differences. You can just change the algorithm so that instead of assigning the pixel the swapcolor it assigns it the difference between the two colors.
// iterate over height (rows)
for (int i = 0; i < height; i++)
{
// iterate over width (columns)
for (int j = 0; j < width; j++)
{
// for each channel
for (int x=0; x<3; x++)
{
diffPtr[0] = Abs(data1Ptr[0]-data2Ptr[0]);
data1Ptr++; // advance image1 ptr
data2Ptr++; // advance image2 ptr
diffPtr++; // advance diff image ptr
}
}
// at the end of each column, skip extra padding
if (rowPadding > 0)
{
data1Ptr += rowPadding;
data2Ptr += rowPadding;
diffPtr += rowPadding;
}
}
How you show/merge the diff will depend on what you are going to do with it.
public unsafe Bitmap MedianFilter(Bitmap Img)
{
int Size =2;
List<byte> R = new List<byte>();
List<byte> G = new List<byte>();
List<byte> B = new List<byte>();
int ApetureMin = -(Size / 2);
int ApetureMax = (Size / 2);
BitmapData imageData = Img.LockBits(new Rectangle(0, 0, Img.Width, Img.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
byte* start = (byte*)imageData.Scan0.ToPointer ();
for (int x = 0; x < imageData.Width; x++)
{
for (int y = 0; y < imageData.Height; y++)
{
for (int x1 = ApetureMin; x1 < ApetureMax; x1++)
{
int valx = x + x1;
if (valx >= 0 && valx < imageData.Width)
{
for (int y1 = ApetureMin; y1 < ApetureMax; y1++)
{
int valy = y + y1;
if (valy >= 0 && valy < imageData.Height)
{
Color tempColor = Img.GetPixel(valx, valy);// error come from here
R.Add(tempColor.R);
G.Add(tempColor.G);
B.Add(tempColor.B);
}
}
}
}
}
}
R.Sort();
G.Sort();
B.Sort();
Img.UnlockBits(imageData);
return Img;
}
I tried to do this. but i got an error call "Bitmap region is already locked" can anyone help how to solve this. (error position is highlighted)
GetPixel is the slooow way to access the image and doesn't work (as you noticed) anymore if someone else starts messing with the image buffer directly. Why would you want to do that?
Check Using the LockBits method to access image data for some good insight into fast image manipulation.
In this case, use something like this instead:
int pixelSize = 4 /* Check below or the site I linked to and make sure this is correct */
byte* color =(byte *)imageData .Scan0+(y*imageData .Stride) + x * pixelSize;
Note that this gives you the first byte for that pixel. Depending on the color format you are looking at (ARGB? RGB? ..) you need to access the following bytes as well. Seems to suite your usecase anyway, since you just care about byte values, not the Color value.
So, after having some spare minutes, this is what I'd came up with (please take your time to understand and check it, I just made sure it compiles):
public void SomeStuff(Bitmap image)
{
var imageWidth = image.Width;
var imageHeight = image.Height;
var imageData = image.LockBits(new Rectangle(0, 0, imageWidth, imageHeight), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
var imageByteCount = imageData.Stride*imageData.Height;
var imageBuffer = new byte[imageByteCount];
Marshal.Copy(imageData.Scan0, imageBuffer, 0, imageByteCount);
for (int x = 0; x < imageWidth; x++)
{
for (int y = 0; y < imageHeight; y++)
{
var pixelColor = GetPixel(imageBuffer, imageData.Stride, x, y);
// Do your stuff
}
}
}
private static Color GetPixel(byte[] imageBuffer, int imageStride, int x, int y)
{
int pixelBase = y*imageStride + x*3;
byte blue = imageBuffer[pixelBase];
byte green = imageBuffer[pixelBase + 1];
byte red = imageBuffer[pixelBase + 2];
return Color.FromArgb(red, green, blue);
}
This
Relies on the PixelFormat you used in your sample (regarding both the pixelsize/bytes per pixel and the order of the values). If you change the PixelFormat this will break.
Doesn't need the unsafe keyword. I doubt that it makes a lot of difference, but you are free to use the pointer based access instead, the method would be the same.
Hello friends am trying to apply 3x3 median filter to fingerprint image of appxo 500x500.
I am using pointers to acess the image data. But i realy cant figure out how to do it. I know the concept very well, but if u guyz help me out in code it will be great help. I searched on net, but i dint get any help. thank you
public void medianfilter(Bitmap image)
{
Byte[,] rtemp = new Byte[3, 3];
Byte[,] gtemp = new Byte[3, 3];
Byte[,] btemp = new Byte[3, 3];
BitmapData data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = data.Stride;
unsafe {
byte* imgPtr = (byte*)(void*)(data.Scan0);
int nOffset = stride - image.Width * 3;
for (int i = 0; i < image.Width; i++)
{
for (int j = 0; j < image.Height; j++)
{
for (int x = i; x < 3 + i; x++)
{
for (int y = j; y < 3 + j; y++) {
rtemp[x, y] = imgPtr[0];
gtemp[x, y] = imgPtr[1];
btemp[x, y] = imgPtr[2];
imgPtr += 3; } } imgPtr += nOffset;
}
}
}
}
First of all you are not modifying the Bitmap at all!
You need to dereference the pointer before to apply the change and then you have to UNLOCK the bitmap...
Here's what I had in my old computer graphics course. Modify it as needed.
unsafe
{
//Go to first pixel, the cast is important
byte* p = (byte*)imageData.Scan0.ToPointer();
//For each line
for (int y = 0; y < bmp.Height; y++)
{
//For each pixel (bmp.Width * 3) because jpg has R, G, and B value if the bitmap has an alpha do a *4 multiplier
for (int x = 0; x < bmp.Width * 3; x++)
{
//Invert from the original image
*p = (byte)(255 - *p);
//Go to next pointer
p++;
}
//Move pointer to the right end of the line and then go down to a new line
//Skip the unused space
p += offset;
}
}
bmp.UnlockBits(imageData);
bmp.Save(path);
Hope it helps!