我们在进行图像分割后,分割结果有时会有一些小孔洞,如图1所示,其中黑白两色表示两种不同的类别。一般情况下,这些孔洞属于错分情况,为了优化结果,我们通常对这些孔洞进行填充。今天我们就用python语言基于OpenCV实现孔洞填充。图1 有孔洞的分割图像

我们要用到的函数是OpenCV里的floodFill函数。使用floodFill函数可以得到只标记孔洞的像素矩阵(孔洞值为0,非孔洞值为指定值)。有了孔洞的位置填充孔洞就容易多了。python代码如下。

import cv2

import numpy as np

'''图像说明:图像为二值化图像,255白色为目标物,0黑色为背景要填充白色目标物中的黑色空洞'''

def FillHole(imgPath,SavePath):

im_in = cv2.imread(imgPath, cv2.IMREAD_GRAYSCALE);

cv2.imwrite("im_in.png",im_in)

# 复制 im_in 图像

im_floodfill = im_in.copy()

# Mask 用于 floodFill,官方要求长宽+2

h, w = im_in.shape[:2]

mask = np.zeros((h+2, w+2), np.uint8)

# floodFill函数中的seedPoint对应像素必须是背景

isbreak = False

for i in range(im_floodfill.shape[0]):

for j in range(im_floodfill.shape[1]):

if(im_floodfill[i][j]==0):

seedPoint=(i,j)

isbreak = True

break

if(isbreak):

break

# 得到im_floodfill 255填充非孔洞值

cv2.floodFill(im_floodfill, mask,seedPoint, 255)

# 得到im_floodfill的逆im_floodfill_inv

im_floodfill_inv = cv2.bitwise_not(im_floodfill)

# 把im_in、im_floodfill_inv这两幅图像结合起来得到前景

im_out = im_in | im_floodfill_inv

# 保存结果

cv2.imwrite(SavePath, im_out)

填充结果如图2所示,三个孔洞均被填充。图2 填充结果

现在又有一个问题,就是说如果我们的多类分割呢,就像图3所示。我们可以利用for循环分颜色进行单类别填充孔洞,然后进行合并。python代码如下。图3 有孔洞的多类图像分割

import cv2;

import numpy as np;

def FillHole_RGB(imgPath,SavePath):

# 读取图像为uint32,之所以选择uint32是因为下面转为0xbbggrr不溢出

im_in_rgb = cv2.imread(imgPath).astype(np.uint32)

# 将im_in_rgb的RGB颜色转换为 0xbbggrr

im_in_lbl = im_in_rgb[:,:,0] + (im_in_rgb[:,:,1] << 8) + (im_in_rgb[:,:,2] << 16)

# 将0xbbggrr颜色转换为0,1,2,...

colors, im_in_lbl_new = np.unique(im_in_lbl, return_inverse=True)

# 将im_in_lbl_new数组reshape为2维

im_in_lbl_new = np.reshape(im_in_lbl_new,im_in_lbl.shape)

# 创建从32位im_in_lbl_new到8位colorize颜色的映射

colorize = np.empty((len(colors), 3), np.uint8)

colorize[:,0] = (colors & 0x0000FF)

colorize[:,1] = (colors & 0x00FF00) >> 8

colorize[:,2] = (colors & 0xFF0000) >> 16

# 输出一下colorize中的color

print("Colors_RGB: \n",colorize)

# 有几种颜色就设置几层数组,每层数组均为各种颜色的二值化数组

im_result = np.zeros((len(colors),)+im_in_lbl_new.shape,np.uint8)

# 初始化二值数组

im_th = np.zeros(im_in_lbl_new.shape,np.uint8)

for i in range(len(colors)):

for j in range(im_th.shape[0]):

for k in range(im_th.shape[1]):

if(im_in_lbl_new[j][k]==i):

im_th[j][k] = 255

else:

im_th[j][k] = 0

# 复制 im_in 图像

im_floodfill = im_th.copy()

# Mask 用于 floodFill,mask多出来的2可以保证扫描的边界上的像素都会被处理.

h, w = im_th.shape[:2]

mask = np.zeros((h+2, w+2), np.uint8)

isbreak = False

for m in range(im_floodfill.shape[0]):

for n in range(im_floodfill.shape[1]):

if(im_floodfill[m][n]==0):

seedPoint=(m,n)

isbreak = True

break

if(isbreak):

break

# 得到im_floodfill

cv2.floodFill(im_floodfill, mask, seedPoint, 255,4)

# help(cv2.floodFill.rect)

# 得到im_floodfill的逆im_floodfill_inv

im_floodfill_inv = cv2.bitwise_not(im_floodfill)

# 把im_in、im_floodfill_inv这两幅图像结合起来得到前景

