色差 に関する投稿を表示しています

視認しにくいテキストを探す | その 2 puppeteer を使って任意の web サイト上のテキストに対して色差を計算する

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

前回は色差を計算する方法の簡単な紹介と chroma.js を使った計算について扱いました。
視認しにくいテキストを探す | その 1 chroma.js を使って色差を計算する | ごみばこいん Blog

今回は実践編として、特定の Web ページに対して色差を計算して「視認しにくいテキストを探す」ことをやります。

Web ページに対して色を確認するには、キャプチャしたものから気合で探すか、 CSS の結果を持って確認するか考えられそう。ここでは後者を puppeteer でやってみます。
puppeteer は以前紹介したとおり、 Headless Chrome を Node.js からお手軽に扱うためのものです。
Node.js から Headless Chrome を操作できる puppeteer を試す | ごみばこいん Blog

ページを読み込んで色差を計算するためにこんなコードを書きました。

// ブラウザの起動
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.setViewport({
    width: 1600,
    height: 900,
})

// デバッグ用に console.log を nodejs 側に渡す
page.on('console', msg => console.log(msg.text()));

// サイトにアクセスする
await page.goto('https://gomiba.co.in/test/color_difference.html');

// 色差を計算するために chroma をページ内で読み込む
await page.addScriptTag({
    url: 'https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.6/chroma.min.js',
});

// 色差が一定以上のものを探す
await page.evaluate(() => {
    // 全要素を探索していく
    document.querySelectorAll('*').forEach((element) => {
        // テキストノード、または SVG を子に持っている要素を探す
        let foundChildNode = Array.prototype.filter.call(element.childNodes, (e) => {
            let status = false;
            status = status || (e.nodeType === Node.TEXT_NODE && e.textContent.trim().length > 0);
            status = status || e.nodeName.toLowerCase() === 'svg';
            return status;
        });
        if (foundChildNode.length === 0) {
            return;
        }

        // 計算されたスタイルから色を取得
        let elementStyle = window.getComputedStyle(element);
        let fontColor = elementStyle.color;
        let backgroundColor = elementStyle.backgroundColor;

        // 色差を計算する
        let colorDiff = chroma.deltaE(fontColor, backgroundColor);

        // 計算された色差に透明度の色差を加える(透明度の差分 * 0.5 * 100)
        colorDiff += (Math.abs(chroma(fontColor).alpha() - chroma(backgroundColor).alpha()) * 0.5) * 100;

        // 色差が大きいものは無視
        if (colorDiff > 40) {
            return;
        }

        // 色差が小さいものに色付けをする
        console.log(element.nodeName + " : " + colorDiff)
        element.style.cssText = element.style.cssText + 'border: 5px dashed red !important;';
    });
});


// スクリーンショットを撮ってみる
await page.screenshot({path: 'example.png'});


// ブラウザを終了する
await browser.close();

puppeteer の playground がオンラインで提供されているので、コピペで動作確認できるはず、お試しあれ。
Try Puppeteer

手元に Node.js の環境があるなら、こちらのリポジトリでも。コマンドライン引数で対象 URL とキャプチャの保存するファイル名を受け取って、マーカーが引かれたキャプチャ画像が出力されます。
sters/web-color-difference

たとえば medium でやってみるとこんな感じ。白背景の部分で、薄めグレーの箇所ががマークされている。

たとえば、ランダムに色を設定するページを用意して試してみるとこんな具合。

確かに見にくい!!!なものはマークされているようだけど、ちょっと怪しいライン。もうちょっと基準値を調整したり CIEDE2000 のロジックも試すかなどして詰めたほうがいいかも…?とりあえず、のところはこれでもいいんじゃなかろーか。
元々やりたかった「視認しにくいテキストを探す」は出来たとしておしまい。

…と思ったけど、視認しにくいテキストっていうと話題が広いな、小さすぎる文字とか他の要素と被っているとかそういうのもそうなんだよねきっと、うーん。

視認しにくいテキストを探す | その 1 chroma.js を使って色差を計算する

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

SEO が云々でも色被ってるの良くないよねっていうけど、いや普通にユーザビリティ悪いやん、適当に色選ぶと見にくいこと往々にしてあるやん、って思ったので、そういう何かがあるのかなあって思って調べたら、 Wikipedia 先生にそのまんまの項目が記載されていた。ので、これをまとめつつ、ライブラリがあったのでそれを使ってみる話。

数式、もといロジックをざっくり読んでみる編

前提として Wikipedia のものをガンガン鵜呑みにしていく。
数式とかなんでそうなの?みたいな理解はせず、そういうものがあるんだなーくらいに留める。

色差を計算するには?

Wikiepedia に色差というページがあって、もうこれ。英語版と微妙に内容が違うけど、それは後で触れる。

色差 - Wikipedia

