Getting different bitmap object from same image file - c#

I am creating Bitmap object from the same image, but in the end getting different results. It should calculate deviation from the image colors and images are the same so why results are different?
double test1 = GetStdDev("C:\\temp\\images\\file.jpg");
Bitmap img = new Bitmap("C:\\temp\\images\\file.jpg");
double test2 = GetStdDev(img);
public static double GetStdDev(string imageFileName)
{
double total = 0, totalVariance = 0;
int count = 0;
double stdDev = 0;
// First get all the bytes
using (Bitmap b = new Bitmap(imageFileName))
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, b.PixelFormat);
int stride = bmData.Stride;
IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b.Width * 3;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
count++;
byte blue = p[0];
byte green = p[1];
byte red = p[2];
int pixelValue = Color.FromArgb(0, red, green, blue).ToArgb();
total += pixelValue;
double avg = total / count;
totalVariance += Math.Pow(pixelValue - avg, 2);
stdDev = Math.Sqrt(totalVariance / count);
p += 3;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
}
return stdDev;
}
private static double GetStdDev(Bitmap img)
{
double total = 0, totalVariance = 0;
int count = 0;
double stdDev = 0;
// First get all the bytes
using (Bitmap b = new Bitmap(img))
{
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, b.PixelFormat);
int stride = bmData.Stride;
IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b.Width * 3;
for (int y = 0; y < b.Height; ++y)
{
for (int x = 0; x < b.Width; ++x)
{
count++;
byte blue = p[0];
byte green = p[1];
byte red = p[2];
int pixelValue = Color.FromArgb(0, red, green, blue).ToArgb();
total += pixelValue;
double avg = total / count;
totalVariance += Math.Pow(pixelValue - avg, 2);
stdDev = Math.Sqrt(totalVariance / count);
p += 3;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
}
return stdDev;
}

Just found solution, it was problem with deep copying bitmap object. instead using new Bitmap(img) use Bitmap img2 = (Bitmap) img.Clone(); don't know it is the right solution, but it does the job.

Related

Convert an image to grayscale parallel loop

I have written a code that converts image to grayscale. but the code only convert partial of it.
I am trying to convert this code to a Parallel computation. I end up with bugs that I can not get my head around them. Any suggestion?
private void button2_Click(object sender, EventArgs e)
{
Bitmap bmp = (Bitmap)pictureBox1.Image;
unsafe {
//get image dimension
//int width = bmp.Width;
//int height = bmp.Height;
BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
//define variable
int bpp = System.Drawing.Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8;
int hip = bitmapData.Height;
int wib = bitmapData.Width + bpp;
//point to first pixel
byte* PtrFirstPixel = (byte*)bitmapData.Scan0;
//color of pixel
// Color p;
//grayscale
Parallel.For(0, hip, y =>
{
byte* currentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < wib; x = x + bpp)
{
//get pixel value
//p = bmp.GetPixel(x, y);
//extract pixel component ARGB
//int a = p.A;
//int r = p.R;
//int g = p.G;
// int b = p.B;
int b = currentLine[x];
int g = currentLine[x + 1];
int r = currentLine[x + 2];
//find average
int avg = (r + g + b) / 3;
//set new pixel value
// bmp.SetPixel(x, y, Color.FromArgb(a, avg, avg, avg));
currentLine[x] = (byte)avg;
currentLine[x + 1] = (byte)avg;
currentLine[x + 2] = (byte)avg;
}
});
bmp.UnlockBits(bitmapData);
//load grayscale image in picturebox2
//pictureBox2.Image = bmp;
}
pictureBox2.Image = bmp;
}
my out put image
int wib = bitmapData.Width + bpp;
should be:
int wib = bitmapData.Width * bpp;
You want the number of bytes which requires a multiply, not an add. There may be other issues, but this is definitely incorrect.

Sharpen filter - no effect

