LoginSignup
9
5

More than 3 years have passed since last update.

いらすとやの画像をドット絵にしたった。(part1)

Posted at

Motive

ご存知の通りいらすとやは今トレンドのフリー素材をリリースし続けています。ふとこれらの画像をドット絵にしたらどうなるかが気になったので作成してみました。

Method

魔法使いの森に書いていたのですがファミコンの表示配色は52くらいだと言われています。(確定的でない。)
ここでは、ファミコンに似せるためにR,G,Bそれぞれの配色を4分割に減色して表示してみようと思います。

それぞれの範囲は0 ~ 255ですが単純に分割すると、[0, 85, 170, 255]となります。

全ての色の組み合わせは64パターンあるのですが実際に画像として出力すると下記の表示になります。

import cv2
import numpy as np

if __name__ == "__main__":
    height = 576
    width = 1024
    range_color = [0, 85, 170, 255]

    mono = np.zeros((height, width, 3), np.uint8)
    mono[:] = tuple((0,0,0))

    for r in range(4):
        for g in range(4):
            for b in range(4):
                mono[144*b:144*(b+1), 64*(r+g*4):64*(r+(g*4+1))] = tuple((85*b,85*g,85*r))

    cv2.imwrite("out.png", mono)

out.png

余談ですが、パッと見灰色がない、と思ったのですが2行6列(85,85,85)、3行11列(170,170,170)にそれぞれ表示されています。

それで、どのようにもとの色データの数字によって判定するかですが、 一つの例として 110 の値を振り分けたい場合、85 ~ 170 がどちらが近いかを判定します。ここでは85が一番近いので110 -> 85に変換して、全ての画素にこのアルゴリズムを適合させます。

Develop

import cv2
import numpy as np
import sys


def resize(src):
    h,w = mat.shape[:-1]
    height = (h // 16) * 16
    width = (w // 16)* 16
    return cv2.resize(mat,(width,height))

def convertReduceColor(src):
    thresholds = [42,127,212]
    range_color = [0, 85, 170, 255]

    count = 0
    for th in thresholds:
        if src <= th:
            break
        count += 1
    return range_color[count]


if __name__ == "__main__":
    __CELL_SIZE__ = 4
    path = sys.argv[1]
    mat = cv2.imread(path,cv2.IMREAD_UNCHANGED)
    mat = resize(mat)

    height, width = mat.shape[:-1]

    for w in range(width//__CELL_SIZE__-1):
        for h in range(height//__CELL_SIZE__-1):
            c = np.mean(
                        np.mean(
                                    mat[h*__CELL_SIZE__:(h+1)*__CELL_SIZE__, 
                                    w*__CELL_SIZE__:(w+1)*__CELL_SIZE__], axis=0
                                ), 
                        axis=0
                    )
            mat[
                h*__CELL_SIZE__:(h+1)*__CELL_SIZE__, 
                w*__CELL_SIZE__:(w+1)*__CELL_SIZE__
                ] = tuple([convertReduceColor(c[0]), convertReduceColor(c[1]), convertReduceColor(c[2]), c[3]])

    cv2.imwrite("output.png",mat)

np.mean(np.mean(mat[h*__CELL_SIZE__:(h+1)*__CELL_SIZE__, w*__CELL_SIZE__:(w+1)*__CELL_SIZE__], axis=0),axis=0)
ここでセルの大きさから画素の平均値を取得しています。
また、convertReduceColorでRGBそれぞれに対して近似判定しています。

Result

ソース画像 画素の大きさ 処理後
group_young_people.png 4 output_group.png
group_young_people.png 8 output.png
group_young_people.png 16 output.png
animal_chara_computer_penguin.png 4 output.png
animal_chara_computer_penguin.png 8 output.png
animal_chara_computer_penguin.png 16 output.png
cat_koubakozuwari_gray.png 4 output.png
cat_koubakozuwari_gray.png 8 output.png
cat_koubakozuwari_gray.png 16 output.png
  • 一番成功したのはセルの大きさを16にした猫の画像です。右端は処理できていないですが全体的にそれっぽいドット絵になっていると思います。右端もドット調にするときはリサイズ処理を再考しないとダメっぽいです。
  • ドット絵に変換するときは一枚のイラストの中に人物・物体が一つの方が処理が成功しやすいです。
  • 基本的にいらすとやの画像ですが凸凹なフェルト調で区画を塗り潰しているのが特徴です。そのためあるオブジェクトに一色で塗り潰されておらずノイズが入っているように見えます。
  • セルの大きさを4にしたペンギンの画像の目が‎(´・ω・`)としている。いらすとやのキャラクターは基本的に目が点になっていて小さいのでセルの大きさを高くすると消えてしまうため注意が必要です。

Future

  • ノイズが気になるので変則的な二値化とモルフォロジー変換でなんとかなりそう
  • 実際のファミコン色ですが今回使った配色と異なるので、使う50くらいの配色を固定値で埋め込んで近傍探索でマッチングさせた方が良さそうです

ファミコンの画面について.png

Reference

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5