OpenCV(python)一键入门--十三篇(二值化去噪与图像连通域)

举报
拓佑豪 发表于 2021/07/28 15:48:22 2021/07/28
【摘要】 本文为上文(十二篇-二值化)进行一个自适应阈值算法的补充,并简单介绍图像二值化与去噪,二值图像连通域的简单用法

本系列OpenCV博客旨在以最简单的方式分享OpenCV知识,欢迎大家交流和提出指导意见


1:自适应阈值函数adaptiveThreshold(上文的阈值分割补充)

在上文我们了解到threshold二值化的操作。adaptiveThreshold是threshold的进阶版本。threshold只是简单的把图像像素根据阈值区分,这样的二值区分比较粗糙。可能会导致图像的信息与特征完全无法提取,或者漏掉一些关键的信息。

import cv2 as cv
import numpy as np

src = cv.imread("ma.jpg")
cv.imshow("input", src)


# 自适应阈值
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
binary = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 25, 10)
# 

cv.imshow("binary", binary)

cv.waitKey(0)
cv.destroyAllWindows()

cv.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)

也可以看成:

cv.adaptiveThreshold(灰度图, 满足条件的像素点需设置的灰度值,自适应方法,二值化方法,分割计算的区域大小---取奇数, 常数, 输出的图像(通常不用写))

1:需要灰度图

2:自适应方法有两种:ADAPTIVE_THRESH_MEAN_C 或 ADAPTIVE_THRESH_GAUSSIAN_C

3:二值化方法:可以用参考—— 一键入门--十二篇(二值化):https://bbs.huaweicloud.com/blogs/288328

4:常数:每个区域计算出的阈值的基础上在减去这个常数作为这个区域的最终阈值,可以为负数

效果如下:

2:图像的二值化与去噪

import cv2 as cv
import numpy as np


def method_1(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)  #直接灰度二值化
    t, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    return binary


def method_2(image):
    blurred = cv.GaussianBlur(image, (3, 3), 0)
    gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)  #先高斯模糊再二值化
    t, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    return binary


def method_3(image):
    blurred = cv.pyrMeanShiftFiltering(image, 10, 100)
    gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)  #先均值漂移再二值化
    t, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    return binary


src = cv.imread("D:/coin.jpg")
ret = method_1(src)#在这里切换 method_1/2/3 三种不同的函数使用
cv.imshow("ret",ret)


#使用numpy将原图和结果图拼在一个窗口
h, w = src.shape[:2]
result = np.zeros([h, w*2, 3], dtype=src.dtype)
result[0:h,0:w,:] = src
result[0:h,w:2*w,:] = cv.cvtColor(ret, cv.COLOR_GRAY2BGR)
#并标上文字
cv.putText(result, "input", (10, 30), cv.FONT_ITALIC, 1.0, (0, 0, 255), 2)
cv.putText(result, "binary", (w+10, 30), cv.FONT_ITALIC, 1.0, (0, 0, 255), 2)
cv.imshow("result", result)

cv.waitKey(0)
cv.destroyAllWindows()

其中,直接二值化和高斯模糊(图像的去噪)在之前的博客已经给出使用方法:

一键入门--第六篇(图像卷积与模糊):https://bbs.huaweicloud.com/blogs/285595

一键入门--十二篇(二值化):https://bbs.huaweicloud.com/blogs/288328

· 均值漂移pyrMeanShiftFiltering函数原型:

cv.pyrMeanShiftFiltering(src, sp, sr[, dst[, maxLevel[, termcrit]]]) -> dst

src参数表示输入图像,8位,三通道图像。
sp参数表示漂移物理空间半径大小。
sr参数表示漂移色彩空间半径大小。
dst参数表示和源图象相同大小、相同格式的输出图象。
maxLevel参数表示金字塔的最大层数。
termcrit参数表示漂移迭代终止条件。

也可以看成是:cv.pyrMeanShiftFiltering(彩色图像,物理空间漂移半径,色彩空间漂移半径,【金字塔的最大层数,可选】,【漂移迭代终止条件,可选】)

这里使用的第二种方法,也就是先模糊去噪,再二值化。效果如下:

附上测试原图给大家:

coin.jpg

3:二值图像,然后连通组件查找

