How do the obstacle detection sensors work in the DJI Windows SDK? - dji-sdk

I want to get the distance of the obstacles in the back and front of the Mavic Air.
I'm using the VissionDetectionStateChanged and it returns values in the 4 sectors, but all of them change only with obstacles in the back. If I put my hand in the front, nothing happens.
The VisionSensorPosition is returning TAIL always and when I put my hand very close to the tail of the aircraft it changes for NOSE.
Shouldn't be the opposite?
Right now I just display the information, but I'd like to be able to detect obstacles in the back and front of the aircraft to try to keep it in the middle of two objects and avoid collisions.
This is my code in the event:
private async void FlightAssistant_VissionDetectionStateChanged(object sender, VissionDetectionState? value)
{
if (value.HasValue)
{
if (txtPosition.Dispatcher.HasThreadAccess)
{
txtPosition.Text = value.Value.position.ToString();
for (int i = 0, count = value.Value.detectionSectors.Count; i < count; i++)
{
ObstacleDetectionSector sector = value.Value.detectionSectors[i];
TextBox txtWarning = this.FindControl<TextBox>("txtWarning" + i.ToString());
if (txtWarning != null)
txtWarning.Text = sector.warningLevel.ToString();
TextBox txtObstacleDistance = this.FindControl<TextBox>("txtObstacleDistance" + i.ToString());
if (txtObstacleDistance != null)
txtObstacleDistance.Text = sector.obstacleDistanceInMeters.ToString();
}
}
else
{
await txtPosition.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
txtIsSensorBeingUsed.Text = value.Value.isSensorBeingUsed.ToString();
txtPosition.Text = value.Value.position.ToString();
for (int i = 0, count = value.Value.detectionSectors.Count; i < count; i++)
{
ObstacleDetectionSector sector = value.Value.detectionSectors[i];
TextBox txtWarning = this.FindControl<TextBox>("txtWarning" + i.ToString());
if (txtWarning != null)
txtWarning.Text = sector.warningLevel.ToString();
TextBox txtObstacleDistance = this.FindControl<TextBox>("txtObstacleDistance" + i.ToString());
if (txtObstacleDistance != null)
txtObstacleDistance.Text = sector.obstacleDistanceInMeters.ToString();
}
});
}
}
}

Related

Optimizations for a recursive formula finding a path between two random nodes

