canvasの画像をピクセル単位でアクセスする方法を学びましたので、2つの画像の差分を表示するプログラムを作成してみました。
以下のフォームは実際動作します。比較したい画像をドロップし「差分」ボタンを押すと結果の画像が表示されます。
結果画像は同じ部分が黒、異なる部分が白で表示されます。
実際動作するフォーム
画像ファイルをドロップしてください
画像ファイルをドロップしてください
差分を表示
ソースコード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<title>Javascriptで画像の差分を表示</title>
<style type="text/css">
canvas {
width: 128px;
height: 128px;
border:solid 1px #222;
}
.btn {
height: 64px;
text-align: center;
padding: 12px;
border:solid 1px #222;
}
</style>
</head>
<body>
<div id="btn1" class="btn">画像ファイルをドロップしてください</div>
<input type="file" id="fileselect1" name="upfile" multiple="multiple" style="display:none;">
<canvas id="canvas1"></canvas>
<div id="btn2" class="btn">画像ファイルをドロップしてください</div>
<input type="file" id="fileselect2" name="upfile2" multiple="multiple" style="display:none;">
<canvas id="canvas2"></canvas>
<div id="btn3" class="btn">差分を表示</div>
<canvas id="canvas3"></canvas>
<script>
let canvas1 = document.getElementById('canvas1');
let ctx1 = canvas1.getContext('2d', { willReadFrequently: true });
let canvas2 = document.getElementById('canvas2');
let ctx2 = canvas2.getContext('2d', { willReadFrequently: true });
let canvas3 = document.getElementById('canvas3');
let ctx3 = canvas3.getContext('2d', { willReadFrequently: true });
let btn1 = document.getElementById('btn1');
let fileselect1 = document.getElementById('fileselect1');
let btn2 = document.getElementById('btn2');
let fileselect2 = document.getElementById('fileselect2');
let btn3 = document.getElementById('btn3');
btn1.addEventListener('click', function(e) {
fileselect1.click()
});
function load_image1() {
let file = fileselect1.files[0];
if (file === void 0) {
return;
}
btn1.innerText = file.name;
let reader = new FileReader();
reader.addEventListener('load', function(e) {
let img = document.createElement("img");
img.setAttribute("src", reader.result);
img.addEventListener('load', function(e){
canvas1.width = img.width;
canvas1.height = img.height;
canvas1.style.width = img.width+"px";
canvas1.style.height = img.height+"px";
ctx1.drawImage(img, 0, 0);
});
});
reader.readAsDataURL(file);
}
function load_image2() {
let file = fileselect2.files[0];
if (file === void 0) {
return;
}
btn2.innerText = file.name;
let reader = new FileReader();
reader.addEventListener('load', function(e) {
let img = document.createElement("img");
img.setAttribute("src", reader.result);
img.addEventListener('load', function(e){
canvas2.width = img.width;
canvas2.height = img.height;
canvas2.style.width = img.width+"px";
canvas2.style.height = img.height+"px";
ctx2.drawImage(img, 0, 0);
});
});
reader.readAsDataURL(file);
}
fileselect1.addEventListener('change', function(e){
load_image1();
});
btn1.addEventListener('dragenter', function(e){
btn1.style.background = '#EEE';
});
btn1.addEventListener('dragleave', function(e){
btn1.style.background = '#DDD';
});
btn1.addEventListener('drop', function(e){
fileselect1.files = e.dataTransfer.files;
btn1.style.background = '#FFF';
load_image1();
});
btn2.addEventListener('click', function(e) {
fileselect2.click()
});
fileselect2.addEventListener('change', function(e){
let file = fileselect2.files[0];
if (file === void 0) {
return;
}
btn2.innerText = file.name;
let reader = new FileReader();
reader.addEventListener('load', function(e) {
let img = document.createElement("img");
img.setAttribute("src", reader.result);
img.addEventListener('load', function(e){
canvas2.width = img.width;
canvas2.height = img.height;
canvas2.style.width = img.width+"px";
canvas2.style.height = img.height+"px";
ctx2.drawImage(img, 0, 0);
});
});
reader.readAsDataURL(file);
});
btn2.addEventListener('dragenter', function(e){
btn2.style.background = '#EEE';
});
btn2.addEventListener('dragleave', function(e){
btn2.style.background = '#DDD';
});
btn2.addEventListener('drop', function(e){
fileselect2.files = e.dataTransfer.files;
btn2.style.background = '#FFF';
load_image2();
});
btn3.addEventListener('click', function(e) {
let imgdata1 = ctx1.getImageData(0, 0, canvas1.width, canvas1.height);
let px1 = imgdata1.data;
let imgdata2 = ctx2.getImageData(0, 0, canvas2.width, canvas2.height);
let px2 = imgdata2.data;
let width = Math.max(canvas1.width, canvas2.width);
let height = Math.max(canvas1.height, canvas2.height);
canvas3.width = width;
canvas3.height = height;
canvas3.style.width = width+"px";
canvas3.style.height = height+"px";
let imgdata3 = ctx3.getImageData(0, 0, canvas3.width, canvas3.height);
let px3 = imgdata3.data;
// ピクセル配列へアクセスするループ
for(let y = 0; y < canvas3.height; y++) {
for(let x = 0; x < canvas3.width; x++) {
let index3 = ( y * canvas3.width + x ) * 4;
px3[index3 + 0] = 255; // 赤
px3[index3 + 1] = 255; // 緑
px3[index3 + 2] = 255; // 青
px3[index3 + 3] = 255; // アルファチャンネル
if (y >= canvas1.height) continue;
if (y >= canvas2.height) continue;
if (x >= canvas1.width) continue;
if (x >= canvas2.width) continue;
let index1 = ( y * canvas1.width + x ) * 4;
let index2 = ( y * canvas2.width + x ) * 4;
if (px1[index1+0] == px2[index2+0] && px1[index1+1] == px2[index2+1] && px1[index1+2] == px2[index2+2]) {
px3[index3 + 0] = 0; // 赤
px3[index3 + 1] = 0; // 緑
px3[index3 + 2] = 0; // 青
px3[index3 + 3] = 255; // アルファチャンネル
}
}
}
ctx3.putImageData(imgdata3, 0, 0);
});
document.addEventListener('dragover',function(e){
e.preventDefault();
});
document.addEventListener('drop',function(e){
e.preventDefault();
});
</script>
</body>
</html>
コメント