Python實(shí)戰(zhàn):從源碼環(huán)境到參數(shù)調(diào)優(yōu))
簡(jiǎn)介面向圖像處理學(xué)習(xí)者和開(kāi)發(fā)者的GFPGAN老照片修復(fù)Python源碼工程以泛用性人臉先驗(yàn)引導(dǎo)修復(fù)網(wǎng)絡(luò)為核心結(jié)合GAN生成對(duì)抗機(jī)制增強(qiáng)人臉細(xì)節(jié)可用于老照片人像修復(fù)、畫(huà)面清晰度恢復(fù)等場(chǎng)景。壓縮包共51個(gè)文件、約6.09MB包含21個(gè)Python腳本、7個(gè)YAML配置文件、6個(gè)PNG與2個(gè)JPG樣例圖、2個(gè)Markdown文檔以及2個(gè)MDB數(shù)據(jù)庫(kù)文件等。Python腳本覆蓋推理、訓(xùn)練與網(wǎng)絡(luò)結(jié)構(gòu)實(shí)現(xiàn)涉及gfpganv1_clean_arch、stylegan2_clean_arch、arcface_arch等核心模塊YAML用于參數(shù)配置與數(shù)據(jù)集說(shuō)明圖片提供修復(fù)前后對(duì)比效果工程內(nèi)還附帶pth模型權(quán)重、VERSION版本文件與license許可說(shuō)明。項(xiàng)目另含CI工作流、代碼規(guī)范及PaperModel論文筆記目錄劃分清晰便于二次開(kāi)發(fā)與算法復(fù)現(xiàn)。已有491人學(xué)習(xí)瀏覽適合具備Python和深度學(xué)習(xí)基礎(chǔ)、想快速上手人臉修復(fù)算法的開(kāi)發(fā)者。1. 老照片修復(fù)為什么繞不開(kāi)GFPGAN從現(xiàn)實(shí)需求到源碼設(shè)計(jì)的起點(diǎn)如果你手頭有一批褪色、有折痕的人臉老照片又不滿足于美圖秀秀那種全局調(diào)色那基于GFPGAN算法的Python老照片修復(fù)設(shè)計(jì)源碼這條路值得認(rèn)真走一遍。GFPGANGenerative Facial Prior GAN是目前把“人臉先驗(yàn)”融入GAN修復(fù)的代表方案它專門(mén)解決人臉區(qū)域反復(fù)重繪出奇怪五官的痛點(diǎn)比起直接拿通用超分模型去修修復(fù)結(jié)果更接近“原來(lái)的那個(gè)人”。這篇文章我來(lái)拆一個(gè)可直接運(yùn)行的老照片修復(fù)項(xiàng)目源碼從環(huán)境配置、模型加載到參數(shù)調(diào)優(yōu)和排錯(cuò)都按我實(shí)際踩坑的經(jīng)驗(yàn)寫(xiě)。適合正在做圖像修復(fù)項(xiàng)目的Python開(kāi)發(fā)者、AI應(yīng)用工程師以及想自己動(dòng)手修復(fù)全家福老照片的玩家。2. 從零搭一個(gè)GFPGAN項(xiàng)目目錄設(shè)計(jì)與環(huán)境準(zhǔn)備2.1 項(xiàng)目目錄設(shè)計(jì)與文件職責(zé)老照片修復(fù)項(xiàng)目看著就一個(gè)推理腳本真做起來(lái)遠(yuǎn)不是“裝個(gè)庫(kù)、跑一段代碼”那么簡(jiǎn)單。模型權(quán)重放在哪、輸入輸出怎么組織、日志寫(xiě)到哪里、后續(xù)要不要接批量處理這些都得在動(dòng)手前定好。我一般會(huì)搭一個(gè)這樣的目錄old_photo_fix/ ├── main.py # 命令行入口支持文件或目錄輸入 ├── requirements.txt # 依賴清單 ├── weights/ │ └── GFPGANv1.4.pth # 預(yù)訓(xùn)練人臉修復(fù)模型 ├── inputs/ # 待修復(fù)的老照片 ├── outputs/ # 修復(fù)結(jié)果輸出目錄 ├── utils/ │ ├── __init__.py │ ├── io_utils.py # 圖片讀寫(xiě)、路徑校驗(yàn) │ └── face_align.py # 人臉檢測(cè)輔助可選 └── configs/ └── fix_config.yaml # 參數(shù)配置方便調(diào)參不動(dòng)代碼為什么要單獨(dú)建weights/和configs/因?yàn)?GFPGAN 的模型文件在 GitHub Release 和 Hugging Face 上都能找到但不同渠道下載的文件名可能帶后綴大小也可能不一致。固定放在weights/目錄并統(tǒng)一命名為GFPGANv1.4.pth代碼里就只需要維護(hù)一個(gè)路徑變量不會(huì)出現(xiàn)“測(cè)試時(shí)能跑、換臺(tái)機(jī)器就跑不了”的尷尬。configs/則把 upscale、strength 這類參數(shù)從代碼里剝離出去后面做參數(shù)掃描時(shí)不用反復(fù)改 main.py。2.2 Python 環(huán)境安裝與依賴要求GFPGAN 依賴 PyTorch 和它配套的人臉工具庫(kù)官方推理用的是gfpgan包 realesrgan做背景增強(qiáng)。我的環(huán)境踩過(guò)幾輪坑最省事的一組命令是conda create -n gfpgan python3.8 -y conda activate gfpgan pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install gfpgan realesrgan opencv-python第一行創(chuàng)建 Python 3.8 環(huán)境不是越新越好GFPGAN 的有些依賴特別是 facexlib在 Python 3.11 上偶爾會(huì)因?yàn)?setuptools 兼容問(wèn)題報(bào)錯(cuò)3.8 最穩(wěn)。第二行裝 PyTorchcu118是 CUDA 11.8 版本如果你的顯卡驅(qū)動(dòng)只支持 CUDA 10.2這里要改成cu102否則導(dǎo)入 torch 時(shí)會(huì)報(bào)libcusparse.so找不到。第三行裝人臉修復(fù)主力庫(kù)opencv-python建議裝 4.5.4 之后的新版本老版本配合gfpgan偶爾會(huì)出現(xiàn)在cv2.rectangle里畫(huà)不出框的詭異問(wèn)題。裝完后先別急著跑把facexlib也要確認(rèn)一下它是人臉檢測(cè)的后端依賴。gfpgan庫(kù)在 pip install 時(shí)通常會(huì)拉進(jìn)來(lái)但如果你用的是精簡(jiǎn)版 Python 鏡像可能缺少可以手動(dòng)補(bǔ)一條pip install facexlib。檢查環(huán)境用python -c import gfpgan; print(gfpgan.__version__)能正常打印就不是環(huán)境問(wèn)題。2.3 獲取預(yù)訓(xùn)練模型權(quán)重放置與校驗(yàn)GFPGAN 的模型文件是一個(gè)很大的.pth文件很多老照片修復(fù)教程都會(huì)讓你去下載但不告訴你下載完怎么確認(rèn)文件沒(méi)壞。我建議加一道校驗(yàn)邏輯避免修復(fù)到一半發(fā)現(xiàn)模型損壞白等幾分鐘。import os model_path weights/GFPGANv1.4.pth if not os.path.exists(model_path): raise FileNotFoundError( 模型缺失請(qǐng)把 GFPGANv1.4.pth 放到 weights/ 目錄 ) file_size os.path.getsize(model_path) if file_size 100 * 1024 * 1024: # 100MB 只是第一道門(mén)檻 raise ValueError( f模型文件異常大小只有 {file_size / 1024 / 1024:.1f}MB 很可能下載斷點(diǎn)完成請(qǐng)重新下載完整文件 ) print(f模型文件通過(guò)基礎(chǔ)校驗(yàn)大小為 {file_size / 1024 / 1024:.1f}MB)這段邏輯放在 main.py 的最前面比直接跑模型更實(shí)在。GFPGANv1.4.pth的實(shí)際體積在 300MB 以上如果只有幾 MB 或幾十 MB基本就是下載不完整。用文件大小做第一道校驗(yàn)好處是簡(jiǎn)單明了不用維護(hù)繁瑣的哈希表但對(duì)追求嚴(yán)謹(jǐn)?shù)墓こ袒?xiàng)目建議拿到文件后用官方提供的 SHA256 再算一遍這里就不展開(kāi)哈希腳本了。3. 核心推理代碼逐段拆解修復(fù)一張老照片的最小可運(yùn)行實(shí)現(xiàn)3.1 先寫(xiě) main.py解析參數(shù)與輸入校驗(yàn)老照片修復(fù)的推理鏈路很清晰讀圖 → 人臉檢測(cè) → GFPGAN 增強(qiáng)人臉 → 貼回原圖 → 保存。但每一環(huán)都有參數(shù)干擾命令行設(shè)計(jì)做不好調(diào)參就會(huì)變成反復(fù)改代碼。我的做法是把變動(dòng)最多的參數(shù)全部提到argparse里。import argparse def parse_args(): parser argparse.ArgumentParser(descriptionGFPGAN老照片修復(fù)) parser.add_argument(--input, typestr, requiredTrue, help輸入圖片路徑或包含多張圖片的目錄) parser.add_argument(--output, typestr, defaultoutputs, help修復(fù)結(jié)果存放目錄) parser.add_argument(--model_path, typestr, defaultweights/GFPGANv1.4.pth, helpGFPGAN權(quán)重路徑) parser.add_argument(--upscale, typefloat, default2, help放大倍率建議2老照片不宜過(guò)大) parser.add_argument(--strength, typefloat, default0.6, help修復(fù)強(qiáng)度0到1越高人臉重繪越激進(jìn)) parser.add_argument(--face_size, typeint, default512, help送入GFPGAN的人臉對(duì)齊尺寸) return parser.parse_args()這段代碼的用意是把“修復(fù)成什么樣”的控制權(quán)交給使用者。--input是必填參數(shù)支持單張圖片或整個(gè)目錄這一點(diǎn)直接決定了腳本能否批量處理一卷老照片。--upscale默認(rèn) 2 而不是 4是因?yàn)槔险掌旧矸直媛实?、噪聲多?qiáng)行放大 4 倍會(huì)讓背景區(qū)域的 JPEG 塊狀感更明顯。--face_size是很多人容易忽略的參數(shù)它控制檢測(cè)到的人臉區(qū)域會(huì)被放大到多大再送入網(wǎng)絡(luò)512 是 GFPGAN v1.4 的推薦值太小會(huì)丟失細(xì)節(jié)太大容易出現(xiàn)“看圖猜臉”的幻覺(jué)。有了參數(shù)解析main.py 的核心入口只需要四行解析參數(shù) → 校驗(yàn)?zāi)P?→ 初始化修復(fù)器 → 批量處理。我習(xí)慣把初始化單獨(dú)拆一個(gè)函數(shù)方便后面做 Web 接口時(shí)復(fù)用。3.2 初始化 GFPGAN 修復(fù)器關(guān)鍵參數(shù)的含義GFPGAN 的GFPGANer類是所有神經(jīng)網(wǎng)絡(luò)黑盒子的封裝點(diǎn)初始化參數(shù)決定了用哪一版模型、放大幾倍、要不要額外做背景增強(qiáng)。import torch from gfpgan import GFPGANer def init_restorer(args): if not torch.cuda.is_available(): print(警告當(dāng)前沒(méi)有可用GPUCPU推理速度會(huì)非常慢) restorer GFPGANer( model_pathargs.model_path, upscaleargs.upscale, archclean, # v1.4模型對(duì)應(yīng) clean 架構(gòu) channel_multiplier2, # 通道數(shù)系數(shù)v1.4固定為2 bg_upsamplerNone, # 背景增強(qiáng)器None表示不做 ) return restorer這里的archclean是最容易翻車的地方。GFPGAN 早期版本的模型用的是original架構(gòu)v1.4 之后官方把生成器改成clean架構(gòu)兩者的網(wǎng)絡(luò)層配置不一樣。你如果拿 v1.4 的權(quán)重去配archoriginal會(huì)直接報(bào)state_dict加載失敗。channel_multiplier也是同理v1.4 模型的通道系數(shù)是 2如果誤寫(xiě)成 1雖然能加載但輸出圖片會(huì)有一層灰蒙蒙的偽影。bg_upsamplerNone在本階段是最可控的選擇。它不增強(qiáng)背景部分只處理和貼回人臉區(qū)域避免把背景里的顆粒噪聲也放大。如果你想連背景一起修復(fù)后面會(huì)講到怎么接realesrgan但代價(jià)是單張圖片的耗時(shí)至少翻倍。3.3 修復(fù)單張圖片預(yù)處理與后處理如果把插件的所有花哨功能拋掉核心的單圖修復(fù)代碼就下面這些但它解決了一個(gè)非常隱蔽的問(wèn)題輸入輸出圖像的通道順序。import cv2 def fix_one(restorer, img_path, strength0.6): 修復(fù)單張老照片返回修復(fù)后的BGR圖像 img cv2.imread(img_path, cv2.IMREAD_COLOR) if img is None: raise ValueError(f無(wú)法讀取圖片: {img_path}) # 記錄原圖尺寸后面檢查修復(fù)結(jié)果尺寸是否有變化 h, w img.shape[:2] # enhance返回三個(gè)對(duì)象裁剪臉、恢復(fù)臉、最終貼回圖 _, _, restored restorer.enhance( img, has_alignedFalse, only_center_faceFalse, paste_backTrue, weightstrength, ) if restored is None: print(f注意{img_path} 未檢測(cè)到人臉?lè)祷卦瓐D) restored img return restoredenhance是 GFPGAN 的推理入口內(nèi)部的流程是先用 RetinaFace 檢測(cè)人臉把檢測(cè)到的人臉裁剪、對(duì)齊、縮放到模型輸入尺寸然后跑一次生成器得到修復(fù)后的人臉最后根據(jù)paste_back參數(shù)決定要不要把修復(fù)臉貼回原圖的對(duì)應(yīng)位置。這里的關(guān)鍵是weight參數(shù)它控制生成器的輸出占比相當(dāng)于一個(gè)人工“復(fù)原程度”旋鈕。has_alignedFalse表示輸入的是普通照片需要內(nèi)部重新檢測(cè)對(duì)齊如果你自己已經(jīng)裁剪過(guò)只有人臉區(qū)域的圖可以傳True跳過(guò)檢測(cè)速度更快但必須保證人臉已經(jīng)對(duì)齊且居中。paste_backTrue是必須開(kāi)的特別是在老照片中背景的折痕和噪點(diǎn)如果也被生成器處理會(huì)丟失大量原始質(zhì)感開(kāi)這個(gè)參數(shù)才能只換臉、留背景。注意返回值有三個(gè)我們只取第三個(gè)restored。前兩個(gè)分別是裁剪出的人臉和獨(dú)立修復(fù)后的人臉?biāo)鼈冇糜谡{(diào)試和人臉對(duì)比普通修復(fù)流程用不上。restored是完整的輸出圖像通道順序是 BGR直接用cv2.imwrite保存不會(huì)偏色。3.4 批量循環(huán)與文件保存實(shí)際拿回家整理老照片不可能一張一張敲命令。我一般會(huì)讓腳本自動(dòng)掃描目錄下所有圖片并給輸出文件加上參數(shù)后綴避免同一張照片用不同參數(shù)修了兩遍后互相覆蓋。import os def batch_fix(restorer, input_path, output_dir, strength): os.makedirs(output_dir, exist_okTrue) if os.path.isfile(input_path): files [input_path] else: files [ os.path.join(input_path, f) for f in os.listdir(input_path) if f.lower().endswith((.jpg, .jpeg, .png, .bmp)) ] if not files: raise ValueError(f輸入目錄中沒(méi)有找到圖片: {input_path}) for idx, file_path in enumerate(files): try: restored fix_one(restorer, file_path, strength) base os.path.splitext(os.path.basename(file_path))[0] out_name f{base}_gfpgan_s{strength}.png out_path os.path.join(output_dir, out_name) cv2.imwrite(out_path, restored) print(f[{idx 1}/{len(files)}] 完成: {out_path}) except Exception as e: print(f處理 {file_path} 出錯(cuò): {e})批量循環(huán)里最值得注意的有兩個(gè)點(diǎn)。第一輸出固定用 PNG 格式因?yàn)槔险掌迯?fù)后的圖片包含更多過(guò)渡色階JPG 壓縮會(huì)在邊緣引入新的振鈴噪聲PNG 是無(wú)損壓縮適合做中間結(jié)果。第二每個(gè)文件的處理都包了一層try/except這不是為了掩蓋錯(cuò)誤而是防止批量任務(wù)運(yùn)行到一半因?yàn)閱螐垞p壞圖片中斷導(dǎo)致后面前功盡棄。批處理最忌“單張失敗、全盤(pán)停擺”。4. 修復(fù)效果調(diào)優(yōu)的五個(gè)必調(diào)參數(shù)讓老照片從能用到可用4.1 upscale放大倍率與老照片分辨率的匹配--upscale控制最終輸出相對(duì)原圖放大的倍數(shù)默認(rèn) 2很多人一上來(lái)就拉到 4。如果你修的是幾百像素的證件照4 倍有意義但大部分老照片是掃描件分辨率少說(shuō)有兩三千像素再放大 4 倍不僅慢還會(huì)把背景里的紙張紋理放大成“磨砂濾鏡”。我的建議對(duì) 1500 像素以上的圖先用 2 倍看效果對(duì) 500 像素以下的縮略圖可以上 4 倍。調(diào)這個(gè)參數(shù)不需要重新訓(xùn)練模型直接改命令行參數(shù)即可。4.2 strength修復(fù)強(qiáng)度如何避免“網(wǎng)紅AI臉”strength是 enhance 函數(shù)里的weight值的范圍是 0 到 1。0 表示完全不用生成器只保留原圖1 表示完全用生成器重建人臉。很多老照片修復(fù)教程會(huì)默認(rèn) 0.5 到 0.8但現(xiàn)實(shí)中修自家掃描的老照片0.6 已經(jīng)會(huì)讓原來(lái)有特征的國(guó)字臉變成窄下巴的“AI 網(wǎng)紅臉”。# 參數(shù)掃描同一張圖讓strength在0.3~0.8之間出多張結(jié)果 for strength in [0.3, 0.5, 0.6, 0.8]: _, _, restored restorer.enhance( img, has_alignedFalse, only_center_faceFalse, paste_backTrue, weightstrength, ) cv2.imwrite(fretry_s{strength}.png, restored)這段掃描代碼是調(diào)參的“后悔藥”。把同一張照片用 4 個(gè)強(qiáng)度各跑一遍用照片查看器標(biāo)清序號(hào)肉眼對(duì)比哪張最像原圖里的親人。我自己的經(jīng)驗(yàn)是折痕和污點(diǎn)嚴(yán)重的圖0.8 能把污點(diǎn)徹底清掉但會(huì)丟掉膚質(zhì)紋理只想去噪、保真0.4 足夠。strength沒(méi)有最優(yōu)值它取決于你對(duì)“像不像本人”的容忍程度。4.3 face_size人臉對(duì)齊尺寸與細(xì)節(jié)量GFPGANer內(nèi)部會(huì)把檢測(cè)到的人臉區(qū)域裁剪后縮放到face_size × face_size再送入生成器。face_size越大生成器看到的細(xì)節(jié)越多但前提是裁剪出來(lái)的人臉區(qū)域本身清晰。老照片人臉通常只有兩三百像素硬放大到 1024 反而會(huì)讓生成器“腦補(bǔ)”出并不存在的皮膚毛孔出現(xiàn)磨皮過(guò)度的塑料感。用 512 就夠了這是 GFPGAN 官方推理腳本的默認(rèn)語(yǔ)義。如果你發(fā)現(xiàn)修復(fù)后眼睛經(jīng)常變成“熊貓眼”把face_size從 512 降到 384 往往能改善。4.4 bg_upsampler要不要開(kāi)啟背景增強(qiáng)bg_upsampler參數(shù)在初始化時(shí)傳入最常見(jiàn)的是傳入realesrgan的 RealESRGANer 實(shí)例。開(kāi)了之后背景區(qū)域也會(huì)被超分增強(qiáng)整張照片看起來(lái)更清晰。但這個(gè)參數(shù)有代價(jià)單張圖的推理時(shí)間從 2 秒變成 8 秒以上GPU 上而且對(duì)滿是噪聲的老照片背景超分算法會(huì)把噪點(diǎn)當(dāng)成細(xì)節(jié)來(lái)強(qiáng)化結(jié)果產(chǎn)生密密麻麻的偽細(xì)節(jié)。我一般只在人臉占畫(huà)面比例超過(guò)三分之一、且背景本身相對(duì)干凈的照片上開(kāi)bg_upsampler。如果只是想快速給全家人看效果保持None是更穩(wěn)的起點(diǎn)。4.5 only_center_face多人合影時(shí)的選擇與風(fēng)險(xiǎn)GFPGAN 默認(rèn)會(huì)檢測(cè)并修復(fù)照片里的所有人臉。但在多人合影里邊緣的人臉經(jīng)常被鼻子、手部遮擋檢測(cè)器會(huì)漏檢或誤檢反而留下一張沒(méi)處理的臉在旁邊對(duì)比感很差。把enhance的only_center_faceTrue打開(kāi)后只處理畫(huà)面中心最大的人臉適合單人照偏多的場(chǎng)景。python main.py --input inputs/1978_family.png --output outputs --strength 0.5 --upscale 2如果遇到多人合影建議先跑一次默認(rèn)參數(shù)然后看輸出圖里漏了哪張臉。漏臉時(shí)就只能用更精細(xì)的折疊方法先裁剪出每個(gè)單人臉區(qū)域分別修復(fù)再拼回去。這個(gè)方案確實(shí)麻煩但對(duì)大合照來(lái)說(shuō)比修正單個(gè)整圖參數(shù)更可靠。但注意only_center_faceTrue對(duì)只有一個(gè)人的老照片沒(méi)有任何副作用可以放心用。5. GFPGAN常見(jiàn)問(wèn)題排查四種最容易翻車的場(chǎng)景及對(duì)策5.1 現(xiàn)象修復(fù)后的人臉五官變形嚴(yán)重原因strength設(shè)得過(guò)高生成器把原圖人臉特征當(dāng)噪聲丟掉直接按照“平均臉”重建了一個(gè)新臉或者face_size設(shè)置過(guò)大導(dǎo)致人臉區(qū)域被過(guò)度放大后局部特征失真。解決先把strength降到 0.4 重新跑同一張圖觀察眼睛距離、顴骨輪廓是否接近原圖。如果還變形把face_size從默認(rèn) 512 改為 384。我在修 1970 年代黑白照片時(shí)發(fā)現(xiàn)這種照片的掃描圖人臉通常只有 200 到 300 像素512 的輸入其實(shí)是在強(qiáng)行補(bǔ)一個(gè)不存在的清晰度所以調(diào)低face_size反而更安全。5.2 現(xiàn)象輸出圖的顏色與原圖差異很大偏紅或偏青原因GFPGAN 內(nèi)部處理的是 BGR 圖像但在調(diào)試時(shí)很多人習(xí)慣用 matplotlib 或 PIL 顯示。如果你用 PIL 的Image.fromarray(restored)直接展示PIL 會(huì)默認(rèn)把 BGR 數(shù)組當(dāng)成 RGB于是人臉就偏藍(lán)。另外老照片本身有嚴(yán)重的泛黃調(diào)性修復(fù)后的皮膚色更粉嫩視覺(jué)上會(huì)覺(jué)得顏色“不對(duì)”。解決顯示時(shí)先做一次通道轉(zhuǎn)換cv2.cvtColor(restored, cv2.COLOR_BGR2RGB)。保存時(shí)不要轉(zhuǎn)直接用cv2.imwrite寫(xiě) BGR 原數(shù)組這樣得到的 PNG 用看圖軟件打開(kāi)顏色是正常的。如果確認(rèn)通道沒(méi)寫(xiě)錯(cuò)還是偏色那就是白平衡問(wèn)題可先用cv2.xphoto.createSimpleWB()對(duì)原圖做一次白平衡再送入修復(fù)但這屬于高級(jí)調(diào)參先用通道問(wèn)題排查。5.3 現(xiàn)象批量修復(fù)時(shí)內(nèi)存溢出或速度越來(lái)越慢原因輸入圖片分辨率過(guò)大。掃描儀導(dǎo)出的老照片動(dòng)輒 6000×9000 像素GFPGANer會(huì)先把整張圖讀進(jìn)內(nèi)存做人臉檢測(cè)批量任務(wù)中一張大圖就能占滿 8GB 顯存。如果開(kāi)了upscale4顯存占用更是成倍增長(zhǎng)。解決在送入enhance之前先把圖片最長(zhǎng)邊限制到 2000 像素修復(fù)完成后再用傳統(tǒng)插值放大到所需尺寸這樣模型只需處理一個(gè)合適的分辨率。MAX_EDGE 2000 img cv2.imread(path) h, w img.shape[:2] scale min(1.0, MAX_EDGE / max(h, w)) if scale 1.0: img cv2.resize(img, (int(w * scale), int(h * scale)), interpolationcv2.INTER_AREA)這段代碼里的INTER_AREA是下采樣時(shí)最常用的插值方式能減少摩爾紋。批量任務(wù)前先統(tǒng)計(jì)所有圖的最長(zhǎng)邊把超大圖統(tǒng)一壓到 2000 內(nèi)速度問(wèn)題基本就能解決。5.4 現(xiàn)象人臉沒(méi)有被檢測(cè)到代碼直接返回原圖原因一張老照片里人臉區(qū)域太小、太糊或者人臉是側(cè)臉、低頭姿勢(shì)。RetinaFace 檢測(cè)器對(duì)“正臉且高度大于 32 像素”的目標(biāo)比較敏感而為掃描壓縮后的老照片人臉寬度常常不足 50 像素檢測(cè)器直接漏過(guò)。解決不要急著調(diào) GFPGAN 參數(shù)先把原圖放大 2 倍再跑修復(fù)同時(shí)對(duì)側(cè)臉照片做一次 90° 旋轉(zhuǎn)再試。如果放大后仍檢測(cè)不到可以用facexlib底層的檢測(cè)器畫(huà)出人臉框確認(rèn)框的位置再?zèng)Q定是不是要把圖片局部裁剪后單獨(dú)修。6. 把修復(fù)從腳本變成工具Web調(diào)用與接口封裝的小技巧6.1 用 Flask 封裝一個(gè)修復(fù)接口如果只是自己修幾張照片命令行腳本已經(jīng)夠用。一旦要讓不懂 Python 的家人使用或者接入一個(gè)小程序后臺(tái)就得把它封裝成 HTTP 接口。我一般用 Flask 寫(xiě)一個(gè)最小可用的服務(wù)import io import numpy as np from flask import Flask, request, send_file app Flask(__name__) restorer None def init_global_restorer(model_path): global restorer if restorer is None: restorer init_restorer(model_path) return restorer app.route(/fix, methods[POST]) def fix_image(): photo request.files.get(photo) if photo is None: return {error: 缺少 photo 文件}, 400 img_bytes photo.read() img_array np.frombuffer(img_bytes, np.uint8) img cv2.imdecode(img_array, cv2.IMREAD_COLOR) _, _, restored restorer.enhance( img, has_alignedFalse, only_center_faceFalse, paste_backTrue, weight0.6, ) ok, buffer cv2.imencode(.png, restored) if not ok: return {error: 圖像編碼失敗}, 500 return send_file(io.BytesIO(buffer.tobytes()), mimetypeimage/png, download_namerestored.png)接口里的restorer必須用全局變量持有不要在每次請(qǐng)求時(shí)初始化。GFPGAN 的模型加載需要讀入 300MB 權(quán)重單次初始化要花 5 到 10 秒如果每個(gè)請(qǐng)求都重新加載一次服務(wù)根本沒(méi)法用。cv2.imdecode能把上傳的字節(jié)流直接解碼成圖像數(shù)組比先保存臨時(shí)文件再讀取要干凈得多。實(shí)際部署時(shí)建議在服務(wù)啟動(dòng)前先調(diào)用一次init_global_restorer做預(yù)熱再對(duì)外提供服務(wù)。6.2 兩個(gè)驗(yàn)證技巧先看局部貼臉再算定量指標(biāo)封裝完成后不能只靠用戶說(shuō)“好像變年輕了”來(lái)驗(yàn)收。老照片修復(fù)沒(méi)有真實(shí)的高清參考圖所以我的習(xí)慣是先做局部驗(yàn)證用facexlib檢測(cè)出人臉框把修復(fù)前后的兩張圖的同一區(qū)域放大到 200%對(duì)比眼角紋理和牙齒輪廓。如果人臉輪廓與原圖一對(duì)比差太多說(shuō)明strength還是太高。定量方面可以算 PSNR但因?yàn)樘幚砗驮瓐D不是同一分辨率直接算 PSNR 沒(méi)有參考意義。更實(shí)用的是把修復(fù)結(jié)果重新壓縮成 JPG再和原圖做結(jié)構(gòu)相似度 SSIM雖然不能用來(lái)證明“修得好”至少能衡量本次修復(fù)是否保留了原始構(gòu)圖結(jié)構(gòu)。我自己在項(xiàng)目里加了一個(gè)--compare參數(shù)輸出修復(fù)圖和原圖的 SSIM 值低于 0.6 就提示人工復(fù)核。6.3 保留 EXIF 與時(shí)間戳給修復(fù)留一份后悔藥最后一個(gè)小技巧老照片掃描件可能帶了拍攝時(shí)間和掃描參數(shù)等 EXIF 信息而 GFPGAN 輸出圖像不會(huì)自動(dòng)保留。我建議在保存時(shí)從原圖讀取 EXIF 寫(xiě)入輸出文件或者至少把原始文件名、修復(fù)參數(shù)寫(xiě)進(jìn)一個(gè)manifest.csv。這樣三個(gè)月后想換一套參數(shù)重新修復(fù)還能知道上次用的是什么strength不用靠記憶猜。我在一次給客戶做批量老照片修復(fù)時(shí)就是因?yàn)橥擞涗泤?shù)發(fā)現(xiàn)同一張圖被兩個(gè)批次用不同強(qiáng)度處理交給客戶時(shí)新老照片分不清最后只好重跑全部批次。從那以后每張輸出圖的名字里都帶上了s和up參數(shù)這也成了我的固定習(xí)慣。這個(gè)方案并不復(fù)雜但它能讓你以后翻看結(jié)果時(shí)少走很多彎路。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取