The following routine is to sharpen an 8 bit indexed grayscale only.
This code seems to have no effect on the input image. That is, what is going in, the same is coming out.
If I increase the value of strength the image seems to be getting darker, but, never filtered.
What could be possibly going wrong?
I am using the following kernel,
double[,] _numericalKernel = new double[,]
{ { -1, -1, -1, },
{ -1, 9, -1, },
{ -1, -1, -1, }, };
The following is my source code for sharpening,
public static Bitmap NonfftSharpen(Bitmap image, double[,] mask, double strength)
{
Bitmap bitmap = (Bitmap)image.Clone();
if (bitmap != null)
{
int width = bitmap.Width;
int height = bitmap.Height;
if (mask.GetLength(0) != mask.GetLength(1))
{
throw new Exception("_numericalKernel dimensions must be same");
}
// Create sharpening filter.
int filterSize = mask.GetLength(0);
double[,] filter = (double[,])mask.Clone();
int channels = sizeof(byte);
double bias = 1.0 - strength;
double factor = strength / 16.0;
int halfOfFilerSize = filterSize / 2;
byte[,] result = new byte[bitmap.Width, bitmap.Height];
// Lock image bits for read/write.
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite,
PixelFormat.Format8bppIndexed);
// Declare an array to hold the bytes of the bitmap.
int memorySize = bitmapData.Stride * height;
byte[] memory = new byte[memorySize];
// Copy the RGB values into the local array.
Marshal.Copy(bitmapData.Scan0, memory, 0, memorySize);
int rgb;
// Fill the color array with the new sharpened color values.
for (int y = halfOfFilerSize; y < height - halfOfFilerSize; y++)
{
for (int x = halfOfFilerSize; x < width - halfOfFilerSize; x++)
{
for (int filterY = 0; filterY < filterSize; filterY++)
{
double grayShade = 0.0;
for (int filterX = 0; filterX < filterSize; filterX++)
{
int imageX = (x - halfOfFilerSize + filterX + width) % width;
int imageY = (y - halfOfFilerSize + filterY + height) % height;
rgb = imageY * bitmapData.Stride + channels * imageX;
grayShade += memory[rgb + 0] * filter[filterX, filterY];
}
rgb = y * bitmapData.Stride + channels * x;
int b = Math.Min(Math.Max((int)(factor * grayShade + (bias * memory[rgb + 0])), 0), 255);
result[x, y] = (byte)b;
}
}
}
// Update the image with the sharpened pixels.
for (int x = halfOfFilerSize; x < width - halfOfFilerSize; x++)
{
for (int y = halfOfFilerSize; y < height - halfOfFilerSize; y++)
{
rgb = y * bitmapData.Stride + channels * x;
memory[rgb + 0] = result[x, y];
}
}
// Copy the RGB values back to the bitmap.
Marshal.Copy(memory, 0, bitmapData.Scan0, memorySize);
// Release image bits.
bitmap.UnlockBits(bitmapData);
return bitmap;
}
else
{
throw new Exception("input image can't be null");
}
}
I've changed your function a bit to make it work.
Take care that the strength parameter has no effect in my function. You can play with the bias and factor values to get different results in brightness and so on.
public static Bitmap NonfftSharpen(Bitmap image, double[,] mask, double strength)
{
Bitmap bitmap = (Bitmap)image.Clone();
if (bitmap != null)
{
int width = bitmap.Width;
int height = bitmap.Height;
if (mask.GetLength(0) != mask.GetLength(1))
{
throw new Exception("_numericalKernel dimensions must be same");
}
// Create sharpening filter.
int filterSize = mask.GetLength(0);
double[,] filter = (double[,])mask.Clone();
int channels = sizeof(byte);
double bias = 0.0; // 1.0 - strength;
double factor = 1.0; // strength / 16.0;
byte[,] result = new byte[bitmap.Width, bitmap.Height];
// Lock image bits for read/write.
BitmapData bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite,
System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
// Declare an array to hold the bytes of the bitmap.
int memorySize = bitmapData.Stride * height;
byte[] memory = new byte[memorySize];
// Copy the RGB values into the local array.
Marshal.Copy(bitmapData.Scan0, memory, 0, memorySize);
int pixel;
// Fill the color array with the new sharpened color values.
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
double grayShade = 0.0;
for (int filterY = 0; filterY < filterSize; filterY++)
{
for (int filterX = 0; filterX < filterSize; filterX++)
{
int imageX = (x - filterSize / 2 + filterX + width) % width;
int imageY = (y - filterSize / 2 + filterY + height) % height;
pixel = imageY * bitmapData.Stride + channels * imageX;
grayShade += memory[pixel] * filter[filterX, filterY];
}
int newPixel = Math.Min(Math.Max((int)(factor * grayShade + bias), 0), 255);
result[x, y] = (byte)newPixel;
}
}
}
// Update the image with the sharpened pixels.
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
pixel = y * bitmapData.Stride + channels * x;
memory[pixel] = result[x, y];
}
}
// Copy the values back to the bitmap.
Marshal.Copy(memory, 0, bitmapData.Scan0, memorySize);
// Release image bits.
bitmap.UnlockBits(bitmapData);
return bitmap;
}
else
{
throw new Exception("input image can't be null");
}
}
I hope this gets you going :)
Regards

