I want to Send Arabic text as Bitmap to a POS printer since I could not print Arabic words directly to the printer. I used below code to convert a text to Bitmap :
Convert_ValueToImage("كيكه", "Simplified Arabic Fixed", 12)
public static Bitmap Convert_ValueToImage(string ValueText, string Fontname, int Fontsize)
{
//creating bitmap image
Bitmap ValueBitmap = new Bitmap(1, 1);
//FromImage method creates a new Graphics from the specified Image.
Graphics Graphics = Graphics.FromImage(ValueBitmap);
// Create the Font object for the image text drawing.
Font Font = new Font(Fontname, Fontsize);
// Instantiating object of Bitmap image again with the correct size for the text and font.
SizeF stringSize = Graphics.MeasureString(ValueText, Font);
ValueBitmap = new Bitmap(ValueBitmap, (int)stringSize.Width, (int)stringSize.Height);
Graphics = Graphics.FromImage(ValueBitmap);
//Draw Specified text with specified format
Graphics.DrawString(ValueText, Font, Brushes.Black, 0, 0);
Font.Dispose();
Graphics.Flush();
Graphics.Dispose();
return ValueBitmap; //return Bitmap Image
}
and when I assign it to pictureBox it works.
Now I want to send it to the printer. I used below method to convert the bitmap image to string with adding the image mode to the string:
public string GetArabic(Bitmap ArabicText)
{
string logo = "";
BitmapData data = GetArabicBitmapData(ArabicText);
BitArray dots = data.Dots;
byte[] width = BitConverter.GetBytes(data.Width);
int offset = 0;
MemoryStream stream = new MemoryStream();
BinaryWriter bw = new BinaryWriter(stream);
bw.Write((char)0x1B);
bw.Write('#');
bw.Write((char)0x1B);
bw.Write('3');
bw.Write((byte)24);
while (offset < data.Height)
{
bw.Write((char)0x1B);
bw.Write('*'); // bit-image mode
bw.Write((byte)33); // 24-dot double-density
bw.Write(width[0]); // width low byte
bw.Write(width[1]); // width high byte
for (int x = 0; x < data.Width; ++x)
{
for (int k = 0; k < 3; ++k)
{
byte slice = 0;
for (int b = 0; b < 8; ++b)
{
int y = (((offset / 8) + k) * 8) + b;
// Calculate the location of the pixel we want in the bit array.
// It'll be at (y * width) + x.
int i = (y * data.Width) + x;
// If the image is shorter than 24 dots, pad with zero.
bool v = false;
if (i < dots.Length)
{
v = dots[i];
}
slice |= (byte)((v ? 1 : 0) << (7 - b));
}
bw.Write(slice);
}
}
offset += 24;
bw.Write((char)0x0A);
}
// Restore the line spacing to the default of 30 dots.
bw.Write((char)0x1B);
bw.Write('3');
bw.Write((byte)30);
bw.Flush();
byte[] bytes = stream.ToArray();
return logo + Encoding.Default.GetString(bytes);
}
public BitmapData GetArabicBitmapData(Bitmap bmpFileName)
{
using (var bitmap = bmpFileName )
{
var threshold = 127;
var index = 0;
double multiplier = 570; // this depends on your printer model. for Beiyang you should use 1000
double scale = (double)(multiplier / (double)bitmap.Width);
int xheight = (int)(bitmap.Height * scale);
int xwidth = (int)(bitmap.Width * scale);
var dimensions = xwidth * xheight;
var dots = new BitArray(dimensions);
for (var y = 0; y < xheight; y++)
{
for (var x = 0; x < xwidth; x++)
{
var _x = (int)(x / scale);
var _y = (int)(y / scale);
var color = bitmap.GetPixel(_x, _y);
var luminance = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);
dots[index] = (luminance < threshold);
index++;
}
}
return new BitmapData()
{
Dots = dots,
Height = (int)(bitmap.Height * scale),
Width = (int)(bitmap.Width * scale)
};
}
}
this code print a black Rectangle. what would help me is if I could print the text with white background and the size is small as the text size.
I am creating a program that can print out the x- & y- Coordinates from a certain pixel. There is a function like 'GetPixel', this will however get the RGB codes from a given coordinate. What I want is just the vice versa, so I have already the RGB codes and now I'm doing a threshold through my Image to know whether it contains a Color pixel that I desired or not.
This is my code:
So firstly I will upload an image:
public BitmapImage bitmap;
public void hochladen_Click(object sender, EventArgs e)
{
// Create OpenFileDialog
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Set filter for file extension and default file extension
dlg.DefaultExt = ".bmp";
dlg.Filter = "BMP Files (*.bmp)|*.bmp";
// Get the selected file name and display in a TextBox
if (dlg.ShowDialog() == true)
{
// Open document
string filename = dlg.FileName;
bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(filename);
bitmap.EndInit();
image.Source = bitmap;
}
}
Then when I click a button in my application, it should do a threshold from my Image, and I am going to detect a red Point (R = 255, G = B = 0)
public Color c;
private void detektieren_Click(object sender, RoutedEventArgs e)
{
double x = bitmap.Width;
double y = bitmap.Height;
bl.Content = x + "x" + y;
So from this Point on, it shouldn't be difficult to find the coordinate:
for (int i = 0; i < x; i++)
{
for (int j = 0; i < j; j++)
{
if (c.R == 255 && c.G == 0 && c.B == 0)
{
//
}
}
}
}
Anyone has idea? Thanks in advance.
Finding pixels matching a RGB value of course may return many pixels, try the following code to get all the pixels represented by Point structure:
public Point[] GetPixelsFromRGB(byte[] rgbData, int stride, Color colorToFind){
int k = stride/4;
return rgbData.Select((x,i)=>new{x,i})
.GroupBy(a=>a.i/4,(key,a)=>a.ToArray())
.Where(g=>g[0].x == colorToFind.Red &&
g[1].x == colorToFind.Green &&
g[2].x == colorToFind.Blue && g[3].x == 255)
.Select(g=> new Point(g[0].i%k, g[0].i / k)).ToArray();
}
//Use this method to get the rgbData
int stride = bitmap.PixelWidth * 4;
byte[] rgbData = new byte[stride * bitmap.PixelHeight];
bitmap.CopyPixels(rgbData, stride, 0);
//then call the method above:
var pixels = GetPixelsFromRGB(rgbData, stride, Colors.Red);
Note that the code above has not been tested, I just typed directly into the answer editor.
After a Little bit modification, it works. So this is my code:
public void detektieren_Click(object sender, RoutedEventArgs e)
{
for (i = 0; i < bitmap.Height; i++)
{
for (j = 0; j < bitmap.Width; j++)
{
stride = bitmap.PixelWidth * (bitmap.Format.BitsPerPixel / 8);
data = new byte[stride * bitmap.PixelHeight];
bitmap.CopyPixels(data, stride, 0);
index = i * stride + 4 * j;
byte A = data[index + 3];
byte R = data[index + 2];
byte G = data[index + 1];
byte B = data[index];
// Create a writer and open the file:
StreamWriter Messdaten;
if (!File.Exists("C:/Users/.../Messdaten.csv"))
{
Messdaten = new StreamWriter("C:/Users/.../Messdaten.csv");
}
else
{
Messdaten = File.AppendText("C:/Users/.../Messdaten.csv");
}
// Write to the file:
Messdaten.WriteLine(index + ";" + A + ";" + R + ";" + G + ";" + B);
// Close the stream:
Messdaten.Close();
if (Convert.ToInt32(R) == 0 && Convert.ToInt32(G) == 0 && Convert.ToInt32(B) == 255)
{
// Create a writer and open the file:
StreamWriter Messdaten2;
if (!File.Exists("C:/Users/.../Messdaten2.csv"))
{
Messdaten2 = new StreamWriter("C:/Users/.../Messdaten2.csv");
}
else
{
Messdaten2 = File.AppendText("C:/Users/.../Messdaten2.csv");
}
// Write to the file:
Messdaten2.WriteLine(index + ";" + i + ";" + j);
// Close the stream:
Messdaten2.Close();
}
}
}
}
In the first Excel file (Messdaten.csv), all RGB values from a each single Pixel will be shown. In the second one (Messdaten2.csv) it will Show all Pixels that -let's take for instance- have a value A=0,R=0,G=0,B=255 (= Blue).
Now, how do I create a sum & mean of Pixel i and Pixel j (they're set of values) ? Tried to do this:
if (Convert.ToInt32(R) == 0 && Convert.ToInt32(G) == 0 && Convert.ToInt32(B) == 255)
{
int x_sum = 0; int y_sum = 0;
int x_count = 0; int y_count = 0;
int x_mw = 0; int y_mw = 0;
x_sum = x_sum + i;
x_count++;
y_sum = y_sum + j;
y_count++;
x_mw = x_sum / x_count;
y_mw = y_sum / y_count;
}
But why it didn't work? The x_sum and y_sum only Show the last value of Pixel i and j, and the x_count and y_count (as presumed) show only the value of 1. What did I wrong?
i have a application which crops an image and save it
the process was to load an image, crop it, delete the original image(so i can replace it) and, save it.
this is my code:
private void DetectSize(object sender, EventArgs e)
{
int x = 1;
Bitmap TempImage = new Bitmap(#cwd + "\\t" + (x + 1) + ".jpg", true);
pictureBox.Image = (Image)TempImage.Clone();
TempImage.Dispose();
Bitmap imgPart = new Bitmap(pictureBox.Image);
int imgHeight = imgPart.Height;
int imgWidth = imgPart.Width;
HalfWidth = imgWidth / 2;
MaxWidth = imgWidth;
try
{
Bitmap imgPart1 = new Bitmap(pictureBox.Image);
Color c;
for (int i = 0; i < imgPart1.Width; i++)
{
for (int j = 0; j < imgPart1.Height; j++)
{
c = imgPart1.GetPixel(i, j);
string cn = c.Name;
for (int z = 0; z <= 9; z++)
{
if (z < 10)
{
if (cn == "ff00000" + z)
{
if (i < HalfWidth)
{
MinWidth = i;
}
else
{
if (i < MaxWidth)
{
MaxWidth = i;
}
}
}
}
else
{
if (cn == "ff0000" + z)
{
if (i < HalfWidth)
{
MinWidth = i;
}
else
{
if (i < MaxWidth)
{
MaxWidth = i;
}
}
}
}
}
}
}
MinWidth += 1;
MaxWidth -= 1;
MaxWidth = imgWidth - MaxWidth;
imgPart1.Dispose();
imgPart.Dispose();
lblLeftMargin.Text = Convert.ToString(MinWidth);
lblRightMargin.Text = Convert.ToString(MaxWidth);
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
}
}
This is for locating the margins that will be used to crop the image.
private void CropSave(object sender, EventArgs e)
{
int x = 1;
Bitmap croppedBitmap = new Bitmap(pictureBox.Image);
croppedBitmap = croppedBitmap.Clone(
new Rectangle(
MinWidth, 0,
(int)croppedBitmap.Width - MinWidth - MaxWidth,
1323),
System.Drawing.Imaging.PixelFormat.DontCare);
if (System.IO.File.Exists(#cwd + "\\t" + (x + 1) + ".jpg"))
System.IO.File.Delete(#cwd + "\\t" + (x + 1) + ".jpg");
croppedBitmap.Save(#cwd + "\\t" + (x + 1) + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
croppedBitmap.Dispose();
MessageBox.Show("File " + (x + 1) + "Done Cropping");
}
and this is for cropping and saving of image
The error shows at line System.IO.File.Delete(#cwd + "\\t" + (x + 1) + ".jpg"
it says
The process cannot access the file 'C:\Users....\t2.jpg' because it is being used by another process.
I'm trying to look where i have been wrong for days, and still nothing.
Please help me.
Bitmap TempImage = new Bitmap(#cwd + "\\t" + (x + 1) + ".jpg", true);
pictureBox.Image = (Image)TempImage.Clone();
TempImage.Dispose();
The Clone() method doesn't do what you hope it does. It still keeps a lock on the file, the memory mapped file object is shared between the two image objects. Disposing the first one just closes one handle on the object, the pictureBox.Image object still has the other handle opened. Write it like this instead:
pictureBox.Image = new Bitmap(TempImage);
Hi i m really new in image processing in C# and the code below basically getpixel from the image I browsed from my computer and will compare the RGB value of the pixel with the right pixel and if its the same value, it will setpixel to cyan color. the problem is with the getpixel, it is really very slow even on a small resolution photos and I'm also looking to add more function to it. I have read about lockbits and was trying it out but was unable to successfully write the code.
namespace Disimage
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Bitmap pic;
public Bitmap pic2;
private bool compare_colour_constant(int original, int sample)
{
if (original == sample)
return true;
else
return false;
}
public void btn_browse_Click_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog open = new OpenFileDialog();
open.Filter = "Image Files(*.jpg; *.jpeg; *.gif; *.bmp)|*.jpg; *.jpeg; *.gif; *.bmp";
if (open.ShowDialog() == DialogResult.OK)
{
pic = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
pic2 = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
//pictureBox1.Image = new Bitmap(open.FileName);
pic = new Bitmap(open.FileName);
pic2 = new Bitmap(open.FileName);
pictureBox1.Image = pic;
pictureBox2.Image = pic2;
pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
textBox1.Text = open.FileName;
pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
}
}
catch (Exception)
{
throw new ApplicationException("Failed loading image");
}
}
public void scan_Click(object sender, EventArgs e)
{
try
{
//Bitmap pic = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
//Bitmap pic2 = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
pictureBox1.Image = pic;
pictureBox2.Image = pic2;
progressBar1.Minimum = 0;
progressBar1.Maximum = pic.Width;
int []RGB = pic.GetPixel();
for (int w = 1; w < pic.Width - 1; w++)
{
progressBar1.Step = 1;
progressBar1.PerformStep();
if (progressBar1.Value == progressBar1.Maximum)
progressBar1.Value = 0;
for (int h = 1; h < pic.Height - 1; h++)
{
int red = pic.GetPixel(w, h).R;
int green = pic.GetPixel(w, h).G;
int blue = pic.GetPixel(w, h).B;
int colour = pic.GetPixel(w, h).R + pic.GetPixel(w, h).G + pic.GetPixel(w, h).B;
int colour2 = pic.GetPixel(w + 1, h).R + pic.GetPixel(w + 1, h).G + pic.GetPixel(w + 1, h).B;
/*textBox2.Text = red.ToString();
textBox3.Text = green.ToString();
textBox4.Text = blue.ToString();
*/
int Lred = pic.GetPixel(w - 1, h).R;
int Lgreen = pic.GetPixel(w - 1, h).G;
int Lblue = pic.GetPixel(w - 1, h).B;
int Rred = pic.GetPixel(w + 1, h).R;
int Rgreen = pic.GetPixel(w + 1, h).G;
int Rblue = pic.GetPixel(w + 1, h).B;
if (compare_colour_constant(colour, colour2) == true)
pic2.SetPixel(w, h, Color.Cyan);
}
}
}
catch (Exception)
{
throw new ApplicationException("Failed loading image");
}
}
}
}
Though a little late, I'd be happy to answer your question for other users. The first thing you would need to do is to declare a BitmapData variable, which would hold (obviously) the data from the Bitmap image that has been placed into the memory. To do this:
System.Drawing.Imaging.BitmapData bmpdata = pic.LockBits(new Rectangle(pictureBox1.Location.X, pictureBox1.Location.Y, pictureBox1.Width, pictureBox1.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite,
System.Drawing.Imaging.PixelFormat);
After calling this code, you can proceed to edit the BitmapData to your liking. In this situation you could call for a loop through a byte array of the Data and compare the RGB to the RGB of the pixel immediately to the right's and determine similarity. Example:
unsafe
{
for (int y = 0; y < bmpdata.Height; y++) // Repeats for each row
{
byte* row = (byte*)bmpdata.Scan0 + (y * bmpdata.Stride); // Array of bytes for the current row of pixels
for (int x = 0; x < bmpdata.Width; x++) // Repeats for each pixel on each row
{
if (row[x * 4] == row[(x + 1) * 4] && row[(x * 4) + 1] == row[((x + 1) * 4) + 1] && row[(x * 4) + 2] == row[((x + 1) * 4) + 2])
{
row[x * 4] = 255; // Blue value of current pixel
row[(x * 4) + 1] = 255; // Green Value of current pixel
row[(x * 4) + 2] = 0; // Red value of current pixel
}
}
}
}
ATTENTION: Though the above might work (and let me stress might), it would probably be much more reliable to go to Bob Powell's site and read his page on LockBits. Though it may be hard to understand at first, it gets simpler as you go along. His page is much more detailed than I could be in this answer, and he probably has working examples.
I have two images the same size. What is the best way to find the rectangle in which they differ. Obviously I could go through the image 4 times in different directions, but i'm wondering if there's an easier way.
Example:
A naive approach would be to start at the origin, and work line by line, column by column. Compare each pixel, keeping note of the topmost, leftmost, rightmost, and bottommost, from which you can calculate your rectangle. There will be cases where this single pass approach would be faster (i.e. where there is a very small differing area)
If you want a single rectangle, use int.MaxValue for the threshold.
var diff = new ImageDiffUtil(filename1, filename2);
var diffRectangles = diff.GetDiffRectangles(int.MaxValue);
If you want multiple rectangles, use a smaller threshold.
var diff = new ImageDiffUtil(filename1, filename2);
var diffRectangles = diff.GetDiffRectangles(8);
ImageDiffUtil.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace diff_images
{
public class ImageDiffUtil
{
Bitmap image1;
Bitmap image2;
public ImageDiffUtil(string filename1, string filename2)
{
image1 = Image.FromFile(filename1) as Bitmap;
image2 = Image.FromFile(filename2) as Bitmap;
}
public IList<Point> GetDiffPixels()
{
var widthRange = Enumerable.Range(0, image1.Width);
var heightRange = Enumerable.Range(0, image1.Height);
var result = widthRange
.SelectMany(x => heightRange, (x, y) => new Point(x, y))
.Select(point => new
{
Point = point,
Pixel1 = image1.GetPixel(point.X, point.Y),
Pixel2 = image2.GetPixel(point.X, point.Y)
})
.Where(pair => pair.Pixel1 != pair.Pixel2)
.Select(pair => pair.Point)
.ToList();
return result;
}
public IEnumerable<Rectangle> GetDiffRectangles(double distanceThreshold)
{
var result = new List<Rectangle>();
var differentPixels = GetDiffPixels();
while (differentPixels.Count > 0)
{
var cluster = new List<Point>()
{
differentPixels[0]
};
differentPixels.RemoveAt(0);
while (true)
{
var left = cluster.Min(p => p.X);
var right = cluster.Max(p => p.X);
var top = cluster.Min(p => p.Y);
var bottom = cluster.Max(p => p.Y);
var width = Math.Max(right - left, 1);
var height = Math.Max(bottom - top, 1);
var clusterBox = new Rectangle(left, top, width, height);
var proximal = differentPixels
.Where(point => GetDistance(clusterBox, point) <= distanceThreshold)
.ToList();
proximal.ForEach(point => differentPixels.Remove(point));
if (proximal.Count == 0)
{
result.Add(clusterBox);
break;
}
else
{
cluster.AddRange(proximal);
}
};
}
return result;
}
static double GetDistance(Rectangle rect, Point p)
{
var dx = Math.Max(rect.Left - p.X, 0);
dx = Math.Max(dx, p.X - rect.Right);
var dy = Math.Max(rect.Top - p.Y, 0);
dy = Math.Max(dy, p.Y - rect.Bottom);
return Math.Sqrt(dx * dx + dy * dy);
}
}
}
Form1.cs
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace diff_images
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var filename1 = #"Gelatin1.PNG";
var filename2 = #"Gelatin2.PNG";
var diff = new ImageDiffUtil(filename1, filename2);
var diffRectangles = diff.GetDiffRectangles(8);
var img3 = Image.FromFile(filename2);
Pen redPen = new Pen(Color.Red, 1);
var padding = 3;
using (var graphics = Graphics.FromImage(img3))
{
diffRectangles
.ToList()
.ForEach(rect =>
{
var largerRect = new Rectangle(rect.X - padding, rect.Y - padding, rect.Width + padding * 2, rect.Height + padding * 2);
graphics.DrawRectangle(redPen, largerRect);
});
}
var pb1 = new PictureBox()
{
Image = Image.FromFile(filename1),
Left = 8,
Top = 8,
SizeMode = PictureBoxSizeMode.AutoSize
};
var pb2 = new PictureBox()
{
Image = Image.FromFile(filename2),
Left = pb1.Left + pb1.Width + 16,
Top = 8,
SizeMode = PictureBoxSizeMode.AutoSize
};
var pb3 = new PictureBox()
{
Image = img3,
Left = pb2.Left + pb2.Width + 16,
Top = 8,
SizeMode = PictureBoxSizeMode.AutoSize
};
Controls.Add(pb1);
Controls.Add(pb2);
Controls.Add(pb3);
}
}
}
Image processing like this is expensive, there are a lot of bits to look at. In real applications, you almost always need to filter the image to get rid of artifacts induced by imperfect image captures.
A common library used for this kind of bit whacking is OpenCV, it takes advantage of dedicated CPU instructions available to make this fast. There are several .NET wrappers available for it, Emgu is one of them.
I don't think there is an easier way.
In fact doing this will just be a (very) few lines of code, so unless you find a library that does that for you directly you won't find a shorter way.
Idea:
Consider an image as a 2D Array with each Array element as a pixel of the image. Hence, I would say Image Differencing is nothing but 2D Array Differencing.
Idea is to just scan through the array elements width-wise and find the place where there is a difference in pixel values. If example [x, y] co-ordinates of both 2D Array are different then our rectangle finding logic starts. Later on the rectangles would be used to patch the last updated Frame Buffer.
We need to scan through the boundaries of the rectangles for differences and if any difference is found in the boundary of rectangle, then the boundary will be increased width-wise or height-wise depending upon the type of scan made.
Consider I scanned width-wise of 2D Array and I found a location where there exist a co-ordinate which is different in both the 2D Arrays, I will create a rectangle with the starting position as [x-1, y-1] and with the width and height as 2 and 2 respectively. Please note that width and height refers to the number of pixels.
eg: Rect Info:
X = 20
Y = 35
W = 26
H = 23
i.e width of the rectangle starts from co-ordinate [20, 35] -> [20, 35 + 26 - 1]. Maybe when you find the code you may be able to understand it better.
Also there are possibilities that there are smaller rectangles inside a bigger rectangle you have found, thus we need to remove the smaller rectangles from our reference because they mean nothing to us except that they occupu my precious space !!
The above logic would be helpful in the case of VNC Server Implementation where there would be a need of rectangles that denotes differences in the image that is currently taken. Those rectangles could be sent in the network to the VNC Client which can patch the rectangles in the local copy of Frame Buffer it possesses thereby displaying it on the VNC Client Display Board.
P.S.:
I will be attaching the code in which I implemented my own algorithm. I would request viewers to comment for any mistakes or performance tuning. I would also request viewers to comment about any better algorithm that would make life simpler.
Code:
Class Rect:
public class Rect {
public int x; // Array Index
public int y; // Array Index
public int w; // Number of hops along the Horizontal
public int h; // Number of hops along the Vertical
#Override
public boolean equals(Object obj) {
Rect rect = (Rect) obj;
if(rect.x == this.x && rect.y == this.y && rect.w == this.w && rect.h == this.h) {
return true;
}
return false;
}
}
Class Image Difference:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import javax.imageio.ImageIO;
public class ImageDifference {
long start = 0, end = 0;
public LinkedList<Rect> differenceImage(int[][] baseFrame, int[][] screenShot, int xOffset, int yOffset, int width, int height) {
// Code starts here
int xRover = 0;
int yRover = 0;
int index = 0;
int limit = 0;
int rover = 0;
boolean isRectChanged = false;
boolean shouldSkip = false;
LinkedList<Rect> rectangles = new LinkedList<Rect>();
Rect rect = null;
start = System.nanoTime();
// xRover - Rovers over the height of 2D Array
// yRover - Rovers over the width of 2D Array
int verticalLimit = xOffset + height;
int horizontalLimit = yOffset + width;
for(xRover = xOffset; xRover < verticalLimit; xRover += 1) {
for(yRover = yOffset; yRover < horizontalLimit; yRover += 1) {
if(baseFrame[xRover][yRover] != screenShot[xRover][yRover]) {
// Skip over the already processed Rectangles
for(Rect itrRect : rectangles) {
if(( (xRover < itrRect.x + itrRect.h) && (xRover >= itrRect.x) ) && ( (yRover < itrRect.y + itrRect.w) && (yRover >= itrRect.y) )) {
shouldSkip = true;
yRover = itrRect.y + itrRect.w - 1;
break;
} // End if(( (xRover < itrRect.x + itrRect.h) && (xRover >= itrRect.x) ) && ( (yRover < itrRect.y + itrRect.w) && (yRover >= itrRect.y) ))
} // End for(Rect itrRect : rectangles)
if(shouldSkip) {
shouldSkip = false;
// Need to come out of the if condition as below that is why "continue" has been provided
// if(( (xRover <= (itrRect.x + itrRect.h)) && (xRover >= itrRect.x) ) && ( (yRover <= (itrRect.y + itrRect.w)) && (yRover >= itrRect.y) ))
continue;
} // End if(shouldSkip)
rect = new Rect();
rect.x = ((xRover - 1) < xOffset) ? xOffset : (xRover - 1);
rect.y = ((yRover - 1) < yOffset) ? yOffset : (yRover - 1);
rect.w = 2;
rect.h = 2;
/* Boolean variable used to re-scan the currently found rectangle
for any change due to previous scanning of boundaries */
isRectChanged = true;
while(isRectChanged) {
isRectChanged = false;
index = 0;
/* I */
/* Scanning of left-side boundary of rectangle */
index = rect.x;
limit = rect.x + rect.h;
while(index < limit && rect.y != yOffset) {
if(baseFrame[index][rect.y] != screenShot[index][rect.y]) {
isRectChanged = true;
rect.y = rect.y - 1;
rect.w = rect.w + 1;
index = rect.x;
continue;
} // End if(baseFrame[index][rect.y] != screenShot[index][rect.y])
index = index + 1;;
} // End while(index < limit && rect.y != yOffset)
/* II */
/* Scanning of bottom boundary of rectangle */
index = rect.y;
limit = rect.y + rect.w;
while( (index < limit) && (rect.x + rect.h != verticalLimit) ) {
rover = rect.x + rect.h - 1;
if(baseFrame[rover][index] != screenShot[rover][index]) {
isRectChanged = true;
rect.h = rect.h + 1;
index = rect.y;
continue;
} // End if(baseFrame[rover][index] != screenShot[rover][index])
index = index + 1;
} // End while( (index < limit) && (rect.x + rect.h != verticalLimit) )
/* III */
/* Scanning of right-side boundary of rectangle */
index = rect.x;
limit = rect.x + rect.h;
while( (index < limit) && (rect.y + rect.w != horizontalLimit) ) {
rover = rect.y + rect.w - 1;
if(baseFrame[index][rover] != screenShot[index][rover]) {
isRectChanged = true;
rect.w = rect.w + 1;
index = rect.x;
continue;
} // End if(baseFrame[index][rover] != screenShot[index][rover])
index = index + 1;
} // End while( (index < limit) && (rect.y + rect.w != horizontalLimit) )
} // while(isRectChanged)
// Remove those rectangles that come inside "rect" rectangle.
int idx = 0;
while(idx < rectangles.size()) {
Rect r = rectangles.get(idx);
if( ( (rect.x <= r.x) && (rect.x + rect.h >= r.x + r.h) ) && ( (rect.y <= r.y) && (rect.y + rect.w >= r.y + r.w) ) ) {
rectangles.remove(r);
} else {
idx += 1;
} // End if( ( (rect.x <= r.x) && (rect.x + rect.h >= r.x + r.h) ) && ( (rect.y <= r.y) && (rect.y + rect.w >= r.y + r.w) ) )
} // End while(idx < rectangles.size())
// Giving a head start to the yRover when a rectangle is found
rectangles.addFirst(rect);
yRover = rect.y + rect.w - 1;
rect = null;
} // End if(baseFrame[xRover][yRover] != screenShot[xRover][yRover])
} // End for(yRover = yOffset; yRover < horizontalLimit; yRover += 1)
} // End for(xRover = xOffset; xRover < verticalLimit; xRover += 1)
end = System.nanoTime();
return rectangles;
}
public static void main(String[] args) throws IOException {
LinkedList<Rect> rectangles = null;
// Buffering the Base image and Screen Shot Image
BufferedImage screenShotImg = ImageIO.read(new File("screenShotImg.png"));
BufferedImage baseImg = ImageIO.read(new File("baseImg.png"));
int width = baseImg.getWidth();
int height = baseImg.getHeight();
int xOffset = 0;
int yOffset = 0;
int length = baseImg.getWidth() * baseImg.getHeight();
// Creating 2 Two Dimensional Arrays for Image Processing
int[][] baseFrame = new int[height][width];
int[][] screenShot = new int[height][width];
// Creating 2 Single Dimensional Arrays to retrieve the Pixel Values
int[] baseImgPix = new int[length];
int[] screenShotImgPix = new int[length];
// Reading the Pixels from the Buffered Image
baseImg.getRGB(0, 0, baseImg.getWidth(), baseImg.getHeight(), baseImgPix, 0, baseImg.getWidth());
screenShotImg.getRGB(0, 0, screenShotImg.getWidth(), screenShotImg.getHeight(), screenShotImgPix, 0, screenShotImg.getWidth());
// Transporting the Single Dimensional Arrays to Two Dimensional Array
long start = System.nanoTime();
for(int row = 0; row < height; row++) {
System.arraycopy(baseImgPix, (row * width), baseFrame[row], 0, width);
System.arraycopy(screenShotImgPix, (row * width), screenShot[row], 0, width);
}
long end = System.nanoTime();
System.out.println("Array Copy : " + ((double)(end - start) / 1000000));
// Finding Differences between the Base Image and ScreenShot Image
ImageDifference imDiff = new ImageDifference();
rectangles = imDiff.differenceImage(baseFrame, screenShot, xOffset, yOffset, width, height);
// Displaying the rectangles found
int index = 0;
for(Rect rect : rectangles) {
System.out.println("\nRect info : " + (++index));
System.out.println("X : " + rect.x);
System.out.println("Y : " + rect.y);
System.out.println("W : " + rect.w);
System.out.println("H : " + rect.h);
// Creating Bounding Box
for(int i = rect.y; i < rect.y + rect.w; i++) {
screenShotImgPix[ ( rect.x * width) + i ] = 0xFFFF0000;
screenShotImgPix[ ((rect.x + rect.h - 1) * width) + i ] = 0xFFFF0000;
}
for(int j = rect.x; j < rect.x + rect.h; j++) {
screenShotImgPix[ (j * width) + rect.y ] = 0xFFFF0000;
screenShotImgPix[ (j * width) + (rect.y + rect.w - 1) ] = 0xFFFF0000;
}
}
// Creating the Resultant Image
screenShotImg.setRGB(0, 0, width, height, screenShotImgPix, 0, width);
ImageIO.write(screenShotImg, "PNG", new File("result.png"));
double d = ((double)(imDiff.end - imDiff.start) / 1000000);
System.out.println("\nTotal Time : " + d + " ms" + " Array Copy : " + ((double)(end - start) / 1000000) + " ms");
}
}
Description:
There would be a function named
public LinkedList<Rect> differenceImage(int[][] baseFrame, int[][] screenShot, int width, int height)
which does the job of finding differences in the images and return a linkedlist of objects. The objects are nothing but the rectangles.
There is main function which does the job of testing the algorithm.
There are 2 sample images passed into the code in main function, they are nothing but the "baseFrame" and "screenShot" thereby creating the resultant image named "result".
I don't possess the desired reputation to post the resultant image which would be very interesting.
There is a blog which would provide the output
Image Difference
I don't think there can be anything better than exhaustively searching from each side in turn for the first point of difference in that direction. Unless, that is, you know a fact that in some way constrains the set of points of difference.
So here comes the easy way if you know how to use Lockbit :)
Bitmap originalBMP = new Bitmap(pictureBox1.ImageLocation);
Bitmap changedBMP = new Bitmap(pictureBox2.ImageLocation);
int width = Math.Min(originalBMP.Width, changedBMP.Width),
height = Math.Min(originalBMP.Height, changedBMP.Height),
xMin = int.MaxValue,
xMax = int.MinValue,
yMin = int.MaxValue,
yMax = int.MinValue;
var originalLock = originalBMP.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, originalBMP.PixelFormat);
var changedLock = changedBMP.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, changedBMP.PixelFormat);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
//generate the address of the colour pixel
int pixelIdxOrg = y * originalLock.Stride + (x * 4);
int pixelIdxCh = y * changedLock.Stride + (x * 4);
if (( Marshal.ReadByte(originalLock.Scan0, pixelIdxOrg + 2)!= Marshal.ReadByte(changedLock.Scan0, pixelIdxCh + 2))
|| (Marshal.ReadByte(originalLock.Scan0, pixelIdxOrg + 1) != Marshal.ReadByte(changedLock.Scan0, pixelIdxCh + 1))
|| (Marshal.ReadByte(originalLock.Scan0, pixelIdxOrg) != Marshal.ReadByte(changedLock.Scan0, pixelIdxCh))
)
{
xMin = Math.Min(xMin, x);
xMax = Math.Max(xMax, x);
yMin = Math.Min(yMin, y);
yMax = Math.Max(yMax, y);
}
}
}
originalBMP.UnlockBits(originalLock);
changedBMP.UnlockBits(changedLock);
var result = changedBMP.Clone(new Rectangle(xMin, yMin, xMax - xMin, yMax - yMin), changedBMP.PixelFormat);
pictureBox3.Image = result;
disclaim it looks like your 2 pictures contains more differences than we can see with the naked eye so the result will be wider than you expect but you can add a tolerance so it wil fit even if the rest isn't 100% identical
to speed things up you will maybe able to us Parallel.For but do it only for the outer loop