k-means法で減色
過去ブログの転載です。
k-means法とはクラスタリングの手法の一つです。数あるクラスタリングの中でもとても実装が簡単で、割と速く収束して結果が得られるという利点があります。k個の平均値を使って、その平均値に近いグループにデータを割り当ててやろうという考えです。
たとえばこんなふうに、2次元平面上にまばらな点があります。これを3個の分類してみましょう。k=3とします。
k-means法を行うには、初期値を決める必要があります。結果が初期値に大きく依存してしまうのが欠点です。このてきとうに与えた3点を初めの平均値としましょう。
次に、各データがはたしてどの平均値に近いかをそれぞれ求めます。赤に近い点は赤になります。この場合は赤が多いですね。実は目で見て何となくでやっているので、嘘が混ざっているかもしれません。
これでデータが3つに分かれました。でも分かれた感じがしないですよね。
そこで、新しく決まったこれらのデータの平均を計算しなおします。すると、平均を意味する×印の位置はちょっと変わります。
あとはその繰り返しです。再び×印に近いものを色分けします。例がよろしくなかったですね。この場合は1つの点が赤から緑に変わっただけです。そしてまた平均を求めて×印の位置を変えて……というのを、変化がなくなるまで繰り返せばOKです。
最終的にこんな感じになりました。どうしても初期値に依存するので初め思っていたのとちょっと違うのですが、まあ、ちゃんとそれっぽく3つに分けてくれたので良いでしょう。
この方法を使って、減色をやってみます。
Windows10の時代になったこのご時世で、未だに私はXPのペイントを使っているのですが、jpg形式で保存するとずいぶんひどい画質になってしまっています。
ペイントでかいた画像なので、単純な色に戻したいわけです。
色をいくつかあらかじめ指定しておいて、それに近い色を選ばせてやればそれで実現できるのですが、色の指定がめんどくさいので、k-means法を用いて色の数kだけ与えて勝手に振り分けてもらいましょう。
色に対してk-means法を適用するイメージです。色は3原色の光量で一意に決まりますので、このような3次元空間上の点と考えることができます。
近い色の距離同士は近いので、この距離を用いてk-means法を適用すれば、勝手に色を分けてくれるようになるという仕組みです。
16色に分けてみて、初期値となる色は彩度、明度が最大で、色相だけ異なるようにしました。各画素をデータと考えて、k-means法を適用します。
29回目で収束しました。結構はっきり分かれてくれましたね。残念ながら、ざらざらした点がいくつか含まれてしまっているので結果は微妙ですが、少なくとももとのjpg画像と比べれば色が単純化したのが確認できます。
ベイ助とベイ太の体の色(体毛なのだ)はちゃんと単色になってますね。ベイ助の服のBの字がおかしいですけど……
実用させるには、ぼかし等と併用したほうが良いかもしれません。
今度はベイ助単体。jpgで保存したので、もやもやが出来てしまっています。これを適用してみます。10色でやってみました。
今度は10回で収束しました。
若干赤が濃かったり、細かい色が省かれていたりしますが、だいたい抽出できてますね。大丈夫そうです。
次はチーター君。jpg保存じゃないのですが、保存の都合でずいぶんぼけてます。こちらもとりあえず16色でやってみました。
66回で収束しました。遅いですね……しかも輪郭の部分のぼけたところが全部別の色になってしまい、うまく分離されていないことが分かります。色数を多くした弊害でしょう。
16色じゃなくて、6色に落としてみると、こうなりました。やっぱり輪郭部分に持っていかれてますね……初期値をうまいこと設定してやれば何とかなるのかもしれません。
次は写真です。これは稚内のノシャップ岬であります。
実際、k-means法を使うなら、このように具体的な色を指定せずに特定の色数に減色することに適用するのが良いでしょう。今の時代、画像の減色なんてほとんどされないのかもしれませんが……
16色で適用してみます。
18回目ぐらいでほぼ変化しなくなったので途中で切りました。いいですね。とても16色しか使用されていないなんて思えません。
まあこんな遊びができるわけでございます。