美图祛斑算法实战——雀斑消除
本文主要根据这篇博文的思路,由于原博文没有放出完整代码,我这里尝试根据作者的思路实现人脸祛斑算法的代码完整版,本代码针对特定图像使用了一些trick,不是通用算法,仅做参考。
算法流程主要为:
- 灰度化
- 对比度增强
- 阈值处理
- 梯度极大值查找
- 连通域分析
- 皮肤排除
- 图像修复 inpaint
本代码用到的图片如下,从这篇博文直接下载得到,我们的目标是去除任务颈肩部位的祛斑。
导入用到的库
import cv2
import matplotlib.pyplot as plt
import numpy as np
import skimage
from skimage import morphology
from skimage.io import imread
读取图像,转为灰度图,然后进行对比度增强
img = cv2.imread('./face.png')
src = img.copy()
src = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) # 转为灰度图
con = skimage.exposure.adjust_sigmoid(src,0.4) # 0.4这个阈值就比较trick了。。。
使用sobel算子进行边缘检测
x_gray = cv2.Sobel(con, cv2.CV_32F, 1, 0, ksize=3)
y_gray = cv2.Sobel(con, cv2.CV_32F, 0, 1, ksize=3)
x_gray = cv2.convertScaleAbs(x_gray)
y_gray = cv2.convertScaleAbs(y_gray)
dst = cv2.add(x_gray, y_gray, dtype=cv2.CV_16S)
dst = cv2.convertScaleAbs(dst)
plt.imshow(dst)
可视化dst后发现边缘都已经被成功识别了,包括雀斑和人脸的边缘。接着对梯度做二值处理,选取一个阈值,大于阈值的设为255,小于阈值的设为0
a = dst.copy()
thres = 40
a[a>thres]=255
a[a<=thres]=0
plt.show(a)
到了这一步,雀斑的轮廓已经很清晰的展示在我们面前了,然而左边脸部有一些很多阴影也被提取进来了,要想办法去掉它们。
为了能用连通域去掉脸部的阴影,先用闭运算处理一下二值图,把阴影散点连成一片,在用连通域分析,去掉面积大于某个阈值的连通域
labels = ((morphology.closing(a, skimage.morphology.disk(7))>0)*255).astype(np.uint8)
num_labels,labels,stats,centers = cv2.connectedComponentsWithStats(labels, connectivity=4,ltype=cv2.CV_32S)
for t in range(1, num_labels, 1):
x, y, w, h, area = stats[t]
if area>300:
index = np.where(labels==t)
labels[index[0], index[1]] = 0
mask = (labels>0).astype(np.uint8)*255
plt.imshow(mask)
利用闭运算+连通域处理,成功把大部分人体边缘去除,留下了雀斑的mask,如下图
有时候祛斑比较大,轮廓中间有空洞,这里用一个函数填充雀斑轮廓的空洞,使它变成实心mask
def remove_holes_in_region(mask_scar):
cnts, _ = cv2.findContours(mask_scar.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for cnt in cnts:
cnt = cnt.transpose(1,0,2) # (1,N,2) N:points number
cv2.fillPoly(mask_scar, cnt, 1)
return mask_scar
mask = remove_holes_in_region(mask)
最后利用 cv2.inpaint 函数,输入原始图像img和我们得到的雀斑mask,修补图像
dst_TELEA = cv2.inpaint(img,mask,3,cv2.INPAINT_TELEA)
plt.imshow(dst)
效果还蛮不错的!大概现实了祛斑的功能
总结
不能说这个算法不好,可以说是完全没用
引用
注:如要转载,请联系作者