网监网站备案,网络营销方式,网络广告营销的典型案例,哪里可以做网站啊OpenCV与图像处理学习九——连通区域分析算法#xff08;含代码#xff09;一、连通区域概要二、Two-Pass算法三、代码实现一、连通区域概要
连通区域#xff08;Connected Component#xff09;一般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域#x…
OpenCV与图像处理学习九——连通区域分析算法含代码一、连通区域概要二、Two-Pass算法三、代码实现一、连通区域概要
连通区域Connected Component一般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域连通区域分析是指将图像中的各个连通区域找出并标记。连通区域分析是一种在CV和图像分析处理的众多应用领域中较为常用和基本的方法。
例如 OCR识别中字符分割提取车牌识别、文本识别、字幕识别等、视觉跟踪中的运动前景 目标分割与提取行人入侵检测、遗留物体检测、基于视觉的车辆检测与跟踪等、医学图像处 理感兴趣目标区域提取等。
在需要将前景目标提取出来以便后续进行处理的应用场景中都能够用到连通区域分析 方法通常连通区域分析处理的对象是一张二值化后的图像。
在图像中最小的单位是像素每个像素周围有邻接像素常见的邻接关系有2种 4邻接与8邻接如下图所示 如果A与B连通 B与C连通则A与C连通在视觉上看来彼此连通的点形成了一个区域而不连通的点形成了不同的区域。这样的一个所有的点彼此连通点构成的集合我们称为一个连通区域。
我们来看一下下面这个二值化的图 对于每一个前景像素只要它的邻域中有像素也是前景那么它们就属于一个连通区域在这张图中如果使用四邻域的规则那么将可以分成三个连通区域而使用八邻域的规则则可以分成两个连通区域。
二、Two-Pass算法
基于上述概念我们再来学习连通区域分析算法中常用的Two-Pass算法 (两遍扫描法)正如其名指的就是通过扫描两遍图像将图像中存在的所有连通域找出并标记。
第一次扫描
从左上角开始遍历像素点找到第一个像素为255因为是二值图只有0和255的点令该像素的label1当该像素的左邻像素和上邻像素为无效值时给该像素置一个新的label值 label 记录集合;当该像素的左邻像素或者上邻像素有一个为有效值时将有效值像素的label赋给该像素的label值当该像素的左邻像素和上邻像素都为有效值时选取其中较小的label值赋给该像素的label值。
若一张二值图如下所示蓝点为前景点 则第一遍扫描之后记录的label如下所示 通过领域像素得到label或成为领域像素得到label依据的像素点都将记录为一个集合只有毫无联系的才会记录为不同的集合如上图所示前景点被分为了两个集合橙色标记。
第二次扫描
对每个点的label进行更新更新为其对于其集合中最小的label 则就可以得到两个连通区域
三、代码实现
import cv2
import numpy as np# 4邻域的连通域和 8邻域的连通域
# [row, col]
NEIGHBOR_HOODS_4 True
OFFSETS_4 [[0, -1], [-1, 0], [0, 0], [1, 0], [0, 1]]NEIGHBOR_HOODS_8 False
OFFSETS_8 [[-1, -1], [0, -1], [1, -1],[-1, 0], [0, 0], [1, 0],[-1, 1], [0, 1], [1, 1]]
#第二遍扫描
def reorganize(binary_img: np.array):index_map []points []index -1rows, cols binary_img.shapefor row in range(rows):for col in range(cols):var binary_img[row][col]if var 0.5:continueif var in index_map:index index_map.index(var)num index 1else:index len(index_map)num index 1index_map.append(var)points.append([])binary_img[row][col] numpoints[index].append([row, col])#print(binary_img)#print(points)return binary_img, points#四领域或八领域判断
def neighbor_value(binary_img: np.array, offsets, reverseFalse):rows, cols binary_img.shapelabel_idx 0rows_ [0, rows, 1] if reverse False else [rows-1, -1, -1]cols_ [0, cols, 1] if reverse False else [cols-1, -1, -1]for row in range(rows_[0], rows_[1], rows_[2]):for col in range(cols_[0], cols_[1], cols_[2]):label 256if binary_img[row][col] 0.5:continuefor offset in offsets:neighbor_row min(max(0, rowoffset[0]), rows-1)neighbor_col min(max(0, coloffset[1]), cols-1)neighbor_val binary_img[neighbor_row, neighbor_col]if neighbor_val 0.5:continuelabel neighbor_val if neighbor_val label else labelif label 255:label_idx 1label label_idxbinary_img[row][col] labelprint(第一遍扫描,binary_img)print(开始第二遍...)return binary_img# binary_img: bg-0, object-255; int
#第一遍扫描
def Two_Pass(binary_img: np.array, neighbor_hoods):if neighbor_hoods NEIGHBOR_HOODS_4:offsets OFFSETS_4elif neighbor_hoods NEIGHBOR_HOODS_8:offsets OFFSETS_8else:raise ValueErrorbinary_img neighbor_value(binary_img, offsets, False)return binary_imgif __name__ __main__:#创建四行七列的矩阵binary_img np.zeros((4, 7), dtypenp.int16)#指定点设置为255index [[0, 2], [0, 5],[1, 0], [1, 1], [1, 2], [1, 4], [1, 5], [1, 6],[2, 2], [2, 5],[3, 1], [3, 2], [3, 4],[3,5], [3, 6]]for i in index:binary_img[i[0], i[1]] np.int16(255)print(原始二值图像)print(binary_img)#print(Two_Pass)#调用Two Pass算法计算第一遍扫面的结果binary_img Two_Pass(binary_img, NEIGHBOR_HOODS_4)#print(binary_img)#计算第一遍扫面的结果binary_img, points reorganize(binary_img)print(binary_img)#print(points)结果如下所示
原始二值图像
[[ 0 0 255 0 0 255 0][255 255 255 0 255 255 255][ 0 0 255 0 0 255 0][ 0 255 255 0 255 255 255]]
第一遍扫描 [[0 0 1 0 0 2 0][3 3 1 0 4 2 2][0 0 1 0 0 2 0][0 5 1 0 6 2 2]]
开始第二遍...
[[0 0 1 0 0 2 0][3 3 1 0 4 2 2][0 0 1 0 0 2 0][0 5 1 0 6 2 2]]