MENU

blog
スタッフブログ

dot
画像が何色系なのか判定する
技術

画像が何色系なのか判定する

みなさん、こんにちは。
クリエイティブSecの長谷川です。

今回は、画像が主に何色の画像なのかを判定したいと思います。
イメージはGoogleの画像検索をするときの色の指定ですね。

ということで、今回は簡単な方法ではありますが、画像の色を抽出したいと思います。

近似色を計算する

パソコン上では、RGBの画像であれば24ビットカラーで表現されます。
光の三原色であるR(赤)、G(緑)、B(青)をそれぞれ0~255の256段階で
表現されるので、256の3乗、つまり16,777,216色で表現可能なわけです。

そのため、今回の例では赤、オレンジ、黃、緑など10色ちょっとに分類して
その画像が何色系なのかを求めます。

近似色の計算については、いろいろ複雑な方法もあるのですが
一番簡単なRGBの値を3次元空間の座標と捉え、探したい色との座標間距離で近似色を求めます。

3次元空間における2点間の距離の公式は下記ですね。

これをプログラムで計算していきます。

実際にプログラムで書いてみる

今回は手っ取り早く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の中身を値順にソートすれば、どの色が多かったのか分かりますね。

実際に、いくつかの画像で試してみた結果は下記のとおりです。
(左が元画像、右が近似色に変換した画像です)

さて、お気づきだと思いますが、ちょっと思うような結果になっていません。
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

では、こちらで再度画像を判定してみます。

さっきよりはまともな結果になりました。
ただ、まだ少し期待値との差があるように感じますね・・・。

さいごに

いかがでしたでしょうか。
近似色を求めるには、その他にもRGBではなくHSL(色相、彩度、輝度)から求める方法などもありますが
今回はとりあえず2パターンを試してみました。

人の目で見たときの色と感覚をコンピュータ情報で近似色として紐づけるのは
なかなか難しいところだなと思いました。

それでは、今回はこのへんで。

dot
dot
PAGETOP