1

使用YOLO的自动面部马赛克

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

从以前开始就一直在写咖啡馆、美食博客,每次上传照片时都要对脸部进行马赛克处理,实在太麻烦了。

虽然在Naver和Tistory也有自动马赛克功能,但如果有30张照片,就要一一点击,而且识别率也不好。

所以我决定自己做一个。

1. 文件夹结构

文件夹结构如下。

在名为mosaic的文件夹内部,按照各个咖啡馆标识创建文件夹和放入照片。

在遍历这些路径时,会对图像进行马赛克处理。

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进行双for循环来检查内部文件。

仅在图像扩展名为图像文件时加载。

偶尔YOLO也会误判马赛克,所以也另外创建了备份文件夹。

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

관련 글

学校事务自动化——利用 AI 检查学生综合素质评价(生记簿)科目别“细部能力及特长事项”
学校事务自动化——利用 AI 检查学生综合素质评价(生记簿)科目别“细部能力及特长事项”
如果要在学校工作里选出一项最无意义、最辛苦、最无聊的,我会选生记簿检查。在初中,学生综合素质评价(生活记录簿)并不那么重要,但在高中它与升学直接相关,因此极其重要。问题在于,这样的生记簿检查最终找的无非就是简单的错别字、禁止填写用语、拼写等。这篇文章就是从这样的疑问开始的。现在这种简单检查,是不是已...
从零构建中学习 LLM 第7章读书心得与挑战回顾
从零构建中学习 LLM 第7章读书心得与挑战回顾
第7章的内容是让模型遵循指令进行微调的过程。也就是让它针对某个问题给出我们期望的回答。果然,最需要的还是数据。1. 指令微调步骤这里的核心是准备好问答数据集,用作输入-输出对来进行训练。这就叫做提示(prompt)风格。其他部分就像之前的流程一样,对内容进行分词(tokenize)、训练和评估,过程...
从零开始构建中学习 LLM 第 6 章读后感
从零开始构建中学习 LLM 第 6 章读后感
第 6 章是为分类进行微调。作为例子给出的任务是构建垃圾邮件分类器。垃圾邮件分类器需要判断一封邮件是不是垃圾邮件,因此输出结果要是类似 0、1 这样的值。1. 微调的顺序微调的过程和训练模型的过程很相似。准备数据集,加载权重值,然后进行训练和评估。稍微不同的一点是,会有一个把输出层映射到 0(非垃圾...
从零开始构建中学习 LLM 第5章读书后记
从零开始构建中学习 LLM 第5章读书后记
今天是12月14日。其实挑战期已经过去整整两周了,但也不能因此就放弃写后记。像这样留下的 TIL(Today I Learned),以后都会变成自己的血和肉。这次打算比起代码本身,更专注在“意义”上来写一写。1. 模型的损失计算这一部分讲的是,在构建好 GPT 模型之后,用什么方式来计算损失。GPT...
从零开始动手实现 LLM 第4章读书心得
从零开始动手实现 LLM 第4章读书心得
今天是11月26日,如果每天读一章并看完的话,这次挑战就算成功。在老大和老二的各种干扰下,不知道能不能做到。1. Dummy Transformer在实现 GPT 模型的过程中,看到是从 PyTorch 里拿来一个 Transformer 的 dummy 模块用的。一查才发现,在 PyTorch 的...
通过从头构建学习的LLM第3章读后感
通过从头构建学习的LLM第3章读后感
我在MacBook上泼了一大杯水后,崩溃了,浪费了大约3-4天。现在回想起来,反正MacBook已经坏了,应该想着送修,干点别的事情。无论如何,虽然有点晚了,但我觉得必须坚持到底,所以留下了第3章的读后感。1. 注意力机制第3章...

댓글을 불러오는 중...