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

# Motive
ご存知の通り[いらすとや](https://www.irasutoya.com/)は今トレンドのフリー素材をリリースし続けています。ふとこれらの画像をドット絵にしたらどうなるかが気になったので作成してみました。

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

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

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

```Python
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/b22de30f-362c-c409-80c6-0f7d92b7de86.png)

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



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


# Develop

```Python
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/b34329c5-1162-641e-e338-c45aa95974cd.png) | 4|![output_group.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/a505d21f-6765-bb36-54a5-49dec73bbe8e.png) |
|![group_young_people.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/b34329c5-1162-641e-e338-c45aa95974cd.png)|8|![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/44f038e3-9f3a-9f4d-e229-158d5ccb3c9d.png)|
|![group_young_people.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/b34329c5-1162-641e-e338-c45aa95974cd.png)|16|![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/75a20477-a323-1c1d-7c05-288c14dfb308.png)|
|![animal_chara_computer_penguin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/160be9c6-9bef-410a-1a7e-65272fb1af0e.png)| 4| ![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/4256f42c-3581-bfc5-ac4a-d4261cbcea5c.png)|
|![animal_chara_computer_penguin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/160be9c6-9bef-410a-1a7e-65272fb1af0e.png)|8|![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/10e92939-253e-15d7-e101-61714361f685.png)|
|![animal_chara_computer_penguin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/160be9c6-9bef-410a-1a7e-65272fb1af0e.png)|16|![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/eb41f783-250d-30f6-5156-cf023e22a5a4.png)|
|![cat_koubakozuwari_gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/7b5f39a5-2a39-ff25-7720-7a4b2e1a96b5.png)|4|![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/15b31f87-0a38-e0d4-0966-fc76dab077ef.png)|
|![cat_koubakozuwari_gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/7b5f39a5-2a39-ff25-7720-7a4b2e1a96b5.png)|8|![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/59a43829-8895-9748-00b3-fc21029986ff.png)|
|![cat_koubakozuwari_gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/7b5f39a5-2a39-ff25-7720-7a4b2e1a96b5.png)|16|![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/d3e310b5-8860-b347-2add-12292a3a9b75.png)|

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

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

<img width="880" alt="ファミコンの画面について.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/3efa5197-e781-709a-b8c9-9135b3577440.png">


# Reference
- [いらすとや](https://www.irasutoya.com/)
- [ファミコンの画面について - 魔法使いの森](https://www.wizforest.com/OldGood/ntsc/famicom.html)