色が似ているかどうかを判定するnpmライブラリ good-friends-colors を公開しました

投稿日
色が似ているかどうかを判定するnpmライブラリ good-friends-colors を公開しました

ある2種類の色が似た色(人の目で違う色と判断しづらい色)かどうかを判定するnodeのライブラリ

good-friends-colorsをnpmに公開しました!


使い方

使い方はシンプル。

2つの色が似た色(人間には違いが分かりづらい色)であればtrue、そうでなければfalseを返します。



c1
R:20 G:192 B:59
c2
R:30 G:188 B:63
c3
R:255 G:188 B:63
import gf from 'good-friends-colors'
 
const c1 = { r: 20, g: 192, b: 59 }
const c2 = { r: 30, g: 188, b: 63 }
const c3 = { r: 255, g: 188, b: 63 }
 
const result1 = gf(c1).isGoodFriend(c2)
console.log(result1)
// => true
 
const result2 = gf(c1).isGoodFriend(c3)
console.log(result2)
// => false


また、2つの色の距離を数値で求める事も出来ます。

import gf from 'good-friends-colors'
 
const c1 = { r: 20, g: 192, b: 59 }
const c2 = { r: 30, g: 188, b: 63 }
const c3 = { r: 255, g: 188, b: 63 }
 
const result1 = gf(c1).diff(c2)
console.log(result1)
// => 1.23840951999051
 
const result2 = gf(c1).diff(c3)
console.log(result2)
// => 40.10746575803398

色の比較方法は色々ある中、

good-friends-colorsでは「「CIEDE2000色素式」 (以降CIEDE2000)」という比較方法を用いています。

(詳しい事は後述)





ライブラリの名前の由来は「同じような色って、その色同士仲が良さそうだな」と思い、

good-friends-colorsと名付けました。笑


「c2はc1にとって仲良しか?」という判定をしているわけですねw



(公開した後にxxとxxはズッ友だょ、のフレーズを思い出して「あ、、」てなったのは内緒)



色の比較方法

CIEDE2000色素式

「CIEDE2000」とは、色々な色の差を求める計算式の中で、

より人の目に近い色の比較を行うための計算式です。





例えば単純なRGBの差から2つの式差を求める「ユークリッド距離」では、以下のそれぞれの2つの色の差は同じとみなされます。

ユークリッド距離

ユークリッド距離


色差 - Wikipediaより引用


R:20 G:192 B:59
R:50 G:222 B:89
// この値が同じになれば最後の平方根の計算は不要なのでここでは省略
const result = (50 - 20) ** 2 + (222 - 192) ** 2 + (89 - 59) ** 2
=> // 2700


R:20 G:192 B:59
R:70 G:202 B:69
const result = (70 - 20) ** 2 + (202 - 192) ** 2 + (69 - 59) ** 2
=> // 2700

いかがでしょう?

「ユークリッド距離」の計算式では同じ色差と判定されますが、

上の方が2つ色の差が多く感じませんか??





これをCIEDE2000で計算すると、

import gf from 'good-friends-colors'
 
const c1 = { r: 20, g: 192, b: 59 }
const c2 = { r: 50, g: 222, b: 89 }
const c3 = { r: 70, g: 202, b: 69 }
 
 
const result1 = gf(c1).diff(c2)
console.log(result1)
// => 7.616975592665211  上の2つ色の差
 
const result2 = gf(c1).diff(c3)
console.log(result2)
// => 3.4469139890109055  下の2つ色の差

「上の2つの色の方が差がある!」と判定されます。




このように人の目は色の差を感じる時単純に表せない計算を行っていて、それを極力近づけた式がCIEDE2000です。


人の目は面白いですね〜 👀



近い色かの判定

コニカミノルタのCIEDE2000について解説されているサイトから図と文章を引用すると

図17は、CIE LAB色空間を表すab色度図の一部を図式化したものです。色度図上に描かれた白い楕円は、各彩度および色相における、人の目の色識別域を表しています。つまり、この白い楕円の範囲に含まれる色は、人の目では色に違いがあっても識別できません。

新しい色差式(CIE DE2000)について。-楽しく学べる知恵袋 | コニカミノルタ より引用


早い話が、

上の図の 白い円の部分は(数値で表せば)違う色だけど、人間の目には同じ色に感じるよ という事が書かれています。





以下の2つの色の組み合わせは、上の白い円内にそれぞれ存在する色の組み合わせです。


CIEDE2000で計算を行うと


R:81 G:175 B:68
R:83 G:171 B:70
import gf from 'good-friends-colors'
 
const c1 = { r: 81, g: 175, b: 68 }
const c2 = { r: 83, g: 171, b: 70 }
 
const result = gf(c1).diff(c2)
console.log(result)
// => 1.287334586325403

R:181 G:201 B:29
R:185 G:198 B:35
import gf from 'good-friends-colors'
 
const c1 = { r: 181, g: 201, b: 29 }
const c2 = { r: 185, g: 198, b: 35 }
 
const result = gf(c1).diff(c2)
console.log(result)
// => 1.4816218271524604

このようにどちらも result < 2 になります。





試しに円の中に含まれない色を試してみると、

R:213 G:216 B:13
R:200 G:223 B:18
import gf from 'good-friends-colors'
 
const c1 = { r: 213, g: 216, b: 13 }
const c2 = { r: 200, g: 223, b: 18 }
 
const result = gf(c1).diff(c2)
console.log(result)
// => 4.158696012580833

result >= 2 になります。



私が測定した限りだと境となる値が2かな…?と感じたため、

デフォルトで閾値を2に設定しています。




ただ、

  • 白い円の部分の差が常にresult < 2か?
  • 閾値がそもそも2なのか?

は残念ながら調べても分からなかったこと、


また、使用時にはその閾値を広げたい場合もあるはず。なので、

オプションで「閾値の値」を変更出来るようにしています。

import gf from 'good-friends-colors'
 
const c1 = { r: 20, g: 192, b: 59 }
const c2 = { r: 5, g: 192, b: 120 }
 
const diff = gf(c1).diff(c2)
console.log(diff)
// => 9.302586534325725
 
 
const result1 = gf(c1).isGoodFriend(c2)
console.log(result1)
// => false
 
const result2 = gf(c1).isGoodFriend(c2, 10)
console.log(result2)
// => true


コニカミノルタのページに色に詳しいページがあります、その名も「色色雑学」!

色色雑学-楽しく学べる知恵袋 | コニカミノルタ


オシャレでかつ見やすく分かりやすく色の事を解説してくれているサイトです。

色に興味がある方は見てみて下さい



実装について

計算式

CIEDE2000の計算式


色差 - Wikipediaより引用

正直難しくてCIEDE2000の式(↑)の内容は理解出来てません..w

理解出来なくても調べていけばライブラリ作れるって便利な時代になりましたね。




作成する上で2つのサイトを参考にさせていただきました。感謝です。





分からないなりに簡単に書くと、CIEDE2000は内部的にLab色空間を用いているため、


RGBLabへ変換CIEDE2000での比較

という流れで変換・比較を行います。

Lab色空間とは



詳しく知りたい人は計算方法もコニカミノルタのページに載っていますのでどうぞ!

CIEDE2000色素式の数式

Lab色空間の弱点である測定結果と視感評価との相違を補正した最も新しい色差式です。明度差ΔL、彩度差ΔC、色相差ΔHをもとに、重価係数(SL、SC、SH)やパラメトリック係数と呼ばれる定数(kL、kC、kH)などの補正を加え、以下のように算出します。

CIE DE2000色差式-楽しく学べる知恵袋 | コニカミノルタ



内部的な作り

TypeScript + Jest で作っています。


TypeScript

TypeScriptの環境は↓記事を参考に構築。

TypeScript + Node.js プロジェクトのはじめかた2020 - Qiita


やはり型安全な世界は良いですね。

IDEも賢くなっていて誤りにいち早く気付ける。



テスト

テストはスタンダードにJestを使っています。



担保しているのは大きく2点。

  1. RGB → Lab への変換が誤っていないか
  2. CIEDE2000の計算が間違っていないか

CIEDE2000のテストは

参考にさせてもらった記事にThe CIEDE2000 Color-Difference Formulaというテストデータについて記載されていたので、私もそちらのデータを使用しました。

The CIEDE2000 Color-Difference Formulaにあったテストデータもパスする事を確認しました。


JavaScriptで指定した色をある基準色に分類する - wadackel.meより引用



作ったきっかけ(余談)

私が今回作った機能を内包しているライブラリはいくつかあると思います。

色に関するライブラリであれば内部的に実装しているケースは多いでしょう。



ではそんな中なぜこのライブラリを作ったか?

と言いますと、この機能だけでコンパクトにまとめて、汎用的に使えるようにしたかったから。


UNIX哲学の 「小さく作って組み合わせて使う」 の部品の1つにしたかったという事です。





元々の発端は「Node.js上で画像の占める色の割合を計算して出力する」ライブラリを作っていて

その計算を行う上で必要だったのですが、


せっかくだったらこの機能だけでモジュール化しておけば、

何か別のライブラリを作ろうと思った時や、他の人が色に関する処理やライブラリを作ろうと思った時に役立てるだろうなぁ、と思い作ってみました。





というわけで、このライブラリが何かの役に立てれば幸いでございます 🙃

プロフィール画像
Yuki Takara
都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。