Auto Mosaico de Rostros Usando YOLO

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

He estado escribiendo blogs sobre cafés y restaurantes durante mucho tiempo, pero me resultaba muy tedioso poner mosaico a las caras cada vez que subía fotos. 

Naver y Tistory también ofrecen soporte para mosaicos automáticos, pero si hay 30 fotos, tienes que presionar cada una y la tasa de reconocimiento no era buena.

Así que decidí hacerlo por mi cuenta.

1. Estructura de Carpetas

La estructura de carpetas es la siguiente.

Dentro de la carpeta llamada masaic hay carpetas y fotos separadas por cada café.

Se exploran estas rutas para aplicar el mosaico a las imágenes.

root 
├─ face.py
└─ mosaic
	├─ Revisión Cafetería Fuerte Canguro
	│	└─ imagenes.png
	└─ Revisión Cafetería Débil Canguro
		└─ imagenes.jpg

2. Modelo de Aprendizaje

No tiene sentido entrenar un modelo de reconocimiento facial solo para un blog.

Afortunadamente, alguien generoso ya ha entrenado un modelo para el reconocimiento facial.

Podemos descargar el modelo con un clic.

3. Cargar Imágenes

Primero, intentemos cargar imágenes con pathlib y opencv.

El problema es que opencv no reconoce rutas con caracteres en coreano.

Podemos resolver esto creando una función.

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

Y usando iterdir e is_dir con pathlib, podemos revisar los archivos internos con un bucle for anidado.

Solo carguemos archivos de imagen cuando la extensión sea de un archivo de imagen.

Como a veces YOLO también se equivoca al hacer el mosaico, también creamos una carpeta de respaldo.

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. Detección de Rostros

El código de detección de rostros disponible en GitHub es muy sencillo.

Cargamos la imagen y el modelo de aprendizaje, luego calculamos con model.predict y recorremos el interior para recuperar la posición del rostro.

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()

Al ejecutar la imagen de ejemplo y el código, podemos obtener los siguientes resultados.

Auto Mosaico de Rostros Usando YOLO-2

5. Finalizando el Código

Después de encontrar rostros con el código anterior, en lugar de dibujar un cuadro, haciendo un desenfoque, el mosaico automático se completa.

El desenfoque se realiza desenfocando la imagen con cv2.blur y reemplazando la imagen en esa ubicación con la desenfocada.

A continuación se muestra el código completo.

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])

댓글을 불러오는 중...