【Google Colab】OpenCVで画像をペンシルスケッチ変換


はじめに

インスタグラムをはじめ、SNSで写真をシェアする方も多いのではないでしょうか。
その際、アプリを使用して画像を加工することもあるかと思います。
明るさや色味の調整、肌をキレイに見せるためのレタッチ、写真をスケッチ風に加工するなど、様々な機能があります。

今回は、OpenCVを用いて簡単な鉛筆画風の画像加工に挑戦してみました。

OpenCVで画像をペンシルスケッチ変換

環境

環境はGoogle Colaboratoryを使用します。
Pythonのバージョンは以下です。

import platform
print("python " + platform.python_version())
# python 3.6.9

画像の表示

では、早速コードを書いていきましょう。
まずは、画像の表示に必要なライブラリのインポートと設定を行います。

import cv2
import matplotlib.pyplot as plt
import matplotlib

%matplotlib inline
matplotlib.rcParams['image.cmap'] = 'gray'

サンプルの画像も用意しておきましょう。
今回は、Pixabayのフリー画像を使用します。

それでは、用意したサンプル画像を表示してみましょう。

image = cv2.imread(input_file) # input_fileは画像のパス

plt.figure(figsize=[10,10])
plt.axis('off')
plt.imshow(image[:,:,::-1])

結論

それではOpenCVを使用して、この画像を鉛筆画風に加工してみましょう。
元画像とあわせて表示してみました。

