效果图---请点击关注公众号,获取源码或exe文件
叶子头像制作工具:从“头”到“脚”帮你把照片变成漫画叶子!
“各位打工人、摸鱼人、秃头星人,早上好、中午好、晚上好!你是否厌倦了微信群里千篇一律的真人头像?是否羡慕别人用卡通叶子当头像,显得既神秘又环保?今天,本代码就像一位托尼老师,拿着剪刀、啫喱水、以及一堆 numpy 和 pillow,咔嚓两下把你的自拍变成一片会眨眼的漫画叶子!从此,你在群里发言自带‘光合作用’Buff,领导看了都说:‘这谁啊,这么绿!’”
项目全景鸟瞰图
leaf-avatar/│├─ 1️⃣ 配置中心(config.py) —— 所有魔法参数的仓库├─ 2️⃣ 人脸探测雷达(face_detector.py) —— 找到你那张脸├─ 3️⃣ 叶子素材军火库(leaf_bank.py) —— 100+ 款叶子随便挑├─ 4️⃣ 美颜滤镜工厂(filter_engine.py)—— 瘦脸、大眼、加高光├─ 5️⃣ 头像合成车间(composer.py) —— 把脸贴到叶子上├─ 6️⃣ 一键导出&社交分享(exporter.py) —— 生成 1080P 高清装逼图└─ 7️⃣ 主驾驶舱(main.py) —— 一键启动,懒人福音
下面,我们一层层拆给你看,每一层都像剥洋葱,保证辣到你的技术味蕾。
1️⃣ 配置中心:所有魔法的起点(config.py)
# config.pyfrom pathlib import PathclassConfig:# 路径配置 ROOT_DIR = Path(__file__).resolve().parent DATA_DIR = ROOT_DIR / "data" OUTPUT_DIR = ROOT_DIR / "output"# 模型配置 FACE_DETECTION_MODEL = "yunet"# 支持 yunet / retinaface FACE_DETECTION_THRESH = 0.85# 叶子素材池 LEAF_DIR = DATA_DIR / "leaf_png" LEAF_CANDIDATES = list(LEAF_DIR.glob("*.png"))# 导出规格 EXPORT_SIZE = (1080, 1080) # 1:1 正方形 EXPORT_QUALITY = 95
结构解析
| | |
|---|
| 用 pathlib 让 Windows、Mac、Linux 三端无痛兼容 | Path(__file__).resolve() |
| | yunet |
| | LEAF_CANDIDATES |
| | |
2️⃣ 人脸探测雷达:找到你那张脸(face_detector.py)
# face_detector.pyimport cv2from config import ConfigclassFaceDetector:def__init__(self): self.model = cv2.FaceDetectorYN.create( model=str(Config.ROOT_DIR / "models" / f"{Config.FACE_DETECTION_MODEL}.onnx"), config="", input_size=(320, 320), score_threshold=Config.FACE_DETECTION_THRESH )defdetect(self, bgr_img): self.model.setInputSize((bgr_img.shape[1], bgr_img.shape[0])) _, faces = self.model.detect(bgr_img)return faces if faces isnotNoneelse [] @staticmethoddefcrop_face(img, face): x, y, w, h = map(int, face[:4])return img[y:y+h, x:x+w]
结构解析
- 层 1:初始化层把 ONNX 模型喂给 OpenCV DNN,一行代码完成 GPU/CPU 自适应。
- 层 2:推理层
detect 返回 ndarray,形如 [[x, y, w, h, score, five_points...]]。 - 层 3:工具层
crop_face 提取人脸 ROI,给下游美颜模块打前站。
3️⃣ 叶子素材军火库:100+ 款叶子随便挑(leaf_bank.py)
# leaf_bank.pyimport randomfrom PIL import Imagefrom config import ConfigclassLeafBank:def__init__(self): self.leaf_paths = Config.LEAF_CANDIDATESdefrandom_leaf(self, size=(1080, 1080)) -> Image.Image: path = random.choice(self.leaf_paths) leaf = Image.open(path).convert("RGBA") leaf = leaf.resize(size, Image.LANCZOS)return leafdefseasonal_leaves(self, season="autumn"): mapping = {"spring": "*spring*","summer": "*green*","autumn": "*autumn*","winter": "*snow*" } pattern = mapping.get(season, "*")return [p for p in self.leaf_paths if pattern in p.name.lower()]
结构解析
- 层 1:随机层
random_leaf() 让程序像老虎机一样摇叶子,专治选择困难症。 - 层 2:季节层
seasonal_leaves() 通过通配符 * 过滤,春天看嫩芽,冬天看雪叶。 - 层 3:缓存层实际生产可加 LRU 缓存,避免每次打开 IO 拖慢体验。
4️⃣ 美颜滤镜工厂:瘦脸、大眼、加高光(filter_engine.py)
# filter_engine.pyimport cv2import numpy as npclassFilterEngine: @staticmethoddefskin_smooth(face_bgr, radius=10): blur = cv2.bilateralFilter(face_bgr, radius, 75, 75)return blur @staticmethoddefbig_eye(face_bgr, landmarks, scale=1.15):# 简版大眼:以双眼中心为圆心做径向缩放 le, re = landmarks[0], landmarks[1] center = ((le[0] + re[0])//2, (le[1] + re[1])//2) M = cv2.getRotationMatrix2D(center, 0, scale) warped = cv2.warpAffine(face_bgr, M, (face_bgr.shape[1], face_bgr.shape[0]))return warped @staticmethoddefadd_highlight(face_bgr):# 高光:在额头随机画两条白色半透明线条 h, w = face_bgr.shape[:2] overlay = face_bgr.copy() cv2.line(overlay, (w//3, h//5), (2*w//3, h//5), (255,255,255), 2) cv2.addWeighted(overlay, 0.5, face_bgr, 0.5, 0, face_bgr)return face_bgr
结构解析
- 层 1:磨皮层
bilateralFilter 保边缘、去瑕疵,美颜届的“热玛吉”。 - 层 2:大眼层用仿射变换矩阵
M 局部放大,防止全图炸裂。 - 层 3:高光层
addWeighted 做 α 混合,效果像 PS 的柔光图层。
5️⃣ 头像合成车间:把脸贴到叶子上(composer.py)
# composer.pyfrom PIL import Imageimport numpy as npclassComposer:def__init__(self, leaf_bank, detector, filter_engine): self.leaf_bank = leaf_bank self.detector = detector self.filter = filter_enginedefmake(self, user_img: np.ndarray, style="random") -> Image.Image:# 0️⃣ 选叶子 leaf = self.leaf_bank.random_leaf() if style == "random" \else Image.open(style).convert("RGBA")# 1️⃣ 探测人脸 faces = self.detector.detect(user_img)ifnot faces:raise ValueError("没检测到脸,建议换张正脸照或开美颜")# 2️⃣ 截取+美颜 face_crop = self.detector.crop_face(user_img, faces[0]) face_crop = self.filter.skin_smooth(face_crop) face_pil = Image.fromarray(cv2.cvtColor(face_crop, cv2.COLOR_BGR2RGBA))# 3️⃣ 计算粘贴坐标(居中偏下) lw, lh = leaf.size fw, fh = face_pil.size paste_x = (lw - fw) // 2 paste_y = lh // 3# 4️⃣ 贴脸 leaf.paste(face_pil, (paste_x, paste_y), face_pil)return leaf
结构解析
- 层 0:素材层动态选择叶子,支持
random 或用户自定义路径。 - 层 2:美颜层把 OpenCV 处理后的 ndarray 转回 PIL,RGBA 透明通道不丢失。
- 层 3:几何层通过简单比例计算,让脸长在叶子的“黄金分割点”。
- 层 4:合成层
paste(..., mask=face_pil) 利用透明通道完美抠脸。
6️⃣ 一键导出 & 社交分享(exporter.py)
# exporter.pyimport jsonfrom datetime import datetimefrom PIL import Imagefrom config import ConfigclassExporter:def__init__(self): Config.OUTPUT_DIR.mkdir(parents=True, exist_ok=True)defsave(self, img: Image.Image, user_id="Unknown"): ts = datetime.now().strftime("%Y%m%d_%H%M%S") filename = Config.OUTPUT_DIR / f"{user_id}_leaf_{ts}.jpg" img = img.convert("RGB") img.save(filename, quality=Config.EXPORT_QUALITY)# 生成分享 JSON meta = {"user": user_id,"created": str(datetime.now()),"file": str(filename.name) } (Config.OUTPUT_DIR / f"{user_id}_meta.json").write_text(json.dumps(meta, indent=2))return filename
结构解析
- 层 1:目录层
mkdir(parents=True) 保证第一次运行不报错。 - 层 2:命名层时间戳+用户 ID,避免多人同时生成互相覆盖。
- 层 3:压缩层
quality=95 让微信压缩后仍保留细节。 - 层 4:元数据层JSON 方便未来做个人中心“我的叶子”列表。
7️⃣ 主驾驶舱:懒人一键启动(main.py)
# main.pyimport cv2import argparsefrom composer import Composerfrom leaf_bank import LeafBankfrom face_detector import FaceDetectorfrom filter_engine import FilterEnginefrom exporter import Exporterdefmain(): parser = argparse.ArgumentParser(description="叶子头像一键生成器") parser.add_argument("-i", "--input", required=True, help="输入照片") parser.add_argument("-o", "--output", default=None, help="输出目录") parser.add_argument("--style", default="random", help="叶子风格路径或关键字") args = parser.parse_args()# 初始化 detector = FaceDetector() leaf_bank = LeafBank() filter_eng = FilterEngine() composer = Composer(leaf_bank, detector, filter_eng) exporter = Exporter()# 读取 img = cv2.imread(args.input)if img isNone:raise FileNotFoundError("图片打不开,确认路径正确")# 合成 avatar = composer.make(img, style=args.style)# 导出 out_path = exporter.save(avatar, user_id=args.input.stem) print(f"🎉 叶子头像已生成:{out_path}")if __name__ == "__main__": main()
使用示例
python main.py -i ./selfie.jpg --style autumn
十秒后,你的 output/selfie_leaf_20250826_143022.jpg 新鲜出炉,直接换头像,隔壁同事都问你要链接!
项目知识点大阅兵
终极目标:让“头像焦虑”成为过去式
- 个人玩家:30 秒生成一张独一无二的叶子头像,比 NFT 还稀缺。
- 社群运营:公众号后台集成,粉丝上传照片秒变“品牌定制叶子”。
- 商业延伸:生成情侣叶子、亲子叶子、节日限定叶子,付费下载美滋滋。
“代码跑起来,头像绿起来,人气涨起来!”
彩蛋:未来 3 个迭代方向
- Web 版:Gradio + FastAPI,浏览器上传即看即得。
- AI 风格化:接入 Stable Diffusion,让叶子长出赛博霓虹边。
“今天的你,对代码好一点;明天的代码,让你帅一点。”