色差を計算するには単純に距離を計算しては、人間の感覚とはだいぶずれてしまってだめ。どうやら CIEDE2000 というものを使うとよいらしい。 CIE は国際照明委員会のことで DE は Delta E だ。2000 は年代。1976 、 1994 と研究され数式が更新されている。で、この CIEDE2000 を計算するには Lab 色空間を使う必要がある。
ただ、ぼくらが普段生きている世界では RGB とか CMYK とか HSV とか HSL とかなので、そこから変換をしてあげないといけない。いや、デザイナーさんとかその手のグラフィック関連に詳しい人なら Lab の世界でも生きているのかもしれない。

(参考)
カラーネーム/RGB/HSL ウェブでのカラー指定いろいろ | アライドアーキテクツのクリエイターブログ

Lab 色空間に生きたい

Lab 色空間も Wikipedia 上にページがある。

Lab色空間 - Wikipedia

単に Lab というと Hunter と CIE の 2 種類があるが CIE の方を最近では指すらしいので、これを使ったらいいみたい。これまた国際照明委員会が決めたもので、明度( L )と、いい感じの色 a と b を使って示し、人間の目で見える全ての色を記述できて機器固有の基準として使えるようにしたものだそうだ。

RGB からどうやって Lab に変換するんだろうねっていうと、 RGB は機器固有の部分があるので、一発変換!とはできず、 RGB を XYZ 色空間 に変換する必要がある。

RGB の世界から XYZ の世界へ移動する

これまた Wikipedia 上にページがある。

CIE 1931 色空間 - Wikipedia

すげーざっくりの理解では「研究成果の結果、 RGB に対する人の感覚を重み付けしたもの」。

ここまでをまとめると?

RGB → XYZ → Lab → 色差が計算できる!

後に回した英語との差分って?

色差の英語版のページを見てみる。
Color difference - Wikipedia

すると「 CMC l:c (1984) 」という日本語にはない数式が出ている。なんか良いやつなんじゃないかな、きっと。色っぽい単語で調べてたらその手の調査したよって論文もでてきた。

DDCPにおける色域外特色近似再現のための色差式評価

優位性とか書いてあるので気になった時に読むとよさそうな気がする。

ライブラリの出番です chroma.js

こう研究された成果があるわけなので、まあきっと便利に使えるようにライブラリ作っている人とかいるんだろうなあ~~とか思っているとやっぱりある。

chroma.js api docs!

色関係の話題をいい感じに取り扱ってくれるライブラリだそうで、各種色空間の変換も任せろ、グラデーションも任せろ、な強いライブラリ。

chroma.js を使って色差を計算する

単純な距離、ユークリッド距離の計算をするには chroma.distance を使う。 mode パラメータで色空間の指定が出来るので、2つの色の間の位相をみたりなんかにもできそうだ。
色差を計算するには chroma.deltaE を使う。ご丁寧に deltaE という命名になっててわかりやすいぞ~。内部的には CMC を使っているらしい。 CIEDE じゃないんだ。

それを実際に試してみようってわけで、こんな HTML + JS を書いて様子を見てみる。

<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.6/chroma.min.js"></script>

<style>
.color-difference td {
    min-width: 15px;
    min-height: 15px;
    padding: 3px;
}
</style>

<table class="color-difference">
    <tr>
        <td></td>
        <td></td>
        <td>distance(RGB)</td>
        <td>deltaE</td>
    </tr>
</table>

<script>
let colors = [
    ['rgb(255,0,0)', 'rgb(0,255,0)'],
    ['rgb(255,0,0)', 'rgb(0,0,255)'],
    ['rgb(255,0,0)', 'rgb(255,255,0)'],
    ['rgb(255,0,0)', 'rgb(255,0,255)'],
    ['rgb(255,0,0)', 'rgb(0,255,255)'],
    ['rgb(255,0,0)', 'rgb(255,128,0)'],
    ['rgb(255,0,0)', 'rgb(255,0,128)'],
    ['rgb(255,0,0)', 'rgb(255,255,255)'],
    ['rgb(255,255,0)', 'rgb(255,0,255)'],
    ['rgb(255,255,0)', 'rgb(0,255,255)'],
    ['rgb(255,255,0)', 'rgb(255,255,255)'],
];

let table = document.querySelector('.color-difference');

colors.forEach((color) => {
    let tr = document.createElement('tr');
    tr.innerHTML = `
        <td style="background-color:${color[0]}"></td>
        <td style="background-color:${color[1]}"></td>
        <td>${chroma.distance(color[0], color[1], 'rgb')}</td>
        <td>${chroma.deltaE(color[0], color[1])}</td>
    `;
    table.appendChild(tr);
});
</script>

こんな出力が得られる。

  • 赤と緑、青よりは、黄ピンクのほうが気持ち近い。水色、距離は遠いんだけど色差はあまり変わらず
  • 赤とオレンジ、マゼンダな色はやっぱり色差が少ない
  • 赤と緑や青を比べるより、赤と白を比べたほうが色差がすくない(!)
  • 黄とマゼンダよりも、黄と水、黄と白のほうが色差がすくない(!)
  • 距離 = 色差 に直結せず、色によって微妙に重み付けがされているのを感じる

わりと見た感覚と近い気がするのと、数字として出るとさらに面白い。

その 2 を書いた。

https://gomiba.co.in/blog/archives/1841