Taking Screenshot & Comparing Images - c#

I'm using these functions below in order to take a screenshot of the user's screen, and compare this screenshot to an image bmpTest which is in resources. If finds something compatible with bmpTest returns location of it.
My problem is: this algorithm used is a little slow and takes about 10-15 seconds to interpret the image and give a result. Does any of you knows other method which does the same but faster? And with maybe, some % of similarity? I just couldnt find on internet.
private Bitmap Screenshot()
{
// this is where we will store a snapshot of the screen
Bitmap bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
// creates a graphics object so we can draw the screen in the bitmap (bmpScreenshot)
Graphics g = Graphics.FromImage(bmpScreenshot);
// copy from screen into the bitmap we created
g.CopyFromScreen(0, 0, 0, 0, Screen.PrimaryScreen.Bounds.Size);
//g.CopyFromScreen(205, 179, 660, 241, new Size(455, 62));
// return the screenshot
return bmpScreenshot;
}
private bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location)
{
for (int outerX = 0; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = 0; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:
continue;
}
}
location = Point.Empty;
return false;
}
private void button8_Click_1(object sender, EventArgs e)
{
// takes a snapshot of the screen
Bitmap bmpScreenshot = Screenshot();
// find the login button and check if it exists
Point location;
bool success = FindBitmap(Properties.Resources.bmpTest, bmpScreenshot, out location);
// check if it found the bitmap
if (success == false)
{
MessageBox.Show("Not Found");
return;
}
else
{
MessageBox.Show("Image Found");
}
}

This occurs because every "GetPixel" call on bitmap object performs a lock which is causing your performance issue.
You can get better performance by dealing with the lock yourself, so you can lock the entire bitmap once, in your case both bitmaps, an then iterate through its pixels.
Check out the code example by Vano Maisuradze

Related

How do I improve the speed of my C# algorithm by using bytes instead of GetPixel() method?

I have a function in C# which compare two images and finds the differences between them. The problem is that it is too slow. How can I optimize the code by using bytes instead of GetPixel() method?
The speed of this calculus is 0.34 seconds and I should do it in at least ten times less. Some colleague told me I can improve the speed by using bytes.
private void btnGo_Click(object sender, EventArgs e)
{
this.Cursor = Cursors.WaitCursor;
Application.DoEvents();
// Load the images.
Bitmap bm1 = (Bitmap)picImage1.Image;
Bitmap bm2 = (Bitmap)picImage2.Image;
// Make a difference image.
int wid = Math.Min(bm1.Width, bm2.Width);
int hgt = Math.Min(bm1.Height, bm2.Height);
Bitmap bm3 = new Bitmap(wid, hgt);
// Create the difference image.
bool are_identical = true;
Color eq_color = Color.White;
Color ne_color = Color.Red;
for (int x = 0; x < wid; x++)
{
for (int y = 0; y < hgt; y++)
{
if (bm1.GetPixel(x, y).Equals(bm2.GetPixel(x, y)))
bm3.SetPixel(x, y, eq_color);
else
{
bm3.SetPixel(x, y, ne_color);
are_identical = false;
}
}
}
// Display the result.
picResult.Image = bm3;
this.Cursor = Cursors.Default;
if ((bm1.Width != bm2.Width) || (bm1.Height != bm2.Height))
are_identical = false;
if (are_identical)
lblResult.Text = "The images are identical";
else
lblResult.Text = "The images are different";
}
By using bytes I should be able to have this computation in 0.04 seconds.

C# Check if image appears on screen

