【Python/OpenCV】写真のアニメ絵化

この記事では、Python版OpenCVで写真をアニメ絵風に加工する方法をソースコード付きで解説します。

写真のアニメ絵化

Python版OpenCVの空間フィルタ処理などを組み合わせると写真をアニメ絵化できます。
大まかな処理手順は以下の通りです。

説明
Cannyアルゴリズムで輪郭画像の作成
入力画像の減色処理を行う(領域分割 or K平均法など)
入力画像から輪郭画像を引く(輪郭部分が黒くなる)

ソースコード(Python3+OpenCV3)

サンプルプログラムのソースコードです。

単純に減色処理

# -*- coding: utf-8 -*-
import cv2
import numpy as np


def anime_filter(img, K=20):
    # グレースケール変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)

    # ぼかしでノイズ低減
    edge = cv2.blur(gray, (3, 3))

    # Cannyアルゴリズムで輪郭抽出
    edge = cv2.Canny(edge, 50, 150, apertureSize=3) 

    # 輪郭画像をRGB色空間に変換
    edge = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)

    # 画像の減色処理
    img = np.array(img/K, dtype=np.uint8)
    img = np.array(img*K, dtype=np.uint8)

    # 差分を返す
    return cv2.subtract(img, edge)


def main():
    # 入力画像の読み込み
    img = cv2.imread("input2.jpg")

    # 画像のアニメ絵化
    anime = anime_filter(img, 30)
    
    # 結果出力
    cv2.imwrite('anime.jpg', anime)

if __name__ == '__main__':
    main()

領域分割で減色

# -*- coding: utf-8 -*-
import cv2
import numpy as np

def anime_filter(img):
    # グレースケール変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)

    # ぼかしでノイズ低減
    edge = cv2.blur(gray, (3, 3))

    # Cannyアルゴリズムで輪郭抽出
    edge = cv2.Canny(edge, 50, 150, apertureSize=3) 

    # 輪郭画像をRGB色空間に変換
    edge = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)

    # 画像の領域分割
    img = cv2.pyrMeanShiftFiltering(img, 5, 20)
    
    # 差分を返す
    return cv2.subtract(img, edge)


def main():
    # 入力画像の読み込み
    img = cv2.imread("input.jpg")

    # 画像のアニメ絵化
    anime = anime_filter(img)
    
    # 結果出力
    cv2.imwrite('anime.jpg', anime)


if __name__ == '__main__':
    main()

cv2.pyrMeanShiftFilteringメソッドは、平均値シフト法で領域分割します。
領域分割は、近傍の画素の輝度値(色)が類似している場合に同じ領域に属すると判定するものです。同じ領域にある全ての画素は、全体の平均値を画素値とします。これを行うことで、写真がフラットになりアニメ絵っぽくなります。

k-means法で減色

# -*- coding: utf-8 -*-
import cv2
import numpy as np

# 減色処理
def sub_color(src, K):
    # 次元数を1落とす
    Z = src.reshape((-1,3))

    # float32型に変換
    Z = np.float32(Z)

    # 基準の定義
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)

    # K-means法で減色
    ret, label, center = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)

    # UINT8に変換
    center = np.uint8(center)

    res = center[label.flatten()]

    # 配列の次元数と入力画像と同じに戻す
    return res.reshape((src.shape))


def anime_filter(img, K):
    # グレースケール変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)

    # ぼかしでノイズ低減
    edge = cv2.blur(gray, (3, 3))

    # Cannyアルゴリズムで輪郭抽出
    edge = cv2.Canny(edge, 50, 150, apertureSize=3) 

    # 輪郭画像をRGB色空間に変換
    edge = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)

    # 画像の領域分割
    img = sub_color(img, K)
    
    # 差分を返す
    return cv2.subtract(img, edge)


def main():

    # 入力画像の読み込み
    img = cv2.imread("input2.jpg")

    # 画像のアニメ絵化
    anime = anime_filter(img, 30)
    
    # 結果出力
    cv2.imwrite('anime.jpg', anime)

if __name__ == '__main__':
    main()

実行結果

■入力画像(左)と出力画像(右)

関連記事
PythonでOpenCV入門 サンプル集
【Python】画像処理プログラミング入門
【画像処理入門】アルゴリズム&プログラミング

シェア&フォローお願いします!