ある2種類の色が似た色(人の目で違う色と判断しづらい色)かどうかを判定するnodeのライブラリ
good-friends-colors
をnpmに公開しました!
使い方
使い方はシンプル。
2つの色が似た色(人間には違いが分かりづらい色)であればtrue
、そうでなければfalse
を返します。
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より引用
// この値が同じになれば最後の平方根の計算は不要なのでここでは省略
const result = (50 - 20) ** 2 + (222 - 192) ** 2 + (89 - 59) ** 2
=> // 2700
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色度図の一部を図式化したものです。色度図上に描かれた白い楕円は、各彩度および色相における、人の目の色識別域を表しています。つまり、この白い楕円の範囲に含まれる色は、人の目では色に違いがあっても識別できません。
早い話が、
上の図の 白い円の部分は(数値で表せば)違う色だけど、人間の目には同じ色に感じるよ という事が書かれています。
以下の2つの色の組み合わせは、上の白い円内にそれぞれ存在する色の組み合わせです。
CIEDE2000で計算を行うと
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
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
になります。
試しに円の中に含まれない色を試してみると、
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
コニカミノルタのページに色に詳しいページがあります、その名も「色色雑学」!
オシャレでかつ見やすく分かりやすく色の事を解説してくれているサイトです。
色に興味がある方は見てみて下さい
実装について
計算式
色差 - Wikipediaより引用
正直難しくてCIEDE2000の式(↑)の内容は理解出来てません..w
理解出来なくても調べていけばライブラリ作れるって便利な時代になりましたね。
作成する上で2つのサイトを参考にさせていただきました。感謝です。
分からないなりに簡単に書くと、CIEDE2000は内部的にLab色空間を用いているため、
RGB
→ Labへ変換
→ CIEDE2000での比較
という流れで変換・比較を行います。
詳しく知りたい人は計算方法もコニカミノルタのページに載っていますのでどうぞ!
Lab色空間の弱点である測定結果と視感評価との相違を補正した最も新しい色差式です。明度差ΔL、彩度差ΔC、色相差ΔHをもとに、重価係数(SL、SC、SH)やパラメトリック係数と呼ばれる定数(kL、kC、kH)などの補正を加え、以下のように算出します。
内部的な作り
TypeScript + Jest で作っています。
TypeScript
TypeScriptの環境は↓記事を参考に構築。
TypeScript + Node.js プロジェクトのはじめかた2020 - Qiita
やはり型安全な世界は良いですね。
IDEも賢くなっていて誤りにいち早く気付ける。
テスト
テストはスタンダードにJest
を使っています。
担保しているのは大きく2点。
- RGB → Lab への変換が誤っていないか
- CIEDE2000の計算が間違っていないか
CIEDE2000のテストは
参考にさせてもらった記事にThe CIEDE2000 Color-Difference Formula
というテストデータについて記載されていたので、私もそちらのデータを使用しました。
The CIEDE2000 Color-Difference Formulaにあったテストデータもパスする事を確認しました。
作ったきっかけ(余談)
私が今回作った機能を内包しているライブラリはいくつかあると思います。
色に関するライブラリであれば内部的に実装しているケースは多いでしょう。
ではそんな中なぜこのライブラリを作ったか?
と言いますと、この機能だけでコンパクトにまとめて、汎用的に使えるようにしたかったから。
UNIX哲学の 「小さく作って組み合わせて使う」 の部品の1つにしたかったという事です。
元々の発端は「Node.js上で画像の占める色の割合を計算して出力する」ライブラリを作っていて
その計算を行う上で必要だったのですが、
せっかくだったらこの機能だけでモジュール化しておけば、
何か別のライブラリを作ろうと思った時や、他の人が色に関する処理やライブラリを作ろうと思った時に役立てるだろうなぁ、と思い作ってみました。
というわけで、このライブラリが何かの役に立てれば幸いでございます 🙃