YOLOを利用した自動顔モザイク

힘센캥거루
2025년 10월 9일(수정됨)
1
32
python

以前からカフェやレストランブログを書いていましたが、写真をアップするたびに顔をモザイクするのがとても面倒でした。 

ネイバーやティストリーでも自動モザイクをサポートしていますが、写真が30枚あると、そのすべてをクリックしていく必要があり、認識率もよくありませんでした。

そこで、ただ作ることにしました。

1. フォルダ構造

フォルダ構造は以下の通りです。

「masaic」というフォルダの中に、それぞれのカフェ別にフォルダと写真が入っています。

これらのパスを探索し、画像をモザイクします。

root 
├─ face.py
└─ mosaic
	├─ 力強いカンガルーカフェのレビュー
	│	└─ 画像たち.png
	└─ 弱いカンガルーカフェのレビュー
		└─ 画像たち.jpg

2. 学習モデル

ブログを運営するために顔を学習させるのは無理です。

また、誰かがすべての人の顔を学習させてくれました。

モデルをクリックでダウンロードしましょう。

3. 画像を読み込む

まずpathlibとopencvで画像を読み込むことから始めてみましょう。

問題はopencvが日本語のパスを認識できないことです。

これを関数にして解決してみましょう。

from pathlib import Path
import cv2

def imread(file, flags=cv2.IMREAD_COLOR, dtype=np.uint8):
    try:
        n = np.fromfile(file, dtype)
        img = cv2.imdecode(n, flags)
        return img
    except Exception as e:
        print(e)
        return None

def imwrite(file, img, params=None):
    try:
        suffix = Path(file).suffix
        result, n = cv2.imencode(suffix, img, params)

        if result:
            with open(file, mode='w+b') as f:
                n.tofile(f)
            return True
        else:
            return False
    except Exception as e:
        print(e)
        return False

そしてpathlibを使ってiterdirとis_dirを利用し、2重forループを回しながら内部ファイルを確認できます。

画像の拡張子が画像ファイルのときのみ読み込むようにしましょう。

稀にYOLOでもモザイクを間違えることがあるため、backupフォルダも別に作りました。

rootpath = Path.cwd()
blur_ratio = 100
folders = Path.cwd()
for folder in folders.iterdir():
    if folder.is_dir():
        backupFolder = folder / "backup"
        backupFolder.mkdir(exist_ok=True)
        for imgPath in folder.iterdir():
            if imgPath.suffix == ".png" or imgPath.suffix ==".jpg" or imgPath.suffix ==".JPG" or imgPath.suffix ==".jpeg" or imgPath.suffix ==".PNG":
                img = imread(str(imgPath))

4. 顔を検出する

GitHubに載っている顔検出コードは非常に単純です。

画像、学習モデルを読み込んだ後、model.predictで演算し、内部を巡回しながら顔の位置を呼び出します。

from pathlib import Path
import matplotlib.pyplot as plt
import cv2
import numpy as np
from ultralytics import YOLO

cwd = Path.cwd()
model = YOLO(cwd / 'yolov11n-face.pt')
picture = cv2.imread(cwd "/faces.jpg")
results = model.predict(picture)

# iterate detection results 
for model in results:
    img = np.copy(model.orig_img)
    para = model.boxes
    # iterate each object contour 
    for box in para:
        x1, y1, x2, y2 = box.xyxy[0]
        x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
        h, w = y2-y1, x2-x1
        picture = cv2.rectangle(picture, (x1,y1), (x2,y2), (255,0,0), 3)
picture = cv2.cvtColor(picture,cv2.COLOR_BGR2RGB)
plt.imshow(picture)
plt.show()

サンプル画像とコードを実行すると、以下のような結果が得られます。

YOLOを利用した自動顔モザイク-2

5. コードを完成させる

上記のコードで顔を見つけた後にボックスを描くのではなく、ブラー処理をすれば自動モザイクは完成です。

ブラー処理は、画像をcv2.blurでブラーした後、該当位置の画像をブラーされた画像で置換します。

以下が全体のコードです。

from pathlib import Path
import cv2
import numpy as np
from ultralytics import YOLO

def imread(file, flags=cv2.IMREAD_COLOR, dtype=np.uint8):
    try:
        n = np.fromfile(file, dtype)
        img = cv2.imdecode(n, flags)
        return img
    except Exception as e:
        print(e)
        return None

def imwrite(file, img, params=None):
    try:
        suffix = Path(file).suffix
        result, n = cv2.imencode(suffix, img, params)

        if result:
            with open(file, mode='w+b') as f:
                n.tofile(f)
            return True
        else:
            return False
    except Exception as e:
        print(e)
        return False

rootpath = Path.cwd()
model = YOLO(rootpath/'yolov11n-face.pt')
blur_ratio = 100
folders = Path(rootpath/"mosaic")
for folder in folders.iterdir():
    if folder.is_dir():
        backupFolder = folder / "backup"
        backupFolder.mkdir(exist_ok=True)
        for imgPath in folder.iterdir():
            if imgPath.suffix == ".png" or imgPath.suffix ==".jpg" or imgPath.suffix ==".JPG" or imgPath.suffix ==".jpeg" or imgPath.suffix ==".PNG":
                img = imread(str(imgPath))
                backupImgPath = backupFolder / f"{imgPath.stem}.webp"
                imwrite(str(backupImgPath), img, [cv2.IMWRITE_WEBP_QUALITY, 90])
                results = model.predict(img, show=False)
                boxes = results[0].boxes.xyxy.cpu().tolist()

                for box in boxes:
                    obj = img[int(box[1]):int(box[3]), int(box[0]):int(box[2])]
                    blur_obj = cv2.blur(obj, (blur_ratio, blur_ratio))
                    img[int(box[1]):int(box[3]), int(box[0]):int(box[2])] = blur_obj
                # iterate detection results 
                newImgPath = imgPath.parent / f"{imgPath.stem}.webp"
                imgPath.unlink()
                imwrite(str(newImgPath), img, [cv2.IMWRITE_WEBP_QUALITY, 90])

댓글을 불러오는 중...