def pencilSketch(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    median = cv2.medianBlur(gray, 5)
    laplacian = cv2.Laplacian(median, cv2.CV_8U, ksize=5)
    _, thresh = cv2.threshold(laplacian, 100, 255, cv2.THRESH_BINARY_INV)
    pencilSketchImage = cv2.cvtColor(thresh, cv2.COLOR_GRAY2BGR)
    return pencilSketchImage

pencilSketchImage = pencilSketch(image)

plt.figure(figsize=[20,10])
plt.subplot(121);plt.imshow(image[:,:,::-1]);plt.axis('off')
plt.title("original image")
plt.subplot(122);plt.imshow(pencilSketchImage[:,:,::-1]);plt.axis('off')
plt.title("pencil sketch image")

鉛筆画風に加工する処理は、pencilSketch関数として定義しています。
pencilSketch関数の処理の流れは以下になります。

  • グレースケール
  • 平滑化(medianBlur)
  • 画像の微分(Laplacian)
  • 二値化

各処理の説明

以下、各処理について説明していきます。

グレースケール

グレースケール(Gray Scaleまたはgrayscale)とは、色の表現方法の一種です。
簡単に言うと、カラー画像を白黒画像にすることです。

グレースケールのコードは以下です。

image = cv2.imread(input_file) # 元画像の読み込み
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # グレースケール

plt.figure(figsize=[20,10])
plt.subplot(121);plt.axis('off');plt.imshow(image[:,:,::-1])
plt.subplot(122);plt.axis('off');plt.imshow(gray)

cv2.cvtColorを使用して、カラーからグレースケールに変換しています。
cv2.cvtColorの用法は以下です。

  • dst = cv2.cvtColor(src, code)
    • src:インプット画像
    • dst:アウトプット画像
    • code:色空間の変換方法

平滑化(medianBlur)

平滑化(へいかつか)、あるいはスムージング処理とは、簡単に言うと画像をぼかすことです。
画像をぼかすということは、画素値の変化を滑らかにすることとも言えます。
ノイズやエッジは、画素値の急激な変化です。
平滑化により、ノイズやエッジを消したり、目立たなくすることができます。

平滑化にはいくつかの方法が存在し、medianBlurはそのうちの一つです。
medianとは「中央値」のことで、指定したカーネル内に含まれる画素から中央値を取り出し、その値でカーネル全体を塗りつぶします。
※「カーネル」とは、着目する画像の領域ですが、難しければ「箱」のイメージで構いません。

平滑化についての詳細は、こちらを参照下さい。

medianBlurのコードは以下です。

median = cv2.medianBlur(gray, 5) # medianBlur

plt.figure(figsize=[20,10])
plt.subplot(121);plt.axis('off');plt.imshow(gray)
plt.subplot(122);plt.axis('off');plt.imshow(median)

画像がぼやけているのが確認できます。

cv2.medianBlurを使用して、グレースケールの画像をぼかしました。
cv2.medianBlurの用法は以下です。

  • dst = cv2.medianBlur(src, ksize)
    • src:インプット画像
    • dst:アウトプット画像
    • ksize:カーネルサイズ(ただし、3以上の奇数)

画像の微分(Laplacian)

画像の微分では、画素値の変化が急激な部分、つまりエッジを検出します。

画像の微分にはいくつかの方法が存在し、Laplacianはそのうちの一つです。
Laplacianフィルターは二回微分であり、一回微分のSobelフィルターより細かいエッジ検出をしたい場合に有効です。

画像の微分についての詳細は、こちらを参照下さい。

Laplacianのコードは以下です。

laplacian = cv2.Laplacian(median, cv2.CV_8U, ksize=5) # Laplacian

plt.figure(figsize=[20,10])
plt.subplot(121);plt.axis('off');plt.imshow(median)
plt.subplot(122);plt.axis('off');plt.imshow(laplacian)

エッジが検出されていることが確認できます。

cv2.Laplacianを使用して、画像を微分しました。
cv2.Laplacianの用法は以下です。

  • dst = cv2.Laplacian(src, ddepth, ksize)
    • src:インプット画像
    • dst:アウトプット画像
    • ddepth:アウトプット画像の型(ビット深度)
    • ksize:カーネルサイズ(奇数)

二値化

二値化とは、画像を白と黒の2つの値(二値)に変換する処理のことです。
白と黒の間も段階的に表示するグレースケールとは異なります。
閾値(しきいち、threshold)と呼ばれる値を決め、画素の値がそれより大きければ白、小さければ黒に変換します。

画像の二値化にはいくつかの方法が存在します。
今回は、一般的な二値化を行いました。

二値化についての詳細は、こちらを参照下さい。

二値化のコードは以下です。

_, thresh = cv2.threshold(laplacian, 100, 255, cv2.THRESH_BINARY_INV) # 二値化

plt.figure(figsize=[20,10])
plt.subplot(121);plt.axis('off');plt.imshow(laplacian)
plt.subplot(122);plt.axis('off');plt.imshow(thresh)

画素値が比較的高いものだけを残すことができました。

cv2.thresholdを使用して、画像を二値化しました。
cv2.thresholdの用法は以下です。

  • ret, dst = cv2.threshold(src, thresh, maxVal, method)
    • src:インプット画像(グレースケール)
    • ret:使用した閾値の値
    • dst:アウトプット画像
    • thresh:閾値
    • maxVal:最大値(閾値以上の値)
    • method:閾値処理
      • cv2.THRESH_BINARY
      • cv2.THRESH_BINARY_INV など

今回、閾値は100としています。
今回、戻り値の閾値(100)は使用しないので、_(アンダースコア)としています。

閾値処理では、cv2.THRESH_BINARY_INVを使用して白黒反転しています。
cv2.THRESH_BINARY_INVは、閾値(今回100)を超えるピクセルは0(黒)に、それ以外のピクセルはmaxVal(今回255)にする処理です。

まとめ

いかがだったでしょうか。

今回は、OpenCVを使用して鉛筆画風のスケッチに挑戦してみました。
処理のフローをおさらいしておきます。

  • グレースケール
  • 平滑化(medianBlur)
  • 画像の微分(Laplacian)
  • 二値化

閾値やカーネルサイズなどパラメーターを変更することで、線の細かさなどを変えることができます。
パラメーターを色々と変えて、画像を出力してみると面白いと思います。