I have a cropped version of an image that should appear on my screen.
Image 6Island = Image.FromFile("C:\\Users\\6Island.png");
Now the next goal is to Take an image of the screen.
Bitmap CaptureScreen()
{
var image = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
var gfx = Graphics.FromImage(image);
gfx.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
return image;
}
Image 6Island = Image.FromFile("C:\\Users\\6Island.png");
Image currentView = CaptureScreen();
I then want to see if I can I can find the image 6Island inside the new image. And the colors may vary a tiny bit. Is there anyway to do that?
This is just sample quick and dirty and very slow, but it works. This code make a "crop" of your big bitmap and compare it with your small bitmap. If equal then percentage must be 100, if unequal then percentage lower than that. I would say, if bigger than 98%, then you found it.
private static void CompareBigAndSmallBitmaps(string fileName1, string fileName2)
{
var bmpBig = (Bitmap) Image.FromFile(fileName1);
var bmpSmall = (Bitmap) Image.FromFile(fileName2);
for (var offX = 0; offX < bmpBig.Width - bmpSmall.Width; offX++)
{
for (var offY = 0; offY < bmpBig.Height - bmpSmall.Height; offY++)
{
var percentage = CompareSmallBitmaps(bmpBig, bmpSmall, offX, offY);
if (percentage > 98.0) // define percentage of equality
{
// Aha... found something here....and exit here if you want
}
}
}
}
private static double CompareSmallBitmaps(Bitmap bmpBig, Bitmap bmpSmall, int offX, int offY)
{
var equals = 0;
for (var x = 0; x < bmpSmall.Width; x++)
{
for (var y = 0; y < bmpSmall.Height; y++)
{
var color1 = bmpBig.GetPixel(x + offX, y + offY).ToArgb();
var color2 = bmpSmall.GetPixel(x, y).ToArgb();
if (color1 == color2)
{
equals++;
}
}
}
return (Convert.ToDouble(equals)/Convert.ToDouble(bmpSmall.Width*bmpSmall.Height))*100.0;
}

Color detection C#