Bitmap to int[,] conversion and vice versa

Consider the following two routines.
//Tested
///Working fine.
public static Bitmap ToBitmap(int [,] image)
{
int Width = image.GetLength(0);
int Height = image.GetLength(1);
int i, j;
Bitmap bitmap = new Bitmap(Width, Height);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Width, Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
unsafe
{
byte* address = (byte*)bitmapData.Scan0;
for (i = 0; i < bitmapData.Height; i++)
{
for (j = 0; j < bitmapData.Width; j++)
{
// write the logic implementation here
address[0] = (byte)image[j, i];
address[1] = (byte)image[j, i];
address[2] = (byte)image[j, i];
address[3] = (byte)255;
//4 bytes per pixel
address += 4;
}//end for j
//4 bytes per pixel
address += (bitmapData.Stride - (bitmapData.Width * 4));
}//end for i
}//end unsafe
bitmap.UnlockBits(bitmapData);
return bitmap;// col;
}
//Tested
///Working fine.
public static int[,] ToInteger(Bitmap bitmap)
{
int[,] array2D = new int[bitmap.Width, bitmap.Height];
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppRgb);
unsafe
{
byte* address = (byte*)bitmapData.Scan0;
int paddingOffset = bitmapData.Stride - (bitmap.Width * 4);//4 bytes per pixel
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
byte[] temp = new byte[4];
temp[0] = address[0];
temp[1] = address[1];
temp[2] = address[2];
temp[3] = address[3];
array2D[j, i] = BitConverter.ToInt32(temp, 0);
//4-bytes per pixel
address += 4;//4-channels
}
address += paddingOffset;
}
}
bitmap.UnlockBits(bitmapData);
return array2D;
}
These two routines work fine for 32bpp images. These routines only work when pixel format is set to PixelFormat.Format32bpp. If I use PixelFormat.Format8bppIndexed, it generates an exception.
In order to avoid that exception (also, I couldn't achieve seamless conversion between byte and int because of address calculation problem), I need to convert that 32 bit Bitmap to gray-scale every time the int[,] is converted back to a Bitmap. I want to get rid of this problem.
Bitmap grayscale = Grayscale.ToGrayscale(InputImage);
//Here, the Bitmap is treated as a 32bit image
//to avoid the exception eventhough it is already
//an 8bpp grayscale image.
int[,] i1 = ImageDataConverter.ToInteger(grayscale);
Complex[,] comp = ImageDataConverter.ToComplex(i1);
int[,] i2 = ImageDataConverter.ToInteger(comp);
Bitmap b2 = ImageDataConverter.ToBitmap(i2);
//It is already a Grayscale image.
//But, the problem is, b2.PixelFormat is set to
//PixelFormat.Formap32bpp because of those routines.
//Hence the unnecessay conversion.
b2 = Grayscale.ToGrayscale(b2);
I need to modify them to operate on 8bpp indexed (grayscale) images only.
How can I achieve that?
If you want to deal with an indexed bitmap, you need to read each byte of the image, and lookup the color from the palette. When you save the image, you'll need to do the reverse logic:
public static Bitmap ToBitmap(int[,] image)
{
int width = image.GetLength(0);
int height = image.GetLength(1);
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
int stride = bitmapData.Stride;
// A dictionary of colors to their index values
Dictionary<int, int> palette = new Dictionary<int, int>();
// A flat list of colors
List<Color> paletteList = new List<Color>();
unsafe
{
byte* address = (byte*)bitmapData.Scan0;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
// Get the color from the Bitmap
int color = image[x, y];
if (!palette.ContainsKey(color))
{
// This color isn't in the palette, go ahead and add it
palette.Add(color, palette.Count);
paletteList.Add(Color.FromArgb(color));
if (palette.Count >= 256)
{
// The palette is too big. Ideally this function would
// dither some pixels so it could handle this condition
// but that would make this example overly complicated
throw new InvalidOperationException("Too many colors in image");
}
}
// And lookup the index of the color in the palette and
// add it to the BitmapData's memory
address[stride * y + x] = (byte)palette[color];
}
}
}
bitmap.UnlockBits(bitmapData);
// Each time you call Bitmap.Palette it actually returns
// a Clone of the object, so we need to ask for a cloned
// copy here.
var newPalette = bitmap.Palette;
// For each one of our colors, add it to the palette object
for (int i = 0; i < paletteList.Count; i++)
{
newPalette.Entries[i] = paletteList[i];
}
// And since this is a clone, assign it back to the bitmap
// so it'll take effect.
bitmap.Palette = newPalette;
return bitmap;
}
public static int[,] ToInteger(Bitmap bitmap)
{
if (bitmap.Palette.Entries.Length == 0)
{
// This doesn't appear to have a palette, so this operation doesn't
// make sense
throw new InvalidOperationException("bitmap is not an indexed bitmap");
}
int width = bitmap.Width;
int height = bitmap.Height;
int[,] array2D = new int[width, height];
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly,
PixelFormat.Format8bppIndexed);
unsafe
{
// Pull out the stride to prevent asking for it many times
int stride = bitmapData.Stride;
byte* address = (byte*)bitmapData.Scan0;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
// Lookup the color based off the pixel, and set it's value
// to the return array
array2D[x, y] = bitmap.Palette.Entries[address[stride * y + x]].ToArgb();
}
}
}
bitmap.UnlockBits(bitmapData);
return array2D;
}
public static int[,] ToInteger(Bitmap bitmap)
{
int[,] array2D = new int[bitmap.Width, bitmap.Height];
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format8bppIndexed);
int bytesPerPixel = sizeof(byte);
unsafe
{
byte* address = (byte*)bitmapData.Scan0;
int paddingOffset = bitmapData.Stride - (bitmap.Width * bytesPerPixel);
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
byte[] temp = new byte[bytesPerPixel];
for (int k = 0; k < bytesPerPixel; k++)
{
temp[k] = address[k];
}
int iii = 0;
if (bytesPerPixel >= sizeof(int))
{
iii = BitConverter.ToInt32(temp, 0);
}
else
{
iii = (int)temp[0];
}
array2D[j, i] = iii;
address += bytesPerPixel;
}
address += paddingOffset;
}
}
bitmap.UnlockBits(bitmapData);
return array2D;
}
public static Bitmap ToBitmap(int[,] image)
{
int Width = image.GetLength(0);
int Height = image.GetLength(1);
int i, j;
Bitmap bitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Width, Height),
ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
int bytesPerPixel = sizeof(byte);
unsafe
{
byte* address = (byte*)bitmapData.Scan0;
for (i = 0; i < bitmapData.Height; i++)
{
for (j = 0; j < bitmapData.Width; j++)
{
byte[] bytes = BitConverter.GetBytes(image[j, i]);
for (int k = 0; k < bytesPerPixel; k++)
{
address[k] = bytes[k];
}
address += bytesPerPixel;
}
address += (bitmapData.Stride - (bitmapData.Width * bytesPerPixel));
}
}
bitmap.UnlockBits(bitmapData);
Grayscale.SetGrayscalePalette(bitmap);
return bitmap;
}

