Format16bppGrayScale : a generic error occurred in gdi+ - c#

I am trying to use Format16bppGrayScale, but I keep getting an error: "a generic error occurred in gdi+". I have a monochromatic camera that is giving me a 16-bit value and I want to store it in Format16bppGrayScale so I can keep my image from the camera. the 16-bit value is coming in an array that is two array indexes per one 16 bit value
public Bitmap ByteToImage1(byte[] blob)
{
Bitmap bmpRGB = new Bitmap(KeepWidth, KeepHeight, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
Rectangle rect = new Rectangle(0, 0, KeepWidth, KeepHeight);
BitmapData bmpData = bmpRGB.LockBits(rect, ImageLockMode.WriteOnly, bmpRGB.PixelFormat);
int padding = bmpData.Stride - 3 * KeepWidth;
unsafe
{
int i = 0;
byte* ptr = (byte*)bmpData.Scan0;
for( int y= 0; y < KeepHeight; y++)
{
for(int x = 0; x < KeepWidth; x++)
{
ptr[1] = blob[i+1];
ptr[0] = blob[i];
i = i + 2;
ptr += 2;
}
ptr += padding;
}
}
bmpRGB.UnlockBits(bmpData);
return bmpRGB;
}
Update: Karsten's code
private Bitmap GenerateDummy16bitImage(byte[] temp)
{
int i2 = 0;
Bitmap b16bpp = new Bitmap(KeepWidth, KeepHeight, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
var rect = new Rectangle(0, 0, KeepWidth, KeepHeight);
var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * KeepHeight;
var bitmapBytes = new short[KeepWidth * KeepHeight];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < KeepWidth; x++)
{
for (int y = 0; y < KeepHeight; y++)
{
var i = ((y * KeepWidth) + x); // 16bpp
// Generate the next random pixel color value.
var value = (short)temp[i2]; //(short)random.Next(5);
bitmapBytes[i] = value; // GRAY
i2++;
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, bitmapBytes.Length);
// Unlock the bitmap, we're all done.
b16bpp.UnlockBits(bitmapData);
return b16bpp;
}

Related

Convert image to byte array C# in a proper way

My problem is that I need to convert an image to a byte array to obtain its pixels.
My image size is 268x188 and when I use the property PixelsFormat it returns Format24bppRgb, so I understand that each pixel contains 3 bytes.
If this is true, the size of the pixels should be 268*188*3 = 151152 bytes, but the byte array that I am creating has a size of 4906 bytes, which is the size of the image file in my computer.
I donĀ“t know if there is another way to obtain these pixels or you can only obtain image file size.
If you want to ignore the header and the compression of the file you can do the following.
var path = ...
using(var image = Image.FromFile(path))
using(var bitmap = new Bitmap(image))
{
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
var bytesPerPixel = 4; // bitmapData.PixelFormat (image.PixelFormat and bitmapData.PixelFormat can be different)
var ptr = bitmapData.Scan0;
var imageSize = bitmapData.Width * bitmapData.Height * bytesPerPixel;
var data = new byte[imageSize];
for (int x = 0; x < imageSize; x += bytesPerPixel)
{
for(var y = 0; y < bytesPerPixel; y++)
{
data[x + y] = Marshal.ReadByte(ptr);
ptr += 1;
}
}
bitmap.UnlockBits(bitmapData);
}
To get image pixel try this:
public static byte[] GetImageRaw(Bitmap image)
{
if (image == null)
{
throw new ArgumentNullException(nameof(image));
}
if (image.PixelFormat != PixelFormat.Format24bppRgb)
{
throw new NotSupportedException("Invalid pixel format.");
}
const int PixelSize = 3;
var data = image.LockBits(
new Rectangle(Point.Empty, image.Size),
ImageLockMode.ReadWrite,
image.PixelFormat);
try
{
var bytes = new byte[data.Width * data.Height * PixelSize];
for (var y = 0; y < data.Height; ++y)
{
var source = (IntPtr)((long)data.Scan0 + y * data.Stride);
// copy row without padding
Marshal.Copy(source, bytes, y * data.Width * PixelSize, data.Width * PixelSize);
}
return bytes;
}
finally
{
image.UnlockBits(data);
}
}
Take a look at Bitmap.LockBits
I use this code in ASP.NET application. Very simple:
var imagePath = GetFilePathToYourImage();
using (var img = System.IO.File.OpenRead(imagePath))
{
var imageBytes = new byte[img.Length];
img.Read(imageBytes, 0, (int)img.Length);
}

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# merge images sent over socket

im trying to send screenshots over socket so i use unsafe pointers to send only the differences:
private unsafe Bitmap GetDiffBitmap(Bitmap bmp, Bitmap bmp2)
{
bmpRes = new Bitmap(1920, 1080,bmp.PixelFormat);
bmData = bmp.LockBits(new Rectangle(0, 0, 1920, 1080), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);
bmDataRes = bmpRes.LockBits(new Rectangle(0, 0, bmpRes.Width, bmpRes.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
IntPtr scan0 = bmData.Scan0;
IntPtr scan02 = bmData2.Scan0;
IntPtr scan0Res = bmDataRes.Scan0;
int stride = bmData.Stride;
int stride2 = bmData2.Stride;
int strideRes = bmDataRes.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
//for(int y = 0; y < nHeight; y++)
System.Threading.Tasks.Parallel.For(0, nHeight, y =>
{
//define the pointers inside the first loop for parallelizing
byte* p = (byte*)scan0.ToPointer();
p += y * stride;
byte* p2 = (byte*)scan02.ToPointer();
p2 += y * stride2;
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] != p2[0] || p[1] != p2[1] || p[2] != p2[2])
{
pRes[0] = p2[0];
pRes[1] = p2[1];
pRes[2] = p2[2];
//alpha (opacity)
pRes[3] = p2[3];
}
p += 4;
p2 += 4;
pRes += 4;
}
});
bmp.UnlockBits(bmData);
bmp2.UnlockBits(bmData2);
bmpRes.UnlockBits(bmDataRes);
return bmpRes;
}
this is the call on the client:
private void startSend()
{
Bitmap curr;
Bitmap pre = screenshot();
byte []bmpBytes = imageToByteArray(pre);
SendVarData(handler, bmpBytes);// this is the first send of the whole screen
while (true)
{
curr = screenshot();
Bitmap diff = GetDiffBitmap(pre, curr);//generate differences.
bmpBytes = imageToByteArray(diff);
SendVarData(handler, bmpBytes);//sending the diff image.
pre = curr;
}
}
SendVarData is a method which send the bytes array over the socket it is not the problem here-leave it.
this is how i get the data in the server side:
public void startListening()
{
Bitmap merge = new Bitmap(1920, 1080);
Graphics g = Graphics.FromImage(merge);
Bitmap prev = byteArrayToImage(ReceiveVarData(client.Client)) as Bitmap;//getting the first full size image.
theImage.Image = prev;//assisning it to picturebox.
while (true)
{
byte[]data = ReceiveVarData(client.Client);
Bitmap curr = byteArrayToImage(data) as Bitmap;//here is the diffrent image
//merge and apply differences
g.DrawImage(prev, 0, 0,1920, 1080);
g.DrawImage(curr, 0, 0, 1920,1080);
theImage.Image = merge;
count++;
prev = merge;
}
}
my problem is that eventhough i merge the two images with the Graphics.Draw it still(after the first dataReceive) looks like not full... this is actually what i see on the server..
i dont know what's wrong here... can anyone light my eyes? :D
#DmitriTrofimov
if (p[0] != p2[0] || p[1] != p2[1] || p[2] != p2[2])
{
pRes[0] = p2[0];
pRes[1] = p2[1];
pRes[2] = p2[2];
//alpha (opacity)
pRes[3] = p2[3];
}
else
pRes[0] = 0;
This is what you get when you keep pixel opacity. You should set Alpha to zero on the pixels that are not different and make sure you are working with 32-bit images (refer to PixelFormat parameter of Bitmap constructor).
P.S. Make sure you compress the diff bitmap otherwise it's no use.

How can i convert bitmap array of rgbvalues 0 and 255 to 2d int array of 0 and 1?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
namespace ConvertBitmapToArray
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Bitmap bitmap = new Bitmap(#"C:\Temp\Gimp Maps\Bitmaps\test1.bmp");
CreateArray(bitmap);
File.Create(#"C:\Temp\Gimp Maps\Maps.cs");
}
private void CreateArray(Bitmap bmp)
{
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);
IntPtr ptr = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
bmp.UnlockBits(bmpData);
for (int i = 0; i < rgbValues.Count(); i++)
{
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
I have a bitmap of black and white size 8x8 or 800x800 and i get array of rgbValues.
Now in the loop :
for (int i = 0; i < rgbValues.Count(); i++)
{
}
I want to create 2d array of int built from 0 and 1. For example like this:
int[,] map = new int[,]
{
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{0,0,0,0,0,0,0,0,},
{1,1,1,1,0,0,0,0,},
{0,0,0,1,0,0,0,0,},
{0,0,1,1,0,0,0,0,},
{0,0,0,0,1,1,1,0,},
{0,0,0,0,0,0,0,1,},
};
So 255 will be 0 and 0 will be 1. In the int array example 1 is black and 0 white.
I tried this:
private void CreateArray(Bitmap bmp)
{
int[] pix = new int[bmp.Width*bmp.Height];
int x, y, rgb, val;
for (y = 0; y < bmp.Height; y++)
{
for (x = 0; x < bmp.Width; x++)
{
Color c = bmp.GetPixel(x, y);
rgb = c.ToArgb();
if (rgb == 0xff000000) // if black
{
val = 0;
}
else
val = 1;
pix[y * bmp.Width + x] = val;
}
}
}
But it's not good i'm not sure about the Color c and the rgb = c.ToArgb();
And on the if condition i get green line say:
Warning 1
Comparison to integral constant is useless; the constant is outside the range of type 'int'
How can i do it with LockBits or with GetPixel ? I want to know how different it is using each method.
This is what i did so far:
private void CreateArray(Bitmap bmp)
{
int[,] array = new int[,]
{
};
// 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[] rgbValues = new byte[bytes];
int x, y, rgb, val;
for (y = 0; y < bmp.Height; y++)
{
for (x = 0; x < bmp.Width; x++)
{
var row = ptr + (y * bmpData.Stride);
var pixel = row + x * ; // bpp will be 4 in your case
// A bit=0
// R bit=1
// G bit=2
// B bit=3
// (depending on your image pixel format)
for (var bit = 0; bit < bpp; bit++)
{
var pixelComponent = pixel[bit];
}
}
}
}
To loop through the bytes of an image in a more logical fashion add the unsafe keyword to your function signature (private unsafe void ...) and make sure you enable unsafe code through your project properties.
var pt = (byte*)bmpData.Scan0;
Now you can easily loop through x and y:
var row = pt + (y * bmpData.Stride);
var pixel = row + x * bpp; // bpp will be 4 in your case
// A bit=0
// R bit=1
// G bit=2
// B bit=3
// (depending on your image pixel format)
for (var bit = 0; bit < bpp; bit++)
{
var pixelComponent = pixel[bit];
}
If you really want the final results as a 2d array, you shoud use [,] or [][] instead of [] (single dimension array)
The huge difference between the LockBits approach vs the GetPixel approach is performance. The GetPixel actually locks the bits, retrieves a single pixel and unlocks the bits again - which means horrible performance when doing it over and over again for many times.
Edit
Here is a full code to work with:
private unsafe void CreateArray(Bitmap bmp)
{
// Note that is it somewhat a standard
// to define 2d array access by [y,x] (not [x,y])
bool[,] bwValues = new bool[bmp.Height, bmp.Width];
// 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.
byte* ptr = (byte*)bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
for (int y = 0; y < bmp.Height; y++)
{
var row = ptr + (y * bmpData.Stride);
for (int x = 0; x < bmp.Width; x++)
{
var pixel = row + x * 4; // ARGB has 4 bytes per pixel
// A bit=0
// R bit=1
// G bit=2
// B bit=3
// (depending on your image pixel format)
// Check if A = R = G = B = 255 (meaning the pixel is white)
bool isWhite = (pixel[0] == 255 &&
pixel[1] == 255 &&
pixel[2] == 255 &&
pixel[3] == 255);
// Assume that anything that isn't white is black
bwValues[y, x] = isWhite;
}
}
// Do whatever you want with vwValues here
}

Find differences between images

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.

Categories