diff --git a/README.md b/README.md old mode 100644 new mode 100755 index b52f6dd..4b3c41f --- a/README.md +++ b/README.md @@ -8,18 +8,39 @@ API * `createCanvas()` create a new Canvas element. * `createImageData(width, height)` create a new ImageData object. +* `displayMask(width, height, regions)` create a image representation of regions mask. * `isImage(object)` tests for Image object. * `isCanvas(object)` tests for Canvas object. * `isContext(object)` tests for CanvasRenderingContext2D object. * `isImageData(object)` tests for ImageData object. * `isImageType(object)` tests for any of the above. * `toImageData(object)` converts image type object to a new ImageData object. -* `equal(a, b, tolerance)` tests image type objects for equality; accepts tolerance in pixels. +* `equal(a, b, tolerance, options)` tests image type objects for equality; accepts tolerance in pixels. * `diff(a, b, options)` performs an image diff on a and b, returning a - b. * `options.align` set to `'top'` to top-align the images when diffing different sizes. * `noConflict()` removes imagediff from the global space for compatibility, returning imagediff. * `imageDataToPNG(imageData, outputFile, [callback])` (node only) renders the imageData to png in outputFile with optional callback. +Regions +_______ + +Sometimes there's only part of the image that you want to test. +`equal` method accepts optional `regions` array that allows you to specify mask for image comparison. +Each region in the array is a five element array in the form `[x0, y0, x1, y1, include]`, where + +* `(x0, y0)` is the smallest coordinate corner of the region (inclusive) +* `(x1, y1)` is the largest coordinate corner of the region (non-inclusive) +* `include` is a boolean value that decides whether the rectangle should be checked for equality. + +Setting `include` to `true` will add the region to the mask, setting it to `false` will substract it. +Initially the mask is empty, therefore passing a single rectangle, e.g.: + +`equal(a, b, 0, {regions: [[20, 20, 240, 120, true]]})` + +will yield `true` if the images are equal within the single passed rectangle (`[20, 20, 240, 120]`). + +You can use `displayMask` method to generate visual representation of created mask. + NodeJS ------ @@ -73,6 +94,9 @@ If you are using js-imagediff pelase drop us a line and let us know what you are Changelog --------- +

NEXT

+* Accept regions for masking image comparison +

1.0.8

* Update canvas dependency. * Expose internal Canvas. diff --git a/examples/1_normal_c.jpg b/examples/1_normal_c.jpg new file mode 100755 index 0000000..4b03f18 Binary files /dev/null and b/examples/1_normal_c.jpg differ diff --git a/examples/index.html b/examples/index.html old mode 100644 new mode 100755 index 41dcf0d..ee41596 --- a/examples/index.html +++ b/examples/index.html @@ -4,7 +4,7 @@ JS-ImageDiff Example - + @@ -12,53 +12,99 @@

JS-ImageDiff Example:

See more examples at humblesoftware.github.com/js-imagediff. Sample images from the github imagediff demo.

-
+
example image one example image two
+ +
diff --git a/js/imagediff.js b/js/imagediff.js old mode 100644 new mode 100755 index 7760ffa..d78a253 --- a/js/imagediff.js +++ b/js/imagediff.js @@ -141,6 +141,62 @@ return canvas; } + // Image mask generation + + + function fillArray(array, value, start, end) { + var i, + i0 = (start !== undefined) ? start : 0, + i1 = (end !== undefined) ? end : array.length; + if(Array.prototype.fill) { + return array.fill(value, start, end); + } + for(i = i0; i < i1; i += 1) { + array[i] = value; + } + return array; + } + /* + region format: [x0, y0, x1, y1, include] + */ + function createMask (width, height, regions) { + var + array = fillArray(Array(width * height), false), + i, y, region; + + for(i = 0; i < regions.length; i += 1) { + region = regions[i]; + for(y = region[1]; y < region[3]; y += 1) { + fillArray(array, region[4], y * width + region[0], y * width + region[2]); + } + } + return array; + } + function displayMask(width, height, regions) { + var + mask = createMask(width, height, regions), + image = getImageData(width, height), + iData = image.data, + length = mask.length, + i, ii, color; + + for (i = 0, ii = 0; i < length; i += 1, ii += 4) { + color = mask[i] ? 255 : 0; + iData[ii] = color; + iData[ii+1] = color; + iData[ii+2] = color; + iData[ii+3] = 255; + + if(!(mask[i] === true || mask[i] === false)) { + iData[ii] = 255; + iData[ii+1] = 0; + iData[ii+2] = 0; + } + } + + return image; + } + // ImageData Equality Operators function equalWidth (a, b) { @@ -152,18 +208,32 @@ function equalDimensions (a, b) { return equalHeight(a, b) && equalWidth(a, b); } - function equal (a, b, tolerance) { + function equal (a, b, tolerance, options) { var aData = a.data, bData = b.data, - length = aData.length, - i; + length = aData.length / 4, + mask = null, + i, ii, j; tolerance = tolerance || 0; + options = options || {}; if (!equalDimensions(a, b)) return false; - for (i = length; i--;) if (aData[i] !== bData[i] && Math.abs(aData[i] - bData[i]) > tolerance) return false; + + if(options.regions) { + mask = createMask(a.width, a.height, options.regions); + } + + for (i = 0; i < length; i += 1) { + if (mask && !mask[i]) continue; + for(j = 0, ii = i * 4; j < 4; j += 1, ii += 1) + if (aData[ii] !== bData[ii] && + Math.abs(aData[ii] - bData[ii]) > tolerance) { + return false; + } + } return true; } @@ -183,14 +253,26 @@ bData = b.data, cData = c.data, length = cData.length, + mask = null, row, column, - i, j, k, v; + i, ii, j, k, v; + + if(options.regions && options.mask) { + mask = createMask(width, height, options.regions); + } - for (i = 0; i < length; i += 4) { - cData[i] = Math.abs(aData[i] - bData[i]); - cData[i+1] = Math.abs(aData[i+1] - bData[i+1]); - cData[i+2] = Math.abs(aData[i+2] - bData[i+2]); - cData[i+3] = Math.abs(255 - Math.abs(aData[i+3] - bData[i+3])); + for (i = 0, ii = 0; ii < length; i += 1, ii += 4) { + if(mask && !mask[i]) { + cData[ii] = 0; + cData[ii+1] = 0; + cData[ii+2] = 92; + cData[ii+3] = 255; + } else { + cData[ii] = Math.abs(aData[ii] - bData[ii]); + cData[ii+1] = Math.abs(aData[ii+1] - bData[ii+1]); + cData[ii+2] = Math.abs(aData[ii+2] - bData[ii+2]); + cData[ii+3] = Math.abs(255 - Math.abs(aData[ii+3] - bData[ii+3])); + } } return c; @@ -350,6 +432,8 @@ createCanvas : getCanvas, createImageData : getImageData, + displayMask : displayMask, + isImage : isImage, isCanvas : isCanvas, isContext : isContext, @@ -362,11 +446,11 @@ return toImageData(object); }, - equal : function (a, b, tolerance) { + equal : function (a, b, tolerance, options) { checkType(a, b); a = toImageData(a); b = toImageData(b); - return equal(a, b, tolerance); + return equal(a, b, tolerance, options); }, diff : function (a, b, options) { checkType(a, b);