【Python】モザイクアートを作成する

Python
田中太郎
田中太郎

モザイクアートを作成する関数を作成します。

モザイクアートをPythonで作成する

モザイクアートをPythonで作成します。

まず、モザイクアートにしたい画像を読み込んで、分割したい数に分割します。

図1:画像を分割する

分割された領域ごとに平均明度と平均彩度を計算します。

図2:画像の平均明度と平均彩度を計算する

モザイクのもととなる画像を用意します。それらを読み込んで、平均明度と平均彩度を計算します。

図3:モザイクのもととなる画像を読み込む

モザイクアートの分割された領域の平均彩度、平均明度と最も近い画像を並べていきます。

図4:画像を並び替える

モザイクアートが完成します。

構成を考える

どのような構成になるかを考えます。

構成を考えると全外の流れとどんな関数が必要かわかります。

図6:構成を考える

図1を見ると以下の要素に分かれていることがわかります。

  • 画像を読み込む
  • 明度・彩度を計算する
  • 画像を分割する
  • 明度・彩度を計算した結果を保存する
  • 画像を並び替える

実装

入力

  • str型:モザイクアートにする元画像(motogazo:Pepper.bmp)
  • int型:水平方向の分割数(x_bunkatsu:64)
  • int型:垂直方向の分割数(y_bunkatsu:64)
  • str型:画像を入れておくディレクトリ名(images:12個の画像)
  • str型:出力画像名(result_filename:mozaiku_art.bmp)
  • int型:出力画像の水平サイズ(x_size_result:2048)
  • int型:出力画像の垂直サイズ(y_size_result:2048)

出力

図7:モザイクアート

コード

import os

import cv2
import numpy as np

# 平均明度を計算する。入力:ndarray型, 出力:int型
def average_brightness(img):
    return int(img.mean())

# 平均彩度を計算する。入力:ndarray型, 出力:int型 × 3
def average_saturation(img):
    r = int(img[:, :, 0].mean())
    g = int(img[:, :, 1].mean())
    b = int(img[:, :, 2].mean())
    return r, g, b

# 元画像とサブ画像を対応付けて、サブ画像の並び順を決定する。
# 入力:list型 × 2, 出力:list型
def decide_order(a_elements, b_elements):
    junban = []
    for a_element in a_elements:
        scores = [abs(sum(a_element - b_element)) for b_element in b_elements]
        for i in range(len(scores)):
            if scores[i] == min(scores):
                junban += [i]
                break
    return junban

# メイン関数
def main(motogazo, x_bunkatsu, y_bunkatsu, sub_image_dir,
        result_filename, x_size_result, y_size_result):
    # 元画像を読み込む
    img = cv2.imread(motogazo)

    # サブ画像のサイズを計算する
    x_size = int(img.shape[0]/x_bunkatsu)
    y_size = int(img.shape[1]/y_bunkatsu)

    # 元画像の明度、彩度を計算してListに格納する。
    img_elements = []
    for y in range(y_bunkatsu):
        for x in range(x_bunkatsu):
            tmp = img[x*x_size:(x+1)*x_size, y*y_size:(y+1)*y_size]
            brightness = average_brightness(tmp)
            r, g, b = average_saturation(tmp)
            img_elements.append(np.array([brightness, r, g, b]))

    print("Mozaiku art image size: {}".format(len(img_elements)))

    # サブ画像のパスを求める。
    file_pathes = [os.path.join(sub_image_dir, i) for i in os.listdir(sub_image_dir)]

    print("Sub image number: {}".format(len(file_pathes)))

    # サブ画像の明度・彩度を計算したListに格納する。
    sub_img_elements = []
    for i in range(len(file_pathes)):
        img = cv2.imread(file_pathes[i])

        brightness = average_brightness(img)
        r, g, b = average_saturation(img)
        sub_img_elements.append(np.array([brightness, r, g, b]))

    # 元画像とサブ画像の明度・彩度を比較して、サブ画像の並び順を決定する。
    junban = decide_order(img_elements, sub_img_elements)

    # モザイクアートの下地を生成する。
    img_result = np.zeros((x_size_result, y_size_result, 3), dtype=np.uint8)

    x_size = int(x_size_result/x_bunkatsu)
    y_size = int(y_size_result/y_bunkatsu)

    # junban に入っているサブ画像の順番の通り画像を並べていきます。
    for y in range(y_bunkatsu):
        for x in range(x_bunkatsu):
            img = cv2.imread(file_pathes[junban[x+x_bunkatsu*y]])
            img = cv2.resize(img, (x_size, y_size))
            img_result[x*x_size:(x+1)*x_size, y*y_size:(y+1)*y_size] = img

    cv2.imwrite(result_filename, img_result)


if __name__ == "__main__":
    """
    CONSTRAINTS:
        1.motogazo size is the multiple of x_bunkatsu and y_bunkatsu
        2.x(y)_size_result is the multiple of x(y)_bunkatsu
    """
    # 入力画像の情報
    motogazo = "Pepper.bmp"
    x_bunkatsu = 64
    y_bunkatsu = 64
    sub_image_dir = "images"

    # 出力画像の情報
    result_filename = "mozaiku_art.bmp"
    x_size_result = 2048
    y_size_result = 2048

    main(motogazo, x_bunkatsu, y_bunkatsu, sub_image_dir,
        result_filename, x_size_result, y_size_result)

まとめ

Pythonを使ってモザイクアートを作るスクリプトを作成しました。

サブ画像が多ければ多いほどきれいなモザイクアートが作れます。

画像をいっぱい用意してモザイクアートを作成しましょう!

タイトルとURLをコピーしました