画像が何色系なのか判定する
みなさん、こんにちは。
クリエイティブSecの長谷川です。
今回は、画像が主に何色の画像なのかを判定したいと思います。
イメージはGoogleの画像検索をするときの色の指定ですね。
![](https://www.id-frontier.jp/wp/wp-content/uploads/2024/06/image.jpg)
ということで、今回は簡単な方法ではありますが、画像の色を抽出したいと思います。
近似色を計算する
パソコン上では、RGBの画像であれば24ビットカラーで表現されます。
光の三原色であるR(赤)、G(緑)、B(青)をそれぞれ0~255の256段階で
表現されるので、256の3乗、つまり16,777,216色で表現可能なわけです。
そのため、今回の例では赤、オレンジ、黃、緑など10色ちょっとに分類して
その画像が何色系なのかを求めます。
近似色の計算については、いろいろ複雑な方法もあるのですが
一番簡単なRGBの値を3次元空間の座標と捉え、探したい色との座標間距離で近似色を求めます。
3次元空間における2点間の距離の公式は下記ですね。
![](https://www.id-frontier.jp/wp/wp-content/uploads/2024/06/image.png)
これをプログラムで計算していきます。
実際にプログラムで書いてみる
今回は手っ取り早くJSで実装しました。
まずは、画像からRGBの値を抽出します。
const canvas = document.getElementById('image');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
const i = (y * width + x) * 4;
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const a = data[i + 3];
if (a === 0) {
// 透明ピクセルは無視
continue;
}
}
}
JSでは上記のような形でcanvasのコンテキストからデータの配列を取得して
オフセットをずらしながら指定した座標上のR、G、Bとアルファ値を取得します。
今回の計算ではアルファ値は不要なのですが、もし透明(α=0)だったら無視します。
次に取得したRGBを計算しますが、その前に今回はどの色と近似しているのか
比べるための大本を定義しておきます。
const targetColors = {
"赤": [204, 0, 0],
"オレンジ": [251, 148, 11],
"黄": [255, 255, 0],
"緑": [0, 204, 0],
"シアン": [3, 192, 198],
"青": [0, 0, 255],
"紫": [118, 44, 167],
"ピンク": [255, 152, 191],
"白": [255, 255, 255],
"黒": [0, 0, 0],
"灰": [153, 153, 153],
"茶": [136, 84, 24],
}
では、近似色の計算部分です。
最初のコードのifブロックのあとに追記します。
let minDistance = Infinity;
let minColor = null;
for (const color in targetColors) {
const [r2, g2, b2] = targetColors[color];
const distance = Math.sqrt((r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2);
if (distance < minDistance) {
minDistance = distance;
minColor = color;
}
}
// 色数をカウント
if (colors[minColor]) {
colors[minColor]++;
} else {
colors[minColor] = 1;
}
これで、近似色を求めた後に、どの色がどれだけあったのかまでを取得できます。
あとは、colorsの中身を値順にソートすれば、どの色が多かったのか分かりますね。
実際に、いくつかの画像で試してみた結果は下記のとおりです。
(左が元画像、右が近似色に変換した画像です)
![](https://www.id-frontier.jp/wp/wp-content/uploads/2024/06/localhost_5173_-1-1.jpg)
![](https://www.id-frontier.jp/wp/wp-content/uploads/2024/06/localhost_5173_-2.jpg)
![](https://www.id-frontier.jp/wp/wp-content/uploads/2024/06/localhost_5173_-2-1.jpg)
さて、お気づきだと思いますが、ちょっと思うような結果になっていません。
1枚目の写真は青と緑のどちらかになってほしいのですが、灰色が1番目になっています。
また、3枚目が黒が1番目なのはいいのですが、2枚目は青か緑か赤のどれかになってほしいところです。
このあたりは人間の視覚的な色の捉え方と、RGBの座標で見たときの近似色の違いが乖離してしまっているため、このような結果となりました。
CIEDE2000を利用してみる
CIEDE2000とは、なにやら複雑なものですが以下のようなものになります。
CIEDE2000は、知覚される色差とのより良い相関を得ることを目的として、心理メトリック量のΔL*、ΔC*、ΔH*に重み付けを伴った色差の計算方法の1つで、CIEに2000年に提案された色差式です。CIE1976に対して見た目の色の差と数値(色差)の対応が改善されています
JAPAN COLOR 【色差式CIE1976とCIEDE2000の違いはなんですか?】
実際にJavaScriptで実装すべき、細かい計算ロジックは下記より参考にしました。
JavaScriptでRGBからLab色空間への変換https://qiita.com/hachisukansw/items/09caabe6bec46a2a0858
https://qiita.com/hachisukansw/items/860f061a2ab7a4f2d06f
では、こちらで再度画像を判定してみます。
![](https://www.id-frontier.jp/wp/wp-content/uploads/2024/06/localhost_5173_-5.jpg)
![](https://www.id-frontier.jp/wp/wp-content/uploads/2024/06/localhost_5173_-3.jpg)
![](https://www.id-frontier.jp/wp/wp-content/uploads/2024/06/localhost_5173_-4-1.jpg)
さっきよりはまともな結果になりました。
ただ、まだ少し期待値との差があるように感じますね・・・。
さいごに
いかがでしたでしょうか。
近似色を求めるには、その他にもRGBではなくHSL(色相、彩度、輝度)から求める方法などもありますが
今回はとりあえず2パターンを試してみました。
人の目で見たときの色と感覚をコンピュータ情報で近似色として紐づけるのは
なかなか難しいところだなと思いました。
それでは、今回はこのへんで。