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]);
};
この記事が気に入ったらサポートをしてみませんか?