LoginSignup
12
8

More than 3 years have passed since last update.

写真表現としての桑原フィルターの提案

Last updated at Posted at 2020-06-15

はじめに

桑原フィルターとは
前記事で桑原フィルターについて解説していますのでご参照ください。

今回は

今回は前記事の最後にチラっと提案した、

"深度マップを用意してそれによって正方形領域のサイズを調整したら面白いかも…"

を実際にやってみたいと思います。

この発想は前回の、正方形領域の一辺が一定でフィルターをかけた画像に対して、メインの被写体がはっきりしない(どこにもピントが合っていない)状態だと、絵画というよりは背景に見えるという意見を元に得たものです。

実装

前回の記事に対して、コードの意味が理解できない(しづらい)という意見も見られたので、計算速度度外視の"そのまんま"コードで書きました。
とはいえ、Pythonにあまり大量のループをそのままさせるのは実行時間が大変なことになりますのでJITコンパイラに力をお借りしました。
Numba(JITを含むライブラリ)が入っていない人は、JITコンパイラライブラリNumbaを使ってPythonコードを劇的に高速化する方法などを参考にしてください。

Kuwahara_with_Depth.py
import matplotlib.pyplot as plt
import numpy as np
import cv2
from numba import jit


@jit
def mean(arr):
    h, w, c = arr.shape
    avg = np.array([0]*c)
    for i in range(h):
        for j in range(w):
            for k in range(c):
                avg[k] += arr[i, j, k]
    return avg/(h*w)


@jit
def var(arr, mean):
    h, w, c = arr.shape
    vari = 0
    for i in range(h):
        for j in range(w):
            for k in range(c):
                vari += (arr[i, j, k]-mean[k])**2
    return vari


@jit
def kuwahara_with_depth(pic, r, r_min, depth):
    h, w, c = pic.shape
    out = np.empty_like(pic)
    pic = np.pad(pic, ((r, r), (r, r), (0, 0)), "edge")
    depth = depth/depth.max()
    surr = ((1, 0), (0, 1), (1, 1))
    for i in range(h):
        for j in range(w):
            dr = int(depth[i, j]*(r-r_min))+r_min
            arr = pic[i+r-dr:i+r, j+r-dr:j+r]
            avg = mean(arr)
            var_min = var(arr, avg)
            color = avg
            for s, t in surr:
                arr = pic[i+r-(1-s)*dr:i+r+s*dr, j+r-(1-t)*dr:j+r+t*dr]
                avg = mean(arr)
                vari = var(arr, avg)
                if vari < var_min:
                    color = avg
                    var_min = vari
            out[i, j] = color
    return out


def main(picpath, r, r_min, rate, depthpath): #入力画像のパス、正方形領域の一辺の最大値、正方形領域の一辺の最小値、画像サイズの縮小率、深度マップのパス
    pic = np.array(plt.imread(picpath))
    pic = cv2.resize(pic, (int(pic.shape[1]*rate), int(pic.shape[0]*rate)))
    depth = cv2.resize(np.array(plt.imread(depthpath)[:, :, 0]), (pic.shape[1], pic.shape[0]))
    # depth=cv2.resize(np.rot90(np.array(plt.imread(depthpath))[:,:,0]),(pic.shape[1],pic.shape[0])) #深度マップの向きが元画像と合っていない時用
    fpic = kuwahara_with_depth(pic, r, r_min, depth).astype(pic.dtype)
    plt.imshow(fpic)
    # plt.imshow(np.rot90(fpic,3)) #出力画像が横を向いてしまっている時用
    plt.show()


picpath = "input_pic.jpg" #入力画像のパス
depthpath = "depthmap.jpg" #深度マップ画像のパス

if __name__ == "__main__":
    main(picpath, 20, 3, 0.5, depthpath)

結果

今回もフランスの写真から、フレンチネッコをサンプルに使用します。
上記までの説明では深度マップと表現していましたが、当然そんなデータはありませんので何らかの方法でそれらしい画像を作ります。今回はフォトショで適当に作りました。ちなみに白いほど正方形領域の一辺は大きくなります。
(一部の背景ぼかしが使えるスマホの写真ファイルには深度マップの情報が含まれているようです。Google Camera Deph map を使用したステレオ画像/多視点画像作成

これらを用いて今回のフィルターをかけたものがこちらです。是非クリックして拡大して見てみてください。

写真と絵が自然に融合しような、レンズのボケの代わりに絵の具でぼかしたような、そんな不思議な仕上がりです。

まとめ

サンプルで用いた写真でも、正方形領域の最大最小値で印象が大きく変わると思います。
上手く調節すれば主題と背景がしっかり描き分けられた絵画のようになるかもしれませんし、フォトジェニックが求められる昨今、タイトルで示したとおり一風変わった写真表現としても活用できると思います。
一度試してみてはいかがでしょうか。

12
8
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
12
8