opencv 图像截取,opencv数字识别图像分割
图像分割和提取前景对象被分割或提取为图像中的目标图像。
对背景本身不感兴趣
采用分水岭算法和GrabCut算法对图像进行分割和提取。
采用分水岭算法实现图像分割和提取。分水岭算法形象地把一幅图像比作一个地理地形表面来实现图像分割,非常有效。
原理任何灰度图像都可以看作地理地形表面,灰度值高的区域可以看作山峰,灰度值低的区域可以看作山谷。
左图为原图,右图为其对应的“地形面”。
该过程将图像分为两个不同的集合:集水区和分水岭线。
我们建的坝就是分水线,也就是原始图像的分割。这是分水岭算法。
由于噪声和其他因素的影响,上述基本分水岭算法经常导致过分割。
过度分割会将图像分割成密集的独立块,使分割失去意义。
为了提高图像分割效果,提出了一种基于掩模的改进分水岭算法。改进的分水岭算法允许用户标记他认为是同一分割区域的部分(被标记的部分称为掩膜)。
分水岭算法在处理时,会将标记部分处理到同一个分割区域中。
例如:
对原图进行标记,其中标记为深色的三个小色块代表当使用掩膜分水岭算法时,这些部分所包含的颜色将被划分到相同的区域。
OpenCV中相关函数介绍,可以使用函数cv2.watershed()来实现分水岭算法。
在具体实现过程中,我们还需要使用形态学函数、距离变换函数cv2.distanceTransform()和cv2.connectedComponents()来完成图像分割。
功能形态学
在使用分水岭算法分割图像之前,需要对图像进行简单的形态学处理。开放式操作
开运算是先腐蚀后膨胀的运算,开运算可以去除图像中的噪声。
在使用分水岭算法对图像进行处理之前,应使用开运算去除图像中的噪声,以避免噪声对图像分割可能产生的干扰。获取图像边界
通过形态学运算和减法可以得到图像的边界。
利用形态学变换获得图像的边界信息,导入cv2。
将numpy作为np导入
将matplotlib.pyplot作为plt导入
o=cv2.imread(my.bmp ,cv2。IMREAD _未更改)
k=np.ones((5,5),np.uint8)
e=cv2.erode(o,k)
b=cv2 .减法(o,e)
plt.subplot(131)
plt.imshow(o)
plt.axis(“关”)
plt.subplot(132)
(英)
plt.axis(“关”)
plt.subplot(133)
plt.imshow(b)
plt.axis(“关”)
Plt.show()可以通过形态学运算和减法获得图像的边界信息。
但是,形态学运算只适用于相对简单的图像。如果图像中的前景物体是连通的,那么通过形态学运算就不能精确地获得每个子图像的边界。
距离变换函数距离变换
当图像中的子图像不相连时,可以通过形态学蚀刻直接确定前景物体,但如果图像中的子图像连在一起,则很难确定前景物体。
此时,在距离变换函数cv2.distanceTransform()的帮助下,可以很容易地提取前景对象。
函数cv2.distanceTransform()计算二进制图像中任意一点与最近的背景点之间的距离。
一般情况下,该函数计算图像中非零像素与最近零像素之间的距离,即二值图像中所有像素与值为0的最近像素之间的距离。如果像素本身的值是0,那么距离也是0。
2.cv2.distanceTransform()的计算结果反映了每个像素与背景(值为0的像素)之间的距离关系。
一般来说,如果前景物体的中心(质心)远离值为0的像素,就会得到较大的值。如果前景对象的边缘靠近值为0的像素,它将获得较小的值。如果对上述计算结果进行阈值处理,则可以获得图像中子图像的中心和骨架等信息。距离变换函数cv2.distanceTransform()可用于计算对象的中心,细化轮廓,并获得图像的前景。
函数cv2.distanceTransform()的语法格式为:
DST=CV2。DistanceTransform (src,distance type,mask size [,dst type]]) src是8位单通道二进制图像。DistanceType是距离类型参数。
MaskSize遮罩的大小。
当distanceType=cv2。L1 DIST或者cv2。DIST_C,maskSize强制为3(因为设置为3和设置为5及更大没有区别)。
DstType是目标图像的类型,默认值是CV_32F。Dst表示计算的目标图像,可以是8位或32位浮点数,大小与src相同。使用距离变换函数cv2.distanceTransform()计算图像的明确前景。
将numpy作为np导入
导入cv2
将matplotlib.pyplot作为plt导入
img=cv2 . im read( water _ coins . jpg )
gray=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
ishow=img.copy()
ret,thresh=cv2.threshold(gray,0,255,cv2。THRESH_BINARY_INV cv2。OTSU)
kernel=np.ones((3,3),np.uint8)
open=cv2 . morphology ex(thresh,cv2.morph _ open,kernel,iterations=2) #打开。
dist _ transform=cv2 . distance transform(opening,cv2。DIST_L2,5)
ret,fore=cv2 . threshold(dist _ transform,0.7*dist_transform.max(),255,0)
plt.subplot(131)
plt.imshow(ishow)
plt.axis(“关”)
plt.subplot(132)
plt.imshow(dist_transform)
plt.axis(“关”)
plt.subplot(133)
plt.imshow(fore)
plt.axis(“关”)
在plt.show()fore image中:左图中“确定的前景”显示得更准确。
确定前景,通常是指前景物体的中心。
之所以认为这些点决定了前景,是因为它们距离背景点足够远,而且都是距离大于一个固定阈值(0.7*dist_transform.max())的点。
确定未知区域
形态学扩展操作可以“扩展和放大”图像中的前景。
当图像中的前景被放大时,背景会被“压缩”,所以此时获得的背景信息一定比实际背景的信息要小,不包括前景的“确定背景”。为了便于说明,将确定背景称为b
距离变换函数cv2.distanceTransform()可以得到图像的“中心”,得到“确定的前景”。
利用图像中确定的前景F和确定的背景B,剩余区域是未知区域UN。这部分区域正是分水岭算法要进一步定义的区域。
对于一幅图像O,未知区域UN可以通过以下关系式得到:未知区域UN=图像O-确定背景B-确定前景f。
未知区域UN=(图像O-确定背景B)-确定前景f。
“图像O-确定背景B”可以通过图像的形态学扩展来获得。
标记图像的明确前景、明确背景和未知区域。
将numpy作为np导入
导入cv2
将matplotlib.pyplot作为plt导入
img=cv2 . im read( water _ coins . jpg )
gray=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
ishow=img.copy()
ret,thresh=cv2.threshold(gray,0,255,cv2。THRESH_BINARY_INV cv2。OTSU)
kernel=np.ones((3,3),np.uint8)
opening=cv2 . morphologyex(thresh,cv2。MORPH_OPEN,内核,迭代次数=2)
bg=cv2.dilate(opening,kernel,iterations=3)
dist=cv2.distanceTransform(打开,cv2。DIST_L2,5)
ret,fore=cv2.threshold(dist,0.7*dist.max(),255,0)
fore=np.uint8(fore)
un=cv2.subtract(背景,前)
plt.subplot(221)
plt.imshow(ishow)
plt.axis(“关”)
plt.subplot(222)
plt.imshow(背景)
plt.axis(“关”)
plt.subplot(223)
plt.imshow(fore)
plt.axis(“关”)
plt.subplot(224)
plt.imshow(un)
plt.axis(“关”)
plt.show()
功能连接组件
前景确定清楚后,就可以对前景确定的图像进行标记了。
在OpenCV中,函数cv2.connectedComponents()可用于注释。该函数将背景标记为0,用从1开始的正整数标记其他对象。
函数cv2.connectedComponents()的语法格式为:Retval,labels=cv2 . connected components(image)image是一个8位的单通道图像,需要进行标记。Retval是返回的标签数。标签是标注的结果图像。使用函数cv2.connectedComponents()来注释图像。
将numpy作为np导入
导入cv2
将matplotlib.pyplot作为plt导入
img=cv2 . im read( water _ coins . jpg )
gray=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
ishow=img.copy()
ret,thresh=cv2.threshold(gray,0,255,cv2。THRESH_BINARY_INV cv2。OTSU)
kernel=np.ones((3,3),np.uint8)
opening=cv2 . morphologyex(thresh,cv2。MORPH_OPEN,内核,迭代次数=2)
sure_bg=cv2.dilate(opening,kernel,iterations=3)
dist _ transform=cv2 . distance transform(opening,cv2。DIST_L2,5)
ret,fore=cv2 . threshold(dist _ transform,0.7*dist_transform.max(),255,0)
fore=np.uint8(fore)
ret,markers=cv2 . connected components(fore)
打印(ret)
plt.subplot(131)
plt.imshow(ishow)
plt.axis(“关”)
plt.subplot(132)
plt.imshow(fore)
plt.axis(“关”)
plt.subplot(133)
plt.imshow(标记)
plt.axis(“关”)
plt.show()
前景图像的中心点被不同地标记(用不同的颜色区分)。
当函数cv2.connectedComponents()对图像进行标记时,会将背景标记为0,其他对象用从1开始的正整数进行标记。具体对应是:
0表示背景区域。从数字1开始的值代表不同的前景区域。在分水岭算法中,标签值0代表未知区域。因此,我们必须调整由函数cv2.connectedComponents()标记的结果:将值1添加到所有标记的结果中。经过上述处理后,在标注结果中:
1代表背景区域。从数字2开始的值代表不同的前景区域。为了使用分水岭算法,需要在原始图像中标记出未知区域,并将计算出的未知区域标记为0。
关键代码:
ret,markers=cv2 . connected components(fore)
标记=标记1
Markers[ unknown area]=0使用函数cv2.connectedComponents()来标记图像并对其进行校正,以便将未知区域标记为0。
将numpy作为np导入
导入cv2
将matplotlib.pyplot作为plt导入
img=cv2 . im read( water _ coins . jpg )
gray=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
ishow=img.copy()
ret,thresh=cv2.threshold(gray,0,255,cv2。THRESH_BINARY_INV cv2。OTSU)
kernel=np.ones((3,3),np.uint8)
opening=cv2 . morphologyex(thresh,cv2。MORPH_OPEN,内核,迭代次数=2)
sure_bg=cv2.dilate(opening,kernel,iterations=3)
dist _ transform=cv2 . distance transform(opening,cv2。DIST_L2,5)
ret,fore=cv2 . threshold(dist _ transform,0.7*dist_transform.max(),255,0)
fore=np.uint8(fore)
ret,markers 1=cv2 . connected components(fore)
foreAdv=fore.copy()
unknown=cv2.subtract(sure_bg,foreAdv)
ret,markers 2=cv2 . connected components(fore adv)
markers2=markers2 1
markers2[unknown==255]=0
plt.subplot(121)
plt.imshow(标记1)
plt.axis(“关”)
plt.subplot(122)
plt.imshow(标记2)
plt.axis(“关”)
plt.show()
前景有一个黑边,是标记的未知区域。
函数cv2.watershed()
经过上述处理后,分水岭算法可用于分割预处理后的图像。
在OpenCV中,实现分水岭算法的函数是cv2.watershed(),其语法格式为:markers=cv2.watershed (image,markers)图像是输入图像,必须是8位三通道图像。在使用cv2.watershed()函数处理图像之前,必须用正数粗略地勾勒出图像中所需的分割区域。每个划分的区域将被标记为1、2、3等。对于未确定的区域,需要标记为0。我们可以把标记区域理解为分水岭算法分割的“种子”区域。
Markers是一个32位单通道注释结果,其大小应该与image相同。在标记中,每个像素要么设置为初始“种子值”,要么设置为* *“-1”以指示边界* *。分水岭算法图像分割的例子分水岭算法用于图像分割时,基本步骤如下:
通过形态学开运算对原始图像O进行去噪。通过蚀刻操作获得“确定背景B”。
注意“原图-确定背景”可以在这里获取。用距离变换函数cv2.distanceTransform()计算原始图像,阈值处理得到“确定的前景F”。未知区域UN(UN=O -B-F)使用函数cv2.connectedComponents()对原图像O进行标注,修正函数cv2.connectedComponents()的标注结果。分水岭函数用于分割图像。使用分水岭算法分割图像。
将numpy作为np导入
导入cv2
将matplotlib.pyplot作为plt导入
img=cv2 . im read( water _ coins . jpg )
gray=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2。COLOR_BGR2RGB)
ishow=img.copy()
ret,thresh=cv2.threshold(gray,0,255,cv2。THRESH_BINARY_INV cv2。OTSU)
kernel=np.ones((3,3),np.uint8)
opening=cv2 . morphologyex(thresh,cv2。MORPH_OPEN,内核,迭代次数=2)
sure_bg=cv2.dilate(opening,kernel,iterations=3)
dist _ transform=cv2 . distance transform(opening,cv2。DIST_L2,5)
ret,sure _ fg=cv2 . threshold(dist _ transform,0.7*dist_transform.max(),255,0)
sure_fg=np.uint8(sure_fg)
unknown=cv2.subtract(sure_bg,sure_fg)
ret,markers=cv2 . connected components(sure _ fg)
标记=标记1
标记[未知==255]=0
标记=cv2.watershed(img,标记)
Img[markers==-1]=[0,255,0]# border
plt.subplot(121)
plt.imshow(ishow)
plt.axis(“关”)
plt.subplot(122)
plt.imshow(img)
plt.axis(“关”)
Plt.show()交互式前景提取经典的前景提取技术主要使用纹理(颜色)信息,比如魔棒工具,或者边缘(对比度)信息,比如智能剪刀。
在前景提取开始时,用一个矩形框指定前景区域的大致位置范围,然后重复分割,直到达到最佳效果。
经过上述处理后,提取前景的效果可能并不理想,存在未提取前景,或者提取背景作为前景的情况。此时,用户需要干预提取过程。
在用户原始图像(或任何与原始图像大小相同的图像)的副本中,将待提取的区域标记为白色前景,将待提取的区域标记为黑色背景。然后将标记后的图像作为掩膜,算法继续迭代提取前景,得到最终结果。
PowerPoint 2016提供了“删除背景”功能。
GrabCut算法的具体实现过程。
用一个矩形框标出前景的大概位置。
此时矩形框只显示前景的大概位置,包含了前景和背景,所以这个区域实际上是一个未确定的区域。然而,该区域之外的区域被认为是“定义背景”。根据矩形框外的“确定背景”数据,区分矩形框区域内的前景和背景。高斯混合模型,GMM)用于建模前景和背景。
GMM将根据用户的输入学习并创建一个新的像素分布。未分类像素(可以是背景或前景)根据它们与已知分类像素(前景和背景)的关系进行分类。根据像素的分布生成一张图片,图片中的节点就是像素。
除了像素,还有两个节点:前景节点和背景节点。的所有前景像素都连接到前景节点,所有背景像素都连接到背景节点。连接到前景节点或背景节点的每个像素的边缘的权重由该像素是前景或背景的概率来确定。图中的每个像素都连接到前景节点或背景节点,它们之间有联系。由两个像素连接的边的权值由它们的相似性决定。两个像素的颜色越接近,边缘的权值越大。节点连接后,要解决的问题就变成了连通图。在这个图中,不同的点根据各自边的权重关系,通过切割分为前景节点和背景节点。重复上述过程,直到分类收敛。在OpenCV中,交互式前景提取函数是cv2.grabCut(),其语法格式为:
Mask,bgdmodel,fgdmodel=cv2.grabcut (img,mask,rect,bgdmodel,fgdmodel,ITER计数[,mode]) IMG是输入图像,要求是8位3通道。要求屏蔽图像为8位单通道。该参数用于确定前景区域、背景区域和不确定区域,有四种形式可以设置。2.cv2。GC_BGD:表示背景确定,也可以用0值表示。2.cv2。GC_FGD:就是确定前景的意思。它也可以用值1来表示。2.GC _ PR _ BGD:表示可能的背景。也可以用值2来表示。2.GC _ PR _ FGD:表示可能的前景。也可以用数值3来表示。最后,在使用模板提取前景时,参数值0和2将合并到背景中(均视为0),参数值1和3将合并到前景中(均视为1)。
通常我们可以用白色笔刷和黑色笔刷标记蒙版图像,然后通过转换将白色像素设置为0,黑色像素设置为1。
Rect指的是包含前景对象的区域,这个区域之外的部分被认为是“定义背景”。所以在选择的时候,要确保前景包含在rect指定的范围内;否则,rect之外的前景部分将不会被提取。只有当参数mode的值设置为矩形模式cv2时,参数rect才有意义。GC_INIT_WITH_RECT。
格式为(x,y,w,h),分别表示区域左上角像素的x轴和y轴坐标以及区域的宽度和高度。
如果前景在右下方,不想判断原图的大小,可以直接对w和h使用较大的值。
使用掩码模式时,将该值设置为“无”。
BgdModel是算法内部使用的数组,只需要创建一个大小为(1,65)的numpy.float64数组。fgdModel是算法内部使用的数组,只需要创建一个大小为(1,65)的numpy.float64数组。IterCount表示迭代次数。模式是指迭代模式。其可能的值和含义如下:
RECT和面具可以结合使用(联合)
使用GrabCut算法提取图像的前景。
将numpy作为np导入
导入cv2
将matplotlib.pyplot作为plt导入
o=cv2.imread(lenacolor.png )
orgb=cv2.cvtColor(o,cv2。COLOR_BGR2RGB)
mask=np.zeros(o.shape[:2],np.uint8)
bgdModel=np.zeros((1,65),np.float64)
fgdModel=np.zeros((1,65),np.float64)
rect=(50,50,400,500)
cv2.grabCut(o,mask,rect,bgdModel,fgdModel,5,cv2。RECT)
mask 2=NP . where((mask==2)(mask==0),0,1)。astype(uint8 )
ogc=o*mask2[:np.newaxis]
ogc=cv2.cvtColor(ogc,cv2。COLOR_BGR2RGB)
plt.subplot(121)
plt.imshow(orgb)
plt.axis(“关”)
plt.subplot(122)
plt.imshow(ogc)
plt.axis(“关”)
Plt.show()需要改进才能得到完整的前景对象。
这里标记原图,设置要保留的部分为白色,设置要删除的背景为黑色。使用标记的图像作为模板,使用函数cv2.grabCut()提取前景。
该过程主要包括以下步骤:
函数cv2.grabCut()用于提取cv2中图像的前景。GC_INIT_WITH_RECT模式,获得初步提取结果图像og。打开图像提取前景,比如lena,使用Windows系统自带的笔刷工具。使用白色笔刷来标记要提取的前景区域。使用黑色笔刷标记要删除的背景区域。将当前设置的lena图像保存为模板图像m0。模板图像m0中的白色和黑色值被映射到模板M中。模板图像m0中的白色值(像素值为255)被映射到模板图像M中的明确前景(像素值为1),模板图像m0中的黑色值(像素值为0)被映射到模板图像M中的明确背景(像素值为0).以模板图像M作为函数cv2.grabCut()的模板参数(mask),完成图像og的前景提取。
用刷子标记的模板图像m0不能直接用作模板(即参数遮罩)
函数cv2.grabCut()要求参数mask的值必须是cv2。GC_BGD(确定背景),cv2。GC_FGD(确定前景),cv2。GC_PR_BGD(可能的背景),cv2。GC_PR_FGD(可能的前台),或者0或者1。
模板图像m0中的白黑值必须先映射到模板M上,然后将模板图像M作为函数cv2.grabCut()的模板参数。GrabCut算法中利用模板提取图像前景:
将numpy作为np导入
导入cv2
将matplotlib.pyplot作为plt导入
o=cv2.imread(lenacolor.png )
orgb=cv2.cvtColor(o,cv2。COLOR_BGR2RGB)
mask=np.zeros(o.shape[:2],np.uint8)
bgd=np.zeros((1,65),np.float64)
fgd=np.zeros((1,65),np.float64)
rect=(50,50,400,500)
grabCut(o,mask,rect,bgd,fgd,5,cv2。RECT)
mask2=cv2.imread(mask.png ,0)
mask2Show=cv2.imread(mask.png ,-1)
m2rgb=cv2.cvtColor(mask2Show,cv2。COLOR_BGR2RGB)
mask[mask2==0]=0
mask[mask2==255]=1
mask,bgd,fgd=cv2.grabCut(o,mask,None,bgd,fgd,5,cv2。GC_INIT_WITH_MASK
mask=NP . where((mask==2)(mask==0),0,1)。astype(uint8 )
ogc=o*mask[:np.newaxis]
ogc=cv2.cvtColor(ogc,cv2。COLOR_BGR2RGB)
plt.subplot(121)
plt.imshow(m2rgb)
plt.axis(“关”)
plt.subplot(122)
plt.imshow(ogc)
plt.axis(“关”)
Plt.show()在函数cv2.grabCut()的实际使用中,也可以不经过矩形初始化直接使用模板模式。构建模板图像,其中:
使用像素值0来标记背景。使用像素值1来标记前景。用像素值2标记可能的背景。使用像素值3来标记可能的前景。模板构造完成后,可以直接在函数cv2.grabCut()中使用,对原始图像进行处理,提取前景。
通常,自定义模板的步骤如下:
先用numpy.zeros构造一个内部像素值全为0(表示背景确定)的图像掩膜,以便在后续步骤中逐步细化模板图像。使用mask[30:512,50:400]=3将模板图像中第30行到第512行,第50列到第400列的区域划分为可能的前景(像素值为3,对应的参数mask表示“可能的前景”)。使用mask[50:300,150:200]=1将模板图像中第50行到第300行,第150列到第200列的区域划分为确定的前景(像素值为1,对应的参数mask表示“确定的前景”)。在GrabCut算法中,直接使用用户自定义的模板来提取图像的前景。
将numpy作为np导入
导入cv2
将matplotlib.pyplot作为plt导入
o=cv2.imread(lenacolor.png )
orgb=cv2.cvtColor(o,cv2。COLOR_BGR2RGB)
bgd=np.zeros((1,65),np.float64)
fgd=np.zeros((1,65),np.float64)
mask2=np.zeros(o.shape[:2],np.uint8)
#首先,将遮罩的所有值构造为0(以确定背景),然后在后续步骤中根据需要修改部分值。
Mask 2 [30: 512,50:400]=Lena头像的3 #可能区域
面具2 [50: 300,150: 200]=1 #丽娜头像的确定区域。如果没有设置这个区域,头像的提取是不完整的。
cv2.grabCut(o,mask2,None,bgd,fgd,5,cv2。GC_INIT_WITH_MASK
mask 2=NP . where((mask 2==2)(mask 2==0),0,1)。astype(uint8 )
ogc=o*mask2[:np.newaxis]
ogc=cv2.cvtColor(ogc,cv2。COLOR_BGR2RGB)
plt.subplot(121)
plt.imshow(orgb)
plt.axis(“关”)
plt.subplot(122)
plt.imshow(ogc)
plt.axis(“关”)
plt.show()
对于不同的图像,要构造不同的模板来划分其确定前景、确定背景、可能前景和可能背景。
风暴中的白杨
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。