C# cut area from image

I'm trying to implement a cropping method myself,using the unsafe code and pointer to speed up the whole process.
This is my code:
private unsafe void Cut(Bitmap bmp, Rectangle r) {
Bitmap result = new Bitmap(r.Width, r.Height, bmp.PixelFormat);
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
BitmapData bmData2 = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, result.PixelFormat);
IntPtr scan0 = bmData.Scan0;
IntPtr scan02 = bmData2.Scan0;
int stride = bmData.Stride;
int stride2 = bmData2.Stride;
int x = r.X;
int y = r.Y;
int width = r.Width;
int height = r.Height;
for (; y < height; y++) {
byte * p = (byte * ) scan0.ToPointer();
p += y * stride;
byte * p2 = (byte * ) scan02.ToPointer();
p2 += y * stride2;
for (; x < width; x++) {
p2[0] = p[0];
p2[1] = p[1];
p2[2] = p[2];
p2[3]=p[3];
p += 4;
p2 += 4;
}
}
result.Save("a.png");
}
And the call to this method:
Bitmap b = (Bitmap)Bitmap.FromFile(#"C:\Users\itapi\Desktop\1.png");
Rectangle r = new Rectangle(200, 500, 300, 450);
Cut(b, r);
When i run the code,i just get a black rectangle as result...not the pixels i wanted to copy from the intial image.
The image from the example above is in32bpprgb format
I'm not sure what i'm doing wrong..i'll appreciate any help.
Thanks.
On the result, you chose "System.Drawing.Imaging.ImageLockMode.ReadOnly" I think you want ReadWrite or WriteOnly.
When I ran through debugger, this statement: for (; y < height; y++) condition failed immediately on my rather large image. So your loop logic is incorrect for running through the lines... Use a debugger :)
EDIT I ran it through the debugger, and your Y and X logic is wrong. I did a quick fix on the Y logic, and got it too crop. You'll have to do something similar to the X to get the correct crop point.
Try this, it cropped and saved a file:
int x = r.X;
int y = r.Y;
int width = r.Width;
int height = r.Height;
int newY = 0;
for (y = r.Y; y < height+r.Y; y++) //For each line in the old image
{
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
byte* p2 = (byte*)scan02.ToPointer();
p2 += newY * stride2;
for (x=r.X; x < width+r.X; x++)
{
p2[0] = p[0];
p2[1] = p[1];
p2[2] = p[2];
p2[3] = p[3];
p += 4;
p2 += 4;
}
newY++;
}
result.Save("\\a.png");

