0

I have got a sample images and a set of other images with which i want to compare the first image. So I am drawing both of them to a canvas and checking if they are same.

i have used the concept here: Compare two Images in JavaScript

The problem is the images are rotated at a random angle. so the base64 value of the images doesn’t seem to match with each other.

NOTE:I have no way to calculate the angles and trial and error is too resource consuming

match=-1;
for(i=0;i<7;i++)
        {
            context1.drawImage(img,xPos[i],5,width[i],height,0,0,width[i],height);;
                if(canvas1.toDataURL()==samplecanvas.toDataURL()){
                    match=i+1;
                    break;
                }

            context1.clearRect(0, 0, canvas1.width, canvas1.height);
        }

In the above code i am iterating through images. I am given sample image at samplecanvas and canvas1 is where i draw my list of images. then i want to compare them. But i dont know how.

How can i compare two images which are rotated in different direction at random angles using JavaScript?

Can i compare colours?

Community
  • 1
  • 1
Cijo
  • 2,726
  • 1
  • 14
  • 24
  • 1
    what did you google so far? – GottZ Oct 12 '15 at 15:16
  • there is almost no info for rotated images using js. i have gone through some libraries like this: [link](https://github.com/HumbleSoftware/js-imagediff/) . Not sure if it will work. Anybody used that one? They don't provide much info on what its suitable for and what its not suitable for. – Cijo Oct 12 '15 at 15:29
  • Just out of curiosity. Can you tell me the exact image size (px) of image 1 and the image size of the rotated image 2? Also, when rotated-normal - do the both images (the colored data) match in size 1:1 ? – Roko C. Buljan Oct 12 '15 at 16:11
  • Both are 85px X 64px in size! – Cijo Oct 12 '15 at 16:18
  • And it wont become 64px X 85px when rotated(most of the space is just white BG and the image makeup just small portion of it). – Cijo Oct 12 '15 at 16:19
  • If the rotation is not a multiple of 90 then you are almost guaranteed to have modified pixels somewhere rather than simply moving them to another location, so "un-rotating" will not give you the same pixel data anyway – Paul S. Oct 12 '15 at 21:07

3 Answers3

2

Assuming the both images are rectangles, and rotating did not crop them, you can loop over all pixels in the canvas to determine the most upper, lower, left and right pixel(*), and from there you can rotate it into a position in which it will only be "correctly rotated" or upside down (or, for squares, 90° to the left or right), leaving you with fewer cases to check.
From there on, you can "subtract" one image from the other one and check whether the difference is close to zero or not.

(*): Theoretically, two corners would be enough, but more corners help to improve accuracy, as we're dealing with approximated values (because float).

var canvas = [];
var ctx = [];
for(var i = 1; i <= 7; i++)
{
    canvas[i] = document.getElementById('canvas' + i);
    ctx[i] = canvas[i].getContext('2d');
    ctx[i].translate(250, 250);
}
var img = new Image();
var result = document.getElementById('result');

var diffCanvases = function(c1, c2)
{
    var other = c2.getImageData(0, 0, 500, 500).data;
    return c1.getImageData(0, 0, 500, 500).data.map(function(val, i)
    {
        return Math.abs(val - other[i]);
    }).reduce(function(previous, current)
    {
        return previous + current;
    });
};

var drawRotatedImage = function()
{
    var angle1 = Math.random() * 2 * Math.PI;
    var angle2 = Math.random() * 2 * Math.PI;
    ctx[1].rotate(angle1);
    ctx[2].rotate(angle2);
    ctx[1].drawImage(img, -200, -150);
    ctx[2].drawImage(img, -200, -150);
}

var undoRotation = function()
{
    var data = [ctx[1].getImageData(0, 0, 500, 500).data, ctx[2].getImageData(0, 0, 500, 500).data];
    var extremes = [{}, {}];
    for(var i = 0; i < 2; i++)
    {
        top:
        for(var y = 0; y < 500; y++)
        {
            for(var x = 0; x < 500; x++)
            {
                if(data[i][(y * 500 + x) * 4] + data[i][(y * 500 + x) * 4 + 1] + data[i][(y * 500 + x) * 4 + 2] + data[i][(y * 500 + x) * 4 + 3] > 0)
                {
                    extremes[i].top = {x: x, y: y};
                    break top;
                }
            }
        }
        bottom:
        for(var y = 499; y >= 0; y--)
        {
            for(var x = 499; x >= 0; x--)
            {
                if(data[i][(y * 500 + x) * 4] + data[i][(y * 500 + x) * 4 + 1] + data[i][(y * 500 + x) * 4 + 2] + data[i][(y * 500 + x) * 4 + 3] > 0)
                {
                    extremes[i].bottom = {x: x, y: y};
                    break bottom;
                }
            }
        }
        left:
        for(var x = 0; x < 500; x++)
        {
            for(var y = 0; y < 500; y++)
            {
                if(data[i][(y * 500 + x) * 4] + data[i][(y * 500 + x) * 4 + 1] + data[i][(y * 500 + x) * 4 + 2] + data[i][(y * 500 + x) * 4 + 3] > 0)
                {
                    extremes[i].left = {x: x, y: y};
                    break left;
                }
            }
        }
        right:
        for(var x = 499; x >= 0; x--)
        {
            for(var y = 499; y >= 0; y--)
            {
                if(data[i][(y * 500 + x) * 4] + data[i][(y * 500 + x) * 4 + 1] + data[i][(y * 500 + x) * 4 + 2] + data[i][(y * 500 + x) * 4 + 3] > 0)
                {
                    extremes[i].right = {x: x, y: y};
                    break right;
                }
            }
        }
    }
    var angles = [];
    for(var i = 0; i < 2; i++)
    {
        // Diagonals yield the highest accuracy
        var topBottom = Math.atan((extremes[i].bottom.y - extremes[i].top.y) / (extremes[i].bottom.x - extremes[i].top.x));
        var leftRight = Math.atan((extremes[i].left.y - extremes[i].right.y) / (extremes[i].left.x - extremes[i].right.x));
        angles.push(((topBottom + leftRight + Math.PI) / 2) % (Math.PI / 2));
    }
    ctx[3].rotate(-angles[0]);
    ctx[3].drawImage(canvas[1], -250, -250);
    for(var i = 0; i < 4; i++)
    {
        ctx[4 + i].rotate(-angles[1] + Math.PI * i / 2);
        ctx[4 + i].drawImage(canvas[2], -250, -250);
    }
    result.textContent = 'Canvas 3 vs 4: ' + diffCanvases(ctx[3], ctx[4]) + '\n' +
                         'Canvas 3 vs 5: ' + diffCanvases(ctx[3], ctx[5]) + '\n' +
                         'Canvas 3 vs 6: ' + diffCanvases(ctx[3], ctx[6]) + '\n' +
                         'Canvas 3 vs 7: ' + diffCanvases(ctx[3], ctx[7]) + '\n';
}

img.addEventListener('load', function()
{
    drawRotatedImage();
    undoRotation();
});
img.crossOrigin = '';
img.src = 'https://i.imgur.com/o2scuuY.jpg';
canvas
{
    border: solid 1px #999;
    display: block;
}
#result
{
    white-space: pre;
}
Original canvases:
<canvas id="canvas1" width="500" height="500"></canvas>
<canvas id="canvas2" width="500" height="500"></canvas>
Canvas 1 "fixed":
<canvas id="canvas3" width="500" height="500"></canvas>
All options for canvas 2 "fixed":
<canvas id="canvas4" width="500" height="500"></canvas>
<canvas id="canvas5" width="500" height="500"></canvas>
<canvas id="canvas6" width="500" height="500"></canvas>
<canvas id="canvas7" width="500" height="500"></canvas>
<div id="result"></div>

In this version, a single pixel shift will make a huge difference already. This could easily be minimised by detecting the top and left offsets of all "fixed" images, comparing those of the first canvas with the other four canvases, and adjusting the images to even out the differences.
But I've messed enough with canvases and images today for my taste.

Example image taken from LoremPixel.

Siguza
  • 21,155
  • 6
  • 52
  • 89
  • Thanks for your answer. I was able to use your code with little modifications for my purpose. – Cijo Oct 21 '15 at 05:52
0

If you don't know the rotation angles than it's a classic image matching case in computer vision.

Image matching is computationally heavy and not suitable for web applications.

I would try to find a way to calculate the rotation angles and rotate the image back before comparing. If there is no other way, I would implement an image matching rest api using opencv or similar libs.

EDIT

In order to access the canvas pixel data use getImageData function.

idoshamun
  • 1,125
  • 9
  • 21
  • The images are same except the rotation. I want to implement it in JavaScript. Is there opencv for JS? – Cijo Oct 12 '15 at 15:32
  • there is opencv for node. I thought that you don't know that the images are the same, I'll try to edit my answer – idoshamun Oct 12 '15 at 15:34
  • well it might sound complicated but using the browser.. the best you could achieve is rotating the image by small degrees, overlaying them by difference and figuring out the percentage of their difference. – GottZ Oct 12 '15 at 15:36
  • is it possible to compare colors in the image – Cijo Oct 12 '15 at 15:46
0

"Same" like.. .really, but really same? Almost impossible.
As soon an image is rotated, probably is cropped trimmed etc. (you missed to explain what are the edge cases and show some code).

Just to give you an idea how to approach to the problem:

  • I'd convert both images to B/W (to get 0 zeros for white and 1 ones for black data)
  • Remove noise (filter/clean the isolated "1"(black) pixels data)
  • Overlap the images and rotate one from 0-359 degree steps
  • On every step (rotation) process the average exclusion algorithm (basically 1-1=0)
  • Than process the entire merged result chunk by chunk

So basically if the merged result is an almost completely white (mostly "0"zeroes) result -
you have a possible match.

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • Rotating each images through 360 is not an efficient method for me. i have to repeat the process several times. Can i compare colour's? May be characteristic colour. – Cijo Oct 12 '15 at 15:48
  • @allinone1915 in that case you're again approximating your guess algorithm. So you'll not be able to get perfect results. You need to figure out the degree rotation of one image. Even after you figure out the rotation might be you're missing completely the rotation by exactly 180 degrees. So you need to process the two images and the current (figured rotation) and than again by rotating one by 180deg. – Roko C. Buljan Oct 12 '15 at 15:51
  • @allinone1915 if both canvases are i.e: 300x300 px, and inside one canvas you have a rotated smaller image... it's canvas is again 300x300px so you have to compare two 300x300 images data. You're missing to explain if the rotated image is centered inside the other canvas, if the canvs background is transparent (default), white etc... – Roko C. Buljan Oct 12 '15 at 15:56
  • @allinone1915 to answer directly to your comment - Using canvas and `getImageData()` you can get every pixel color, but so what. You could eventually process both images by let's say 20x20px data colors and extract the average color (again using an algorithm to get the average color). after you mapped your images, you still cannot be sure how to compare the 2 arrays of data since the other image is rotated. You need to figure out a way to extract the second image rotation to than apply another method that realizes if both images are similar. And **"similar"** is the best you'll got. – Roko C. Buljan Oct 12 '15 at 16:02