Python + opencvで非写実的レンダリング試してみた


# はじめに
opencvのライブラリに用意されている非写実的レンダリング(ノンフォトリアリスティックレンダリング:以下NPR)を試してみました。
NPRは入力された画像や映像に対して手書きの絵画のような非写実的表現を目指します。下記で実際の変換結果を参考にイメージしてください。

こちらのサイトを参考に実装しました。
[OpenCV Non-Photorealistic Rendering](https://docs.opencv.org/master/df/dac/group__photo__render.html)
[Learn OpenCV](https://www.learnopencv.com/non-photorealistic-rendering-using-opencv-python-c/)
# 結果
先にNPRの実行結果を載せておきます。
入力は[こちら](https://www.pakutaso.com/20200336089post-26662.html)の雪桜

![httpswww.pakutaso.com20200336089post-26662.html.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135147/e1ee539d-d412-3f3d-1269-b27a1db002d1.jpeg)

NPR変換結果
※opencvのNPRは4種類あります。
## edgePreservingFilter
![edgePreservingFilter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135147/9dccaeb8-f552-908d-6a90-557ae0fa7859.png)

## detailEnhance
![detailEnhance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135147/28f268fa-779d-9092-65dc-82745259fde2.png)

## pencilSketch
![pencilSketch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135147/d3078180-5328-35cc-882b-9b800bec3fe4.png)

## stylization
![stylization.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135147/1b8be149-3bc0-7267-df73-306c3ae3ac83.png)

# 実装
以下、Pythonのコードです。

```NPR.py
import cv2
import argparse
from PIL import Image

def NPR(src):
    epf = cv2.edgePreservingFilter(src, flags=1, sigma_s=60, sigma_r=0.4)
    de = cv2.detailEnhance(src, sigma_s=10, sigma_r=0.15)
    pen_gray, pen_col = cv2.pencilSketch(src, sigma_s=60, sigma_r=0.1, shade_factor=0.03)
    style = cv2.stylization(src, sigma_s=60, sigma_r=0.07)
    return epf, de, pen_col, style

def webcam_or_pic2npr(out,is_webcam,pic):
    if is_webcam:
        cap = cv2.VideoCapture(0)

        while True:
            ret, frame = cap.read()
            e,d,p,s = NPR(frame)
            cv2.imshow('raw_input', frame)
            cv2.imshow('edgePreservingFilter',e)
            cv2.imshow('detailEnhance',d)
            cv2.imshow('pencilSketch',p)
            cv2.imshow('stylization',s)

            key = cv2.waitKey(1) & 0xFF
            if key == ord('q'):
                break
            elif key == ord('s'):
                cv2.imwrite(out,frame)
                cv2.imwrite(out,p)
        cap.release()
        cv2.destroyAllWindows()
    else:
        img = cv2.imread(pic, cv2.IMREAD_COLOR)
        e,d,p,s = NPR(img)
        cv2.imwrite(str(out)+'edgePreservingFilter.png',e)
        cv2.imwrite(str(out)+'detailEnhance.png',d)
        cv2.imwrite(str(out)+'pencilSketch.png',p)
        cv2.imwrite(str(out)+'stylization.png',s)

def main():
    parser = argparse.ArgumentParser(description='python+opencv_npr')
    parser.add_argument('--in_pic','-i',default='sample.png',help='input_picture_name')
    parser.add_argument('--out','-o',default='./',help='output_dir')
    parser.add_argument('--is_webcam',action='store_true',help='use webwebcam_or_pic2npr')
    args = parser.parse_args()
    webcam_or_pic2npr(args.out, args.is_webcam, args.in_pic)

if __name__ == "__main__":
    main()
```

## 実行コマンド(入力=画像)
```
python NPR.py -i picture_name -o output_dir 
```

## 実行コマンド(入力=webカメラ)
```
python NPR.py --is_webcam
```

# 所感
pythonとopencvで簡易に実装できます。
ライブラリ4種類のロジックの詳細は時間があれば追加記述します。
色々試していただくと画像が鮮鋭になったりボケたりしていることが分かるかと思います。

# まとめ
python + opencvで非写実的レンダリング4種類を試してみました。
ぜひ、手元の画像でお試しください。
ご一読ありがとうございました。
ソースコードの不正等、ご指摘あればよろしくお願いします。
LGTMも大歓迎です！