参考:
1、http://docs.opencv.org/3.3.0/ 官方文档api
2、http://docs.opencv.org/3.3.0/d6/d00/tutorial_py_root.html 官方英文教程
3、https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html
4、https://github.com/makelove/OpenCV-Python-Tutorial# 进阶教程
5、https://docs.opencv.org/3.3.0/index.html 官方英文教程
6、https://github.com/abidrahmank/OpenCV2-Python-Tutorials
7、https://www.learnopencv.com/
8、http://answers.opencv.org/questions/ OpenCV论坛
注:安装的版本 opencv_python-3.3.0-cp36-cp36m-win_amd64.whl
参考:https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html
- Understand what contours are.
- Learn to find contours, draw contours etc
- You will see these functions : cv2.findContours(), cv2.drawContours()
让我们看看如何找到二进制图像的轮廓:
import numpy as np import cv2 im = cv2.imread('Lenna.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) cv2.imshow('img',image) cv2.waitKey(0) cv2.destroyAllWindows()参见cv2.findContours()函数有三个参数,第一个是源图像,第二个是轮廓检索模式,第三个是轮廓逼近方法。 并输出图像,轮廓和层次结构。 轮廓是图像中所有轮廓的Python列表。 每个单独的轮廓是对象的边界点的(x,y)坐标的Numpy数组。
稍后我们将详细讨论第二和第三个参数和关于层次结构。 在此之前,代码示例中赋予它们的值对所有图像都可以正常工作。
要绘制轮廓,使用cv2.drawContours函数。 它也可以用来绘制任何形状,只要你有它的边界点。 它的第一个参数是源图像,第二个参数是应该作为Python列表传递的轮廓,第三个参数是轮廓的索引(在绘制单个轮廓时有用)绘制所有轮廓,传递-1),剩余的参数是颜色,厚度 等等
绘制图像中的所有轮廓:
img = cv2.drawContours(img, contours, -1, (0,255,0), 3)绘制第4个轮廓
img = cv2.drawContours(img, contours, 3, (0,255,0), 3)
但大多数时候,下面的方法将是有用的:
cnt = contours[4] img = cv2.drawContours(img, [cnt], 0, (0,255,0), 3)
最后两个方法是一样的,但是当你向前看时,你会看到最后一个更有用。
import numpy as np import cv2 im = cv2.imread('messi5.jpg') imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) cv2.imshow('img0',imgray) ret,thresh = cv2.threshold(imgray,127,255,0) image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) img = cv2.drawContours(imgray, contours, -1, (0,255,0), 3) # -1 表示绘制所有轮廓 cv2.imshow('img1',img) img = cv2.drawContours(imgray, contours, 3, (0,255,0), 3) # 绘制第4个轮廓 cv2.imshow('img2',img) # 但大多数时候,下面的方法将是有用的: cnt = contours[4] img = cv2.drawContours(imgray, [cnt], 0, (0,255,0), 3) cv2.imshow('img3',img) cv2.imshow('img',image) cv2.waitKey(0) cv2.destroyAllWindows()
In this article, we will learn
- To find the different features of contours, like area, perimeter, centroid, bounding box etc
- You will see plenty of functions related to contours.
import cv2 import numpy as np img = cv2.imread('lenna.png',0) ret,thresh = cv2.threshold(img,127,255,0) image,contours,hierarchy = cv2.findContours(thresh, 1, 2) cnt = contours[0] M = cv2.moments(cnt) print(M)从这 moments,您可以提取有用的数据,如面积,质心等。质心由关系给出, and
cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00'])
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt,True)
它根据我们指定的精度将轮廓形状近似为具有较少数量顶点的另一形状。 这是Douglas-Peucker算法的实现。 检查维基百科页面的算法和演示。
要理解这一点,假设您正在尝试在图像中找到一个正方形,但是由于图像中存在一些问题,您没有获得一个完美的方形,而是一个“坏的形状”(如下图所示)。 现在您可以使用此功能近似形状。 在这里,第二个参数称为epsilon,它是从轮廓到近似轮廓的最大距离。 这是一个精度参数。 需要一个明智的选择来获得正确的输出。
epsilon = 0.1*cv2.arcLength(cnt,True) approx = cv2.approxPolyDP(cnt,epsilon,True)
Convex Hull将看起来类似于轮廓近似,但它不是(两者可能在某些情况下提供相同的结果)。 这里,cv2.convexHull()函数检查一个曲线的凸性缺陷并进行修正。 一般来说,凸曲线是总是凸出的或至少平坦的曲线。 如果内部膨胀,则称为凸面缺陷。 例如,检查下面的图像。 红线显示手的凸包。 双面箭头标记显示凸度缺陷,这是船体与轮廓的局部最大偏差。
hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]参数细节:
hull = cv2.convexHull(cnt)但是如果要查找凸性缺陷,则需要传递returnPoints = False。 要理解它,我们将把上面的矩形图像。 首先我发现它的轮廓为cnt。 现在我发现它的凸包具有returnPoints = True,我得到以下值:[[[234 202]],[[51 202]],[[51 79]],[[234 79]]] 矩形点。 现在如果对returnPoints = False做同样的,我得到以下结果:[[129],[67],[0],[142]]。 这些是轮廓中相应点的指标。 例如,检查与第一个结果相同的第一个值:cnt [129] = [[234,202]](对于其他结果,等等)。
k = cv2.isContourConvex(cnt)
有两种类型的边界矩形。
x,y,w,h = cv2.boundingRect(cnt) img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
rect = cv2.minAreaRect(cnt) box = cv2.boxPoints(rect) box = np.int0(box) im = cv2.drawContours(im,[box],0,(0,0,255),2)两个矩形都显示在单个图像中。 绿色矩形显示正常的边界。 红色矩形是旋转的矩形。
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
ellipse = cv2.fitEllipse(cnt)
im = cv2.ellipse(im,ellipse,(0,255,0),2)
rows,cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)
import cv2 import numpy as np img = cv2.imread('j.png',0) ret,thresh = cv2.threshold(img,127,255,0) image,contours,hierarchy = cv2.findContours(thresh, 1, 2) cnt = contours[0] # x,y,w,h = cv2.boundingRect(cnt) # img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2) # rect = cv2.minAreaRect(cnt) # box = cv2.boxPoints(rect) # box = np.int0(box) # img = cv2.drawContours(img,[box],0,(0,0,255),2) # # cv2.imshow('img',img) # (x,y),radius = cv2.minEnclosingCircle(cnt) # center = (int(x),int(y)) # radius = int(radius) # img = cv2.circle(img,center,radius,(0,255,0),2) ellipse = cv2.fitEllipse(cnt) img = cv2.ellipse(img,ellipse,(0,255,0),2) cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()
x,y,w,h = cv2.boundingRect(cnt)
aspect_ratio = float(w)/h
area = cv2.contourArea(cnt)
x,y,w,h = cv2.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area
area = cv2.contourArea(cnt)
hull = cv2.convexHull(cnt)
hull_area = cv2.contourArea(hull)
solidity = float(area)/hull_area
area = cv2.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)
(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)
mask = np.zeros(imgray.shape,np.uint8) cv2.drawContours(mask,[cnt],0,255,-1) pixelpoints = np.transpose(np.nonzero(mask)) #pixelpoints = cv2.findNonZero(mask)这里,使用两个方法,一个使用Numpy函数,下一个使用OpenCV函数(最后一个注释行)也是这样做的。 结果也是一样的,但略有不同。 Numpy以(行,列)格式给出坐标,而OpenCV以(x,y)格式给出坐标。 所以基本上答案将互换。 请注意,row = x和column = y。
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,mask = mask)
mean_val = cv2.mean(im,mask = mask)
leftmost = tuple(cnt[cnt[:,:,0].argmin()][0]) rightmost = tuple(cnt[cnt[:,:,0].argmax()][0]) topmost = tuple(cnt[cnt[:,:,1].argmin()][0]) bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])例如,如果我将其应用于印度地图,我会得到以下结果:
hull = cv2.convexHull(cnt,returnPoints = False) defects = cv2.convexityDefects(cnt,hull)记住我们必须在找到凸包时传递returnPoints = False,才能找到凸性缺陷。
它返回一个数组,其中每行包含这些值 - [起始点,终点,最远点,到最远点的近似距离]。 我们可以使用图像进行可视化。 我们画一条加入起点和终点的线,然后在最远点画一个圆。 记住返回的前三个值是cnt的索引。 所以我们必须把这些值从cnt。
import cv2 import numpy as np img = cv2.imread('contour.jpg') img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(img_gray, 127, 255,0) image,contours,hierarchy = cv2.findContours(thresh,2,1) cnt = contours[0] hull = cv2.convexHull(cnt,returnPoints = False) defects = cv2.convexityDefects(cnt,hull) for i in range(defects.shape[0]): s,e,f,d = defects[i,0] start = tuple(cnt[s][0]) end = tuple(cnt[e][0]) far = tuple(cnt[f][0]) cv2.line(img,start,end,[0,255,0],2) cv2.circle(img,far,5,[0,0,255],-1) cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()
该功能可以找到图像中某个点与轮廓之间的最短距离。 当点在轮廓外部时,返回的距离为负,当点在内部时为正,如果点在轮廓上,则为零。
例如,我们可以检查点(50,50)如下:
dist = cv2.pointPolygonTest(cnt,(50,50),True)
在函数中,第三个参数是measureDist。 如果为True,则找到已签名的距离。 如果为False,则会发现该点是在内部还是外部还是在轮廓上(分别返回+1,-1,0)。
如果您不想找距离,请确保第三个参数为False,因为这是一个耗时的过程。 所以,使它成为假提供2-3倍加速。
import cv2 import numpy as np img1 = cv2.imread('star.jpg',0) img2 = cv2.imread('star2.jpg',0) ret, thresh = cv2.threshold(img1, 127, 255,0) ret, thresh2 = cv2.threshold(img2, 127, 255,0) contours,hierarchy = cv2.findContours(thresh,2,1) cnt1 = contours[0] contours,hierarchy = cv2.findContours(thresh2,2,1) cnt2 = contours[0] ret = cv2.matchShapes(cnt1,cnt2,1,0.0) print ret
This time, we learn about the hierarchy of contours, i.e. the parent-child relationship in Contours.
在这个图像中,有几个形状,我从0-5编号。 2和2a表示最外面的盒子的外部和内部轮廓。
这里,轮廓0,1,2是外部的或最外面的。 我们可以说,它们处于层次结构0,或者只是它们处于相同的层次结构。
接下来是轮廓2a。 它可以被认为是轮廓-2(或相反的方式,轮廓-2是轮廓2a的父母)的小孩。 所以让它在层次结构1中。 类似的轮廓-3是轮廓-2的小孩,它进入下一层。 最后轮廓4,5是轮廓3a的孩子,他们进入最后一层次。 从我编号框的方式,我会说轮廓-4是轮廓3a的第一个孩子(它也可以是轮廓-5)。
我提到这些东西要了解同样的层次结构,外部轮廓,子轮廓,父轮廓,第一个孩子等等。现在让我们进入OpenCV。
如果没有孩子或父母,那么该字段被取为-1
所以现在我们知道OpenCV中使用的层次结构样式,我们可以借助上面给出的相同图像来查看OpenCV中的轮廓检索模式。 即,像cv2.RETR_LIST,cv2.RETR_TREE,cv2.RETR_CCOMP,cv2.RETR_EXTERNAL等标志是什么意思?
>>> hierarchy array([[[ 1, -1, -1, -1], [ 2, 0, -1, -1], [ 3, 1, -1, -1], [ 4, 2, -1, -1], [ 5, 3, -1, -1], [ 6, 4, -1, -1], [ 7, 5, -1, -1], [-1, 6, -1, -1]]])如果您不使用任何层次结构功能,这是您的代码中使用的好选择。
>>> hierarchy array([[[ 1, -1, -1, -1], [ 2, 0, -1, -1], [-1, 1, -1, -1]]])如果要 仅提取外部轮廓,则可以使用此标志。 在某些情况下可能会有用。
所以考虑第一个轮廓,即轮廓0。它是层次结构1。它有两个孔,轮廓1和2,它们属于层次2。因此对于轮廓0,相同层级的下一轮廓是轮廓-3。而没有以前的。而第一个是小孩是等级-2的轮廓1。它没有父级,因为它在层次结构1中。所以它的层次数组是[3,-1,1,-1]
现在轮廓-1。它在层次结构2中。下一个层次相同(轮廓-1的父母身份)是轮廓2。没有前一个。没有孩子,但父母是轮廓0。所以数组是[2,-1,-1,0]。
类似的轮廓-2:它在层次2。轮廓0下的相同层次结构中没有下一个轮廓。所以没有下一个以前是轮廓-1。没有孩子,父母是轮廓0。所以数组是[-1,1,-1,0]。
轮廓-3:层次结构-1中的下一个是轮廓-5。以前是轮廓0。孩子是轮廓4,没有父母。所以数组是[5,0,4,-1]。
轮廓-4:轮廓3下的等级2,没有兄弟。所以没有下一个,没有以前,没有孩子,父母是轮廓-3。所以数组是[-1,-1,-1,3]。
剩下你可以填写这是我得到的最后答案:
>>> hierarchy array([[[ 3, -1, 1, -1], [ 2, -1, -1, 0], [-1, 1, -1, 0], [ 5, 0, 4, -1], [-1, -1, -1, 3], [ 7, 3, 6, -1], [-1, -1, -1, 5], [ 8, 5, -1, -1], [-1, 7, -1, -1]]])
取轮廓0:处于层次结构0。 相同层次结构中的下一个轮廓是轮廓7。 没有以前的轮廓。 孩子是轮廓-1。 没有父母 所以数组是[7,-1,1,-1]。
轮廓2:它在层次结构1。 没有轮廓在同一水平。 没有前一个。 孩子是轮廓2。 父轮廓为0。 所以数组是[-1,-1,2,0]。
剩下的,尝试一下自己。 以下是完整的答案:
>>> hierarchy array([[[ 7, -1, 1, -1], [-1, -1, 2, 0], [-1, -1, 3, 1], [-1, -1, 4, 2], [-1, -1, 5, 3], [ 6, -1, -1, 4], [-1, 5, -1, 4], [ 8, 0, -1, -1], [-1, 7, -1, -1]]])