久しぶりシステム担当の記事です…
最近、社内でPhotoshopを使ってサイゼリヤの間違い探しを解いたのですが、
前回の話「【ネタバレあり】おうちdeサイゼ!大人の間違い探しの答え」に続き、15分くらいあれば簡単なJavascriptを使って、探せるじゃないかとチャレンジしてみました。
しかもライブラリを使わず、簡単なJavaScriptのみのプログラムで実装したいと思います!
まずは、Photoshopで探すのと同じ要領で、違う画像を2つのLayer分けて重ね、差分を見つけます。
早速紹介します。
今回はhtml canvasを使います。
1. html canvasを初期化する
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
2. 画像を読み込みと表示
var image = new Image();
image.src = 'a.jpg';
image.onload = function () {
context.drawImage(image, 0, 0);
}
ここまでは一般的な画像表示方法ですが、画像データを繰り返し使いたい場合、
pass by referenceが必要になるので、変数をobjectに変えます。
※ポイントは画像のwidth、heightとデータ(grey)を覚えるobjectに変えます。
※特にgreyはカラーデータをモノクロデータに変換した後に使います。
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var imageGreyA = {width: 0, height: 0, grey: ""}; //width,height,greyを覚える
var imageGreyB = {width: 0, height: 0, grey: ""}; //width,height,greyを覚える
loadImage(context, imageGreyA, 'a.jpg', 0, 0);
loadImage(context, imageGreyB, 'b.jpg', 600, 0);
function loadImage(context, greyData, filename, x, y) {
var image = new Image();
image.src = filename;
image.onload = function () {
greyData.width = image.width; //widthを取得
greyData.height = image.height; //heightを取得
context.drawImage(image, x, y);
var imageData = context.getImageData(x, y, greyData.width, greyData.height).data;
greyData.grey = new Uint8ClampedArray(greyData.width * greyData.height); //画像データを取得
}
}
3. 色をモノクロに
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var imageGreyA = {width: 0, height: 0, grey: ""};
var imageGreyB = {width: 0, height: 0, grey: ""};
loadImage(context, imageGreyA, 'a.jpg', 0, 0);
loadImage(context, imageGreyB, 'b.jpg', 600, 0);
function loadImage(context, greyData, filename, x, y) {
var image = new Image();
image.src = filename;
image.onload = function () {
greyData.width = image.width;
greyData.height = image.height;
context.drawImage(image, x, y);
var imageData = context.getImageData(x, y, greyData.width, greyData.height).data;
greyData.grey = new Uint8ClampedArray(greyData.width * greyData.height); //pixelをarrayに変換
for (var i = 0; i < imageData.length; i += 4) {
greyData.grey[i / 4] = 0.30 * imageData[i + 0] + 0.59 * imageData[i + 1] + 0.11 * imageData[i + 2]; //pixelを変換
}
}
}
4. 2つ画像を比べる関数を作る
function diffData() {
if (imageGreyA.grey !== "" && imageGreyB.grey !== "") {
var diff = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height); //pixelをarrayに変換
var diff2 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height); //pixelをarrayに変換
for (var i = 0; i < diff.length; i++) {
diff[i] = Math.abs(imageGreyA.grey[i] - imageGreyB.grey[i]); //画像Aと画像Bの差
diff2[i] = Math.abs(imageGreyB.grey[i] - imageGreyA.grey[i]); //繰り返す画像Bと画像Aの差
diff[i] = diff[i] & diff2[i]; //差の結果を合体する
}
}
}
5. 結果を表示する
var resultData = context.getImageData(0, 600, imageGreyA.width, imageGreyA.height);
var diffLength = diff.length * 4;
var rgba = new Uint8ClampedArray(diffLength); //ここからarrayをRGBAに戻す
for (var j = 0; j < diffLength; j += 4) {
rgba[j] = diff[j / 4];
rgba[j + 1] = diff[j / 4];
rgba[j + 2] = diff[j / 4];
rgba[j + 3] = 255; //aは関係ないので255にする
}
resultData.data.set(rgba); //画像データをimageDataに設定
context.putImageData(resultData, 0, 600); //contextに表示
6. 結果を良くするためには調整が必要です。
携帯で撮った写真なので、少しpixelがずれてます、それをソースで改善してみました。
違うモニターにも見やすくために白黒を調整しました。
function diffData() {
if (imageGreyA.grey !== "" && imageGreyB.grey !== "") {
//start diff
//main diff
var diff = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
var diff2 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
//+1 pixel diff
var diff3 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height); //1px差を無してみます
var diff4 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
//-1 pixel diff
var diff5 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height); //1px差を無してみます
var diff6 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
for (var i = 0; i < diff.length; i++) {
diff[i] = Math.abs(imageGreyA.grey[i] - imageGreyB.grey[i]);
diff2[i] = Math.abs(imageGreyB.grey[i] - imageGreyA.grey[i]);
diff3[i] = Math.abs(imageGreyA.grey[i + 1] - imageGreyB.grey[i]);
diff4[i] = Math.abs(imageGreyA.grey[i] - imageGreyB.grey[i - 1]);
diff5[i] = Math.abs(imageGreyB.grey[i + 1] - imageGreyA.grey[i]);
diff6[i] = Math.abs(imageGreyB.grey[i] - imageGreyA.grey[i - 1]);
diff[i] = diff[i] & diff2[i] & diff3[i] & diff4[i] & diff5[i] & diff6[i];
if (diff[i] >= 128) { //見やすさを調整してみます:黒さ半分以下pixelは黒、白さ半分以上pixelは白にします
diff[i] = 255;
} else {
diff[i] = 0;
}
}
//draw result
var resultData = context.getImageData(0, 600, imageGreyA.width, imageGreyA.height);
var diffLength = diff.length * 4;
var rgba = new Uint8ClampedArray(diffLength);
for (var j = 0; j < diffLength; j += 4) {
rgba[j] = diff[j / 4];
rgba[j + 1] = diff[j / 4];
rgba[j + 2] = diff[j / 4];
rgba[j + 3] = 255;
}
resultData.data.set(rgba);
context.putImageData(resultData, 0, 600);
}
}
15分で作って見ましたが、調整いろいろ含めて30分に掛かりました。
最終結果はこちらになります、悪くはないでしょうね。
最終ソースコードはこちらです。
function main() {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var imageGreyA = {width: 0, height: 0, grey: ""};
var imageGreyB = {width: 0, height: 0, grey: ""};
loadImage(context, imageGreyA, 'a.jpg', 0, 0);
loadImage(context, imageGreyB, 'b.jpg', 600, 0);
function loadImage(context, greyData, filename, x, y) {
var image = new Image();
image.src = filename;
image.onload = function () {
greyData.width = image.width;
greyData.height = image.height;
context.drawImage(image, x, y);
var imageData = context.getImageData(x, y, greyData.width, greyData.height).data;
greyData.grey = new Uint8ClampedArray(greyData.width * greyData.height);
for (var i = 0; i < imageData.length; i += 4) {
greyData.grey[i / 4] = 0.30 * imageData[i + 0] + 0.59 * imageData[i + 1] + 0.11 * imageData[i + 2];
}
diffData();
}
}
function diffData() {
if (imageGreyA.grey !== "" && imageGreyB.grey !== "") {
//start diff
//main diff
var diff = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
var diff2 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
//+1 pixel diff
var diff3 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
var diff4 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
//-1 pixel diff
var diff5 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
var diff6 = new Uint8ClampedArray(imageGreyA.width * imageGreyA.height);
for (var i = 0; i < diff.length; i++) {
diff[i] = Math.abs(imageGreyA.grey[i] - imageGreyB.grey[i]);
diff2[i] = Math.abs(imageGreyB.grey[i] - imageGreyA.grey[i]);
diff3[i] = Math.abs(imageGreyA.grey[i + 1] - imageGreyB.grey[i]);
diff4[i] = Math.abs(imageGreyA.grey[i] - imageGreyB.grey[i - 1]);
diff5[i] = Math.abs(imageGreyB.grey[i + 1] - imageGreyA.grey[i]);
diff6[i] = Math.abs(imageGreyB.grey[i] - imageGreyA.grey[i - 1]);
diff[i] = diff[i] & diff2[i] & diff3[i] & diff4[i] & diff5[i] & diff6[i];
if (diff[i] >= 128) {
diff[i] = 255;
} else {
diff[i] = 0;
}
}
//draw result
var resultData = context.getImageData(0, 600, imageGreyA.width, imageGreyA.height);
var diffLength = diff.length * 4;
var rgba = new Uint8ClampedArray(diffLength);
for (var j = 0; j < diffLength; j += 4) {
rgba[j] = diff[j / 4];
rgba[j + 1] = diff[j / 4];
rgba[j + 2] = diff[j / 4];
rgba[j + 3] = 255;
}
resultData.data.set(rgba);
context.putImageData(resultData, 0, 600);
}
}
}



