写真の人の顔に自動的にぼかしを入れる (OpenCV)
目標
たくさん写真があって他人が写っているとSNSにはあげられないので、ぼかしを入れることが多いと思いますが、たくさん写真があると作業がめんどくさいので、自動化スクリプトを作ります。
結果的に、opencvでの検出は思ったよりも上手くいかなかったので、retinanetで検出だけ行ってみました。長くなるので別の記事 RetinaNetで顔検出 - 情報関連の備忘録 で説明します。
ここではopencvだけの手順について触れます。
以下すべて
pythonで作業します。
opencvの準備は
GitHub - opencv/opencv: Open Source Computer Vision Library
とpip install opencv-python
で。
手順
- jpgで画像を読み込み
- OpenCVで顔を認識
- そのエリアにぼかしを入れる。適当に平滑化する
まずは1つの画像で手順を見ていきます。
1. jpgで画像を読み込み
import numpy as np import cv2 as cv img = cv.imread("sample.jpg")
opencvはBGRになっているので注意。 描画するときは以下のようにする必要がある。
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.show()
2. OpenCVで顔を認識
OpenCVに入っているカスケード分類機を使います。brew install opencv3
で入れたら/usr/local/opt/opencv/share/opencv4/haarcascades/
以下に入ってました。
face_cascade = "/usr/local/opt/opencv/share/opencv4/haarcascades/haarcascade_frontalface_default.xml" cascade = cv.CascadeClassifier(face_cascade) # RBGにする img = cv.cvtColor(img, cv.COLOR_BGR2RGB) facerect = cascade.detectMultiScale(img, scaleFactor=1.2, minNeighbors=2, minSize=(1, 1)) # show result print(facerect) if len(facerect) > 0: # 検出した場所すべてに赤色で枠を描画する for rect in facerect: cv.rectangle(img, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (0, 0, 255), thickness=3)
haarcascade_frontalcatface.xml
でdetectしましたが、ほとんど検出できてないです。
3. そのエリアにぼかしを入れる。適当に平滑化する
facerect
のエリアの中だけ平滑化してしまう。
平滑化フィルタにはいくつか種類があります。
- Average fileter
- Gaussian filter
- median filter
一番ぼかせそうな平均値の物を使っていきます。
facerect
のエリアだけ取ってきて、ぼかします。そのあと、元の画像のfacerect
のエリアにぼかしたものを代入します。
一応描画してどうなっているか確認します。
def bluring(img, x, y, width, height): tmp = img.copy() tmp[y:y + height, x:x + width] = cv.blur(tmp[y:y + height, x:x + width], (weight/3, height/3)) return tmp blured = img.copy() for x, y, w, h in facerect: blured = bluring(blured, x, y, w, h)
bluring
の処理を変えたら好きなようにぼかせます。
上のデータでは難しかったので、別のもので行ってみました。
完成したコード
import cv2 as cv import glob import re face_cascade = "/usr/local/opt/opencv/share/opencv4/haarcascades/haarcascade_frontalface_default.xml" cascade = cv.CascadeClassifier(face_cascade) def detect(img): """ RGBで入れる """ facerect = cascade.detectMultiScale(img, scaleFactor=1.2, minNeighbors=2, minSize=(1, 1)) return facerect def bluring(img, x, y, width, height): tmp = img.copy() tmp[y:y + height, x:x + width] = cv.blur(tmp[y:y + height, x:x + width], (weight/3, height/3)) return tmp def img2blured(img): facerect = detect(img) blured = img.copy() for x, y, w, h in facerect: blured = bluring(blured, x, y, w, h) return blured if __name__ == "__main__": jpgs = glob.glob("path/*.jpg") for jpg_path in jpbs: img = cv.imread(jpg_path) img = img2blured(cv.cvtColor(img, cv.COLOR_BGR2RGB)) cv.imwrite("blured_" + jpg_path, cv.cvtColor(img, cv.COLOR_RGB2BGR))
補足: RAW
データを直接扱う時
直接RAW
を扱うときはそれ用のモジュールを入れる。どこの会社のRAWか勝手に判定してくれるみたい。
rawpy · PyPI
brew install libraw
してから、pip install rawpy
でOK。
参考までに読み込みのコード
>>> import rawpy >>> raw = rawpy.imread("sample1.ARW") # SONY >>> type(raw) rawpy._rawpy.RawPy >>> raw.sizes ImageSizes(raw_height=4024, raw_width=6048, height=4024, width=6024, top_margin=0, left_margin=0, iheight=4024, iwidth=6024, pixel_aspect=1.0, flip=0) >>> # numpyに変換 >>> rgb = raw.postprocess(use_camera_wb=True) >>> # saving >>> cv.imwrite("sample1.jpg", cv.cvtColor(rgb, cv.COLOR_RGB2BGR))