JavaScriptで画像の差分を表示するプログラム

ツール コンピュータ
ツール

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>

コメント