1 实验目的
熟悉opencv读入图片,高斯去噪,二值化,绘制目标矩形边框。
2 实验设备
安装了python和pychrm的电脑一台。
3 实验内容
包含图片的导入、绘制矩形、显示图片。
4实验原理
opencv中轮廓特征包括:如面积,周长,质心,边界框等。
我们将图像二值化后,用findContours函数就可以很方便的找出图像的轮廓信息,循环遍历每一个信息点,cv2.boundingRect()函数再计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的,cv2.rectangle函数画出来即可。
函数原型:
findContours( image,mode, method, Point offset=Point())
第一个参数:image,单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像。
第二个参数:mode 检索轮廓的模式,若取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略。
取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓。
取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层。两个等级关系:顶层为连通域的外围边界,次层为孔的内层边界。
取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
第三个参数:int型的method,定义轮廓的近似方法:
取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内。
取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法。
findContours函数最终会返回三个值,第一个是图像,其中找到的轮廓信息都会被保存到第二个参数contours列表里,其中存储这图像中的所有轮廓;第三个参数是(轮廓的)层析结构,这是一个ndarray,其中的元素个数和轮廓个数相同,对于每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数。每一个轮廓都是一个 Numpy 数组,包含对象边界点(x,y)的坐标。
绘制轮廓
函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设置为 -1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。
import numpy as np
import cv2
im = cv2.imread('../images/numb.png')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,0)
image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
绘制独立轮廓,如第四个轮廓。
img = cv2.drawContour(img, contours, -1, (0,255,0), 3)
但是大多数时候,下面的方法更有用。
img = cv2.drawContours(img, contours, 3, (0,255,0), 3)
在理解层级关系前,需要对数据结构有一定的了解。
例如下图(将树结构和轮廓级联系在一起),A和B为同级,可以认为B是A的后一个轮廓,A是B的前一个轮廓;C和D为A的子树,E和F为B的子树,可以认为C和D为A的子轮廓,A为C和D的父轮廓。
树结构
为了更加直观,可以利用实际的图像进一步说明轮廓层级的关系,例如以下的源图,一共有4个轮廓,序号分别为0 1 2 3。
特别地,序号在程序中findcontours函数会自动标记,也是轮廓检索的序号,n个轮廓其检索序号从 0 到 n-1 。
轮廓层级关系
经分析,0号轮廓没有同级轮廓,有两个子级轮廓1和3,没有父级轮廓,所以其轮廓继承关系向量hierarchy为[-1 -1 1-1],-1表示无对应的关系,1表示0号轮廓的一个子轮廓的序号为1号。
同样地,1号轮廓有同级轮廓3(认为是后一个轮廓,那么无前一个轮廓),也有子级轮廓2,也有父级轮廓0,所以其轮廓继承关系向量hierarchy为[3 -1 2 0]。
同样地,2号轮廓没有同级轮廓,也没有子级轮廓,但是有父级轮廓1,所以其轮廓继承关系向量hierarchy为[-1 -1 -1 1]。
同样地,3号轮廓有同级轮廓(其前一个轮廓为1,无后一个轮廓),没有子级轮廓,但是有父级轮廓0,所以其轮廓继承关系向量hierarchy为[-1 1 -1 0]。
以上的轮廓层级关系,可以用树结构表示如下:
子级和父级关系
找出图像的轮廓信息后,可以用下面的方法筛选特定的轮廓(比如我们需要轮廓面积大于某个值,还有周长等)
cv2.boundingRect()函数获取轮廓的范围,即左上角原点,以及他的高和宽,这样就能得到外接矩形。然后可以用cv2.rectangle()方法画出矩形轮廓。
cv2.minAreaRect函数是找出面积最小的矩形,需要传入轮廓的信息点。
cv2.minEnclosingCircle函数计算最小包围圆的中心和半径。
cv2.minAreaRect©函数计算出面积最小的矩形。
图像的矩可以帮助我们计算图像的质心,面积等。首先,使用findContour()函数找出图像中包含的轮廓信息,然后对所有轮廓进行遍历,并计算每一个轮廓的力矩(Moment),就可以得出物体的质心位置。Moments函数可以求出轮廓的几何矩空间矩的公式为:
可以知道,对于01二值化的图像,m00即为轮廓的面积,中心矩的公式为:
其中中心Center(x0,y0):
这样就可以求得质心,最后画出质心和轮廓。列如:
import cv2import numpy as npimg = cv2.imread('star.jpg',0)
ret,thresh = cv2.threshold(img,127,255,0)
contours,hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv2.moments(cnt)
print(M)
根据这些矩的值,我们可以计算出对象的重心:
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
Opencv 提供Rectangle用来绘制一个矩形框的,通常用在图片的标记上。Rectangle函数原型:
rectangle(img, pt1, pt2, color, shift)
参数:
img:源图像
pt1,pt2为左上角坐标和右下坐标
color为颜色
shift为线的大小(像素为单位)
5实验步骤
打开pycharm。我们右击相应的文件目录,选择new—>点击Python File,然后输入新建的文件名,点击确定,相应的.py文件就建好了,可以进行编写代码了。
ipoert cv2
import numpy as np
读取图片。
image = cv2.imread("..\images\\numb.png")
将源图像转为灰度图像。
gray = cv2.cvtColor(image,cv.COLOR_BGR2GRAY)
获取二值化图像(掩模图像)。
ret,binary=cv2.threshold(gray,0,255,cv.THRESH_OTSU|cv.THRESH_BINARY_INV)
窗口显示源图像和掩模图像。
cv2.imshow("binary image",binary)
cv2.imshow(" image",image)
findContours找出图像轮廓,这里我们使用cv.RETR_EXTERNAL参数只检测外围轮廓和cv.CHAIN_APPROX_SIMPLE只保存拐点信息,就像只要四个点一样就能画出一个矩形。
i,contours,hireachy= cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
遍历每一个轮廓。contourArea函数获取每个轮廓的面积。boundingRect函数获取轮廓的外接矩形。
for i,contour in enumerate(contours):
area = cv.contourArea(contour)
x,y,w,h = cv.boundingRect(contour)
获取外接矩形宽高比,可以起到一定的筛选作。
rate = min(w,h)/max(w,h)
求轮廓的几何距离。
mm = cv.moments(contour)
求出质心。
cx = mm['m10']/mm['m00']
cy = mm['m01']/mm['m00']
根据几何距获取的中心点,画出中心圆。
cv.circle(image,(np.int(cx),np.int(cy)),2,(0,255,255),-1)
根据轮廓外接矩形返回数据,画出外接矩形。
cv.rectangle(image,(x,y),(x+w,y+h),(0,0,255),2)
显示绘制轮廓的图像。
cv2.imshow("contours", img)
cv2.waitKey()
cv2.destroyAllWindows()
可以看到效果如下。
当然,你也可以绘制外接圆,用minEnclosingCircle函数计算最小包围圆的中心和半径,然后画处理。