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

# Background
直接的なドット絵の改善についてではないですが、[いらすとやの画像をドット絵にしたった。(part1)](https://qiita.com/satsukiya/items/8a1753a0085e94d0f1bf)で周辺の画像がドットに変換しきれなかったので解決方法について説明します。

![neko16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/36fa2f4b-7afc-1d89-79ed-a55183546da1.png)

# Method

方法としては、元画像の幅高さサイズの２倍ある画像を用意します。イメージとしてはラップの上にサンドイッチを置くイメージです:hamburger:


```Python

def wrapMat(height, width):
	dst = np.zeros((height, width, 4), np.uint8)
	dst[:] = tuple((0,255,0,128))
	return dst

if __name__ == "__main__":
	#元画像
	mat = cv2.imread(path,cv2.IMREAD_UNCHANGED)

	#幅高さを二つ折りにする
	top = int(mat.shape[0] * 0.5)
	left = int(mat.shape[1] * 0.5)
	wrap = wrapMat(top*4,left*4)

	cv2.imwrite("mono_wrap.png", wrap)
```

![mono_wrap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/53521aac-c3a5-25ed-6ad8-8e5ff1a64381.png)

見た目でわかりやすく、`G=255`、`α(透過度)=128`にしています。

次にドット絵にしたい画像を真ん中に配置します。

```Python
	wrap[top:top + mat.shape[0], left:left + mat.shape[1]] = mat
	cv2.imwrite("wrap.png", wrap)
```

![wrap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/ba7d71a9-6ff3-095a-0d93-1c56ed844efc.png)

で、ドット絵に変換します。
![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/90b70de8-11ab-c0a4-2ada-62394659947b.png)
周辺に灰色のラインが生成されていますが、ちょうど境界線上でラップ画像の配色と透明色の平均値が算出されたためだと思います。

あとはこの画像を元画像のサイズにトリミングします。

```Python	
cv2.imwrite("trim.png",wrap[top:top + mat.shape[0], left:left + mat.shape[1]])
```

![trim.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/c5691b17-a57d-b47e-ecde-0825616485fc.png)

縁の方に灰色のラインが残るかと思ったのですが期待通りに出力できました。もし表示される場合はラップ画像の配色を`(R,G,B,A)=(0,0,0,0)` にすれば問題ないです。

# Development

全体のコードです。

```Python
import cv2
import numpy as np
import sys


def wrapMat(height, width):
    dst = np.zeros((height, width, 4), np.uint8)
    dst[:] = tuple((0, 255, 0, 128))
    return dst


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]


def main():
    CELL_SIZE = 16

    path = sys.argv[1]
    mat = cv2.imread(path, cv2.IMREAD_UNCHANGED)

    top = int(mat.shape[0] * 0.5)
    left = int(mat.shape[1] * 0.5)
    # ラップ画像作成
    wrap = wrapMat(top * 4, left * 4)
    # 元画像を配置
    wrap[top:top + mat.shape[0], left:left + mat.shape[1]] = mat

    # ドット絵作成
    height, width = wrap.shape[:-1]
    for w in range(width // CELL_SIZE - 1):
        for h in range(height // CELL_SIZE - 1):
            c = np.mean(
                np.mean(
                    wrap[h * CELL_SIZE:(h + 1) * CELL_SIZE,
                         w * CELL_SIZE:(w + 1) * CELL_SIZE], axis=0
                ),
                axis=0
            )
            wrap[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]])

    # トリミング
    trim = wrap[top:top + mat.shape[0], left:left + mat.shape[1]]
    cv2.imwrite("trim.png", trim)


if __name__ == "__main__":
    main()

```

# Future
次回はモノクロ画像を4つの配色にする方法を考えてみるとです。


# Reference
- [Python/OpenCVで単色の画像を生成させる方法（numpy使用）](http://pineplanter.moo.jp/non-it-salaryman/2019/03/24/post-7337/)