OpenCV(python)一键入门--十三篇(二值化去噪与图像连通域)
本系列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(彩色图像,物理空间漂移半径,色彩空间漂移半径,【金字塔的最大层数,可选】,【漂移迭代终止条件,可选】)
这里使用的第二种方法,也就是先模糊去噪,再二值化。效果如下:
附上测试原图给大家:
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…表示(不同的数字表示不同的连通域)
效果如下:
测试原图如下:
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()
效果:
- 点赞
- 收藏
- 关注作者
评论(0)