毛孔识别

本文仅仅描述如何用opencv完成一个入门级别的毛孔识别,基于python3.7和 opencv 4.3

原图以及识别生成的效果图

一、首先引入需要的包,然后读取需要识别的图片

import cv2
import numpy as np
imageMat = cv2.imread("b.png")

二、选取B通道和均值滤波

选取通道是为了将图片转换为灰度图,以便后续的阀值处理,具体选取BGR哪个根据图片不同而不同;之后均值滤波以降噪,要在失真和去噪效果之间取得平衡,选取合适大小的卷积核

#选取blue通道
blueChannelMat = imageMat[:,:,0]
#均值滤波(滤波窗口越大图像失真越严重即更模糊)
blurMat = cv2.blur(blueChannelMat,(5,5))#5,5表示卷积核大小是5x5

三、阀值分割

阀值分割是将灰度图转换为二值图,这也是关乎识别质量很重要的一步,关键在于阀值大小的选取;图中大于阀值的像素会被设置为最大值(一般为255),小于或等于阀值的像素灰度值会被设置为0

otsu法是一种自动计算阀值的算法,通过如下代码可以获取阀值t和二值图

#动态阀值分割(otsu法)
t,thresMat = cv2.threshold(blurMat,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

但有时otsu的效果并不好,就比如现在这样,阀值明显是偏大了

这时就需要手动设置一个合适的阀值(根据阀值处理的效果不断调整到合适为止),并且将参数从THRESH_BINARY修改为THRESH_BINARY_INV即反向阀值处理,因为我们识别的对象是黑色的毛孔,像素值趋向0,阀值处理后像素会被设置为0,而反向阀值则把毛孔的像素设置为255,为什么这么做后面会说明

t,thresMat = cv2.threshold(blurMat,70,255,cv2.THRESH_BINARY_INV)

看到这里你应该能理解识别是怎么做到的了,其实就是因为黑色的毛孔像素值低,可以通过阀值分割出来,完成了对毛孔和皮肤的区分,图里的白色部分就是分割出的毛孔

四、腐蚀膨胀

腐蚀和膨胀简单是去除那些单独小的噪点和让毛孔边缘更平滑,这里不展开说明;根据图片和毛孔的大小调整卷积核的大小,毛孔小卷积核也要取小点,一般取3x3,5x5,7x7......

下面腐蚀膨胀的处理参考了论文: 《基于计算机视觉的皮肤毛孔识别及精准定位》 - 苏晓朋

#核为矩形5x5腐蚀
k1 = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))#生成形状为矩形5x5的卷积核
x0 = cv2.erode(thresMat, k1) #核为圆形9x9开运算
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(9,9))
x1 = cv2.morphologyEx(thresMat,cv2.MORPH_OPEN,kernel) #9x9椭圆核开闭运算
x1 = cv2.morphologyEx(x1,cv2.MORPH_OPEN,kernel)
x2 = cv2.morphologyEx(x1,cv2.MORPH_CLOSE,kernel)

腐蚀膨胀处理后的效果

五、标识毛孔

这里采用opencv的查找轮廓函数来查找连通区域(即图中的白色区域),然后通过计算每个轮廓的面积来大致计算毛孔面积。前面提到阀值分割时要使用反向阀值分割,是因为opencv查找轮廓是以白色区域为对象,就是只会查找白色区域的轮廓

#计算连通区域面积
#查找图像轮廓,参数分别表示只检测外轮廓,存储所有轮廓点
contours,hierarchy=cv2.findContours(x2,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) #计算轮廓面积
contoursArea=[]#存储面积的列表
contours_sel=[]#存储符合要求的轮廓
for i in range(len(contours)):
temp = cv2.contourArea(contours[i])
if temp>30:#筛选出面积大于30的轮廓
contoursArea.append(temp)
contours_sel.append(contours[i]) #绘制轮廓,第一个参数是你要绘制轮廓的图,第二个参数是使用的轮廓,
#第一个-1表示绘制所有轮廓,(0,255,0)是绘制使用的颜色,第二个-1表示填充轮廓内部
x2 = cv2.drawContours(imageMat,contours_sel,-1,(0,255,0),-1)
cv2.imshow("b",x2)#显示图像
cv2.waitKey()
cv2.destroyAllWindows()

下图是识别出的轮廓叠加在原图上的结果

六、如何生成mask图

一般我们用于训练神经网络使用的label图是mask图,当然这里的label图还要人工修正,这里只说说如何从查找到的轮廓生成mask图

#先生成一张纯白的图
mask = np.zeros(imageMat.shape,np.uint8)#生成和原图大小一致的黑色图片
t,mask = cv2.threshold(mask,0,255,cv2.THRESH_BINARY_INV)#通过阀值将其反色为白图
#在白图上绘制查找到的轮廓即可
mask = cv2.drawContours(mask,contours_sel,-1,(0,0,0),-1)
cv2.imshow('m',mask)

第一次写博客,经验不足请多多包涵,之后应该会更新如何使用Unet神经网络来分割出毛孔的文章

参考文献

[1] 林绵. 面部皮肤评测系统的设计与实现[D].华南理工大学,2016.

[2] 李立综. OpenCV轻松入门:面向Python[M].电子工业出版社,2019-05-01

[3] 苏晓朋. 基于计算机视觉的皮肤毛孔识别及精准定位[D].华北电力大学(北京),2018.

本文为博主原创文章,未经博主允许禁止转载

