見出し画像

Canvas+JavaScriptで画像内の色の使用率を調べてみた

はじめに

画像ファイルからイメージカラーを抽出したいなと思い、
1. 画像処理可能な形式でファイルを読み込む
2. 使用している色を頻度順に並び替える
の2つを実装しました。

画像の右に上位10位までの色コードと使用率を表示しています。

鴨川か九十九里浜の写真

※上の海の画像の色数は82186色でした。一番頻度が高い色でも0.3%程度と極わずかで、上位の数色=イメージカラーとするのはやはり無理がありました。次の話でK-meansなどクラスタリングを行なってみようと思います。(できるか心配)

html

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8" />
	<title>canvas test page</title>
	<link href="css/style.css" rel="stylesheet" type="text/css">
	<script type="text/javascript" src="js/main.js"></script>
</head>
<body>
	<div class="container">
		<div class="item-head">
			<input id="input" type="file" accept='image/*' onchange="setImage(this);">
		</div>
		<div class="item-body">
			<div class="item-left">
				<canvas id="canvas"></canvas>
			</div>
			<div class="item-right">
				<div id="a-0" class="ranking"></div>
				<div id="a-1" class="ranking"></div>
				<div id="a-2" class="ranking"></div>
				<div id="a-3" class="ranking"></div>
				<div id="a-4" class="ranking"></div>
				<div id="a-5" class="ranking"></div>
				<div id="a-6" class="ranking"></div>
				<div id="a-7" class="ranking"></div>
				<div id="a-8" class="ranking"></div>
				<div id="a-9" class="ranking"></div>
			</div>
		</div>
		<div class="item-foot"></div>
	</div>	
</body>
</html>

CSS

*{
    margin: 0;
    padding: 0;
}

body{
    width:100%;
    height:100vh;
    display:flex;
    justify-content: center;
    align-items: center;
}

canvas{
    max-height : 720px;
}

.ranking{
    width:100%;
    display:none;
}

.item-body{
	display: flex;
}

JavaScript

"use strict";
// 画像が選択されたら、base64形式(eventFR.target.result)で取得する
function setImage(target) {
    if (!target.files.length) {
        if (flagDebug)
            console.log("  setImage() : !Err! file not found");
        return;
    }

    const file = target.files[0];
    const fr = new FileReader();
    fr.onload = (eventFR) => {
        setBase64ToCanvas(eventFR.target.result);
    };
    fr.readAsDataURL(file);
}
// base64形式の画像をcanvasに表示する
function setBase64ToCanvas(data) {
    let img = new Image();
    img.src = data;
    img.onload = function () {

        // canvasのお作法
        let cvs = document.getElementById("canvas");
        let ctx = cvs.getContext('2d');

        // canvasの縦横幅を指定する
        cvs.width = img.naturalWidth;
        cvs.height = img.naturalHeight;

        // canvasにimgを表示する
        ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight);

        // pixel操作の準備
        let imgData = ctx.getImageData(0, 0, cvs.width, cvs.height);
        analysePixel(imgData);

    };
}

// canvasに表示された画像を基に解析を行う
function analysePixel(arrData) {

    let w = arrData.width;
    let h = arrData.height;
    let size = w * h;

    let r = 0;
    let g = 0;
    let b = 0;
    let a = 0;

    let mapData = {};
    let hex = "";

    // mapDataを作成
    // mapdata["#258fe3"] = 7193
    for (let i = 0; i < arrData.data.length; i += 4) {
        r = arrData.data[i]; // r
        g = arrData.data[i + 1]; // g
        b = arrData.data[i + 2]; // b
        a = arrData.data[i + 3]; // a (透過度)
        hex = rgb2hex(r, g, b);

        if (mapData[hex]) {
            mapData[hex]++;
        }
        else {
            mapData[hex] = 1;
        }
    }

    //キーを含んだ配列に変換 オブジェクト⇒配列
    // arrSort[0]={key: '#258fe3', value: 7193}
    // arrSort[0].key = '#258fe3'
    // arrSort[0].value = 7193
    var arrSort = Object.keys(mapData).map((k) => ({ key: k, value: mapData[k] }));
    arrSort.sort((a, b) => b.value > a.value ? 1 : -1);
    // itme-rightに順位を記載
    for (let i = 0; i < arrSort.length; i++) {
        if (i > 9) break;
        let element = document.getElementById("a-" + i);
        element.style.backgroundColor = arrSort[i].key;
        let gs = hex2Grayscale(arrSort[i].key);
        if (gs < 125) {
            element.style.color = "#FFFFFF";
        }
        else {
            element.style.color = "#000000";
        }
        element.style.display = "flex";

        let str = "";
        str += (i + 1) + "位<br/>";
        str += arrSort[i].key + "<br/>";
        str += make4digitPercent(arrSort[i].value / size);
        element.innerHTML = str;
    }
}

// 0.987654321 => 98.76%
// 0.001234567 => 0.123%
let make4digitPercent = function (num) {
    if (num == 1) {
        return "100.00%";
    }
    else if (num >= 0.1) {
        return ((Math.round(num * 10000) / 10000 * 100) + "").slice(0, 5) + "%";
    }
    else if (num >= 0.01) {
        return ((Math.round(num * 100000) / 100000 * 100) + "").slice(0, 5) + "%";
    }
    else {
        return ((Math.round(num * 1000000) / 1000000 * 100) + "").slice(0, 5) + "%";
    }
};

// rgbを#xxxxxxに変換する
let rgb2hex = function (r, g, b) {
    return "#" + ("000000" + (r * 256 * 256 + g * 256 + b).toString(16)).slice(-6);
};

// #xxxxxxをrgbに変換する
let hex2rgb = function (hex) {
    let r = 0;
    let g = 0;
    let b = 0;
    if (hex.length == 7) {
        r = parseInt(hex.slice(1, 3), 16);
        g = parseInt(hex.slice(3, 5), 16);
        b = parseInt(hex.slice(5, 7), 16);
    }
    else if (hex.length == 4) {
        r = parseInt(hex.slice(1, 2), 16) * 16;
        g = parseInt(hex.slice(2, 3), 16) * 16;
        b = parseInt(hex.slice(3, 4), 16) * 16;
    }
    else {
    }
    return [r, g, b];
};

// グレースケールを計算する
let rgb2Grayscale = function (r, g, b) {
    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
};

// グレースケールを計算する
let hex2Grayscale = function (hex) {
    let rgb = hex2rgb(hex);
    return rgb2Grayscale(rgb[0], rgb[1], rgb[2]);
};

この記事が気に入ったらサポートをしてみませんか?