以前からカフェやレストランブログを書いていましたが、写真をアップするたびに顔をモザイクするのがとても面倒でした。
ネイバーやティストリーでも自動モザイクをサポートしていますが、写真が30枚あると、そのすべてをクリックしていく必要があり、認識率もよくありませんでした。
そこで、ただ作ることにしました。
1. フォルダ構造
フォルダ構造は以下の通りです。
「masaic」というフォルダの中に、それぞれのカフェ別にフォルダと写真が入っています。
これらのパスを探索し、画像をモザイクします。
root
├─ face.py
└─ mosaic
├─ 力強いカンガルーカフェのレビュー
│ └─ 画像たち.png
└─ 弱いカンガルーカフェのレビュー
└─ 画像たち.jpg2. 学習モデル
ブログを運営するために顔を学習させるのは無理です。
また、誰かがすべての人の顔を学習させてくれました。
モデルをクリックでダウンロードしましょう。
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()サンプル画像とコードを実行すると、以下のような結果が得られます。

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])
댓글을 불러오는 중...