im_out = im_th | im_floodfill_inv

im_result[i] = im_out

# rgb结果图像

im_fillhole = np.zeros((im_in_lbl_new.shape[0],im_in_lbl_new.shape[1],3),np.uint8)

# 之前的颜色映射起到了作用

for i in range(im_result.shape[1]):

for j in range(im_result.shape[2]):

for k in range(im_result.shape[0]):

if(im_result[k][i][j] == 255):

im_fillhole[i][j] = colorize[k]

break

# 保存图像

cv2.imwrite(SavePath, im_fillhole)

填充结果如图4所示,所有孔洞均被填充。图4 填充结果

现在问题又来了,如果我只想填充特定大小阈值以内的孔洞,不填充大空洞怎么办呢?OpenCV里有个函数findContours可以获取每一个孔洞的轮廓,然后用contourArea函数计算每个孔洞的面积,如果面积大于阈值就不填充。代码如下。

import cv2;

import numpy as np;

def FillHole_RGB(imgPath,SavePath,SizeThreshold):

# 读取图像为uint32,之所以选择uint32是因为下面转为0xbbggrr不溢出

im_in_rgb = cv2.imread(imgPath).astype(np.uint32)

# 将im_in_rgb的RGB颜色转换为 0xbbggrr

im_in_lbl = im_in_rgb[:,:,0] + (im_in_rgb[:,:,1] << 8) + (im_in_rgb[:,:,2] << 16)

# 将0xbbggrr颜色转换为0,1,2,...

colors, im_in_lbl_new = np.unique(im_in_lbl, return_inverse=True)

# 将im_in_lbl_new数组reshape为2维

im_in_lbl_new = np.reshape(im_in_lbl_new,im_in_lbl.shape)

# 创建从32位im_in_lbl_new到8位colorize颜色的映射

colorize = np.empty((len(colors), 3), np.uint8)

colorize[:,0] = (colors & 0x0000FF)

colorize[:,1] = (colors & 0x00FF00) >> 8

colorize[:,2] = (colors & 0xFF0000) >> 16

# 输出一下colorize中的color

print("Colors_RGB: \n",colorize)

# 有几种颜色就设置几层数组,每层数组均为各种颜色的二值化数组

im_result = np.zeros((len(colors),)+im_in_lbl_new.shape,np.uint8)

# 初始化二值数组

im_th = np.zeros(im_in_lbl_new.shape,np.uint8)

for i in range(len(colors)):

for j in range(im_th.shape[0]):

for k in range(im_th.shape[1]):

if(im_in_lbl_new[j][k]==i):

im_th[j][k] = 255

else:

im_th[j][k] = 0

# 复制 im_in 图像

im_floodfill = im_th.copy()

# Mask 用于 floodFill,mask多出来的2可以保证扫描的边界上的像素都会被处理.

h, w = im_th.shape[:2]

mask = np.zeros((h+2, w+2), np.uint8)

isbreak = False

for m in range(im_floodfill.shape[0]):

for n in range(im_floodfill.shape[1]):

if(im_floodfill[m][n]==0):

seedPoint=(m,n)

isbreak = True

break

if(isbreak):

break

# 得到im_floodfill

cv2.floodFill(im_floodfill, mask, seedPoint, 255,4)

# 得到im_floodfill的逆im_floodfill_inv,im_floodfill_inv包含所有孔洞

im_floodfill_inv = cv2.bitwise_not(im_floodfill)

# 之所以复制一份im_floodfill_inv是因为函数findContours会改变im_floodfill_inv_copy

im_floodfill_inv_copy = im_floodfill_inv.copy()

# 函数findContours获取轮廓