I want to make color_detection_bot and This bot provide me that when I click the button in this form, mousecursor changes position to facebook login button and click it.First of all I created a bitmap in Paint and save in System.Resources name as bmpLogin and this bitmap shows a 24 bit bitmap of facebook login button.
private void btn_login_Click(object sender, EventArgs e)
{
this.BackgroundImage = Screenshot(); //Method which is take screenshot
Point location;
bool success=FindBitmap(Properties.Resources.bmpLogin, Screenshot(), out location);
if (success == false)
{
MessageBox.Show("Couldn't find the login button");
return;
}
Cursor.Position = location; //bitmap location within screenshot
MouseCLick(); //Mouse click event works successfull
}
/// <summary>
/// Find the location of bitmap within another bitmap and return if it was succesfully found
/// </summary>
/// <param name="bmpNeedle"> The image we want to find </param>
/// <param name="bmpHaystack"> Where we want to search for the image </param>
/// <param name="location"> Where we found the image </param>
/// <returns> If the bmpNeedle was found succesfully </returns>
private Boolean FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location)
{
for (int outerX = 0; outerX < bmpHaystack.Width; outerX++)
{
for (int outerY = 0; outerY < bmpHaystack.Height ; outerY++)
{
for (int innerX = 0; innerX <bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY <bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto Notfound;
}
}
}
location = new Point(outerX, outerY);
return true;
Notfound:
continue;
}
}
location = Point.Empty;
return false;
}
This codes works perfectly But When I change if statemant as shown in below, Bot doesnt work.Where am I doing logical error
private Boolean FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location)
{
for (int outerX = 0; outerX < bmpHaystack.Width; outerX++)
{
for (int outerY = 0; outerY < bmpHaystack.Height; outerY++)
{
for (int innerX = 0; innerX <bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY <bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R == cHaystack.R && cNeedle.G == cHaystack.G && cNeedle.B == cHaystack.B)
{
location = new Point(outerX, outerY);
return true;
}
}
}
}
}
location = Point.Empty;
return false;
}
In your first code sample, the algorithm is matching all the pixels. In your second code sample, the algorithm is returning the location of the first matched pixel.
It's not enough just to match one pixel. For example, say your full image is:
1 2 3 4
1 a a b b
2 a a c a
3 a b a a
And you're searching for:
1 2
1 a b
2 a c
Your second block of code looks first at 1,1 and says, brilliant, they are both a, job done, and merrily returns the wrong answer. However your first block of code will look at 1,1, it matches so then it goes to 1,2 see that it is b whereas they haystack is a, and then the goto takes it to not found, and it searches the next position of the haystack.
As already pointed out, it's a good demonstration of what's wrong with goto. See here GOTO still considered harmful? for a full discussion on the topic.
Two other problems with this - one already mentioned, that if your search image is in the bottom right, you're going to go out of bounds when you compare it to the larger image - if we use the simple example above, if your search start point is at 4,3, then as you go to compare to the second column of the smaller image, you're looking at 5,3, which doesn't exist. Second problem, GetPixel is very slow, for a large search image this code is going to take a while - and it can't be multi-threaded. A faster but more complicated implementation is to use LockBits, which is much faster to access. Sample implementation is below.
static void Main(string[] args)
{
Bitmap haystack = (Bitmap)Image.FromFile(#"c:\temp\haystack.bmp");
Bitmap needle = (Bitmap)Image.FromFile(#"c:\temp\needle.bmp");
Point location;
if (FindBitmap(haystack, needle, out location) == false)
{
Console.WriteLine("Didn't find it");
}
else
{
using (Graphics g = Graphics.FromImage(haystack))
{
Brush b = new SolidBrush(Color.Red);
Pen pen = new Pen(b, 2);
g.DrawRectangle(pen, new Rectangle(location, new Size(needle.Width, needle.Height)));
}
haystack.Save(#"c:\temp\found.bmp");
Console.WriteLine("Found it # {0}, {1}", location.X, location.Y);
}
Console.ReadKey();
}
public class LockedBitmap : IDisposable
{
private BitmapData _data = null;
private Bitmap _sourceBitmap = null;
private byte[] _pixelData = null;
public readonly int BytesPerPixel, WidthInBytes, Height, Width, ScanWidth;
public LockedBitmap(Bitmap bitmap)
{
_sourceBitmap = bitmap;
_data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
BytesPerPixel = Bitmap.GetPixelFormatSize(bitmap.PixelFormat) / 8;
ScanWidth = _data.Stride;
int byteCount = _data.Stride * bitmap.Height;
_pixelData = new byte[byteCount];
Marshal.Copy(_data.Scan0, _pixelData, 0, byteCount);
WidthInBytes = bitmap.Width * BytesPerPixel;
Height = bitmap.Height;
Width = bitmap.Width;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void GetPixel(int x, int y, out int r, out int g, out int b)
{
int realY = y * ScanWidth;
int realX = x * BytesPerPixel;
b = (int)_pixelData[realY + realX];
g = (int)_pixelData[realY + realX + 1];
r = (int)_pixelData[realY + realX + 2];
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (_sourceBitmap != null && _data != null)
_sourceBitmap.UnlockBits(_data);
}
}
private static bool FindBitmap(Bitmap haystack, Bitmap needle, out Point location)
{
using (LockedBitmap haystackToSearch = new LockedBitmap(haystack))
using (LockedBitmap needleToSearch = new LockedBitmap(needle))
{
for (int outerY = 0; outerY <= (haystackToSearch.Height - needleToSearch.Height); outerY++)
{
for (int outerX = 0; outerX <= (haystackToSearch.Width - needleToSearch.Width); outerX++)
{
bool isMatched = true;
for (int innerY = 0; innerY < needleToSearch.Height; innerY++)
{
for (int innerX = 0; innerX < needleToSearch.Width; innerX++)
{
int needleR, needleG, needleB;
int haystackR, haystackG, haystackB;
haystackToSearch.GetPixel(outerX + innerX, outerY + innerY, out haystackR, out haystackG, out haystackB);
needleToSearch.GetPixel(innerX, innerY, out needleR, out needleG, out needleB);
isMatched = isMatched && needleR == haystackR && haystackG == needleG && haystackB == needleB;
if (!isMatched)
break;
}
if (!isMatched)
break;
}
if (isMatched)
{
location = new Point(outerX, outerY);
return true;
}
}
}
}
location = new Point();
return false;
}

Obtain RGB value of image and potentially move image file in C#

I'm looking at doing a project in C# that looks at an image file not sure of extension yet, and notes the RGB value and if its too dark moves it to another folder for me to look at later
So here it is in block form
Load multiple images from directory > Check RGB value of every file > if too dark > move to different folder. if not ignore (leave in original folder)
I know the basics like get files from dir but checking RGB value of whole picture and then moving it or ignoring it I'm stumped.
I have this code:
private void button1_Click(object sender, EventArgs e)
{
CompareImages(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "checked"), 127.0, new string[] {"*.jpg", "*.png"});
}
private void CompareImages(string sourceFolder, string disposedImgFolder, double threshold, string[] extensions)
{
if (Directory.Exists(sourceFolder))
{
DirectoryInfo dir = new DirectoryInfo(sourceFolder);
List<FileInfo> pictures = new List<FileInfo>();
foreach (string ext in extensions)
{
FileInfo[] fi = dir.GetFiles(ext);
pictures.AddRange(fi);
}
Directory.CreateDirectory(disposedImgFolder);
int j = 0;
if (pictures.Count > 0)
{
for (int i = 0; i < pictures.Count; i++)
{
Image img = null;
Bitmap bmp = null;
try
{
img = Image.FromFile(pictures[i].FullName);
bmp = new Bitmap(img);
img.Dispose();
double avg = GetAveragePixelValue(bmp);
bmp.Dispose();
if (avg < threshold)
{
string dest = Path.Combine(disposedImgFolder, pictures[i].Name);
if (File.Exists(dest) == false)
{
pictures[i].MoveTo(dest);
j++;
}
else
{
}
}
else
{
}
}
catch
{
if (img != null)
img.Dispose();
if (bmp != null)
bmp.Dispose();
}
}
MessageBox.Show("Done, " + j.ToString() + " files moved.");
}
}
}
private unsafe double GetAveragePixelValue(Bitmap bmp)
{
BitmapData bmData = null;
try
{
bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
IntPtr scan0 = bmData.Scan0;
int w = bmData.Width;
int h = bmData.Height;
double sum = 0;
long pixels = bmp.Width * bmp.Height;
byte* p = (byte*)scan0.ToPointer();
for (int y = 0; y < h; y++)
{
p = (byte*)scan0.ToPointer();
p += y * stride;
for (int x = 0; x < w; x++)
{
double i = ((double)p[0] + p[1] + p[2]) / 3.0;
sum += i;
p += 4;
}
}
bmp.UnlockBits(bmData);
double result = sum / (double)pixels;
return result;
}
catch
{
try
{
bmp.UnlockBits(bmData);
}
catch
{
}
}
return -1;
}
How do I define the threashold?
If you want to read every pixel of an image, it must be converted to a bitmap. Then you can use the GetPixel method. But, this process is very slow and it takes a lot of CPU. If you do so, you really should run some test before.
using (var m = new MemoryStream())
{
using (var img = Image.FromFile(args[0]))
{
img.Save(m, ImageFormat.Bmp);
}
m.Position = 0;
using (var bitmap = (Bitmap)Bitmap.FromStream(m))
{
for (var x = 0; x < bitmap.Width; x++)
{
for (var y = 0; y < bitmap.Height; y++)
{
var color = bitmap.GetPixel(x, y);
// TODO: Do what ever you want
}
}
}
}
I think you need to read up a bit on RGB. Every pixel will have an Red, Green and Blue value associated with it and i guess you are looking for a way to get some meassure of how bright the "average" pixel is? If so you need to loop over all pixels. While doing so calculate brightness of each pixel. "Brightness" of each pixels can be calculated in several ways, you could simply do (R + G + B)/3, or you could try to mimic that the human eye isn't equaly sensitive to R, G and B.
Then you will have to decide how to use your "brightness" of pixel. Mean, Median, something else? It depends on what you want to do..
Update: After reading more of your comments I'm still not really sure what you mean by "bright" or "dark". It also seems that you have your terminology a bit confused, you keep talking about a RGB value for an entire image, but RGB values in an image refer to individual pixel values.
I believe that this page could help you doing what you want:
http://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx
Also, some complimentary reading to understand RGB:
http://en.wikipedia.org/wiki/Luma_(video)
http://en.wikipedia.org/wiki/RGB_color_space

Clean up the surface on WaterMark of an Image

There is an image for the surface, and a text is written on the image for 184 rows of date..
Thus, it is expected to see 184 different text written image files are generated with all the same background images. (The code is declared below...)
The problem is that the first text is written for all 184 different data.. I think I have to remove something in the loop. But what is that ??
for (int i = 0; i < dt.Rows.Count; i++) {
date = Convert.ToDateTime(dt.Rows[i]["PAYMENT_DATE"]);
branchCode = Convert.ToInt32(dt.Rows[i]["BRANCH_CODE"]);
refNum = Convert.ToInt32(dt.Rows[i]["REF_NUM"]);
accountNumber = Convert.ToInt32(dt.Rows[i]["ACCOUNT_NUMBER"]);
email = dt.Rows[i]["EMAIL"].ToString();
tableCode = dt.Rows[i]["CUSTOMER_TABLE_CODE"].ToString();
TranLogKey logKey = new TranLogKey(date, branchCode, refNum);
TranLogEntry entry = Log.SelectLogEntry(logKey, false);
if (Intertech.Core.Framework.Context.CurrentContext.LogEntry == null)
Intertech.Core.Framework.Context.CurrentContext.LogEntry = entry;
try {
receiptText = TransactionManager.GenerateReceipt(true, logKey, null, null, accountNumber, false);
}
catch (Exception exp) {
continue;
}
if (receiptText != null) {
if (receiptText.IndexOf("SURETTİR\r\n") != -1)
receiptText = receiptText.Substring(receiptText.IndexOf("SURETTİR\r\n") + 10).Trim();
if (receiptText.IndexOf("İşlemi Yapan") != -1)
receiptText = receiptText.Substring(0, receiptText.IndexOf("İşlemi Yapan"));
if (receiptText.IndexOf("MÜŞTERİ İMZASI") != -1)
receiptText = receiptText.Substring(0, receiptText.IndexOf("MÜŞTERİ İMZASI"));
Bitmap bmp = (Bitmap)Bitmap.FromFile(imageDir);
Graphics g = Graphics.FromImage(bmp);
SizeF size;
Font font = new Font("Courier New", 8, FontStyle.Regular);
byte ALPHA = 200;
size = g.MeasureString(receiptText, font);
Bitmap waterbmp = new Bitmap((int)size.Width, (int)size.Height);
Graphics waterg = Graphics.FromImage(waterbmp);
waterg.DrawString(receiptText, font, new SolidBrush(System.Drawing.Color.Black), 2, 2);
DrawWatermark(ref waterbmp, ref bmp, LeftIndex, TopIndex, ALPHA);
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
try {
GoServices.Core.SendMailOutside("The Portugal Life", "info#portugal.tr.friendship.pt",
"blgnklc#skype-account.com", " e-Wish " + email, "", new string[] { "dekont.jpg" }, new object[] { ms.ToArray() });
LogNotificationState("K", refNum, accountNumber, 2, true, null, tableCode);
}
catch (Exception ex) {
LogNotificationState("K", 0, -1, 2, false, ex, tableCode);
}
}
private static void DrawWatermark(ref Bitmap watermark_bm, ref Bitmap result_bm, int x, int y, byte ALPHA) {
System.Drawing.Color clr;
int py, px;
for (py = 0; py <= watermark_bm.Height - 1; py++) {
for (px = 0; px <= watermark_bm.Width - 1; px++) {
clr = watermark_bm.GetPixel(px, py);
if (clr.A != 0 || clr.R != 0 || clr.G != 0 || clr.B != 0)
watermark_bm.SetPixel(px, py, System.Drawing.Color.FromArgb(ALPHA, clr.R, clr.G, clr.B));
}
}
Graphics gr = Graphics.FromImage(result_bm);
gr.DrawImage(watermark_bm, x, y);
}
I would look at how you are creating the bitmap.
Bitmap bmp = (Bitmap)Bitmap.FromFile(imageDir);
does this line need to be in there everytime, i.e is imagedir the same all the time?
Do you dispose of the generated bitmap after creating it?
what happens in this function:
DrawWatermark(ref waterbmp, ref bmp, LeftIndex, TopIndex, ALPHA);
You seem to then save the bmp file but not dispose of it?
I have seen some strange behaviour from GDI+ so I would start here.

Categories

Resources