opencv 区域生长(种子自动选取)python

首先是老师布置的作业,叫我们练习区域生长,种子可以手动选取,但最好自动找出来。于是乎我就像挑战一下,感觉网上例程很多的样子,比如:

网上的通用版本,都一模一样的

本人搜了半天,网上虽然看似很多,但就其根本就只有这一个。于是就用了一下其中的找种子环节(函数)。

#初始种子选择
def originalSeed(gray, th):
    ret, thresh = cv2.cv2.threshold(gray, th, 255, cv2.THRESH_BINARY)#二值图,种子区域(不同划分可获得不同种子)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))#3×3结构元

    thresh_copy = thresh.copy() #复制thresh_A到thresh_copy
    thresh_B = np.zeros(gray.shape, np.uint8) #thresh_B大小与A相同,像素值为0

    seeds = [ ] #为了记录种子坐标

    #循环,直到thresh_copy中的像素值全部为0
    while thresh_copy.any():

        Xa_copy, Ya_copy = np.where(thresh_copy > 0) #thresh_A_copy中值为255的像素的坐标
        thresh_B[Xa_copy[0], Ya_copy[0]] = 255 #选取第一个点,并将thresh_B中对应像素值改为255

        #连通分量算法,先对thresh_B进行膨胀,再和thresh执行and操作(取交集)
        for i in range(200):
            dilation_B = cv2.dilate(thresh_B, kernel, iterations=1)
            thresh_B = cv2.bitwise_and(thresh, dilation_B)

        #取thresh_B值为255的像素坐标,并将thresh_copy中对应坐标像素值变为0
        Xb, Yb = np.where(thresh_B > 0)
        thresh_copy[Xb, Yb] = 0

        #循环,在thresh_B中只有一个像素点时停止
        while str(thresh_B.tolist()).count("255") > 1:
            thresh_B = cv2.erode(thresh_B,  kernel, iterations=1) #腐蚀操作

        X_seed, Y_seed = np.where(thresh_B > 0) #取处种子坐标
        if X_seed.size > 0 and Y_seed.size > 0:
            seeds.append((X_seed[0], Y_seed[0]))#将种子坐标写入seeds
        thresh_B[Xb, Yb] = 0 #将thresh_B像素值置零
    return seeds
我怀着敬重的心情cv了一下,运行,发现我的pycharm没动静了。这是为什么呢?然后我仔细观察了一下代码,看到了其中的:
while{
	for(200次){
	}
	whlie(xxx > 1){
	} 
}

是这种结构。。。其中那个for中间是一次腐蚀操作和并集操作。对于这么多像素都要做一次,这运算量可想而知。但我大概是了解这串操作是什么意思。大概是像通过腐蚀操作将此时图像中的连通域变小,然后一次次地迭代,找出每一个连通域的种子。
那么有没有更加方便地操作呢?
本人学图像处理时间也不多,深度也不深,但我想选取种子肯定得与连通域有关。我们想尽量选择每个连通域中较为中间地点,而且每个连通域尽量只取一个点来进行区域生长最合适。多的话会重复生长,浪费时间。那么,个人认为主要确实得通过腐蚀或者用其他方法使连通域变小,直到小到一定程度,取其中的一个点,把此连通域删除。即一层一层腐蚀,删除连通域进行迭代。但本人想了半天不知道怎么做。。。判断函数+删除连通域函数就很难马上实现(作业马上就要交了)。所以在网上搜了一下,发现了有连通域的专属操作:

connectedComponentsWithStats(image[,
							 labels[,
							 stats[, 
							 centroids[, 
							 connectivity[, 
							 ltype]]]]]) 
#-> retval, labels, stats, centroids
真好!

返回值里有一个centroids------质心?这个函数就是对于每个连通域进行轮廓的提取操作,质心刚好能实现在中间取一个点的操作。这里有人会问:质心不一定在连通域中怎么办?emmmm确实是一个问题。大家有好的方法可以在评论区说一下。那我现在就先将就一下。于是我把质心提取出来,又由于种子的位置一定是int型的,所以做个转化。提取seed的代码如下:

#初始种子选择
def originalSeed(gray):
    ret, img1 = cv2.threshold(gray, 245, 255, cv2.THRESH_BINARY)  # 二值图,种子区域(不同划分可获得不同种子)
    retval, labels, stats, centroids = cv2.connectedComponentsWithStats(img1)#进行连通域操作,取其质点
    centroids = centroids.astype(int)#转化为整数
    # seed = []
    # for i  in range(img1.shape[0]):
    #     for j in range(img1.shape[1]):
    #         if(img1[i,j] == 255):
    #             seed.append([i,j])

    return centroids

