I am in the process of creating a TCP remote desktop application
i want to send only the difference of the previously sent frame.
When I compare the original image and the second image I put information of pixels that have changed in ArrayList
five Item in the ArrayList containing information one pixel
The first Item contains a Height point
The second Item contains a Width point
The third Item contains RGB.red
The fourth Item contains RGB.Green
The Fifth Item contains RGB.Blue
This is the code
private void button1_Click(object sender, EventArgs e)
{
ArrayList new_pixel = Unsafe_diff_array(pictureBox2.Image, pictureBox1.Image);
Bitmap new_bit_map = (Bitmap)pictureBox3.Image;
for (int i = 0; i < new_pixel.Count; i+=5)
{
int x = (int)new_pixel[i +1];
int y=(int)new_pixel[i];
int red= Convert.ToInt16(new_pixel[i + 4]) ;
int green= Convert.ToInt16(new_pixel[i + 3]) ;
int blue=Convert.ToInt16(new_pixel[i + 2]);
new_bit_map.SetPixel(x, y, Color.FromArgb(red , green, blue));
}
pictureBox3.Image = new_bit_map;
}
public ArrayList Unsafe_diff_array(Image OrginalImage, Image SecondImage)
{
Bitmap BOrginalImage = new Bitmap(OrginalImage);
Bitmap BSecondImage = new Bitmap(SecondImage);
BitmapData bitmapData1 = BOrginalImage.LockBits(new Rectangle(0, 0,
OrginalImage.Width, OrginalImage.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
BitmapData bitmapData2 = BSecondImage.LockBits(new Rectangle(0, 0,
SecondImage.Width, SecondImage.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
ArrayList siblings = new ArrayList();
unsafe
{
byte* imagePointer1 = (byte*)bitmapData1.Scan0;
byte* imagePointer2 = (byte*)bitmapData2.Scan0;
for (int i = 0; i < bitmapData1.Height; i++)
{
for (int j = 0; j < bitmapData1.Width; j++)
{
// write the logic implementation here
if ((imagePointer1[0] != imagePointer2[0]) || (imagePointer1[1] != imagePointer2[1]) || (imagePointer1[2] != imagePointer2[2]))
{
imagePointer2[0] = imagePointer1[0];
imagePointer2[1] = imagePointer1[1];
imagePointer2[2] = imagePointer1[2];
siblings.Add(i);
siblings.Add(j);
siblings.Add(imagePointer2[0]);
siblings.Add(imagePointer2[1]);
siblings.Add(imagePointer2[2]);
}
imagePointer2[3] = imagePointer1[3];
imagePointer1 += 4;
imagePointer2 += 4;
}//end for j
imagePointer1 += bitmapData1.Stride -
(bitmapData1.Width * 4);
imagePointer2 += bitmapData1.Stride -
(bitmapData1.Width * 4);
}//end for i
}//end unsafe
BOrginalImage.UnlockBits(bitmapData1);
BSecondImage.UnlockBits(bitmapData2);
return siblings ;
// return BSecondImage.GetThumbnailImage(SecondImage.Width, SecondImage.Height, null, new IntPtr()); ;
}
the problem is when i Serialize ArrayList to MemoryStream
I find that the size larger than the images
I have also tried to put information of pixels that changed in Short Array, but also found a size larger than the images !!!!!?
How do I make this process so that I can Thumbnail size to the smallest possible size ?
the code which i used to Serialize ArrayList
private System.IO.MemoryStream SerializeBinary(object obj){
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream memStrm = new System.IO.MemoryStream();
serializer.Serialize(memStrm, obj);
return memStrm;
}
A typical 32-bit WxH image uses WxHx4 bytes of memory (that's 4MB for a 1024x1024 bitmap).
Assuming that you're sending the list of different pixels in [x][y][color] format with 16-bit [x] and [y] and a 32-bit [color] value, and there are D different pixels, the difference will use Dx8 bytes of memory. Thus, the difference will be larger than the image if more than half the pixels are different (D > WxH/2).
In your case, you're using an array, which means that [color] is actually represented as three [r][g][b] 16-bit values. The memory usage becomes Dx10 and the threshold can be found if 40% pixels have changed (D > WxH/2.5 ).
Consider using a bit to tell whether you're sending a list of differences or a full image, and send the smallest of the two. Also consider things like RLE encoding, using a single [offset] 16-bit value instead of [x] and [y], or just making any unchanged values transparent and saving the result in PNG format.
This, of course, is compounded by any additional serialization data is inserted by your runtime, and the fact that images can be compressed (PNG is lossless, for instance).
Of course it will be larger!
Bitmap has an in-memory structure consisting of header, pixel data and sometimes palette while binary formatter serialization result of an ArrayList has a totally different format consisting of assembly metadata, type metatdata, etc.
Also it is a pity all your performance gain of doing unsafe coding on pixels will be wasted on boxing happening of using ArrayList.
Related
I searched all question about byte array but i always failed. I have never coded c# i am new in this side. Could you help me how to make image file from byte array.
Here is my function which stores byte in array named imageData
public void imageReady( byte[] imageData, int fWidth, int fHeight))
You'll need to get those bytes into a MemoryStream:
Bitmap bmp;
using (var ms = new MemoryStream(imageData))
{
bmp = new Bitmap(ms);
}
That uses the Bitmap(Stream stream) constructor overload.
UPDATE: keep in mind that according to the documentation, and the source code I've been reading through, an ArgumentException will be thrown on these conditions:
stream does not contain image data or is null.
-or-
stream contains a PNG image file with a single dimension greater than 65,535 pixels.
Guys thank you for your help. I think all of this answers works. However i think my byte array contains raw bytes. That's why all of those solutions didnt work for my code.
However i found a solution. Maybe this solution helps other coders who have problem like mine.
static byte[] PadLines(byte[] bytes, int rows, int columns) {
int currentStride = columns; // 3
int newStride = columns; // 4
byte[] newBytes = new byte[newStride * rows];
for (int i = 0; i < rows; i++)
Buffer.BlockCopy(bytes, currentStride * i, newBytes, newStride * i, currentStride);
return newBytes;
}
int columns = imageWidth;
int rows = imageHeight;
int stride = columns;
byte[] newbytes = PadLines(imageData, rows, columns);
Bitmap im = new Bitmap(columns, rows, stride,
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(newbytes, 0));
im.Save("C:\\Users\\musa\\Documents\\Hobby\\image21.bmp");
This solutions works for 8bit 256 bpp (Format8bppIndexed). If your image has another format you should change PixelFormat .
And there is a problem with colors right now. As soon as i solved this one i will edit my answer for other users.
*PS = I am not sure about stride value but for 8bit it should be equal to columns.
And also this function Works for me.. This function copies 8 bit greyscale image into a 32bit layout.
public void SaveBitmap(string fileName, int width, int height, byte[] imageData)
{
byte[] data = new byte[width * height * 4];
int o = 0;
for (int i = 0; i < width * height; i++)
{
byte value = imageData[i];
data[o++] = value;
data[o++] = value;
data[o++] = value;
data[o++] = 0;
}
unsafe
{
fixed (byte* ptr = data)
{
using (Bitmap image = new Bitmap(width, height, width * 4,
PixelFormat.Format32bppRgb, new IntPtr(ptr)))
{
image.Save(Path.ChangeExtension(fileName, ".jpg"));
}
}
}
}
Can be as easy as:
var ms = new MemoryStream(imageData);
System.Drawing.Image image = Image.FromStream(ms);
image.Save("c:\\image.jpg");
Testing it out:
byte[] imageData;
// Create the byte array.
var originalImage = Image.FromFile(#"C:\original.jpg");
using (var ms = new MemoryStream())
{
originalImage.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// Convert back to image.
using (var ms = new MemoryStream(imageData))
{
Image image = Image.FromStream(ms);
image.Save(#"C:\newImage.jpg");
}
In addition, you can simply convert byte array to Bitmap.
var bmp = new Bitmap(new MemoryStream(imgByte));
You can also get Bitmap from file Path directly.
Bitmap bmp = new Bitmap(Image.FromFile(filePath));
This was helpful to me: https://www.tek-tips.com/viewthread.cfm?qid=1264492 (Reference answer)
I understand the question as follows:
I have a byte array that contains pixel data e.g. in RGB format (24bit/pixel)
From this raw pixel data I want to create a Bitmap
This code worked for me:
int width = ...;
int height = ...;
byte[] pixelArray = new byte[] {
// Creation of the actual data is not in the scope of this answer
};
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
// Create a BitmapData and lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
// Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(pixelArray, 0, bmpData.Scan0, pixelArray.Length);
// Unlock the pixels
bmp.UnlockBits(bmpData);
// Do something with your image, e.g. save it to disc
bmp.Save("c:\\temp\\mybmp.bmp", ImageFormat.Bmp);
Based on the accepted answer the OP wanted to interpret imageData byte array as the pixel buffer, rather than an already encoded bitmap stream as the most upvoted answer suggests. And though it works, it contains a lot of copies, as well as palette issues ("And there is a problem with colors right now").
I actually happen to have a drawing library exactly for this purpose (among others). The platform-independent core library allows you to interpret any array of primitive types as a bitmap data:
// Unlike in the accepted answer, no extra buffer allocation or
// array copy happens in the background. Note that we can specify
// a palette for the indexed format so the colors will be interpreted correctly
using var myBitmap = BitmapDataFactory.CreateBitmapData(imageData, new Size(fWidth, fHeight),
stride: fWidth, // stride is same as width because of the 8bpp pixel format
pixelFormat: KnownPixelFormat.Format8bppIndexed,
palette: Palette.Grayscale256());
myBitmap is now an IReadWriteBitmapData instance, allowing a lot of operations (just see the available extension methods). It also offers a pretty fast SetPixel method, which respects the palette so in this particular case it turns any color to grayscale. But if you know the actual pixel format you can also can use the WriteRaw<T> method to access the pixels directly.
And if you use the technology-specific packages such as the one for GDI+ or WPF, then you can simply convert your buffer into known bitmap types such as System.Drawing.Bitmap or System.Windows.Media.WriteableBitmap:
// the accepted answer creates two bitmaps due to the color problems where
// the 2nd one is a 32 bpp image. This solution is much faster, simpler, it avoids
// unnecessary allocations and uses parallel processing internally if possible
var systemBitmap = myBitmap.ToBitmap(); // or ToBitmapAsync, ToWriteableBitmap, etc.
I searched all question about byte array but i always failed. I have never coded c# i am new in this side. Could you help me how to make image file from byte array.
Here is my function which stores byte in array named imageData
public void imageReady( byte[] imageData, int fWidth, int fHeight))
You'll need to get those bytes into a MemoryStream:
Bitmap bmp;
using (var ms = new MemoryStream(imageData))
{
bmp = new Bitmap(ms);
}
That uses the Bitmap(Stream stream) constructor overload.
UPDATE: keep in mind that according to the documentation, and the source code I've been reading through, an ArgumentException will be thrown on these conditions:
stream does not contain image data or is null.
-or-
stream contains a PNG image file with a single dimension greater than 65,535 pixels.
Guys thank you for your help. I think all of this answers works. However i think my byte array contains raw bytes. That's why all of those solutions didnt work for my code.
However i found a solution. Maybe this solution helps other coders who have problem like mine.
static byte[] PadLines(byte[] bytes, int rows, int columns) {
int currentStride = columns; // 3
int newStride = columns; // 4
byte[] newBytes = new byte[newStride * rows];
for (int i = 0; i < rows; i++)
Buffer.BlockCopy(bytes, currentStride * i, newBytes, newStride * i, currentStride);
return newBytes;
}
int columns = imageWidth;
int rows = imageHeight;
int stride = columns;
byte[] newbytes = PadLines(imageData, rows, columns);
Bitmap im = new Bitmap(columns, rows, stride,
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(newbytes, 0));
im.Save("C:\\Users\\musa\\Documents\\Hobby\\image21.bmp");
This solutions works for 8bit 256 bpp (Format8bppIndexed). If your image has another format you should change PixelFormat .
And there is a problem with colors right now. As soon as i solved this one i will edit my answer for other users.
*PS = I am not sure about stride value but for 8bit it should be equal to columns.
And also this function Works for me.. This function copies 8 bit greyscale image into a 32bit layout.
public void SaveBitmap(string fileName, int width, int height, byte[] imageData)
{
byte[] data = new byte[width * height * 4];
int o = 0;
for (int i = 0; i < width * height; i++)
{
byte value = imageData[i];
data[o++] = value;
data[o++] = value;
data[o++] = value;
data[o++] = 0;
}
unsafe
{
fixed (byte* ptr = data)
{
using (Bitmap image = new Bitmap(width, height, width * 4,
PixelFormat.Format32bppRgb, new IntPtr(ptr)))
{
image.Save(Path.ChangeExtension(fileName, ".jpg"));
}
}
}
}
Can be as easy as:
var ms = new MemoryStream(imageData);
System.Drawing.Image image = Image.FromStream(ms);
image.Save("c:\\image.jpg");
Testing it out:
byte[] imageData;
// Create the byte array.
var originalImage = Image.FromFile(#"C:\original.jpg");
using (var ms = new MemoryStream())
{
originalImage.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// Convert back to image.
using (var ms = new MemoryStream(imageData))
{
Image image = Image.FromStream(ms);
image.Save(#"C:\newImage.jpg");
}
In addition, you can simply convert byte array to Bitmap.
var bmp = new Bitmap(new MemoryStream(imgByte));
You can also get Bitmap from file Path directly.
Bitmap bmp = new Bitmap(Image.FromFile(filePath));
This was helpful to me: https://www.tek-tips.com/viewthread.cfm?qid=1264492 (Reference answer)
I understand the question as follows:
I have a byte array that contains pixel data e.g. in RGB format (24bit/pixel)
From this raw pixel data I want to create a Bitmap
This code worked for me:
int width = ...;
int height = ...;
byte[] pixelArray = new byte[] {
// Creation of the actual data is not in the scope of this answer
};
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
// Create a BitmapData and lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
// Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(pixelArray, 0, bmpData.Scan0, pixelArray.Length);
// Unlock the pixels
bmp.UnlockBits(bmpData);
// Do something with your image, e.g. save it to disc
bmp.Save("c:\\temp\\mybmp.bmp", ImageFormat.Bmp);
Based on the accepted answer the OP wanted to interpret imageData byte array as the pixel buffer, rather than an already encoded bitmap stream as the most upvoted answer suggests. And though it works, it contains a lot of copies, as well as palette issues ("And there is a problem with colors right now").
I actually happen to have a drawing library exactly for this purpose (among others). The platform-independent core library allows you to interpret any array of primitive types as a bitmap data:
// Unlike in the accepted answer, no extra buffer allocation or
// array copy happens in the background. Note that we can specify
// a palette for the indexed format so the colors will be interpreted correctly
using var myBitmap = BitmapDataFactory.CreateBitmapData(imageData, new Size(fWidth, fHeight),
stride: fWidth, // stride is same as width because of the 8bpp pixel format
pixelFormat: KnownPixelFormat.Format8bppIndexed,
palette: Palette.Grayscale256());
myBitmap is now an IReadWriteBitmapData instance, allowing a lot of operations (just see the available extension methods). It also offers a pretty fast SetPixel method, which respects the palette so in this particular case it turns any color to grayscale. But if you know the actual pixel format you can also can use the WriteRaw<T> method to access the pixels directly.
And if you use the technology-specific packages such as the one for GDI+ or WPF, then you can simply convert your buffer into known bitmap types such as System.Drawing.Bitmap or System.Windows.Media.WriteableBitmap:
// the accepted answer creates two bitmaps due to the color problems where
// the 2nd one is a 32 bpp image. This solution is much faster, simpler, it avoids
// unnecessary allocations and uses parallel processing internally if possible
var systemBitmap = myBitmap.ToBitmap(); // or ToBitmapAsync, ToWriteableBitmap, etc.
I simply want to convert a previously loaded BMP-File into a byte[][]. It works pretty well for my own testing images (just some black spots on white background) which are all in the 8 bits per pixel format.
Now I tried the same code for some bitmaps somebody gave me ( also black squares, rectangles on white background) but it's not working as I expected it:
I expected a white pixel to be represented by a value of 255 (and black just by 0 ) in the resulting array, but i found different values there. In one case pixels that are supposed to be white end up with a value of 1 in the array.
Again, all these files are of 8 bit color depth.
Also, I noticed, when I open the Images in paint, save them again as a 256 color bitmap, then it works.
So my questions are:
What is causing this problem ? (Do color palettes maybe play a role ?)
And how can I make it work ?
Here's my amateurish code:
public byte[][] ConvertImageToArray (BitmapSource Image)
{
byte[][] Result = null;
if (Image != null)
{
int Index = 0;
int size = Image.PixelWidth * Image.PixelHeight * Image.Format.BitsPerPixel/8;
byte[] RawImg = new byte[size];
BitmapPalette test = Image.Palette;
int stride = (Image.PixelWidth * Image.Format.BitsPerPixel) / 8;
Image.CopyPixels(RawImg, stride, 0);
Result = new byte[Image.PixelHeight][];
int Width = Image.PixelWidth;
for (int i = 0; i < Result.Length; i++)
{
Result[i] = new byte[Width];
for (int k = 0; k < Result[i].Length; k++)
{
Result[i][k] = RawImg[Index++];
}
}
}
return Result;
}
I'm playing around with swapping color values of an image, but I seem to have found something I don't quite understand - and I can't seem to find a good read on google on the matter. I can accomplish swapping the colors of an image, but it also alters size of the output file as compared to the size of the input file.
Below is a test class I've written to test the matter, and what it does, to sum it up is:
Assign the bitmap to memory.
Make an array of RGB values.
Split the array of RGB values into three separate arrays (r, g and b).
Swap all values in red (r[0] <-> r[1], r[2] <-> r[3] etc.)
Join the three arrays and assign to the array of RGB values.
Copy back into bitmap.
Free the allocated memory.
Export the file.
Code is as seen below:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace GraphTheory
{
class Test
{
public Test(Bitmap bmp)
{
#region Assign bitmap to memory
// Rectangle to hold the bmp.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
// Lock the bitmap to the rectangle / system memory.
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
// Get the adress 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[] rgb = new byte[bytes];
// Copy the RGB values of the bitmap into the array.
Marshal.Copy(ptr, rgb, 0, bytes);
#endregion
#region Split rgb array into three arrays
// Number of colors in the image.
int colors = bytes / 3;
// Declare three arrays to hold the RGB values of the bitmap.
byte[] r = new byte[colors];
byte[] g = new byte[colors];
byte[] b = new byte[colors];
// Set starting pos of color index.
int colorIndex = 0;
// Split the array of RGB values into three seperate arrays.
for (int i = 0; i < rgb.Length; i += 3)
{
int j = i + 1, k = i + 2;
r[colorIndex] = rgb[k];
g[colorIndex] = rgb[j];
b[colorIndex] = rgb[i];
colorIndex++;
}
#endregion
#region Hide data in the colors of the bitmap
for (int i = 0; i < colors; i += 2)
{
switchBits(ref r[i], ref r[i + 1]);
}
#endregion
#region Join the three arrays into one rgb array
// Reset color index.
colorIndex = 0;
// Replace the values of the rgb array with the values of the r, g and b arrays.
for (int i = 0; i < rgb.Length; i += 3)
{
int j = i + 1, k = i + 2;
rgb[k] = r[colorIndex];
rgb[j] = g[colorIndex];
rgb[i] = b[colorIndex];
colorIndex++;
}
#endregion
#region Free bitmap from memory and save to file
// Copy the RGB values back to the bitmap
Marshal.Copy(rgb, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Export the image.
bmp.Save("../../output.png");
#endregion
}
private void switchBits(ref byte bit1, ref byte bit2)
{
byte tmp = bit1;
bit1 = bit2;
bit2 = tmp;
}
}
}
I simply don't understand why that would alter the image size of the bitmap, as I am not replacing any color values, merely rearranging them.
Size of input file: [884 KB]
Size of output file: [1335 KB]
No the image does not contain an alpha channel:
Image.IsAlphaPixelFormat(image.PixelFormat) == false
PNG uses (lossless) compression. This means that the size of the ouput file will depend on the data you give it. Compression takes advantage of redundancy in the data and by removing it, it can achieve smaller size. Images tend to have a lot of redundancy because neighbour pixels are correlated, i.e., they have similar values. What happens in your case is your shuffling somewhat interferes with the natural pattern of the image, thus reducing pixel correlations and redudancy. So, when it comes to compressing the data they take up more space.
I wouldn't be surprised if you were to shuffle all components, you'd notice the output size growing even bigger than just shuffling the red alone.
Your check for alpha is for the image, not what's being saved. PNG by default will save an alpha.Try this:
WPF - How do I save a PNG without any alpha channel?
I have a dictionary of pattern images for many letters and I also have a bitmap that has to be recognized! Heights of "both" images are the same! Some Pattern images have different width.
How to iterate over X axis and recognize letters from pattern?
Right now I'm using this function to Check if X bitmap column has Black pixels in it:
static Boolean GetColumnState(Bitmap bmp, int x)
{
BitmapData pixelData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
Boolean state = false;
unsafe
{
int* pData = (int*)pixelData.Scan0.ToPointer();
pData += x;
for (int i = 0; i < bmp.Height; ++i)
{
pData += bmp.Width;
if (Color.FromArgb(*pData) == Color.FromArgb(255, 0, 0, 0))
{
state = true;
break;
//pixelColumn[i] = Color.FromArgb(*pData);
}
}
}
bmp.UnlockBits(pixelData);
return state;
}
If GetColumnState() returns True, then I crop an image of the same size as pattern image and compare them.
int y = target.Count;
for (int i = 0; i < b1.Width; i++)
{
if (GetColumnState(b1, i + count) == true)
{
int trWidth = target[5].Value.Width;
int trHeight = target[5].Value.Height;
Bitmap bitm = new Bitmap(trWidth, trHeight);
Rectangle section = new Rectangle(new Point(0, b1.Height - trHeight-1), new Size(trWidth, trHeight));
Bitmap cropped = CropImage(b1, section);
cropped.Save(#"C:\111.png");
target[5].Value.Save(#"C:\000.png");
if (CompareMemCmp(cropped, target[5].Value) == true)
{
//count = target[5].Value.Width;
textBox2.AppendText(target[5].Key);
break;
}
else { textBox2.AppendText("noo"); }
//textBox1.Text = "yes!";
}
else
{
//textBox1.Text = "noo";
}
break;
}
Unfortunately, even though Cropped image visually looks the same - it has different size so memcmp (my method of comparison is based on this) returns false..
Bitmap that has to be recognized and Pattern images are all in BlackAndWhite colors.. I'm wondering if there is a more reliable way to compare one image within another Image and return its value through dictionary(OCR)..
Pattern matching based on pixel values is not robust, as you have discovered. If the font is completely predictable and consistently rendered, you can make this work by normalizing the images (align, scale, rotate to match), and computing the mean square difference between the images, then accepting if it's small enough.
In the face of unknown fonts, or varying image sources (screencapture, camera, scan, etc), you need a more robust scheme based on some type of machine learning (ML). Recognizing digits from bitmaps is a classical introductory example to neural networks, but many other ML schemes will work - see Supervised learning#Approaches_and_algorithms on Wikipedia. Note that this will involve training, implying you need a good diverse set of data to train it on.