import cv2 as cv
import numpy as np

src = cv.imread("D:/rice.png")
cv.imshow("rice",src)
h, w = src.shape[:2]

src = cv.GaussianBlur(src, (3, 3), 0)#高斯滤波
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)#灰度
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)#二进制阈值化
print(f"ret:{ret}")
cv.imshow("binary", binary)

output = cv.connectedComponents(binary, connectivity=8, ltype=cv.CV_32S)#计算连同域

num_labels = output[0]
labels = output[1]

colors = []
#随机生成RGB三颜色
for i in range(num_labels):
    b = np.random.randint(0, 256)
    g = np.random.randint(0, 256)
    r = np.random.randint(0, 256)
    colors.append((b, g, r))

colors[0] = (0, 0, 0) #大背景设置成黑色
h, w = gray.shape
image = np.zeros((h, w, 3), dtype=np.uint8) #创建三通道空图
for row in range(h):
    for col in range(w):
        image[row, col] = colors[labels[row, col]] #遍历像素,上色

cv.imshow("colored labels", image)
print("total rice : ", num_labels - 1)

cv.waitKey(0)
cv.destroyAllWindows()

cv.connectedComponents (image, connectivity=8, ltype=None)

可以看成:cv.connectedComponents(图像,连通设置,图像标记类型)

image:也就是输入图像,必须是二值图,即8位单通道图像。(因此输入图像必须先进行二值化处理才能被这个函数接受)
connectivity:可选值为4或8,也就是使用4连通还是8连通。
ltype:输出图像标记的类型,目前支持CV_32S 和 CV_16U。 

返回值:
num_labels:所有连通域的数目
labels:图像上每一像素的标记,用数字1、2、3…表示(不同的数字表示不同的连通域)

效果如下:

测试原图如下:

rice.png

4:二值统计连通组件状态统计  (cv.connectedComponentsWithStats)

前面提到的cv.connectedComponents 是不带统计信息的API。这个cv.connectedComponentsWithStats带有四个返回值。函数原型为:

num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(image, connectivity=8, ltype=None)

前面大部分内容一样,我还是在这重复一遍,重点在加深部分:

参数介绍如下:
image:也就是输入图像,必须是二值图,即8位单通道图像。(因此输入图像必须先进行二值化处理才能被这个函数接受)
connectivity:可选值为4或8,也就是使用4连通还是8连通。
ltype:输出图像标记的类型,目前支持CV_32S 和 CV_16U。 返回值:
返回值:
num_labels:所有连通域的数目
labels:图像上每一像素的标记,用数字1、2、3…表示(不同的数字表示不同的连通域)
stats:每一个标记的统计信息,是一个5列的矩阵,每一行对应每个连通区域的外接矩形的x、y、width、height和面积,示例如下: 0 0 720 720 291805
centroids:连通域的中心点

import cv2 as cv
import numpy as np

src = cv.imread("D:/rice.png")

src = cv.GaussianBlur(src, (3, 3), 0)  #高斯模糊
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)#二值化
cv.imshow("binary", binary)

#主要API
num_labels, labels, stats, centers = cv.connectedComponentsWithStats(binary, connectivity=8, ltype=cv.CV_32S)

colors = []
#生成随机颜色
for i in range(num_labels):
    b = np.random.randint(0, 256)
    g = np.random.randint(0, 256)
    r = np.random.randint(0, 256)
    colors.append((b, g, r))

colors[0] = (0, 0, 0)
image = np.copy(src)
for t in range(1, num_labels, 1):
    x, y, w, h, area = stats[t]
    cx, cy = centers[t]

    #绘制图像
    cv.circle(image, (np.int32(cx), np.int32(cy)), 2, (0, 255, 0), 2, 8, 0)
    cv.rectangle(image, (x, y), (x+w, y+h), colors[t], 1, 8, 0)

    cv.putText(image, "num:" + str(t), (x, y), cv.FONT_HERSHEY_SIMPLEX, .5, (0, 0, 255), 1)
    print("label index %d, area of the label : %d"%(t, area))

cv.imshow("colored labels", image)
print("total rice : ", num_labels - 1)

cv.waitKey(0)
cv.destroyAllWindows()

效果:

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。