美颜算法之自动祛斑算法实现 | 案例分享
hello,大家好!今天给大家分享一个最近在做的一个项目,是基于美图的一个专利进行复现的。
希望今天的内容,能让你有所收获,同时值得你分享一波
专利链接:https://patentimages.storage.googleapis.com/21/09/cd/37e7c3a8bdcecf/CN103440633A.pdf
根据他人的思路复现是一项非常重要的能力,由于保密协议,不能将完整的代码分享出来,不过我会提供一些实现各个主要功能的代码,依照这些我想你一定也能够顺利的复现出来。
给你代码只能让你跑通一个案例,
教你解题思路能让你解决一类问题。
专利的主要思路如下:
# 一种数字图像自动祛除斑点的方法
1、灰度化
2、对比度增强
3、梯度极大值查找
4、皮肤排除
5、孤立点消除
6、高斯模糊
7、阈值处理
8、区域表求和得到最终结果 D
9、根据结果D 与梯度最大值查找的结果对图像A里的斑点进行泊松放出处理,得到自动祛斑的最终效果
这里对其进行了简化,简化成如下几个主要步骤:
1、sobel算子 ——> 找到斑点的可能区域
sobel算子的主要目的是为了检测边缘。
那什么是边缘呢?
图像边缘一般指图像的灰度变化率最大的位置。
具体的计算公式也很容易,这里就不展开说明了
关键代码:
src = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) # 转为灰度图
x_gray = cv2.Sobel(src, cv2.CV_32F, 1, 0)
y_gray = cv2.Sobel(src, cv2.CV_32F, 0, 1)
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)
注意事项:
进行对比度增强能在一定程度上提升最终的效果
对比度增强
# nResult = nColor + (nColor-128)*(1.0+Contrast)/255
# 实现:http://www.cppcns.com/jiaoben/python/218683.html
2、连通域分析——> 确定斑点的位置
听起来好像很复杂,但是实际上看下面的图,你就能明白它要做的是什么东西。
从上图可以发现,使用连通域分析能够找到米粒的位置,同样的,我们也可以将这个算法应用到确定斑点的位置。
但是由于,上面直接使用sobel算子计算边缘后,五官等区域也会被视作为一个连通域,如果直接作为后续的处理,那显然是不正确的。
作者是采用直方图颜色模型的统计信息来区分是否为皮肤区域,而我这里则是设置了一个面积的阈值来区别开斑点和其他区域。
关键代码:
# 注意:输入的是二值图,高斯滤波后效果更好
num_labels,labels,stats,centers = cv2.connectedComponentsWithStats(binary, connectivity=8,ltype=cv2.CV_32S)
# 参数说明:
num_labels: 代表连通域的数量,包含背景
labels : 记录img中每个位置对应的label
stats: 每个连通域的外接矩形和面积
x, y, w, h, area = stats[t]
centers : 连通域的质心坐标
依据连通域面积的大小做阈值分割的标准:
for t in range(1, num_labels, 1):
x, y, w, h, area = stats[t]
if area>100:
index = np.where(labels==t)
labels[index[0], index[1]] = 0
PS:通过遍历每个连通域,并根据连通域的面积(这里设置阈值100),将面积超过的连通域label设置为0,即为背景。
3、图像修复 inpaint ——> 去除斑点
OpenCV提供了两种算法。两者都可以通过相同的函数访问,cv2.inpaint()。
第一种算法基于Alexandru Telea于2004年发表的“基于快速行进方法的图像修复技术”。它基于快速行进方法。考虑图像中要修复的区域。算法从该区域的边界开始,然后进入区域内,逐渐填充边界中的所有内容。它需要在邻近的像素周围的一个小邻域进行修复。该像素由邻居中所有已知像素的归一化加权和代替。选择权重是一个重要的问题。对于靠近该点的那些像素,靠近边界的法线和位于边界轮廓上的像素,给予更多的权重。一旦像素被修复,它将使用快速行进方法移动到下一个最近的像素。FMM确保首先修复已知像素附近的像素,这样它就像手动启发式操作一样工作。使用标志cv2.INPAINT_TELEA启用此算法。
第二种算法基于Bertalmio,Marcelo,Andrea L. Bertozzi和Guillermo Sapiro于2001年撰写的“Navier-Stokes,流体动力学和图像和视频修补”一文。该算法基于流体动力学并利用偏微分方程。基本原则是heurisitic。它首先沿着已知区域的边缘行进到未知区域(因为边缘是连续的)。它继续等照片(连接具有相同强度的点的线,就像轮廓连接具有相同高度的点一样),同时在修复区域的边界处匹配渐变矢量。为此,使用来自流体动力学的一些方法。获得颜色后,填充颜色以减少该区域的最小差异。使用标志cv2.INPAINT_NS启用此算法。
# 方法 1:
dst_TELEA = cv2.inpaint(img,mask,3,cv2.INPAINT_TELEA)
# 方法2:
dst_NS = cv2.inpaint(img,mask,3,cv2.INPAINT_NS)
PS:这里的img是输入的原图,mask是上面连通域分析查找到的斑点位置。
查看官方的demo:
通过上述一系列的操作后,最终的祛斑效果如下:
是不是看上去还不错。锁骨上的斑点基本上都已经被清除干净了,同时增加了对比度显得皮肤更加有光泽。动手试一波吧!
整理完这篇文章已经十二点多了!
小伙伴,求分享,明天还要七点多起床上班,需要你的鼓励。
参考文献:
sobel算子:https://blog.csdn.net/qq_37124237/article/details/82183177
连通域分析:https://zhuanlan.zhihu.com/p/101371934
美图的专利:https://patentimages.storage.googleapis.com/21/09/cd/37e7c3a8bdcecf/CN103440633A.pdf
图像修补 inpaint:
https://blog.csdn.net/github_39611196/article/details/81252757
cv2.inpaint的原理:
https://www.cnblogs.com/lfri/p/10618417.html
最后分析一句话,与君共勉!
也许你想造火箭,但此刻建议你先学会拧螺丝
- Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式
- android常用接口(二)
- Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用
- RxAndroid完全教程
- 全能型反汇编引擎 – Capstone-Engine
- Hijack攻击揭秘
- 都在说微服务,那么微服务的反模式和陷阱是什么(二)
- Spring Boot 2.0 - WebFlux framework
- Spring Cloud构建微服务架构:服务网关(路由配置)【Dalston版】
- SpringCloud实战小贴士:Zuul的路径匹配
- 程序员你为什么这么累【续】:编码习惯之参数校验和国际化规范
- 程序员你为什么这么累【续】:编码习惯-函数编写建议
- 那些年,我们一起碰到过的骗局
- Spring Security (五) 动手实现一个IP_Login
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 简说中台
- JVM进阶
- MapperReduce常见错误及解决方案
- MapReduce案例之寻找共同好友
- JavaScript或ES6如何实现多继承总结【Mixin混合继承模式】
- Hadoop之MapReduce开发总结
- python之文件操作
- 血的教训!千万别在生产使用这些 redis 指令
- Python selenium chrome parses blob url
- 实战|仅用18行JavaScript构建一个倒数计时器
- 译|通过Node和Redis进行API速率限制
- EasySwoole实现Execl导入导出
- 设计模式:参与者风格
- vuepress集成element-ui
- 如何利用unli-app框架快速构建EasyRTC_SFU安卓app项目?