Python, OpenCVで画像にモザイク処理(全面、一部、顔など)
Python, OpenCVを使って画像にモザイク処理を行う。
- 画像全体にモザイク処理
- 画像の一部をモザイク処理
- 顔検出して顔部分にモザイク処理
- 徐々にモザイクがかかるGIFアニメ作成
についてサンプルコードとともに説明する。
画像全体にモザイク処理
モザイク処理といっても複雑なアルゴリズムは必要なく、画像を一旦縮小してから拡大して元のサイズに戻すだけでOK。
以下のように実現できる。
import cv2
src = cv2.imread('data/src/lena.jpg')
def mosaic(src, ratio=0.1):
small = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
return cv2.resize(small, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)
dst_01 = mosaic(src)
cv2.imwrite('data/dst/opencv_mosaic_01.jpg', dst_01)
dst_005 = mosaic(src, ratio=0.05)
cv2.imwrite('data/dst/opencv_mosaic_005.jpg', dst_005)
画像を縮小する際のcv2.resize()
では、第二引数に縮小後のサイズを指定するのではなく、倍率を指定する引数fx
, fy
を使っている。第二引数は省略できないのでNone
を渡している。
拡大する際は第二引数に元の画像のサイズを渡す。
OpenCVの画像のサイズはshape
(NumPy配列ndarray
の属性)で(高さ, 幅[, 色数])
のかたちで取得できるが、cv2.resize()
の引数に渡すときは(幅、高さ)
である必要があるのでスライスを利用して変換している。
詳細は以下の記事を参照。
また、引数interpolation
でニアレストネイバー法cv2.INTER_NEAREST
を指定して、滑らかにリサイズしないようにしている。
結果は以下の通り。縮小率ratio
によってモザイクの粗さを制御できる。
ratio=0.1
の場合。
ratio=0.05
の場合。
画像の一部をモザイク処理
画像の一部だけにモザイクをかけたい場合は、スライスで領域を指定してその領域に上述のモザイク処理の関数を適用すればよい。
例ではモザイク領域の左上の点の座標と領域の幅と高さを指定するようにしている。
def mosaic_area(src, x, y, width, height, ratio=0.1):
dst = src.copy()
dst[y:y + height, x:x + width] = mosaic(dst[y:y + height, x:x + width], ratio)
return dst
dst_area = mosaic_area(src, 100, 50, 100, 150)
cv2.imwrite('data/dst/opencv_mosaic_area.jpg', dst_area)
結果は以下の通り。
顔検出して顔部分にモザイク処理
画像の一部領域にモザイク処理ができれば、顔検出と組み合わせて顔にモザイクをかけるのも簡単。
face_cascade_path = '/usr/local/opt/opencv/share/'\
'OpenCV/haarcascades/haarcascade_frontalface_default.xml'
face_cascade = cv2.CascadeClassifier(face_cascade_path)
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(src_gray)
for x, y, w, h in faces:
dst_face = mosaic_area(src, x, y, w, h)
cv2.imwrite('data/dst/opencv_mosaic_face.jpg', dst_face)
顔検出については以下の記事を参照。
徐々にモザイクがかかるGIFアニメ作成
画像処理ライブラリPillowを使うと複数の静止画からアニメーションGIF画像を作成できる。
モザイクの粗さを徐々に変化させた静止画のリストを生成し、GIFとして保存するサンプルコードは以下の通り。
import cv2
from PIL import Image
def mosaic(src, ratio=0.1):
small = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
return cv2.resize(small, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)
src = cv2.cvtColor(cv2.imread('data/src/lena.jpg'), cv2.COLOR_BGR2RGB)
imgs = [Image.fromarray(mosaic(src, 1 / i)) for i in range(1, 25)]
imgs += imgs[-2::-1] + [Image.fromarray(src)] * 5
imgs[0].save('data/temp/opencv_mosaic.gif',
save_all=True, append_images=imgs[1:], optimize=False, duration=50, loop=0)
OpenCVで読み込んだ画像はBGRの並びで、Pillowで保存するにはRGBの並びにする必要があるので注意。
画像のリストの作成にはリスト内包表記を使っている。自分で定義した関数mosaic()
の結果をImage.fromarray()
でNumPy配列ndarrayからPillow(PIL)のImage
に変換してリスト化。
- 関連記事: Pythonリスト内包表記の使い方
- 関連記事: Python, NumPyで画像処理(読み込み、演算、保存)
負数を使ったスライスで逆順のリストを作成し連結してエンドレスループするようにしている。-2
からスタートしているのは最終画像が2枚連続するのを防ぐため。さらに、モザイク無し画像を複数枚追加し、モザイクのない状態の間隔を長くしている。
結果は以下の通り。