然后再结合老师原来的例程就可以实现了,还要记得将threshold设置成5。所以全程序如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt


#求两个点的差值
def getGrayDiff(image,currentPoint,tmpPoint):
    return abs(int(image[currentPoint[0],currentPoint[1]]) - int(image[tmpPoint[0],tmpPoint[1]]))
#区域生长算法
def regional_growth (gray,seeds,threshold=5) :
    #每次区域生长的时候的像素之间的八个邻接点
    connects = [(-1, -1), (0, -1), (1, -1), (1, 0), (1, 1), \
                        (0, 1), (-1, 1), (-1, 0)]
    threshold = threshold #生长时候的相似性阈值,默认即灰度级不相差超过15以内的都算为相同
    height, weight = gray.shape
    seedMark = np.zeros(gray.shape)
    seedList = []
    for seed in seeds:
        if(seed[0] < gray.shape[0] and seed[1] < gray.shape[1] and seed[0]  > 0 and seed[1] > 0):
            seedList.append(seed)   #将添加到的列表中
    print(seedList)
    label = 1	#标记点的flag
    while(len(seedList)>0):     #如果列表里还存在点
        currentPoint = seedList.pop(0)  #将最前面的那个抛出
        seedMark[currentPoint[0],currentPoint[1]] = label   #将对应位置的点标志为1
        for i in range(8):  #对这个点周围的8个点一次进行相似性判断
            tmpX = currentPoint[0] + connects[i][0]
            tmpY = currentPoint[1] + connects[i][1]
            if tmpX < 0 or tmpY < 0 or tmpX >= height or tmpY >= weight:    #如果超出限定的阈值范围
                continue    #跳过并继续
            grayDiff = getGrayDiff(gray,currentPoint,(tmpX,tmpY))   #计算此点与像素点的灰度级之差
            if grayDiff < threshold and seedMark[tmpX,tmpY] == 0:
                seedMark[tmpX,tmpY] = label
                seedList.append((tmpX,tmpY))
    return seedMark

#初始种子选择
def originalSeed(gray):
    ret, img1 = cv2.threshold(gray, 245, 255, cv2.THRESH_BINARY)  # 二值图,种子区域(不同划分可获得不同种子)
    retval, labels, stats, centroids = cv2.connectedComponentsWithStats(img1)#进行连通域操作,取其质点
    centroids = centroids.astype(int)#转化为整数
    # seed = []
    # for i  in range(img1.shape[0]):
    #     for j in range(img1.shape[1]):
    #         if(img1[i,j] == 255):
    #             seed.append([i,j])

    return centroids

# if __name__ == "__main__":
img = cv2.imread('10.2.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
seed = originalSeed(img)
img = regional_growth(img,seed)

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 图像显示
plt.figure(figsize=(10, 5))  # width * height
plt.subplot(111), plt.imshow(img, cmap='gray'), plt.title('区域生长以后'), plt.axis("off")
plt.show()
print("ok...")

效果如下:
在这里插入图片描述
右边虽然有点噪声,但可以通过滤波器消去。
然后如果你老师也是付老师的话,那就点个赞或者评论一下吧!哈哈哈!

  • 23
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
OpenCV中,区域生长是一种基于像素相似性的图像分割算法,它根据一组种子(seed)像素开始,逐渐将相邻的像素加入到种子区域中。选取适合的种子是进行区域生长的重要一步。 在选择种子时,可以考虑以下几个因素: 1. 确定感兴趣的区域:首先需要明确感兴趣的目标区域是什么。例如,如果我们要分割出物体的轮廓,可以选择物体的边缘像素作为种子。 2. 确定种子像素:在目标区域确定后,可以选择边缘像素周围的像素作为种子。例如,可以选择边缘像素的相邻像素作为种子,以确保种子位于目标区域的内部。 3. 确定相似性准则:区域生长的关键是确定像素之间的相似性准则。可以根据颜色、灰度等特征来衡量像素之间的相似程度。一般而言,种子像素与相邻像素的相似性越高,被加入到种子区域的几率就越大。 4. 确定生长准则:生长准则是指判断一个像素是否可以加入到种子区域的条件。可以使用阈值来判断像素的相似性,当一个像素与种子像素的相似度超过阈值时,可以将其加入到种子区域中。 在实际应用中,如何选择合适的种子像素需要根据具体情况进行调试和优化。可以通过调整相似性准则的阈值、选择不同位置的种子像素等方式来优化区域生长算法的结果。 总之,选择合适的种子对于区域生长算法的成功与否起着至关重要的作用。好的种子选择可以提高分割结果的准确性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值