I have written a recursive formula that finds a path between two nodes arranged in a grid pattern. My code works with two problems. The first is that sometimes the start node's position is changed from one to another number, but I fixed this by reassigning it after the recursion so it's not that big of a deal. The second issue is that it runs unbearably slow. It takes about 30 seconds to generate a 5x5 and I haven't been able to generate a 7x7 which is my ultimate goal. I am hoping that someone will see if there are any optimizations that can be made.
The Node class, shown below, has a Key property and a Value property. The Key is the position in the grid starting at 0. So, for a 5x5, the top left node will have a Key of 0 and the bottom right node will have a Key of 24. Each node has Up, Down, Left and Right properties that are the other nodes it is connected to. When there are no nodes in that direction, the value is null. For example, in a 5x5, a node with Key = 0 will have an Up of null, a Down of the node with Key = 5, a Left of null, and a Right of the node with Key = 1. As another example, still in a 5x5, a node with Key = 6 will have an Up of the node with Key = 1, a Down of the node with Key = 11, a Left of the node with Key = 5, and a Right of the node with Key = 7. The Position property is the path. The path starts with the node with Position = 1, then goes to the node with Position = 2 etc. until it reaches the end node, which would be position N*N on a NxN board (e.g. a 5x5 board would have an end node with position 25). These nodes are added to a list called nodeList-(a global variable). One of these nodes gets randomly marked as Start-(boolean) and a different node gets randomly assigned as End-(boolean).
The next part is the path. We want to find a random path (starting at 1) between the Start and End nodes that touches every other node without touching the same node twice. This is for a game, so it is important that it is random so the user doesn't play the same board twice. If this is not possible given the Start and End Positions, new Start and End positions are chosen and the algorithm is run again.
class Node
{
public int Key { get; set; }
public int? Position { get; set; } = null;
public Node Up { get; set; } = null;
public Node Down { get; set; } = null;
public Node Left { get; set; } = null;
public Node Right { get; set; } = null;
public bool Start = false;
public bool End = false;
public Node(int key)
{
Key = key;
}
}
public bool GeneratePath()
{
var current = nodeList.Where(w => w.Start).FirstOrDefault();
var start = current;
int position = 1;
bool Recurse(Node caller)
{
if (current.Position == null)
{
current.Position = position;
}
if (current.End)
{
return true;
}
var directions = GetDirections();
for (var i = 0; i < 4; i++)
{
var done = false;
if (directions[i] == 0 && current.Up != null && current.Up.Position == null
&& (!current.Up.End || position == n * n - 1))
{
var temp = current;
current = current.Up;
position++;
done = Recurse(temp);
}
else if (directions[i] == 1 && current.Down != null && current.Down.Position == null
&& (!current.Down.End || position == n * n - 1))
{
var temp = current;
current = current.Down;
position++;
done = Recurse(temp);
}
else if (directions[i] == 2 && current.Left != null && current.Left.Position == null
&& (!current.Left.End || position == n * n - 1))
{
var temp = current;
current = current.Left;
position++;
done = Recurse(temp);
}
else if (directions[i] == 3 && current.Right != null && current.Right.Position == null
&& (!current.Right.End || position == n*n - 1))
{
var temp = current;
current = current.Right;
position++;
done = Recurse(temp);
}
if(done)
{
return true;
}
}
current.Position = null;
position--;
if(caller == null)
{
return false;
}
current = caller;
return false;
}
var success = Recurse(null);
if (success)
{
start.Position = 1;
}
return success;
}
private int[] GetDirections()
{
List<int> toPerm = new List<int>();
for (var i = 0; i < 4; i++)
{
toPerm.Add(i);
}
Random random = new Random();
var perms = HelperMethods.GetPermutations(toPerm, toPerm.Count);
var randomNumber = random.Next(0, perms.Count());
var directions = perms.ElementAt(randomNumber).ToArray();
return directions;
}
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(o => !t.Contains(o)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
To reiterate, I am wondering if there are optimizations I can make as it runs too slow for my purposes.
So I found an implementation of an amazing algorithm that can produce 10,000 7x7's in 12 seconds. You can find the implementation here. The author is Nathan Clisby and it is based off of a paper “Secondary structures in long compact polymers”. The idea is to produce a non-random path between the two points, then randomly mutate the path as many times as the user wishes. Supposedly, with enough iterations, this algorithm can produce paths that are nearly as random as the algorithm I posted in the question.
Way to go computer scientists!

Path finding with theta* when the triangle inequality is not fulfilled

I try to use the theta* algorithm (aigamedev.com/open/tutorial/lazy-theta-star) to find the fastest path on a rectangular grid. Distances are euqlidian, but speed between nodes is time dependent and varies between directions. So the triangle inequality, which is a prerequisite for the algorithm, is violated. Still it works excellently in most cases. How could I modify the code to work nicely also in turbulent areas? I suspect I may have to reevaluate some closed nodes and put them back into the open list. If so, under what conditions? An extensive web search hasn't helped.
vertex_t *thetastar(vertex_t *startnode, vertex_t *finishnode, double starttime) {
vertex_t *s, *s1;
double gold, gnew;
int dir; //8 directions to search for node neighbours
//Initialize
vertex[0].row = startnode->row;
vertex[0].col = startnode->col;
vertex[0].g = starttime;
vertex[0].h = h(&vertex[0], finishnode);
vertex[0].open = true;
vertex[0].closed = false;
vertex[0].parent = &vertex[0];
openlist[0] = &vertex[0];
openlist[1] = NULL;
//Find path
while ((s = pop(openlist)) != NULL) {
if (s->row == finishnode->row && s->col == finishnode->col) {
return s;
}
s->closed = true;
for (dir = 0; dir < 8; dir++) {
if ((s1 = nghbrvis(s, dir)) != NULL) {
if (!s1->closed) {
if (!s1->open) {
s1->g = inftime;
s1->parent = NULL;
}
gold = s1->g;
//Path 2
if (lineofsight(s->parent, s1)) {
gnew = (s->parent)->g + c(s->parent, s1);
if (gnew < s1->g) {
s1->parent = s->parent;
s1->g = gnew;
} }
//Path 1
gnew = s->g + c(s, s1);
if (gnew < s1->g) {
s1->parent = s;
s1->g = gnew;
}
if (s1->g < gold) {
s1->h = h(s1, finishnode);
if (s1->open)
remove(s1, openlist);
insert(s1, openlist);
} } } } }
return NULL;
}

EWS The server cannot service this request right now

I am seeing errors while exporting email in office 365 account using ews managed api, "The server cannot service this request right now. Try again later." Why is that error occurring and what can be done about it?
I am using the following code for that work:-
_GetEmail = (EmailMessage)item;
bool isread = _GetEmail.IsRead;
sub = _GetEmail.Subject;
fold = folder.DisplayName;
historicalDate = _GetEmail.DateTimeSent.Subtract(folder.Service.TimeZone.GetUtcOffset(_GetEmail.DateTimeSent));
props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(_source, item.Id, props);
bytes = new byte[email.MimeContent.Content.Length];
fs = new MemoryStream(bytes, 0, email.MimeContent.Content.Length, true);
fs.Write(email.MimeContent.Content, 0, email.MimeContent.Content.Length);
Demail = new EmailMessage(_destination);
Demail.MimeContent = new MimeContent("UTF-8", bytes);
// 'SetExtendedProperty' used to maintain historical date of items
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(57, MapiPropertyType.SystemTime), historicalDate);
// PR_MESSAGE_DELIVERY_TIME
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(3590, MapiPropertyType.SystemTime), historicalDate);
if (isread == false)
{
Demail.IsRead = isread;
}
if (_source.RequestedServerVersion == flagVersion && _destination.RequestedServerVersion == flagVersion)
{
Demail.Flag = _GetEmail.Flag;
}
_lstdestmail.Add(Demail);
_objtask = new TaskStatu();
_objtask.TaskId = _taskid;
_objtask.SubTaskId = subtaskid;
_objtask.FolderId = Convert.ToInt64(folderId);
_objtask.SourceItemId = Convert.ToString(_GetEmail.InternetMessageId.ToString());
_objtask.DestinationEmail = Convert.ToString(_fromEmail);
_objtask.CreatedOn = DateTime.UtcNow;
_objtask.IsSubFolder = false;
_objtask.FolderName = fold;
_objdbcontext.TaskStatus.Add(_objtask);
try
{
if (counter == countGroup)
{
Demails = new EmailMessage(_destination);
Demails.Service.CreateItems(_lstdestmail, _destinationFolder.Id, MessageDisposition.SaveOnly, SendInvitationsMode.SendToNone);
_objdbcontext.SaveChanges();
counter = 0;
_lstdestmail.Clear();
}
}
catch (Exception ex)
{
ClouldErrorLog.CreateError(_taskid, subtaskid, ex.Message + GetLineNumber(ex, _taskid, subtaskid), CreateInnerException(sub, fold, historicalDate));
counter = 0;
_lstdestmail.Clear();
continue;
}
This error occurs only if try to export in office 365 accounts and works fine in case of outlook 2010, 2013, 2016 etc..
Usually this is the case when exceed the EWS throttling in Exchange. It is explain in here.
Make sure you already knew throttling policies and your code comply with them.
You can find throttling policies using Get-ThrottlingPolicy if you have the server.
One way to solve the throttling issue you are experiencing is to implement paging instead of requesting all items in one go. You can refer to this link.
For instance:
using Microsoft.Exchange.WebServices.Data;
static void PageSearchItems(ExchangeService service, WellKnownFolderName folder)
{
int pageSize = 5;
int offset = 0;
// Request one more item than your actual pageSize.
// This will be used to detect a change to the result
// set while paging.
ItemView view = new ItemView(pageSize + 1, offset);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
view.Traversal = ItemTraversal.Shallow;
bool moreItems = true;
ItemId anchorId = null;
while (moreItems)
{
try
{
FindItemsResults<Item> results = service.FindItems(folder, view);
moreItems = results.MoreAvailable;
if (moreItems && anchorId != null)
{
// Check the first result to make sure it matches
// the last result (anchor) from the previous page.
// If it doesn't, that means that something was added
// or deleted since you started the search.
if (results.Items.First<Item>().Id != anchorId)
{
Console.WriteLine("The collection has changed while paging. Some results may be missed.");
}
}
if (moreItems)
view.Offset += pageSize;
anchorId = results.Items.Last<Item>().Id;
// Because you’re including an additional item on the end of your results
// as an anchor, you don't want to display it.
// Set the number to loop as the smaller value between
// the number of items in the collection and the page size.
int displayCount = results.Items.Count > pageSize ? pageSize : results.Items.Count;
for (int i = 0; i < displayCount; i++)
{
Item item = results.Items[i];
Console.WriteLine("Subject: {0}", item.Subject);
Console.WriteLine("Id: {0}\n", item.Id.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine("Exception while paging results: {0}", ex.Message);
}
}
}

How to correct loop counters for maze algorithm?

I have figured out how to move my character around the maze using the algorithm I have written, but the count is not figuring correctly. At the end of each row my character moves up and down several times until the count reaches the specified number to exit the loop, then the character moves along the next row down until it reaches the other side and repeats the moving up and down until the count reaches the specified number again. Can anyone help me find why my count keeps getting off? The algorithm and the maze class I am calling from is listed below.
public class P4 {
public static void main(String[] args) {
// Create maze
String fileName = args[3];
Maze maze = new Maze(fileName);
System.out.println("Maze name: " + fileName);
// Get dimensions
int mazeWidth = maze.getWidth();
int mazeHeight = maze.getHeight();
// Print maze size
System.out.println("Maze width: " + mazeWidth);
System.out.println("Maze height: " + mazeHeight);
int r = 0;
int c = 0;
// Move commands
while (true){
for (c = 0; c <= mazeWidth; c++){
if (maze.moveRight()){
maze.isDone();
c++;
}
if (maze.isDone() == true){
System.exit(1);
}
if (maze.moveRight() == false && c != mazeWidth){
maze.moveDown();
maze.moveRight();
maze.moveRight();
maze.moveUp();
c++;
}
}
for (r = 0; r % 2 == 0; r++){
maze.moveDown();
maze.isDone();
if (maze.isDone() == true){
System.exit(1);
}
}
for (c = mazeWidth; c >= 0; c--){
if (maze.moveLeft()){
c--;
maze.isDone();
System.out.println(c);
}
if (maze.isDone() == true){
System.exit(1);
}
if (maze.moveLeft() == false && c != 0){
maze.moveDown();
maze.moveLeft();
maze.moveLeft();
maze.moveUp();
c--;
}
}
for (r = 1; r % 2 != 0; r++){
maze.moveDown();
maze.isDone();
if (maze.isDone() == true){
System.exit(1);
}
}
}
}
}
public class Maze {
// Maze variables
private char mazeData[][];
private int mazeHeight, mazeWidth;
private int finalRow, finalCol;
int currRow;
private int currCol;
private int prevRow = -1;
private int prevCol = -1;
// User interface
private JFrame frame;
private JPanel panel;
private Image java, student, success, donotpass;
private ArrayList<JButton> buttons;
// Maze constructor
public Maze(String fileName) {
// Read maze
readMaze(fileName);
// Graphics setup
setupGraphics();
}
// Get height
public int getHeight() {
return mazeHeight;
}
// Get width
public int getWidth() {
return mazeWidth;
}
// Move right
public boolean moveRight() {
// Legal move?
if (currCol + 1 < mazeWidth) {
// Do not pass?
if (mazeData[currRow][currCol + 1] != 'D')
{
currCol++;
redraw(true);
return true;
}
}
return false;
}
// Move left
public boolean moveLeft() {
// Legal move?
if (currCol - 1 >= 0) {
// Do not pass?
if (mazeData[currRow][currCol - 1] != 'D')
{
currCol--;
redraw(true);
return true;
}
}
return false;
}
// Move up
public boolean moveUp() {
// Legal move?
if (currRow - 1 >= 0) {
// Do not pass?
if (mazeData[currRow - 1][currCol] != 'D')
{
currRow--;
redraw(true);
return true;
}
}
return false;
}
// Move down
public boolean moveDown() {
// Legal move?
if (currRow + 1 < mazeHeight) {
// Do not pass?
if (mazeData[currRow + 1][currCol] != 'D')
{
currRow++;
redraw(true);
return true;
}
}
return false;
}
public boolean isDone() {
// Maze solved?
if ((currRow == finalRow) && (currCol == finalCol))
return true;
else
return false;
}
private void redraw(boolean print) {
// Wait for awhile
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
if (print)
System.out.println("Moved to row " + currRow + ", column " + currCol);
// Compute index and remove icon
int index = (prevRow * mazeWidth) + prevCol;
if ((prevRow >= 0) && (prevCol >= 0)) {
buttons.get(index).setIcon(null);
}
// Compute index and add icon
index = (currRow * mazeWidth) + currCol;
if ((currRow == finalRow) && (currCol == finalCol))
buttons.get(index).setIcon(new ImageIcon(success));
else
buttons.get(index).setIcon(new ImageIcon(student));
// Store previous location
prevRow = currRow;
prevCol = currCol;
}
// Set button
private void setButton(JButton button, int row, int col) {
if (mazeData[row][col] == 'S') {
button.setIcon(new ImageIcon(student));
currRow = row;
currCol = col;
} else if (mazeData[row][col] == 'J') {
button.setIcon(new ImageIcon(java));
finalRow = row;
finalCol = col;
} else if (mazeData[row][col] == 'D') {
button.setIcon(new ImageIcon(donotpass));
}
}
// Read maze
private void readMaze(String filename) {
try {
// Open file
Scanner scan = new Scanner(new File(filename));
// Read numbers
mazeHeight = scan.nextInt();
mazeWidth = scan.nextInt();
// Allocate maze
mazeData = new char[mazeHeight][mazeWidth];
// Read maze
for (int row = 0; row < mazeHeight; row++) {
// Read line
String line = scan.next();
for (int col = 0; col < mazeWidth; col++) {
mazeData[row][col] = line.charAt(col);
}
}
// Close file
scan.close();
} catch (IOException e) {
System.out.println("Cannot read maze: " + filename);
System.exit(0);
}
}
// Setup graphics
private void setupGraphics() {
// Create grid
frame = new JFrame();
panel = new JPanel();
panel.setLayout(new GridLayout(mazeHeight, mazeWidth, 0, 0));
frame.add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH);
frame.add(panel, BorderLayout.CENTER);
// Look and feel
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
// Configure window
frame.setSize(mazeWidth * 100, mazeHeight * 100);
frame.setTitle("Maze");
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setAlwaysOnTop(true);
// Load and scale images
ImageIcon icon0 = new ImageIcon("Java.jpg");
Image image0 = icon0.getImage();
java = image0.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon1 = new ImageIcon("Student.jpg");
Image image1 = icon1.getImage();
student = image1.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon2 = new ImageIcon("Success.jpg");
Image image2 = icon2.getImage();
success = image2.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
ImageIcon icon3 = new ImageIcon("DoNotPass.jpg");
Image image3 = icon3.getImage();
donotpass = image3.getScaledInstance(100, 100, Image.SCALE_DEFAULT);
// Build panel of buttons
buttons = new ArrayList<JButton>();
for (int row = 0; row < mazeHeight; row++) {
for (int col = 0; col < mazeWidth; col++) {
// Initialize and add button
JButton button = new JButton();
Border border = new LineBorder(Color.darkGray, 4);
button.setOpaque(true);
button.setBackground(Color.gray);
button.setBorder(border);
setButton(button, row, col);
panel.add(button);
buttons.add(button);
}
}
// Show window
redraw(false);
frame.setVisible(true);
}
}
One error I can see in your code is that you're incrementing your c counter more often than you should. You start with it managed by your for loop, which means that it will be incremented (or decremented, for the leftward moving version) at the end of each pass through the loop. However, you also increment it an additional time in two of your if statements. That means that c might increase by two or three on a single pass through the loop, which is probably not what you intend.
Furthermore, the count doesn't necessarily have anything obvious to do with the number of moves you make. The loop code will always increase it by one, even if you're repeatedly trying to move through an impassible wall.
I don't really understand what your algorithm is supposed to be, so I don't have any detailed advice for how to fix your code.
One suggestion I have though is that you probably don't ever want to be calling methods on your Maze class without paying attention to their return values. You have a bunch of places where you call isDone but ignore the return value, which doesn't make any sense. Similarly, you should always be checking the return values from your moveX calls, to see if the move was successful or not. Otherwise you may just blunder around a bunch, without your code having any clue where you are in the maze.

In Selenium, how to compare images?

As i have been asked to automate our Company's website using Selenium Automation tooL.
But i am new to Selenium tool to proceed with, but i have learnt the basics of Selenium IDE and RC. But i am very much confused with how to compare actual and original images as we usually do in other automation tools. How do we come to a result that there bug in the website? Its obviously through image comparison but i wonder as selenium is one of the very popular tools but it doesn't have image comparing option. On the other hand i doubt whether my way of proceeding with the automation process is correct! Could somebody please help me out..
Thanks in Advance!!
Sanjay S
I had simillar task. I needed to compare more than 3000 images on a WebPage.
First of all I scrolled page to load all images:
public void compareImage() throws InterruptedException {
driver.get(baseUrl);
driver.manage().window().maximize();
JavascriptExecutor executor = (JavascriptExecutor) driver;
Long previousHeight;
Long currentHeight;
do {
previousHeight = (Long) executor.executeScript("return document.documentElement.scrollHeight");
executor.executeScript("window.scrollBy(0, document.documentElement.scrollHeight)");
Thread.sleep(500);
currentHeight = (Long) executor.executeScript("return document.documentElement.scrollHeight");
} while (Long.compare(previousHeight, currentHeight) != 0);
after I compared size of all images with first image(or you can just write size):
List<WebElement> images = driver.findElements(By.cssSelector("img[class='playable']"));
List<String> errors = new LinkedList<>();
int imgWidth, imgHeight, elWidth, elHeight;
int imgNum = 0;
imgWidth = images.get(0).getSize().getWidth();
imgHeight = images.get(0).getSize().getHeight();
for (WebElement el : images) {
imgNum++;
elWidth = el.getSize().getWidth();
elHeight = el.getSize().getHeight();
if (imgWidth != elWidth || imgHeight != elHeight) {
errors.add(String.format("Picture # %d has incorrect size (%d : %d) px"
, imgNum, elWidth, elHeight));
}
}
for (String str : errors)
System.out.println(str);
if (errors.size() == 0)
System.out.println("All images have the same size");
}
Since you mention knowledge about Selenium RC, you can easily extend Selenium's capability using a library for your chosen programming language. For instance, in Java you can use the PixelGrabber class for comparing two images and assert their match.
imagemagick and imagediff are also two good tools to use for image matching. You would require Selenium RC and a programming language knowledge to work with it.
Image comparison on C#. To get exact results I recommend to disable anti aliasing browser feature before taking screenshots, otherwise pixels each time are a little bit different drawn. For example HTML canvas element options.AddArgument("disable-canvas-aa");
private static bool ImageCompare(Bitmap bmp1, Bitmap bmp2, Double TolerasnceInPercent)
{
bool equals = true;
bool flag = true; //Inner loop isn't broken
//Test to see if we have the same size of image
if (bmp1.Size == bmp2.Size)
{
for (int x = 0; x < bmp1.Width; ++x)
{
for (int y = 0; y < bmp1.Height; ++y)
{
Color Bitmap1 = bmp1.GetPixel(x, y);
Color Bitmap2 = bmp2.GetPixel(x, y);
if (Bitmap1.A != Bitmap2.A)
{
if (!CalculateTolerance(Bitmap1.A, Bitmap2.A, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.R != Bitmap2.R)
{
if (!CalculateTolerance(Bitmap1.R, Bitmap2.R, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.G != Bitmap2.G)
{
if (!CalculateTolerance(Bitmap1.G, Bitmap2.G, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
if (Bitmap1.B != Bitmap2.B)
{
if (!CalculateTolerance(Bitmap1.B, Bitmap2.B, TolerasnceInPercent))
{
flag = false;
equals = false;
break;
}
}
}
if (!flag)
{
break;
}
}
}
else
{
equals = false;
}
return equals;
}
This C# function calculates tolerance
private static bool CalculateTolerance(Byte FirstImagePixel, Byte SecondImagePixel, Double TolerasnceInPercent)
{
double OneHundredPercent;
double DifferencesInPix;
double DifferencesPercentage;
if (FirstImagePixel > SecondImagePixel)
{
OneHundredPercent = FirstImagePixel;
}
else
{
OneHundredPercent = SecondImagePixel;
}
if (FirstImagePixel > SecondImagePixel)
{
DifferencesInPix = FirstImagePixel - SecondImagePixel;
}
else
{
DifferencesInPix = SecondImagePixel - FirstImagePixel;
}
DifferencesPercentage = (DifferencesInPix * 100) / OneHundredPercent;
DifferencesPercentage = Math.Round(DifferencesPercentage, 2);
if (DifferencesPercentage > TolerasnceInPercent)
{
return false;
}
return true;
}

Resources