久しぶりシステム担当の記事です…
最近、社内で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); } } }