Pythonで水墨画を作ってみる（線画の応用）

みなさんこんにちは！

前回、[Pythonで線画を作ってみる](https://qiita.com/yam_dev/private/beadcc5b74451c8c0f24)という記事を書きました。

やってみたら面白かったので、それを応用して、水墨画風にしてみようと思います。

どこまで水墨画に近付けるか分かりませんが、やってみましょう:muscle:

環境構築などは[前回の記事](https://qiita.com/yam_dev/private/beadcc5b74451c8c0f24)を参照してください:bow:

# 要件
- 線は太め
- モノクロ
- 濃淡で表現する
- ちょっとぼかす

ぱっと思いつくのはこのくらいなので、この要件で進めてみます。

# 追加したコード
ぼかし、コントラスト、ハイライトを使って、水墨画っぽくしてます。
これが正解かは分かりませんが、色々と調整した結果、ここに落ち着きました。
```python
def ink_painting_effect(img):
    # グレースケールに変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # bilateralFilterを適用してぼかしを強化
    filtered = cv2.bilateralFilter(gray, 9, 200, 75)

    # CLAHEを適用してコントラストを強化
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(5, 5))
    contrast_enhanced = clahe.apply(filtered)

    # ハイライトを強調
    alpha = 1.3 # コントラスト
    beta = 40   # 明るさ
    highlighted = cv2.addWeighted(contrast_enhanced, alpha, np.zeros_like(contrast_enhanced), 0, beta)

    # エッジ検出
    edges = cv2.adaptiveThreshold(highlighted, 255,
                                  cv2.ADAPTIVE_THRESH_MEAN_C,
                                  cv2.THRESH_BINARY, 15, 7)

    # マスクを使用して元の画像とエッジを組み合わせる
    ink_painting = cv2.bitwise_and(highlighted, highlighted, mask=edges)

    return ink_painting
```

# コード全体
前回のコードも残してます。
```python:sample.py
import cv2
import numpy as np

def line_drawing_image(img):
    # 画像のコントラストを調整
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.equalizeHist(gray)

    # ガウシアンブラーを適用
    gray_blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    
    # エッジ検出で線画を生成
    edges = cv2.adaptiveThreshold(gray_blurred, 255,
                                  cv2.ADAPTIVE_THRESH_MEAN_C,
                                  cv2.THRESH_BINARY, 11, 7)
    
    return edges

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

    # バイラテラルフィルタを適用してぼかしを強化
    filtered = cv2.bilateralFilter(gray, 9, 200, 75)

    # CLAHEを適用してコントラストを強化
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(5, 5))
    contrast_enhanced = clahe.apply(filtered)

    # ハイライトを強調
    alpha = 1.3 # コントラスト
    beta = 40   # 明るさ
    highlighted = cv2.addWeighted(contrast_enhanced, alpha, np.zeros_like(contrast_enhanced), 0, beta)

    # エッジ検出（閾値を調整）
    edges = cv2.adaptiveThreshold(highlighted, 255,
                                  cv2.ADAPTIVE_THRESH_MEAN_C,
                                  cv2.THRESH_BINARY, 15, 7)

    # マスクを使用して元の画像とエッジを組み合わせる
    ink_painting = cv2.bitwise_and(highlighted, highlighted, mask=edges)

    return ink_painting

cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("カメラが開けません。")
    exit()

frame_count = 0
saved_images = 0

# カメラがオープンしている間はループ
while saved_images < 4:
    # フレームをキャプチャする
    ret, frame = cap.read()
    if not ret:
        print("フレームの取得に失敗しました。")
        break

    # 線画風の画像を生成
    # line_drawing = line_drawing_image(frame)
    # 水墨画風の画像を生成
    line_drawing = ink_painting_effect(frame)

    # 生成した画像を表示する
    cv2.imshow('Line Drawing Camera', line_drawing)

    # 10フレームごとに画像を保存
    if frame_count % 10 == 0:
        cv2.imwrite(f'line_drawing_{frame_count}.jpg', line_drawing)
        print(f'line_drawing_{frame_count}.jpg を保存しました。')
        saved_images += 1

    frame_count += 1

    # 'q'が押されたらループを抜ける
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# カメラをリリースしてウィンドウを閉じる
cap.release()
cv2.destroyAllWindows()
```

# 実行結果
さあ、結果はどうなったでしょうか。
生成された画像はこちらです！
![test2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/d84018af-8033-8f5a-0031-99543050749e.jpeg)
ちょっとそれっぽく出来ているのではないでしょうか。

## 前回との比較
せっかくなので、比較してみます。
### 前回（線画）
![test.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/340aec34-d8a9-721c-aebf-cfbcd5793c76.jpeg)

### 今回（水墨画）
![test2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/e214144f-3341-ce71-816c-33de5fa71ea2.jpeg)

もう少し調整しても良さそうですが、個人的には満足です！

# まとめ
前回に引き続き、Pythonで画像を生成してみました。
カメラの起動、画像データの取得と処理など、比較的簡単な実装で実現できました。

これをさらに応用すると、ボディカメラやドライブレコーダーと連携して動き回るだけで、様々な画像の学習ができそうですね。

他にも便利なライブラリがたくさんあるので、色々と使ってみて、スキルアップやサービスへの応用に役立てたいと思います:grinning: