如何定义OpenCV中stream域的标记?
我正在用OpenCV为Android写作。 我使用标记控制分水岭来分割类似于下图的图像,而无需用户手动标记图像。 我打算使用区域最大值作为标记。
minMaxLoc()
会给我的价值,但我怎么能限制它是我感兴趣的blob? 我可以利用findContours()
或cvBlob blob的结果限制ROI并将最大值应用于每个blob?
首先:函数minMaxLoc
只能find给定input的全局最小值和全局最大值,所以在确定区域最小值和/或区域最大值时,最没有用处。 但是你的想法是正确的,基于区域最小/最大值提取基于标记进行分水岭变换的标记是完全正确的。 让我试着澄清什么是分水岭变换,以及如何正确使用OpenCV中的实现。
与stream域有关的一些相当数量的论文对它的描述与之类似(如果你不确定的话,我可能会错过一些细节:问)。 考虑一些你知道的地区的表面,它包含山谷和山峰(在这里与我们无关)。 假设在这个表面下面你只有水,有色水。 现在,在你的表面的每个山谷打洞,然后水开始填满整个地区。 在某个时候,不同颜色的水将会遇到,当这种情况发生的时候,你build造一个水坝,使它们不相互接触。 最后你有一个水坝的集合,这是分离所有不同的有色水的分水岭。
现在,如果你在这个表面上做了太多的洞,结果就是太多的区域:过度分割。 如果你做得太less,你会得到一个细分。 所以,实际上任何build议使用分水岭的论文实际上都提出了技术来避免这些问题在本文所涉及的应用中。
我写了所有这些(对于那些知道什么是分水岭变换的人来说,这可能太天真了),因为它直接反映了你应该如何使用分水岭实现(当前接受的答案是以完全错误的方式)。 现在让我们从OpenCV示例开始,使用Python绑定。
问题中提出的图像是由许多对象组成的,这些对象通常太靠近,在某些情况下会重叠。 这里stream域的用处是将这些对象正确分开,而不是把它们分成一个单一的组成部分。 所以每个物体至less需要一个标记,背景需要至less一个标记。 举例来说,首先通过Otsu对input图像进行二值化处理,然后进行形态学开放以去除小物体。 这一步的结果如左图所示。 现在用二值图像考虑应用距离变换,结果在右边。
利用距离变换的结果,我们可以考虑一些阈值,使得我们只考虑距背景最远的区域(下面的左图)。 这样做,我们可以通过在较早的阈值之后标记不同的区域来获得每个对象的标记。 现在,我们也可以考虑上面左图的扩张版本的边界来组成我们的标记。 完整的标记如右图所示(某些标记太暗而不能被看到,但左图中的每个白色区域都在右图中表示)。
这个标记我们在这里有很多意义。 每个colored water == one marker
将开始填补该地区,stream域改造将build设水坝,阻止不同的“颜色”合并。 如果我们进行转换,我们会得到左边的图像。 只考虑水坝与原始图像的组合,我们得到的结果是正确的。
import sys import cv2 import numpy from scipy.ndimage import label def segment_on_dt(a, img): border = cv2.dilate(img, None, iterations=5) border = border - cv2.erode(border, None) dt = cv2.distanceTransform(img, 2, 3) dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(numpy.uint8) _, dt = cv2.threshold(dt, 180, 255, cv2.THRESH_BINARY) lbl, ncc = label(dt) lbl = lbl * (255/ncc) # Completing the markers now. lbl[border == 255] = 255 lbl = lbl.astype(numpy.int32) cv2.watershed(a, lbl) lbl[lbl == -1] = 0 lbl = lbl.astype(numpy.uint8) return 255 - lbl img = cv2.imread(sys.argv[1]) # Pre-processing. img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, img_bin = cv2.threshold(img_gray, 0, 255, cv2.THRESH_OTSU) img_bin = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, numpy.ones((3, 3), dtype=int)) result = segment_on_dt(img, img_bin) cv2.imwrite(sys.argv[2], result) result[result != 255] = 0 result = cv2.dilate(result, None) img[result == 255] = (0, 0, 255) cv2.imwrite(sys.argv[3], img)
我想在这里解释一下如何使用分水岭的简单代码。 我正在使用OpenCV-Python,但是我希望你不会有任何困难去理解。
在这个代码中,我将使用分水岭作为前景背景提取的工具。 (这个例子是OpenCV Cookbook中C ++代码的Python对应)。 理解分水岭是一个简单的例子。 除此之外,您可以使用分水岭计算此图像中的对象数量。 这将是这个代码的一个稍微高级的版本。
1 – 首先我们加载我们的图像,将其转换为灰度,并用合适的值对其进行阈值。 我采取了大津的二元化 ,所以它会find最好的门槛值。
import cv2 import numpy as np img = cv2.imread('sofwatershed.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
以下是我得到的结果:
(即使这个结果是好的,因为前景和背景图像之间的巨大对比)
2 – 现在我们必须创build标记。 标记是与原始图像大小相同的图像,即32SC1(32位有符号单通道)。
现在在原始图像中会有一些区域可以确定,那部分属于前景。 在标记图像上标记255。 现在你肯定是背景的区域将被标记为128.你不确定的区域将被标记为0.这就是我们将要做的下一步。
A – 前景地区 : – 我们已经有一个阈值的形象,药丸是白色的。 我们侵蚀他们一点,以便我们确信剩余的地区属于前景。
fg = cv2.erode(thresh,None,iterations = 2)
fg :
B – 背景区域 : – 这里我们扩大阈值的图像,使背景区域减less。 但是我们确定剩下的黑色区域是100%的背景。 我们把它设置为128。
bgt = cv2.dilate(thresh,None,iterations = 3) ret,bg = cv2.threshold(bgt,1,128,1)
现在我们得到bg如下:
C – 现在我们添加fg和bg :
marker = cv2.add(fg,bg)
以下是我们得到的:
现在我们从上面的图像可以清楚地看出,白色区域是100%前景,灰色区域是100%背景,黑色区域我们不确定。
然后我们把它转换成32SC1:
marker32 = np.int32(marker)
3 – 最后我们应用分水岭并将结果转换回uint8图像:
cv2.watershed(img,marker32) m = cv2.convertScaleAbs(marker32)
米:
4 – 我们将其正确地进行阈值获取,并使用input图像执行bitwise_and
:
ret,thresh = cv2.threshold(m,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) res = cv2.bitwise_and(img,img,mask = thresh)
res:
希望它有帮助!
方舟