James Cryer
12 years ago
6 changed files with 613 additions and 0 deletions
@ -0,0 +1,91 @@
|
||||
$(function(){ |
||||
var $target = $('#drop-zone'); |
||||
|
||||
function dropZone($target, onDrop){ |
||||
$target. |
||||
bind('dragover', function(){ |
||||
$target.addClass( 'drag-over' ); |
||||
return false; |
||||
}). |
||||
bind("dragend", function () { |
||||
$target.removeClass( 'drag-over' ); |
||||
return false; |
||||
}). |
||||
bind("mouseout", function () { |
||||
$target.removeClass( 'drag-over' ); |
||||
return false; |
||||
}). |
||||
bind("drop", function(event) { |
||||
var file = event.originalEvent.dataTransfer.files[0]; |
||||
|
||||
event.stopPropagation(); |
||||
event.preventDefault(); |
||||
|
||||
$target.removeClass( 'drag-over' ); |
||||
|
||||
var droppedImage = new Image(); |
||||
var fileReader = new FileReader(); |
||||
|
||||
fileReader.onload = function (event) { |
||||
droppedImage.src = event.target.result; |
||||
$target.html(droppedImage); |
||||
}; |
||||
|
||||
fileReader.readAsDataURL(file); |
||||
|
||||
onDrop(file); |
||||
}); |
||||
} |
||||
|
||||
dropZone($target, function(file){ |
||||
|
||||
resemble(file).onComplete(function(data){ |
||||
$('#image-data').show(); |
||||
$('#red').css('width',data.red+'%'); |
||||
$('#green').css('width',data.green+'%'); |
||||
$('#blue').css('width',data.blue+'%'); |
||||
$('#brightness').css('width',data.brightness+'%'); |
||||
}); |
||||
|
||||
}); |
||||
|
||||
function onComplete(data){ |
||||
var diffImage = new Image(); |
||||
diffImage.src = data.imageDiffFileData; |
||||
$('#image-diff').html(diffImage); |
||||
|
||||
$(diffImage).click(function(){ |
||||
window.open(diffImage.src, '_blank'); |
||||
}); |
||||
|
||||
if(data.misMatchPercentage == 0){ |
||||
$('#thesame').show(); |
||||
$('#diff-results').hide(); |
||||
} else { |
||||
$('#mismatch').text(data.misMatchPercentage); |
||||
if(!data.isSameDimensions){ |
||||
$('#differentdimensions').show(); |
||||
} else { |
||||
$('#differentdimensions').hide(); |
||||
} |
||||
$('#diff-results').show(); |
||||
$('#thesame').hide(); |
||||
} |
||||
} |
||||
|
||||
var file1; |
||||
var file2; |
||||
dropZone($('#dropzone1'), function(file){ |
||||
file1 = file; |
||||
if(file2){ |
||||
resemble(file).compareTo(file2).onComplete(onComplete); |
||||
} |
||||
}); |
||||
dropZone($('#dropzone2'), function(file){ |
||||
file2 = file; |
||||
if(file1){ |
||||
resemble(file).compareTo(file1).onComplete(onComplete); |
||||
} |
||||
}); |
||||
|
||||
}); |
@ -0,0 +1,64 @@
|
||||
|
||||
|
||||
h1 { |
||||
text-align: center; |
||||
} |
||||
|
||||
body { |
||||
padding-top: 60px; |
||||
} |
||||
|
||||
footer { |
||||
margin-top: 45px; |
||||
padding: 35px 0 36px; |
||||
border-top: 1px solid #e5e5e5; |
||||
} |
||||
|
||||
.drop-zone{ |
||||
border: 10px dashed #ccc; |
||||
color: #ccc; |
||||
font-size: 32px; |
||||
height: 400px; |
||||
line-height: 400px; |
||||
text-align: center; |
||||
width: 400px; |
||||
overflow: hidden; |
||||
position: relative; |
||||
} |
||||
.drop-zone.drag-over{ |
||||
border: 10px dashed #0088CC; |
||||
} |
||||
.drop-zone img { |
||||
width: 400px; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
} |
||||
|
||||
.small-drop-zone{ |
||||
margin-top: 20px; |
||||
margin-left: 90px; |
||||
border: 10px dashed #ccc; |
||||
color: #ccc; |
||||
font-size: 32px; |
||||
height: 330px; |
||||
line-height: 330px; |
||||
text-align: center; |
||||
width: 330px; |
||||
overflow: hidden; |
||||
position: relative; |
||||
} |
||||
.small-drop-zone.drag-over{ |
||||
border: 10px dashed #0088CC; |
||||
} |
||||
.small-drop-zone img { |
||||
width: 330px; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
} |
||||
#image-diff { |
||||
margin-left: 0px; |
||||
margin-top: 90px; |
||||
border-style: solid; |
||||
} |
@ -0,0 +1,148 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
||||
<title>Resemble.js : Image analysis</title> |
||||
<meta name="description" content=""> |
||||
<meta name="viewport" content="width=device-width"> |
||||
<link rel="stylesheet" href="libs/twitter-bootstrap/bootstrap.min.css"> |
||||
<link rel="stylesheet" href="demoassets/resemble.css"> |
||||
</head> |
||||
<body> |
||||
<div class="container"> |
||||
<header> |
||||
<div class="page-header"> |
||||
<h1>Resemble.js : Image analysis and comparison</h1> |
||||
</div> |
||||
</header> |
||||
|
||||
<section role="main"> |
||||
<div class="row"> |
||||
<div class="span12"> |
||||
<div class="hero-unit"> |
||||
<div class="row"> |
||||
<div class="span6"> |
||||
<p> |
||||
<div id="drop-zone" class="drop-zone"> |
||||
Drop image here. |
||||
</div> |
||||
</p> |
||||
</div> |
||||
<div class="span4"> |
||||
<h2>What is this?</h2> |
||||
<p> |
||||
Resemble.js analyses and compares images with HTML5 canvas and JavaScript. |
||||
</p> |
||||
<p> |
||||
<strong>Try it for yourself.</strong> |
||||
</p> |
||||
<div id="image-data" style="display:none"> |
||||
|
||||
RGB |
||||
<div class="progress progress-danger"> |
||||
<div id="red" class="bar" style="width: 0%;"></div> |
||||
</div> |
||||
<div class="progress progress-success"> |
||||
<div id="green" class="bar" style="width: 0%;"></div> |
||||
</div> |
||||
<div class="progress"> |
||||
<div id="blue" class="bar" style="width: 0%;"></div> |
||||
</div> |
||||
|
||||
Brightness |
||||
<div class="progress progress-warning"> |
||||
<div id="brightness" class="bar" style="width: 0%;"></div> |
||||
</div> |
||||
|
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="row"> |
||||
<div class="span12"> |
||||
<div class="row"> |
||||
<div class="span6"> |
||||
<div id="dropzone1" class="small-drop-zone"> |
||||
Drop first image |
||||
</div> |
||||
<div id="dropzone2" class="small-drop-zone"> |
||||
Drop second image |
||||
</div> |
||||
</div> |
||||
<div class="span6"> |
||||
<h2>Compare two images?</h2> |
||||
<p> |
||||
Drop two images on the boxes to the right. The box below will show a generated 'diff' image, pink areas show mismatch. This example best works with two very similar but slightly different images. Try for yourself! |
||||
</p> |
||||
<div id="image-diff" class="small-drop-zone"> |
||||
Diff will appear here. |
||||
</div> |
||||
<br/> |
||||
<p id="diff-results" style="display:none;"> |
||||
<strong>The second image is <span id="mismatch"></span>% different compared to the first. |
||||
<span id="differentdimensions" style="display:none;">And they have different dimensions.</span></strong> |
||||
</p> |
||||
<p id="thesame" style="display:none;"> |
||||
<strong>These images are the same!</strong> |
||||
</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<br/><br/> |
||||
<div class="row"> |
||||
<div class="span6"> |
||||
<h2>How does it work?</h2> |
||||
<p> |
||||
Dark magic. |
||||
</p> |
||||
<p> |
||||
<br/> |
||||
<a class="btn btn-large btn-primary" href="#"><strong>View project on Github</strong></a> |
||||
</p> |
||||
</div> |
||||
<div class="span6"> |
||||
<h2>How can I use it?</h2> |
||||
<p>Invoke Resemble on an image or canvas image to extract data</p> |
||||
<pre> |
||||
var data = resemble('img').onComplete(function(data){ |
||||
return data; |
||||
}); |
||||
/* |
||||
{ |
||||
red: 255, |
||||
green: 255, |
||||
blue: 255, |
||||
brightness: 255 |
||||
} |
||||
*/</pre> |
||||
<p>Use resemble to compare two image</p> |
||||
<pre> |
||||
resemble(file).compareTo(file2).onComplete(function(){ |
||||
return data; |
||||
}); |
||||
/* |
||||
{ |
||||
misMatchPercentage : 100, // % |
||||
isSameDimensions: true, // or false |
||||
imageDiffFileData: {} // dataUrl of image as png |
||||
} |
||||
*/</pre> |
||||
</div> |
||||
</div> |
||||
</section> |
||||
|
||||
<footer class="footer"> |
||||
<p> |
||||
Footer message |
||||
</p> |
||||
</footer> |
||||
</div> |
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> |
||||
<script src="resemble.js"></script> |
||||
<script src="demoassets/main.js"></script> |
||||
</body> |
||||
</html> |
File diff suppressed because one or more lines are too long
@ -0,0 +1,67 @@
|
||||
|
||||
|
||||
h1 { |
||||
text-align: center; |
||||
} |
||||
|
||||
body { |
||||
padding-top: 60px; |
||||
} |
||||
|
||||
footer { |
||||
margin-top: 45px; |
||||
padding: 35px 0 36px; |
||||
border-top: 1px solid #e5e5e5; |
||||
} |
||||
|
||||
.drop-zone{ |
||||
border: 10px dashed #ccc; |
||||
color: #ccc; |
||||
font-size: 32px; |
||||
height: 400px; |
||||
line-height: 400px; |
||||
text-align: center; |
||||
width: 400px; |
||||
overflow: hidden; |
||||
position: relative; |
||||
} |
||||
.drop-zone.drag-over{ |
||||
border: 10px dashed #0088CC; |
||||
} |
||||
.drop-zone img { |
||||
width: 400px; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
} |
||||
|
||||
.small-drop-zone{ |
||||
margin-top: 20px; |
||||
margin-left: 90px; |
||||
border: 10px dashed #ccc; |
||||
color: #ccc; |
||||
font-size: 32px; |
||||
height: 330px; |
||||
line-height: 330px; |
||||
text-align: center; |
||||
width: 330px; |
||||
overflow: hidden; |
||||
position: relative; |
||||
} |
||||
.small-drop-zone.drag-over{ |
||||
border: 10px dashed #0088CC; |
||||
} |
||||
.small-drop-zone img { |
||||
width: 330px; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
} |
||||
#image-diff { |
||||
margin-left: 0px; |
||||
margin-top: 90px; |
||||
border-style: solid; |
||||
} |
||||
#image-diff img { |
||||
cursor: pointer !important; |
||||
} |
@ -0,0 +1,234 @@
|
||||
(function(_this){ |
||||
|
||||
_this['resemble'] = function( fileData ){ |
||||
|
||||
var data = {}; |
||||
var images = []; |
||||
var updateCallbackArray = []; |
||||
|
||||
var tolerance = [ // between 0 and 255
|
||||
255, // red
|
||||
255, // green 16
|
||||
255, // blue 24
|
||||
255, // alpha
|
||||
255 // brightness
|
||||
]; |
||||
|
||||
function triggerDataUpdate(){ |
||||
var len = updateCallbackArray.length; |
||||
var i; |
||||
for(i=0;i<len;i++){ |
||||
if (typeof updateCallbackArray[i] === 'function'){ |
||||
updateCallbackArray[i](data); |
||||
} |
||||
} |
||||
} |
||||
|
||||
function loop(x, y, callback){ |
||||
var i,j; |
||||
for (i=0;i<x;i++){ |
||||
for (j=0;j<y;j++){ |
||||
callback(i, j); |
||||
} |
||||
} |
||||
} |
||||
|
||||
function parseImage(sourceImageData, width, height){ |
||||
|
||||
var pixleCount = 0; |
||||
var redTotal = 0; |
||||
var greenTotal = 0; |
||||
var blueTotal = 0; |
||||
var brightnessTotal = 0; |
||||
|
||||
loop(height, width, function(verticalPos, horizontalPos){ |
||||
var offset = (verticalPos*width + horizontalPos) * 4; |
||||
var red = sourceImageData[offset]; |
||||
var green = sourceImageData[offset + 1]; |
||||
var blue = sourceImageData[offset + 2]; |
||||
var alpha = sourceImageData[offset + 3]; |
||||
var brightness = (0.3*red + 0.59*green + 0.11*blue); |
||||
|
||||
pixleCount++; |
||||
|
||||
redTotal += red / 255 * 100; |
||||
greenTotal += green / 255 * 100; |
||||
blueTotal += blue / 255 * 100; |
||||
brightnessTotal += brightness / 255 * 100; |
||||
}); |
||||
|
||||
data.red = Math.floor(redTotal / pixleCount); |
||||
data.green = Math.floor(greenTotal / pixleCount); |
||||
data.blue = Math.floor(blueTotal / pixleCount); |
||||
data.brightness = Math.floor(brightnessTotal / pixleCount); |
||||
|
||||
triggerDataUpdate(); |
||||
} |
||||
|
||||
function loadImageData( fileData, callback ){ |
||||
var hiddenImage = new Image(); |
||||
var fileReader = new FileReader(); |
||||
|
||||
fileReader.onload = function (event) { |
||||
hiddenImage.src = event.target.result; |
||||
}; |
||||
|
||||
hiddenImage.onload = function() { |
||||
|
||||
var hiddenCanvas = document.createElement('canvas'); |
||||
var imageData; |
||||
var width = hiddenImage.width; |
||||
var height = hiddenImage.height; |
||||
|
||||
hiddenCanvas.width = width; |
||||
hiddenCanvas.height = height; |
||||
hiddenCanvas.getContext('2d').drawImage(hiddenImage, 0, 0, width, height); |
||||
imageData = hiddenCanvas.getContext('2d').getImageData(0, 0, width, height); |
||||
|
||||
images.push(imageData); |
||||
|
||||
callback(imageData, width, height); |
||||
}; |
||||
|
||||
fileReader.readAsDataURL(fileData); |
||||
} |
||||
|
||||
function isPixelDifferent(d1, d2, off, plus){ |
||||
var a = d1[off + plus]; |
||||
var b = d2[off + plus]; |
||||
|
||||
var absDiff = Math.abs(a - b); |
||||
|
||||
if(typeof a === 'undefined'){ |
||||
return false; |
||||
} |
||||
if(typeof b === 'undefined'){ |
||||
return false; |
||||
} |
||||
|
||||
if(a === b){ |
||||
return true; |
||||
} else if ( absDiff < tolerance[plus] ) { |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
function compareBrightness(data1, data2, offset){ |
||||
var red1 = data1[offset + 0]; |
||||
var green1 = data1[offset + 1]; |
||||
var blue1 = data1[offset + 2]; |
||||
|
||||
var red2 = data2[offset + 0]; |
||||
var green2 = data2[offset + 1]; |
||||
var blue2 = data2[offset + 2]; |
||||
|
||||
var brightness1; |
||||
var brightness2; |
||||
|
||||
if(red1 && green1 && blue1 && red2 && green2 && blue2){ |
||||
|
||||
brightness1 = (0.3*red1 + 0.59*green1 + 0.11*blue1); |
||||
brightness2 = (0.3*red2 + 0.59*green2 + 0.11*blue2); |
||||
|
||||
return Math.abs(brightness1 - brightness2) < tolerance[4]; |
||||
|
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
function analyseImages(img1, img2, width, height){ |
||||
|
||||
var hiddenCanvas = document.createElement('canvas'); |
||||
|
||||
var data1 = img1.data; |
||||
var data2 = img2.data; |
||||
|
||||
hiddenCanvas.width = width; |
||||
hiddenCanvas.height = height; |
||||
|
||||
var context = hiddenCanvas.getContext('2d'); |
||||
var imgd = context.createImageData(width,height); |
||||
var pix = imgd.data; |
||||
|
||||
var mismatchCount = 0; |
||||
|
||||
loop(height, width, function(verticalPos, horizontalPos){ |
||||
|
||||
var offset = (verticalPos*width + horizontalPos) * 4; |
||||
var red = isPixelDifferent(data1, data2, offset, 0); |
||||
var green = isPixelDifferent(data1, data2, offset, 1); |
||||
var blue = isPixelDifferent(data1, data2, offset, 2); |
||||
var alpha = isPixelDifferent(data1, data2, offset, 3); |
||||
|
||||
var brightness = compareBrightness(data1, data2, offset); |
||||
|
||||
if(brightness && red && green && blue){ |
||||
|
||||
pix[offset] = data1[offset + 0]; |
||||
pix[offset + 1] = data1[offset + 1]; |
||||
pix[offset + 2] = data1[offset + 2]; |
||||
pix[offset + 3] = data1[offset + 3]; |
||||
|
||||
} else { |
||||
pix[offset] = 255; |
||||
pix[offset + 1] = 0; |
||||
pix[offset + 2] = 255; |
||||
pix[offset + 3] = 255; |
||||
|
||||
mismatchCount++; |
||||
} |
||||
}); |
||||
|
||||
context.putImageData(imgd, 0,0); |
||||
|
||||
data.misMatchPercentage = (mismatchCount / (height*width) * 100).toFixed(2); |
||||
data.imageDiffFileData = hiddenCanvas.toDataURL("image/png"); |
||||
} |
||||
|
||||
function compare(one, two){ |
||||
|
||||
function onceWeHaveBoth(){ |
||||
var width; |
||||
var height; |
||||
if(images.length === 2){ |
||||
width = images[0].width > images[1].width ? images[0].width : images[1].width; |
||||
height = images[0].height > images[1].height ? images[0].height : images[1].height; |
||||
|
||||
if( (images[0].width === images[1].width) && (images[0].height === images[1].height) ){ |
||||
data.isSameDimensions = true; |
||||
} else { |
||||
data.isSameDimensions = false; |
||||
} |
||||
|
||||
analyseImages(images[0], images[1], width, height); |
||||
|
||||
triggerDataUpdate(); |
||||
} |
||||
} |
||||
|
||||
loadImageData(one, onceWeHaveBoth); |
||||
loadImageData(two, onceWeHaveBoth); |
||||
} |
||||
|
||||
return { |
||||
onComplete: function( callback ){ |
||||
updateCallbackArray.push(callback); |
||||
loadImageData(fileData, function(imageData, width, height){ |
||||
parseImage(imageData.data, width, height); |
||||
}); |
||||
}, |
||||
compareTo: function(secondFileData){ |
||||
return { |
||||
onComplete: function( callback ){ |
||||
updateCallbackArray.push(callback); |
||||
compare(fileData, secondFileData); |
||||
} |
||||
}; |
||||
} |
||||
}; |
||||
|
||||
}; |
||||
}(this)); |
Loading…
Reference in new issue