compare one pixel with all pixels

i want to compare pixel of an image with all pixel of second image and then next pixel with all pixel of second image;
i am using this code in which i am comparing pixel (converted in byte) one pixel of one image with second pixel of second but i do not want this approach. Please reply fast.
Thanks in advance.
public static double GetDifferentPercentageSneller(ref Bitmap bmp1, ref Bitmap bmp2)
{
//if (bmp1 == null || bmp2 == null)
// return 100.0;
//if (bmp1.Size != bmp2.Size)
// return 100.0;
//if (bmp1.PixelFormat != bmp2.PixelFormat)
// return 100.0;
int iMismatch = 0;
int iMatch = 0;
unsafe
{
BitmapData data1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadOnly, bmp1.PixelFormat);
BitmapData data2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadOnly, bmp2.PixelFormat);
int pixelBytes = 0;
switch (data1.PixelFormat)
{
case PixelFormat.Format32bppArgb:
pixelBytes = 4;
break;
case PixelFormat.Format24bppRgb:
pixelBytes = 3;
break;
default:
throw new Exception("Bitmap format not supported");
}
int paddingBytes = data1.Stride % pixelBytes;
byte* location1 = (byte*)data1.Scan0;
byte* location2 = (byte*)data2.Scan0;
for (int y = 0; y < data1.Height; ++y)
{
for (int x = 0; x < data1.Width; ++x)
{
if (*location1 == *location2)
{
iMatch++;
}
else
{
iMismatch++;
}
location1 += pixelBytes;
location2 += pixelBytes;
}
location1 += paddingBytes;
location2 += paddingBytes;
}
bmp1.UnlockBits(data1);
bmp2.UnlockBits(data2);
}
double percent = (double)iMatch/ (double)(iMismatch + iMatch);
return percent * 100.0;
}
You have to always compare the LARGER image (both x, y) with the SMALLER. Although I don't know what your are exactly after, you can do it simply like this.
BitmapImage Image1 = new BitmapImage(ImageStream);
BitmapImage Image2 = new BitmapImage(ImageStream);
int X = Image1.Width > Image2.Width ? Image2.Width : Image1.Width;
int Y = Image1.Hieght > Image2.Height ? Image2.Heigth : Image1.Height;
for(int x = 0; x < X; x++){
for(int y = 0; y < Y; y++){
Color color1 = Image1.GetPixel(x, y);
Color color2 = Image2.GetPixel(x, y);
// Do comparison here
}
}

Categories