声明:本文同步发布于CSDN:https://blog.csdn.net/Misaki____Mei/article/details/107272751

opencv毛孔识别(python实现)的更多相关文章

  1. 人脸检测及识别python实现系列(6)——终篇:从实时视频流识别出“我”

    人脸检测及识别python实现系列(6)——终篇:从实时视频流识别出“我” 终于到了最后一步,激动时刻就要来临了,先平复一下心情,把剩下的代码加上,首先是为Model类增加一个预测函数: #识别人脸 ...

  2. 人脸检测及识别python实现系列(3)——为模型训练准备人脸数据

    人脸检测及识别python实现系列(3)——为模型训练准备人脸数据 机器学习最本质的地方就是基于海量数据统计的学习,说白了,机器学习其实就是在模拟人类儿童的学习行为.举一个简单的例子,成年人并没有主动 ...

  3. 人脸检测及识别python实现系列(1)——配置、获取实时视频流

    人脸检测及识别python实现系列(1)——配置.获取实时视频流 1. 前言 今天用多半天的时间把QQ空间里的几篇年前的旧文搬到了这里,算是完成了博客搬家.QQ空间里还剩下一些记录自己数学学习路线的学 ...

  4. OpenCV.物体识别

    1.度娘:“OpenCV 物体识别” 1.1.opencv实时识别指定物体 - 诺花雨的博客 - CSDN博客.html(https://blog.csdn.net/qq_27063119/artic ...

  5. opencv +数字识别

    现在很多场景需要使用的数字识别,比如银行卡识别,以及车牌识别等,在AI领域有很多图像识别算法,大多是居于opencv 或者谷歌开源的tesseract 识别. 由于公司业务需要,需要开发一个客户端程序 ...

  6. opencv人脸识别代码

    opencv人脸识别C++代码 /* * Copyright (c) 2011,2012. Philipp Wagner <bytefish[at]gmx[dot]de>. * Relea ...

  7. 人脸检测及识别python实现系列(2)——识别出人脸

    人脸检测及识别python实现系列(2)——识别出人脸 http://www.cnblogs.com/neo-T/p/6430583.html

  8. OpenCV人脸识别的原理 .

    OpenCV人脸识别的原理 . 在之前讲到的人脸测试后,提取出人脸来,并且保存下来,以供训练或识别是用,提取人脸的代码如下: void GetImageRect(IplImage* orgImage, ...

  9. 人脸检测及识别python实现系列(5)——利用keras库训练人脸识别模型

    人脸检测及识别python实现系列(5)——利用keras库训练人脸识别模型 经过前面稍显罗嗦的准备工作,现在,我们终于可以尝试训练我们自己的卷积神经网络模型了.CNN擅长图像处理,keras库的te ...

随机推荐

  1. 深入理解 nth-child 和 nth-of-type 的区别

    ele:nth-of-type(n)  为什么叫 of-type ,就是说它是以“type”来区分的,也就是说ele:nth-of-type(n)指的是父元素下第n个ele元素. ele:nth-ch ...

  2. IE11下文档模式默认值是7, 而且无法更改

    IE9以上是支持css3的,但是有的IE11的浏览器里面,文档模式默认值是7,而且是无法改变的,就会导致网页布局错乱 我的IE11的文档模式默认值是11 ,如下图  (打开页面按F12) 对于默认值是 ...

  3. 使用Vim写LaTeX代码(Vim+Vimtex+Skim)

    最近在写博客的时候发现对数学公式的支持并不好,于是就想寻找一个解决方案.我本身是一个爱折腾的人,有时尽管有现成的解决方案我有事也不愿意去用.于是多方查找资料,想寻求一个自定义的解决方案,最终把自己的目 ...

  4. rust 函数-生命周期

    记录一下自己理解的生命周期. 每个变量都有自己的生命周期. 在c++里生命周期好比作用域, 小的作用域的可以使用大作用域的变量. 如果把这里的每个作用域取个名,那么就相当于rust里的生命周期注解. ...

  5. 1.WebPack概念

    一.什么是WebPack 官方解释:本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler).当 webpack 处理应用程序时,它会递归地构 ...

  6. css样式学习笔记

    视频参见php中文网玉女心经视频教程 讲解的相当的清楚和明白 第1章     :css快速入门 1.1     什么是css 改变html框架的样式. 1.2     css的三种引入形式 第一种形式 ...

  7. disruptor架构二

    小故事:Disruptor说的是生产者和消费者的故事. 有一个数组.生产者往里面扔芝麻.消费者从里面捡芝麻. 但是扔芝麻和捡芝麻也要考虑速度的问题. 1 消费者捡的比扔的快 那么消费者要停下来.生产者 ...

  8. java集合--模拟斗地主发牌洗牌

    import java.util.*; /** * @Date: 2020/6/17 19:53 */public class Test04 { public static void main(Str ...

  9. 怎样用 I/O流读取txt文件?

    java.io包提供了用来永久保存对象状态的机制,可处理各种类型的流,如文件流.字节流.字符流等,还提供实现可串行化Serializable接口.可处理对象流. Java语言提供3种自动生成的标准流. ...

  10. JavaScript笔记- 函数声明和函数表达式(001)

    function 是 Javascript 中的第一类对象,这就意味着函数可以像其他值一样被传递.一个最常见的用法就是将一个匿名函数作为回调函数传递到另外一个异步函数中. 函数声明 ? 1 funct ...