contours, hierarchy = cv2.findContours(im_floodfill_inv_copy, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

for num in range(len(contours)):

if(cv2.contourArea(contours[num])>=SizeThreshold):

cv2.fillConvexPoly(im_floodfill_inv, contours[num], 0)

# 把im_in、im_floodfill_inv这两幅图像结合起来得到前景

im_out = im_th | im_floodfill_inv

im_result[i] = im_out

# rgb结果图像

im_fillhole = np.zeros((im_in_lbl_new.shape[0],im_in_lbl_new.shape[1],3),np.uint8)

# 之前的颜色映射起到了作用

for i in range(im_result.shape[1]):

for j in range(im_result.shape[2]):

for k in range(im_result.shape[0]):

if(im_result[k][i][j] == 255):

im_fillhole[i][j] = colorize[k]

break

# 保存图像

cv2.imwrite(SavePath, im_fillhole)

填充结果如图5所示,所有阈值大小以内孔洞均被填充。图5 有阈值的填充结果

目前以上代码测试没有问题,如有问题请留言,我们共同交流。

【后言】

python实现全连接CRFs后处理文字:馨意:【图像后处理】python实现全连接CRFs后处理​zhuanlan.zhihu.com

python颜色填充函数_【图像后处理】python+OpenCV填充孔洞相关推荐

  1. python中add函数_如何使用python中的add函数?

    之前向大家介绍过python中的求和函数sum函数,numpy中的sum函数,对于数组可以指定维度进行相加.numpy中还有另一种求和运算方法,即add函数.add函数不仅作用于numpy中加法运算, ...

  2. python怎么创建函数_如何在python中创建自己的map()函数

    调用函数时,请使用星号*: def mapper(func, *sequences): result = [] if len(sequences) > 0: minl = min(len(sub ...

  3. python numpy sum函数_如何使用Python中的sum函数?

    之前小编向大家介绍过python中的sum函数(https://www.py.cn/jishu/jichu/22025.html).在python中sunm函数使用分为两种情况,一种是python自带 ...

  4. python中延时函数_详解python中实现延时回调普通函数

    这篇文章主要给大家介绍了关于python中实现延时回调普通函数的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧. 前言 ...

  5. python中label函数_图像分析函数:skimage.measure中的label、regionprops

    算法解释详细,有算法执行过程动态GIF图的:https://blog.csdn.net/icvpr/article/details/10259577 算法文字解释的简介易懂的:https://www. ...

  6. python中save 函数_超全Python图像处理讲解!花五天才整理的!

    文章目录1.1 .打开图片和显示图片 1.2.创建一个简单的图像 1.4.图像旋转和格式转换 三.ImageChops模块(图像合成) 四.ImageEnhance模块(色彩.亮度) Pillow模块 ...

  7. python asyncio回调函数_最近用 Python 的 asyncio,有好多不懂。。

    15 2017-03-14 19:35:58 +08:00   16 说下我对这 python 这几种 web 模型的理解吧: 首先是 http server + wsgi server(contai ...

  8. python中index函数_详解python中的index函数用法

    1.函数的创建 def fun(): #定义 print('hellow') #函数的执行代码 retrun 1 #返回值 fun() #执行函数 2.函数的参数 普通参数 :要按照顺序输入参数 de ...

  9. python类和函数_构建程序. Python中的类和函数

    如果希望mainfunc的所有实例都使用相同的KeySeq对象,则可以使用默认参数值技巧: def mainfunc(ks=KeySeq()): key = ks.next() 只要您实际上没有传递k ...

  10. python 中arange函数_浅谈Python中range与Numpy中arange的比较

    本文先比较range与arange的异同点,再详细介绍各自的用法,然后列举了几个简单的示例,最后对xrange进行了简单的说明. 1. range与arange的比较 (1)相同点:A.参数的可选性. ...

最新文章

  1. 毫秒级百万数据分页存储过程
  2. 算法导论-VLSI芯片测试问题
  3. 计算机关闭的时候自动更新,win7自动更新关闭有什么影响_win7系统关闭自动更新的步骤-win7之家...
  4. WeChatExtension for Mac(mac微信小助手)支持big surv2.7.0中文修复版
  5. Make WAR file 1.0
  6. 【图论】新年好(最短路的综合问题)
  7. win10无法运行jre java_win10系统无法安装jre的解决方法
  8. 孤儿进程与僵尸进程产生及其处理
  9. 华东师范大学2017年数学分析考研试题
  10. 极简数据抓取教程:山水济南,Say I love you with data
  11. docker镜像完全卸载
  12. 操作系统OS作业整合
  13. android友盟微信分享到朋友圈,2020年友盟分享到微信朋友圈
  14. Spring Data JPA 原理与实战第二天 掌握Repoitory和DQM
  15. 萌系外表+丰富功能,i宝机器人成CES人气展品
  16. python闲鱼二手爬虫_Python 爬虫咸鱼版
  17. 在 Android* 商务应用中实施地图和地理围栏特性
  18. 大搜车mysql面试题_【大搜车面试|面试题】-看准网
  19. ppt形状html,PPT如何才能高大上?“形状”在PPT有这些妙用
  20. 去除WPS推广程序和广告

热门文章

  1. Java网络蜘蛛/网络爬虫 Spiderman
  2. idea设置字符编码
  3. 苑姓起源——古老而孤独
  4. 微信首张身份证”网证“正式发放,坐地铁再也不怕临时检查了!
  5. 领英辅助工具领英精灵都有哪些功能
  6. 【Git】Git概述、Git安装和使用、工作区,暂存区和版本库
  7. windows文件设置标签
  8. 龙蛇演义--国术的修炼
  9. linux系统工程师的前途在哪里?
  10. 文字编排的对齐方式——错位