How to find the difference between two images? - c#

I'm developing a screen-sharing application. In this project I need to transport images over the internet. Obviously, I can't send a new picture over the internet every few seconds, it would be extremely slow.
I want to send one image of the server's screen to the client, and afterwards, instead of sending a new picture sending only the pixels that have been changed since the last image (the one the client already has).
I have written this code:
private List<Color> CompareBitmaps(Image old, Image _new)
{
List<Color> returnList = new List<Color>();
for(int i = 0; i < old.Width; i++)
for (int j = 0; j < old.Height; j++)
{
if (((Bitmap)old).GetPixel(i, j) != ((Bitmap)_new).GetPixel(i, j))
{
returnList.Add(((Bitmap)_new).GetPixel(i, j));
}
}
return returnList;
}
However, it works way too slow.
I'm looking for a faster algorithm, one with a better complexity.
Note: I don't want a built library that does that. I need an algorithm.

This routine finds the differences between two Bitmaps and returns them in the 1st Bitmap by setting everything else to almost black and pretty much transparent. It can also restore the original 2nd file by adding the result back into the previous image..
I shrunk a screenshot of 800MB 1o 12k - but there was really just a very small change at the Clocks hands ;-) If your images differ in many pixels, the compression will not be as spectacular..but I believe it will b good enough for tranmitting and I doubt anything on a pixel by pixel basis will compare to the compression routines of png or jpg file formats.. (you don't transmit bmps, I hope!)
The routine uses LockBits and is pretty fast.
The bool parameter decides whether to create the difference bitmap or to restore the changed bitmap.
public static Bitmap Difference(Bitmap bmp0, Bitmap bmp1, bool restore)
{
int Bpp = 4; // assuming an effective pixelformat of 32bpp
var bmpData0 = bmp0.LockBits(
new Rectangle(0, 0, bmp0.Width, bmp0.Height),
ImageLockMode.ReadWrite, bmp0.PixelFormat);
var bmpData1 = bmp1.LockBits(
new Rectangle(0, 0, bmp1.Width, bmp1.Height),
ImageLockMode.ReadOnly, bmp1.PixelFormat);
int len = bmpData0.Height * bmpData0.Stride;
byte[] data0 = new byte[len];
byte[] data1 = new byte[len];
Marshal.Copy(bmpData0.Scan0, data0, 0, len);
Marshal.Copy(bmpData1.Scan0, data1, 0, len);
for (int i = 0; i < len; i += Bpp)
{
if (restore)
{
bool toberestored = (data1[i ] != 2 && data1[i+1] != 3 &&
data1[i+2] != 7 && data1[i+2] != 42);
if (toberestored)
{
data0[i ] = data1[i]; // Blue
data0[i+1] = data1[i+1]; // Green
data0[i+2] = data1[i+2]; // Red
data0[i+3] = data1[i+3]; // Alpha
}
}
else
{
bool changed = ((data0[i ] != data1[i ]) ||
(data0[i+1] != data1[i+1]) || (data0[i+2] != data1[i+2]) );
data0[i ] = changed ? data1[i ] : (byte)2; // special markers
data0[i+1] = changed ? data1[i+1] : (byte)3; // special markers
data0[i+2] = changed ? data1[i+2] : (byte)7; // special markers
data0[i+3] = changed ? (byte)255 : (byte)42; // special markers
}
}
Marshal.Copy(data0, 0, bmpData0.Scan0, len);
bmp0.UnlockBits(bmpData0);
bmp1.UnlockBits(bmpData1);
return bmp0;
}
Notes:
- I have chosen a special color to mark those pixels that need to be restored at the recipient. Here I chose alpha=42 and R=7; G=3; B=2;.. Not 100% safe but almost; not a lot of pixels will be missed; and maybe you don't have tranparency anyway..?
I append two smaller images, both PNGs, around 400kB:
This is the difference image (3kB):
The restored image is the same as the 2nd image.

You need to return all the changed pixels, so the complexity will have to be m*n.
(Bitmap)_new).GetPixel(i, j) is called twice, use a temp value to store it might be a little better.
the pixel should have several values right? can you try to create a function called comprareTwoPixel(color A, color B)? and compare all the values one by one, if one of them is false, you don`t need to compare the rest, just return false. (Not sure if this will make it faster or not though.)
Like:
bool comprareTwoPixel(color A, color B)
{
if(A.a!=B.b)
return false;
if(A.b!=B.b)
return false;
if(A.c!=B.c)
return false;
return true;
}

I'm not sure of what you're trying to do with this code, because you aren't saving the indexes that have changed...
You may want to use an other constructor for your List, because List() haves a default capacity of 0
That mean you will re-allocate the internal buffer of the List maybe many times if many pixels have changed
Maybe recording the average number of pixels that has changed and setting the initial capacity of your List to that number can accelerate your code. At least, you can say that, E.g. 10% of the pixel change every frame :
List<Color> returnList = new List<Color>( (int)( 0.10 * numberOfPixel ) );

This may or may not work perfectly, but here goes. You can parallelize this if you see so fit by adding AsParallel.
I also copied and pasted a few lines in here, so feel free to let me know or edit if I have any typos or mismatched variables. But this is the gist of it. This should be pretty quick, within reason.
Essentially, since this might be a bit hard to understand as-is, the idea is to "lock" the bits, then use that pointer to copy them to a byte[]. That effectively copies out all the RGB(A) values, which you can then access really easily. This is a ton faster than GetPixel when you're reading, as you are, more than a pixel or two, because it takes a little while to grab the pixels out, but then it's just simple reads against memory.
Once I get them into the byte[]s, it's easy enough to just compare those for each pixel coordinate. I chose to use LINQ so that it's really easy to parallelize if need be, but you may or may not choose to actually implement that. I'm not sure whether you'll need to.
I've made a few assumptions here that I believe are fair, since it sounds like your implementation has all images coming from a single source. Namely, I assume that the images are the same size and format. So if that's not actually the case, you'll want to address that with some extra code in here, but that's still pretty easy.
private byte[] UnlockBits(Bitmap bmp, out int stride)
{
BitmapData bmpData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
stride = bmpData.Stride;
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] ret = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, ret, 0, bytes);
bmp.UnlockBits(bmpData);
return ret;
}
private bool AreArraysEqual(byte[] a, byte[] b, int offset, int length)
{
for (int v = 0; v < length; v++)
{
int c = v + offset;
if (a[c] != b[c])
{
return false;
}
}
return true;
}
private IEnumerable<KeyValuePair<Point, Tuple<Color, Color>>> GetDifferences(Bitmap a, Bitmap b)
{
if (a.PixelFormat != b.PixelFormat)
throw new ArgumentException("Unmatched formats!");
if (a.Size != b.Size)
throw new ArgumentException("Unmatched length!");
int stride;
byte[] rgbValuesA = UnlockBits(a, out stride);
byte[] rgbValuesB = UnlockBits(b, out stride);
if (rgbValuesA.Length != rgbValuesB.Length)
throw new ArgumentException("Unmatched array lengths (unexpected error)!");
int bytesPerPixel = Image.GetPixelFormatSize(a.PixelFormat) / 8;
return Enumerable.Range(0, a.Height).SelectMany(y =>
Enumerable.Range(0, a.Width)
.Where(x => !AreArraysEqual(rgbValuesA,
rgbValuesB,
(y * stride) + (x * bytesPerPixel),
bytesPerPixel))
.Select(x =>
{
Point pt = new Point(x, y);
int pixelIndex = (y * stride) + (x * bytesPerPixel);
Color colorA = ReadPixel(rgbValuesA, pixelIndex, bytesPerPixel);
Color colorB = ReadPixel(rgbValuesB, pixelIndex, bytesPerPixel);
return new KeyValuePair<Point, Tuple<Color, Color>>(pt, colorA, colorB);
}
}
private Color ReadPixel(byte[] bytes, int offset, int bytesPerPixel)
{
int argb = BitConverter.ToInt32(pixelBytes, offset);
if (bytesPerPixel == 3) // no alpha
argb |= (255 << 24);
return Color.FromArgb(argb);
}
public IEnumerable<KeyValuePair<Point, Color>> GetNewColors(Bitmap _new, Bitmap old)
{
return GetDifferences(_new, old).Select(c => new KeyValuePair<Point, Color>(c.Key, c.Value.Item1));
}
In a true implementation, you might want to think about endianness and pixel format a bit more thoroughly than I have, but this should work more or less as a proof of concept, and I believe should handle a majority of practical cases.
And as #TaW said in a comment, you could also try blanking out (setting alpha to zero, probably) any that haven't changed. You can also benefit from pixel unlocking for that, too. Again, there are likely tutorials that tell you how to do it. But much of this could remain the same.

Related

Fastest way to copy data from ReadOnlySpan to output with pixel conversion

I'm having performance issue when I copy the data from input (ReadOnlySpan) to output (Span) using (Loops like 'for')
there is Span.CopyTo, it's perfect and very fast
but for now it's useless without converting the pixels.
below is the code, I have feeling that there is some short way to do that instead of the current process:
public unsafe void UpdateFromOutput(CanvasDevice device, ReadOnlySpan<byte> data, uint width, uint height, uint pitch)
{
using (var renderTargetMap = new BitmapMap(device, RenderTarget))
{
var inputPitch = (int)pitch;
var mapPitch = (int)renderTargetMap.PitchBytes;
var mapData = new Span<byte>(new IntPtr(renderTargetMap.Data).ToPointer(), (int)RenderTarget.Size.Height * mapPitch);
switch (CurrentPixelFormat)
{
case PixelFormats.RGB0555:
FramebufferConverter.ConvertFrameBufferRGB0555ToXRGB8888(width, height, data, inputPitch, mapData, mapPitch);
break;
case PixelFormats.RGB565:
FramebufferConverter.ConvertFrameBufferRGB565ToXRGB8888(width, height, data, inputPitch, mapData, mapPitch);
break;
}
}
}
then inside function like ConvertFrameBufferRGB0555ToXRGB8888
I will go through width and height like below:
var castInput = MemoryMarshal.Cast<byte, ushort>(input);
var castInputPitch = inputPitch / sizeof(ushort);
var castOutput = MemoryMarshal.Cast<byte, uint>(output);
var castOutputPitch = outputPitch / sizeof(uint);
castOutput.Fill(0);
for (var i = 0; i < height;i++)
{
var inputLine = castInput.Slice(i * castInputPitch, castInputPitch);
var outputLine = castOutput.Slice(i * castOutputPitch, castOutputPitch);
for (var j = 0; j < width;j++)
{
outputLine[j] = ConverToRGB888(inputLine[j]);
}
}
The code above working but slow in some cases.
Please note: I'm modifying a project so the code above was written by the original developer and I need help because I don't understand how the process is working, still very confused.. specially in the Slice part.
Tried as test only to copy the input to output directly data.CopyTo(mapData); and I got this (as expected):
Hope there is some solution with Marshal and Span functions
Many thanks.
Update regarding (ConverToRGB888)
As for ConverToRGB888, the original code contains RGB565LookupTable:
private const uint LookupTableSize = ushort.MaxValue + 1;
private static uint[] RGB565LookupTable = new uint[LookupTableSize];
public static void SetRGB0565LookupTable()
{
uint r565, g565, b565;
double red = 255.0;
double green = 255.0;
double blue = 255.0;
for (uint i = 0; i < LookupTableSize; i++)
{
//RGB565
r565 = (i >> 11) & 0x1F;
g565 = (i >> 5) & 0x3F;
b565 = (i & 0x1F);
r565 = (uint)Math.Round(r565 * red / 31.0);
g565 = (uint)Math.Round(g565 * green / 63.0);
b565 = (uint)Math.Round(b565 * blue / 31.0);
RGB565LookupTable[i] = (0xFF000000 | r565 << 16 | g565 << 8 | b565);
}
}
private static uint ConverToRGB888(ushort x)
{
return RGB565LookupTable[x];
}
SetRGB0565LookupTable() will be called only once to fill the values.
Conclusion:
The Fill(0) was not important and it was causing delay
The unsafe version (accepted answer) was clear for me and a bit faster
Avoiding For partially even faster like Here [Tested]
Pre-Lookup table is very helpful and made the conversion faster
Memory helpers like Span.CopyTo, Buffer.MemoryCopy source available Here
Using Parallel.For is faster in some cases with help of UnsafeMemory
If you have input with (Pixels Type) supported by Win2D there is possibility to avoid loops like:
byte[] dataBytes = new byte[data.Length];
fixed (byte* inputPointer = &data[0])
Marshal.Copy((IntPtr)inputPointer, dataBytes, 0, data.Length);
RenderTarget = CanvasBitmap.CreateFromBytes(renderPanel, dataBytes, (int)width, (int)height, DirectXPixelFormat.R8G8UIntNormalized, 92, CanvasAlphaMode.Ignore);
but not sure from the last point as I wasn't able to test on 565,555.
Thanks for DekuDesu the explanation and simplified version he provide helped me to do more tests.
I'm having performance issue when I copy the data from input (ReadOnlySpan) to output (Span) using (Loops like 'for')
The the code you provided is already pretty safe and has the best complexity you're going get for pixel-by-pixel operations. The presence of nested for loops does not necessarily correspond to performance issues or increased complexity.
I need help because I don't understand how the process is working, still very confused.. specially in the Slice part.
This code looks like it's meant to convert one bitmap format to another. Bitmaps come in varying sizes and formats. Because of this they include an additional piece of information along with width and height, pitch.
Pitch is the distance in bytes between two lines of pixel information, this is used to account for formats that don't include full 32/64bit color information.
Knowing this I commented the method in question as to help explain what it's doing.
public static void ConvertFrameBufferRGB565ToXRGB8888(uint width, uint height, ReadOnlySpan<byte> input, int inputPitch, Span<byte> output, int outputPitch)
{
// convert the span of bytes into a span of ushorts
// so we can use span[i] to get a ushort
var castInput = MemoryMarshal.Cast<byte, ushort>(input);
// pitch is the number of bytes between the first byte of a line and the first byte of the next line
// convert the pitch from bytes into ushort pitch
var castInputPitch = inputPitch / sizeof(ushort);
// convert the span of bytes into a span of ushorts
// so we can use span[i] to get a ushort
var castOutput = MemoryMarshal.Cast<byte, uint>(output);
var castOutputPitch = outputPitch / sizeof(uint);
for (var i = 0; i < height; i++)
{
// get a line from the input
// remember that pitch is the number of ushorts between lines
// so i * pitch here gives us the index of the i'th line, and we don't need the padding
// ushorts at the end so we only take castInputPitch number of ushorts
var inputLine = castInput.Slice(i * castInputPitch, castInputPitch);
// same thing as above but for the output
var outputLine = castOutput.Slice(i * castOutputPitch, castOutputPitch);
for (var j = 0; j < width; j++)
{
// iterate through the line, converting each pixel and storing it in the output span
outputLine[j] = ConverToRGB888(inputLine[j]);
}
}
}
Fastest way to copy data from ReadOnlySpan to output with pixel conversion
Honestly the method you provided is just fine, it's safe and fast ish. Keep in mind that copying data like bitmaps linearly on CPU's is an inherently slow process. The most performance savings you could hope for is avoiding copying data redundantly. Unless this needs absolutely blazing speed I would not recommend changes other than removing .fill(0) since it's probably unnecessary, but you would have to test that.
If you ABSOLUTELY must get more performance out of this you may want to consider something like what I've provided below. I caution you however, unsafe code like this is well.. unsafe and is prone to errors. It has almost no error checking and makes a LOT of assumptions, so that's up for you to implement.
If it's still not fast enough consider writing a .dll in C and use interop maybe.
public static unsafe void ConvertExtremelyUnsafe(ulong height, ref byte inputArray, ulong inputLength, ulong inputPitch, ref byte outputArray, ulong outputLength, ulong outputPitch)
{
// pin down pointers so they dont move on the heap
fixed (byte* inputPointer = &inputArray, outputPointer = &outputArray)
{
// since we have to account for padding we should go line by line
for (ulong y = 0; y < height; y++)
{
// get a pointer for the first byte of the line of the input
byte* inputLinePointer = inputPointer + (y * inputPitch);
// get a pointer for the first byte of the line of the output
byte* outputLinePointer = outputPointer + (y * outputPitch);
// traverse the input line by ushorts
for (ulong i = 0; i < (inputPitch / sizeof(ushort)); i++)
{
// calculate the offset for the i'th ushort,
// becuase we loop based on the input and ushort we dont need an index check here
ulong inputOffset = i * sizeof(ushort);
// get a pointer to the i'th ushort
ushort* rgb565Pointer = (ushort*)(inputLinePointer + inputOffset);
ushort rgb565Value = *rgb565Pointer;
// convert the rgb to the other format
uint rgb888Value = ConverToRGB888(rgb565Value);
// calculate the offset for i'th uint
ulong outputOffset = i * sizeof(uint);
// at least attempt to avoid overflowing a buffer, not that the runtime would let you do that, i would hope..
if (outputOffset >= outputLength)
{
throw new IndexOutOfRangeException($"{nameof(outputArray)}[{outputOffset}]");
}
// get a pointer to the i'th uint
uint* rgb888Pointer = (uint*)(outputLinePointer + outputOffset);
// write the bytes of the rgb888 to the output array
*rgb888Pointer = rgb888Value;
}
}
}
}
disclaimer: I wrote this on mobile

Convert 2 successive Bytes to one int value Increase speed in C#

I need to combine two Bytes into one int value.
I receive from my camera a 16bit Image were two successive bytes have the intensity value of one pixel. My goal is to combine these two bytes into one "int" vale.
I manage to do this using the following code:
for (int i = 0; i < VectorLength * 2; i = i + 2)
{
NewImageVector[ImagePointer] = ((int)(buffer.Array[i + 1]) << 8) | ((int)(buffer.Array[i]));
ImagePointer++;
}
My image is 1280*960 so VectorLength==1228800 and the incomming buffer size is 2*1228800=2457600 elements...
Is there any way that I can speed this up?
Maybe there is another way so I don't need to use a for-loop.
Thank you
You could use the equivalent to the union of c. Im not sure if faster, but more elegant:
[StructLayout(LayoutKind.Explicit)]
struct byte_array
{
[FieldOffset(0)]
public byte byte1;
[FieldOffset(1)]
public byte byte2;
[FieldOffset(0)]
public short int0;
}
use it like this:
byte_array ba = new byte_array();
//insert the two bytes
ba.byte1 = (byte)(buffer.Array[i]);
ba.byte2 = (byte)(buffer.Array[i + 1]);
//get the integer
NewImageVector[ImagePointer] = ba.int1;
You can fill your two bytes and use the int. To find the faster way take the StopWatch-Class and compare the two ways like this:
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
//The code
stopWatch.Stop();
MessageBox.Show(stopWatch.ElapsedTicks.ToString()); //Or milliseconds ,...
Assuming you can (re-)define NewImageVector as a short[], and every two consecutive bytes in Buffer should be transformed into a short (which basically what you're doing now, only you cast to an int afterwards), you can use Buffer.BlockCopy to do it for you.
As the documentation tells, you Buffer.BlockCopy copies bytes from one array to another, so in order to copy your bytes in buffer you need to do the following:
Buffer.BlockCopy(Buffer, 0, NewImageVector, 0, [NumberOfExpectedShorts] * 2)
This tells BlockCopy that you want to start copying bytes from Buffer, starting at index 0, to NewImageVector starting at index 0, and you want to copy [NumberOfExpectedShorts] * 2 bytes (since every short is two bytes long).
No loops, but it does depend on the ability of using a short[] array instead of an int[] array (and indeed, on using an array to begin with).
Note that this also requires the bytes in Buffer to be in little-endian order (i.e. Buffer[index] contains the low byte, buffer[index + 1] the high byte).
You can achieve a small performance increase by using unsafe pointers to iterate the arrays. The following code assumes that source is the input byte array (buffer.Array in your case). It also assumes that source has an even number of elements. In production code you would obviously have to check these things.
int[] output = new int[source.Length / 2];
fixed (byte* pSource = source)
fixed (int* pDestination = output)
{
byte* sourceIterator = pSource;
int* destIterator = pDestination;
for (int i = 0; i < output.Length; i++)
{
(*destIterator) = ((*sourceIterator) | (*(sourceIterator + 1) << 8));
destIterator++;
sourceIterator += 2;
}
}
return output;

Libtiff - Different BitsPerSample - valid or buggy tiff tags?

We've come across some TIFF images that are type 6 OJPEG compressed, many have the same BitsPerSample of "8 8 8" and work fine.
However, now and then we see ones that have a BitsPerSample value of say "8 25608 0". I'm wondering if this is even possible and if not whether its a bug in the system that generated the TIFFs. If it is could they potentially be fixed by simply modifying the tag so the BitsPerSample is "8 8 8"?
Currently I've only found one tool (LEAD) that can handle them by converting them to uncompressed TIFFs, obviously I've no idea how it does this and it's a big overhead just to solve an issue with a very small minority of images.
Has anyone come across this or have more knowledge than me regarding whether this is simple or complicated to fix, so far my attempts at poking around in the Libtiff.net code haven't been very fruitful!
Cheers
UPDATE: Using litiff.net with something as simple as this:
static void Main(string[] args)
{
using (Tiff image = Tiff.Open(args[0], "r"))
{
FieldValue[] value = image.GetField(TiffTag.IMAGEWIDTH);
int width = value[0].ToInt();
Console.WriteLine(string.Format("Width = {0}", width));
}
}
Results in (obviously skipped the exception handling!):
ReadDirectory: Warning, 10.TIF: unknown field with tag 33000 (0x80e8) encountered
10.TIF: Cannot handle different per-sample values for field "BitsPerSample"
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at tifftest.Program.Main(String[] args) in C:\tiffstuff\tifftest\tifftest\Program.cs:line 15
Segmentation fault
I'm now getting it to bypass the check and assume 8 for "BitsPerSample" however I'm now trying to extract the image to save as a BMP using the example on the bitmiracle site:
using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
byte[] bits = new byte[bmpdata.Stride * bmpdata.Height];
for (int y = 0; y < bmp.Height; y++)
{
int rasterOffset = y * bmp.Width;
int bitsOffset = (bmp.Height - y - 1) * bmpdata.Stride;
for (int x = 0; x < bmp.Width; x++)
{
int rgba = raster[rasterOffset++];
bits[bitsOffset++] = (byte)((rgba >> 16) & 0xff);
bits[bitsOffset++] = (byte)((rgba >> 8) & 0xff);
bits[bitsOffset++] = (byte)(rgba & 0xff);
}
}
System.Runtime.InteropServices.Marshal.Copy(bits, 0, bmpdata.Scan0, bits.Length);
bmp.UnlockBits(bmpdata);
bmp.Save("c:\\tiffstuff\\TiffTo24BitBitmap.bmp");
}
I obviously get the OJPEG warning and then the following:
OJPEGReadHeaderInfoSecTablesQTable: Missing JPEG tables
The resultant image is just 100% green.
Am I just too far out of my depth in terms of fixing this? I know the JPEG is in there somewhere just can't seem to get at it and decompress it successfully :-(
A bit depth of 25608 is definitely wrong. JPEG compression never uses a bit depth other than 8 bits per pixel. There are other image formats that can use 16 or even 32 bits per pixel, but that is as high as it gets for any normal application.
In JPEG there is RGB images, i.e. "8 8 8", CMYK images, i.e "8 8 8 8", and monochrome images, ie. "8".
It's possible that the images are CMYK, and that the software that you use doesn't support that. In that case you need to convert them to RGB to use them with that software.
If the images are RGB then the tag is simply corrupt, and fixing it would make the images work just fine. (Assuming of course that there isn't anything else that is also currupted.)
It seems like the file is semi-broken. As #Guffa mentioned, for OJPEG files bits per sample value should be 8 8 8 (RGB) or 8 8 8 8 (CMYK)
Unfortunately, LibTiff.Net does not give a chance to repair such important tags like BITSPERSAMPLE.
The only option I see is to add auto-correction code for such files into the library.
The code that gives the error is in Tiff_DirRead.cs file (in three places shown below).
private bool fetchPerSampleShorts(TiffDirEntry dir, out short pl)
{
...
for (ushort i = 1; i < check_count; i++)
{
if (v[i] != v[0])
{
ErrorExt(this, m_clientdata, m_name,
"Cannot handle different per-sample values for field \"{0}\"",
FieldWithTag(dir.tdir_tag).Name);
failed = true;
break;
}
}
...
}
private bool fetchPerSampleLongs(TiffDirEntry dir, out int pl)
{
...
for (ushort i = 1; i < check_count; i++)
{
if (v[i] != v[0])
{
ErrorExt(this, m_clientdata, m_name,
"Cannot handle different per-sample values for field \"{0}\"",
FieldWithTag(dir.tdir_tag).Name);
failed = true;
break;
}
}
...
}
private bool fetchPerSampleAnys(TiffDirEntry dir, out double pl)
{
...
for (ushort i = 1; i < check_count; i++)
{
if (v[i] != v[0])
{
ErrorExt(this, m_clientdata, m_name,
"Cannot handle different per-sample values for field \"{0}\"",
FieldWithTag(dir.tdir_tag).Name);
failed = true;
break;
}
}
...
}
You might try to remove if (v[i] != v[0]) clause and corresponding error-handling code. I think this will let you open the file (if there is no other problems in it).
You might want to convert or at least patch such files somehow.

Converting raw byte data to float[]

I have this code for converting a byte[] to float[].
public float[] ConvertByteToFloat(byte[] array)
{
float[] floatArr = new float[array.Length / sizeof(float)];
int index = 0;
for (int i = 0; i < floatArr.Length; i++)
{
floatArr[i] = BitConverter.ToSingle(array, index);
index += sizeof(float);
}
return floatArr;
}
Problem is, I usually get a NaN result! Why should this be? I checked if there is data in the byte[] and the data seems to be fine. If it helps, an example of the values are:
new byte[] {
231,
255,
235,
255,
}
But this returns NaN (Not a Number) after conversion to float. What could be the problem? Are there other better ways of converting byte[] to float[]? I am sure that the values read into the buffer are correct since I compared it with my other program (which performs amplification for a .wav file).
If endianness is the problem, you should check the value of BitConverter.IsLittleEndian to determine if the bytes have to be reversed:
public static float[] ConvertByteToFloat(byte[] array) {
float[] floatArr = new float[array.Length / 4];
for (int i = 0; i < floatArr.Length; i++) {
if (BitConverter.IsLittleEndian) {
Array.Reverse(array, i * 4, 4);
}
floatArr[i] = BitConverter.ToSingle(array, i * 4);
}
return floatArr;
}
Isn't the problem that the 255 in the exponent represents NaN (see Wikipedia in the exponent section), so you should get a NaN. Try changing the last 255 to something else...
If it's the endianness that is wrong (you are reading big endian numbers) try these (be aware that they are "unsafe" so you have to check the unsafe flag in your project properties)
public static unsafe int ToInt32(byte[] value, int startIndex)
{
fixed (byte* numRef = &value[startIndex])
{
var num = (uint)((numRef[0] << 0x18) | (numRef[1] << 0x10) | (numRef[2] << 0x8) | numRef[3]);
return (int)num;
}
}
public static unsafe float ToSingle(byte[] value, int startIndex)
{
int val = ToInt32(value, startIndex);
return *(float*)&val;
}
I assume that your byte[] doesn't contain the binary representation of floats, but either a sequence of Int8s or Int16s. The examples you posted don't look like audio samples based on float (neither NaN nor -2.41E+24 are in the range -1 to 1. While some new audio formats might support floats outside that range, traditionally audio data consists of signed 8 or 16 bit integer samples.
Another thing you need to be aware of is that often the different channels are interleaved. For example it could contain the first sample for the left, then for the right channel, and then the second sample for both... So you need to separate the channels while parsing.
It's also possible, but uncommon that the samples are unsigned. In which case you need to remove the offset from the conversion functions.
So you first need to parse current position in the byte array into an Int8/16. And then convert that integer to a float in the range -1 to 1.
If the format is little endian you can use BitConverter. Another possibility that works with both endiannesses is getting two bytes manually and combining them with a bit-shift. I don't remember if little or big endian is common. So you need to try that yourself.
This can be done with functions like the following(I didn't test them):
float Int8ToFloat(Int8 i)
{
return ((i-Int8.MinValue)*(1f/0xFF))-0.5f;
}
float Int16ToFloat(Int16 i)
{
return ((i-Int16.MinValue)*(1f/0xFFFF))-0.5f;
}
Depends on how you want to convert the bytes to float. Start out by trying to pin-point what is actually stored in the byte array. Perhaps there is a file format specification? Other transformations in your code?
In the case each byte should be converted to a float between 0 and 255:
public float[] ConvertByteToFloat(byte[] array)
{
return array.Select(b => (float)b).ToArray();
}
If the bytes array contains binary representation of floats, there are several representation and if the representation stored in your file does not match the c# language standard floating point representation (IEEE 754) weird things like this will happen.

What is the fastest way I can compare two equal-size bitmaps to determine whether they are identical?

I am trying to write a function to determine whether two equal-size bitmaps are identical or not. The function I have right now simply compares a pixel at a time in each bitmap, returning false at the first non-equal pixel.
While this works, and works well for small bitmaps, in production I'm going to be using this in a tight loop and on larger images, so I need a better way. Does anyone have any recommendations?
The language I'm using is C# by the way - and yes, I am already using the .LockBits method. =)
Edit: I've coded up implementations of some of the suggestions given, and here are the benchmarks. The setup: two identical (worst-case) bitmaps, 100x100 in size, with 10,000 iterations each. Here are the results:
CompareByInts (Marc Gravell) : 1107ms
CompareByMD5 (Skilldrick) : 4222ms
CompareByMask (GrayWizardX) : 949ms
In CompareByInts and CompareByMask I'm using pointers to access the memory directly; in the MD5 method I'm using Marshal.Copy to retrieve a byte array and pass that as an argument to MD5.ComputeHash. CompareByMask is only slightly faster, but given the context I think any improvement is useful.
Thanks everyone. =)
Edit 2: Forgot to turn optimizations on - doing that gives GrayWizardX's answer even more of a boost:
CompareByInts (Marc Gravell) : 944ms
CompareByMD5 (Skilldrick) : 4275ms
CompareByMask (GrayWizardX) : 630ms
CompareByMemCmp (Erik) : 105ms
Interesting that the MD5 method didn't improve at all.
Edit 3: Posted my answer (MemCmp) which blew the other methods out of the water. o.O
Edit 8-31-12: per Joey's comment below, be mindful of the format of the bitmaps you compare. They may contain padding on the strides that render the bitmaps unequal, despite being equivalent pixel-wise. See this question for more details.
Reading this answer to a question regarding comparing byte arrays has yielded a MUCH FASTER method: using P/Invoke and the memcmp API call in msvcrt. Here's the code:
[DllImport("msvcrt.dll")]
private static extern int memcmp(IntPtr b1, IntPtr b2, long count);
public static bool CompareMemCmp(Bitmap b1, Bitmap b2)
{
if ((b1 == null) != (b2 == null)) return false;
if (b1.Size != b2.Size) return false;
var bd1 = b1.LockBits(new Rectangle(new Point(0, 0), b1.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var bd2 = b2.LockBits(new Rectangle(new Point(0, 0), b2.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
try
{
IntPtr bd1scan0 = bd1.Scan0;
IntPtr bd2scan0 = bd2.Scan0;
int stride = bd1.Stride;
int len = stride * b1.Height;
return memcmp(bd1scan0, bd2scan0, len) == 0;
}
finally
{
b1.UnlockBits(bd1);
b2.UnlockBits(bd2);
}
}
If you are trying to determine if they are 100% equal, you can invert one and add it to the other if its zero they are identical. Extending this using unsafe code, take 64 bits at a time as a long and do the math that way, any differences can cause an immediate fail.
If the images are not 100% identical (comparing png to jpeg), or if you are not looking for a 100% match then you have some more work ahead of you.
Good luck.
Well, you're using .LockBits, so presumably you're using unsafe code. Rather than treating each row origin (Scan0 + y * Stride) as a byte*, consider treating it as an int*; int arithmetic is pretty quick, and you only have to do 1/4 as much work. And for images in ARGB you might still be talking in pixels, making the math simple.
Could you take a hash of each and compare? It would be slightly probabilistic, but practically not.
Thanks to Ram, here's a sample implementation of this technique.
If the original problem is just to find the exact duplicates among two bitmaps, then just a bit level comparison will have to do. I don't know C# but in C I would use the following function:
int areEqual (long size, long *a, long *b)
{
long start = size / 2;
long i;
for (i = start; i != size; i++) { if (a[i] != b[i]) return 0 }
for (i = 0; i != start; i++) { if (a[i] != b[i]) return 0 }
return 1;
}
I would start looking in the middle because I suspect there is a much better chance of finding unequal bits near the middle of the image than the beginning; of course, this would really depend on the images you are deduping, selecting a random place to start may be best.
If you are trying to find the exact duplicates among hundreds of images then comparing all pairs of them is unnecessary. First compute the MD5 hash of each image and place it in a list of pairs (md5Hash, imageId); then sort the list by the m5Hash. Next, only do pairwise comparisons on the images that have the same md5Hash.
If these bitmaps are already on your graphics card then you can parallelize such a check by doing it on the graphics card using a language like CUDA or OpenCL.
I'll explain in terms of CUDA, since that's the one I know. Basically CUDA lets you write general purpose code to run in parallel across each node of your graphics card. You can access bitmaps that are in shared memory. Each invocation of the function is also given an index within the set of parallel runs. So, for a problem like this, you'd just run one of the above comparison functions for some subset of the bitmap - using parallelization to cover the entire bitmap. Then, just write a 1 to a certain memory location if the comparison fails (and write nothing if it succeeds).
If you don't already have the bitmaps on your graphics card, this probably isn't the way to go, since the costs for loading the two bitmaps on your card will easily eclipse the savings such parallelization will gain you.
Here's some (pretty bad) example code (it's been a little while since I programmed CUDA). There's better ways to access bitmaps that are already loaded as textures, but I didn't bother here.
// kernel to run on GPU, once per thread
__global__ void compare_bitmaps(long const * const A, long const * const B, char * const retValue, size_t const len)
{
// divide the work equally among the threads (each thread is in a block, each block is in a grid)
size_t const threads_per_block = blockDim.x * blockDim.y * blockDim.z;
size_t const len_to_compare = len / (gridDim.x * gridDim.y * gridDim.z * threads_per_block);
# define offset3(idx3,dim3) (idx3.x + dim3.x * (idx3.y + dim3.y * idx3.z))
size_t const start_offset = len_to_compare * (offset3(threadIdx,blockDim) + threads_per_block * offset3(blockIdx,gridDim));
size_t const stop_offset = start_offset + len_to_compare;
# undef offset3
size_t i;
for (i = start_offset; i < stop_offset; i++)
{
if (A[i] != B[i])
{
*retValue = 1;
break;
}
}
return;
}
If you can implement something like Duff's Device in your language, that might give you a significant speed boost over a simple loop. Usually it's used for copying data, but there's no reason it can't be used for comparing data instead.
Or, for that matter, you may just want to use some equivalent to memcmp().
You could try to add them to a database "blob" then use the database engine to compare their binaries. This would only give you a yes or no answer to whether the binary data is the same. It would be very easy to make 2 images that produce the same graphic but have different binary though.
You could also select a few random pixels and compare them, then if they are the same continue with more until you've checked all the pixels. This would only return a faster negative match though, it still would take as long to find 100% positive matches
Based on the approach of comparing hashes instead of comparing every single pixel, this is what I use:
public static class Utils
{
public static byte[] ShaHash(this Image image)
{
var bytes = new byte[1];
bytes = (byte[])(new ImageConverter()).ConvertTo(image, bytes.GetType());
return (new SHA256Managed()).ComputeHash(bytes);
}
public static bool AreEqual(Image imageA, Image imageB)
{
if (imageA.Width != imageB.Width) return false;
if (imageA.Height != imageB.Height) return false;
var hashA = imageA.ShaHash();
var hashB = imageB.ShaHash();
return !hashA
.Where((nextByte, index) => nextByte != hashB[index])
.Any();
}
]
Usage is straight forward:
bool isMatch = Utils.AreEqual(bitmapOne, bitmapTwo);

Categories