3.5、Canny边缘检测
OpenCV提供了Canny边缘检测函数来识别边缘。它有5个步骤:使用高斯滤波器对图像进行去噪、计算梯度、在边缘上使用最大抑制(NMS)、在检测到的边缘上使用双阀值去除
假阳性(false positive),最后还会分析出所有的边缘及其之间的连接,以保留真正的边缘并消除不明显的边缘。
import cv2 import numpy as np img = cv2.imread("flower.jpg") cv2.imwrite("canny.jpg", cv2.Canny(img, 200, 300)) cv2.imshow("image", img) cv2.imshow("canny", cv2.imread("canny.jpg")) cv2.waitKey() cv2.destroyAllWindows()
运行效果:
3.6、轮廓检测
在计算机视觉中,轮廓检测是另一个比较重要的任务,不单是用来检测图像或视频中物体的轮廓,而且还有其他操作与轮廓检测有关。这些操作有:计算多边形边界、
形状逼近和计算感兴趣区域。这是与图像数据交互时的简单操作,因为NumPy中的矩形区域可以使用数组切片(slice)来定义。在介绍物体检测(包括人脸)和物体跟踪的概念时会大量使用这种技术。
import cv2 import numpy as np import os img = np.zeros((200, 200), dtype = np.uint8) # 创建一个200x200大小的黑色空白图像, img[50:150, 50:150] = 255 # 在图像的中央放置一个白色方块 ret, thresh = cv2.threshold(img, 127, 255, 0) #对图像进行二值化操作 contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 寻找轮廓 color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # 颜色空间转换 img = cv2.drawContours(color, contours, -1, (0, 0, 255), 5) # 画出轮廓,-1,表示所有轮廓,画笔颜色为(0, 255, 255),即Red,粗细为5 cv2.imshow("contours",color) cv2.waitKey() cv2.destroyAllWindows()
findContours()函数有三个参数:输入图像、层次类型和轮廓逼近方法。
这个函数会修改输入图像,因此建议使用原始图像的一份拷贝(如:通过img.copy()来作为输入图像)。
由函数返回的层次树相当重要:cv2.RETR_TREE参数会得到图像中轮廓的整体层次结构,以此来建立轮廓之间的“关系”。如果只想得到最外面的轮廓,可使用cv2.RETR_EXTERNAL。
这对消除包含在其他轮廓中的轮廓很有用(如在大多数情形下,不需要检测一个目标包含在另一个与之相同的目标里面)
findContours()函数有三个返回值:修改后的图像、图像的轮廓以及它们的层次。使用轮廓来画出图像的彩色版本(即把轮廓画成绿色),并显示出来。
注意:返回三个值报错,实际上只返回两个值(图像的轮廓以及它们的层次)
cv2.threshold()简单阈值
这个函数有四个参数,第一个原图像,第二个进行分类的阈值,第三个是高于(低于)阈值时赋予的新值,第四个是一个方法选择参数,常用的有:
cv2.THRESH_BINARY(黑白二值)
cv2.THRESH_BINARY_INV(黑白二值反转)
cv2.THRESH_TRUNC (得到的图像为多像素值)
cv2.THRESH_TOZERO
cv2.THRESH_TOZERO_INV
该函数有两个返回值,第一个retVal(得到的阈值(在后面一个方法中会用到)),第二个就是阈值化后的图像。
cvCvtColor(...)是Opencv里的颜色空间转换函数,可以实现RGB颜色向HSV,HSI等颜色空间的转换,也可以转换为灰度图像。参数CV_RGB2GRAY是RGB到gray。参数 CV_GRAY2RGB是gray到RGB.处理结果是彩色的,则转灰色就是了
运行效果如下:
3.7、边界框、最小矩形区域和最小闭圆的轮廓
找到一个正方形轮廓很简单,要找到不规则的、歪斜的以及旋转的形状可用 OpenCV的cv2.findContours函数,它能得到最好的结果,下面来看一幅图像:
现实的应用对目标的边界框、最小矩形面积、最小闭圆特别感兴趣。将cv2.findContours函数与少量的OpenCV的功能想结合就能非常容易地实现这些功能。
绘制矩形
函数:cv2.rectangle(img,(380,0),(511,111),(255,0,0),3),需要确定的就是矩形的两个点(左上角与右下角),颜色,线的类型(不设置就默认)
import cv2 import numpy as np import os from matplotlib import pyplot as plt img = np.zeros((512,512,3),np.uint8) #生成一个空彩色图像 cv2.rectangle(img,(20,20),(411,411),(0,255,0),5) plt.imshow(img,'brg') plt.show()
import cv2 import numpy as np import os img = cv2.pyrDown(cv2.imread("hammer.png", cv2.IMREAD_UNCHANGED)) ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY) contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: # find bounding box coordinates # 现计算出一个简单的边界框 x, y, w, h = cv2.boundingRect(c) # 将轮廓信息转换成(x, y)坐标,并加上矩形的高度和宽度 cv2.rectangle(img, (x,y), (x+w, y+h), (0, 255, 0), 2) # 画出矩形 # find minimum area # 计算包围目标的最小矩形区域 rect = cv2.minAreaRect(c) # calculate coordinate of the minimum area rectangle box = cv2.boxPoints(rect) # normalize coordinates to integers box =np.int0(box) # 注:OpenCV没有函数能直接从轮廓信息中计算出最小矩形顶点的坐标。所以需要计算出最小矩形区域, # 然后计算这个矩形的顶点。由于计算出来的顶点坐标是浮点型,但是所得像素的坐标值是整数(不能获取像素的一部分), # 所以需要做一个转换 # draw contours cv2.drawContours(img, [box], 0, (0, 0, 255), 3) # 画出该矩形 # calculate center and radius of minimum enclosing circle (x, y), radius = cv2.minEnclosingCircle(c) # 会返回一个二元组,第一个元素为圆心的坐标组成的元组,第二个元素为圆的半径值。 # cast to integers center = (int(x), int(y)) radius = int(radius) # draw the circle img = cv2.circle(img, center, radius, (0, 255, 0), 2) cv2.drawContours(img, contours, -1, (255, 0, 0), 1) cv2.imshow("contours", img) cv2.waitKey() cv2.destroyAllWindows()
在导入模块后,加载图像,然后在源图像的灰度图像上执行一个二值化操作。这样做之后,可在这个灰度图像上执行所有计算轮廓的操作,但在源图像上可利用色彩信息来画这些轮廓。
cv2.drawContours(img, [box], 0, (0, 0, 255), 3):首先,该函数与所有绘图函数一样,它会修改源图像。其次,该函数的第二个参数接收一个保存着轮廓的数组,
从而可以在一次操作中绘制一系列的轮廓。因此如果只有一组点来表示多边形轮廓,就需要把这组点放到一个数组里。该函数的第三个参数是要绘制的轮廓数组的索引:-1表示绘制所有的轮廓,
否则只会绘制轮廓数组里指定的轮廓。大多数绘图函数把绘图的颜色和密度(thickness)放在最后两个参数里。
函数介绍:
cv2.imread():读入图片,共两个参数,第一个参数为要读入的图片文件名,第二个参数为如何读取图片,包括cv2.IMREAD_COLOR:读入一副彩色图片;
cv2.IMREAD_GRAYSCALE:以灰度模式读入图片;cv2.IMREAD_UNCHANGED:读入一幅图片,并包括其alpha通道。cv2.imread('flower.jpg',0)表示已灰度模式读入
cv2.imshow():创建一个窗口显示图片,共两个参数,第一个参数表示窗口名字,可以创建多个窗口中,但是每个窗口不能重名;第二个参数是读入的图片。
cv2.waitKey():键盘绑定函数,共一个参数,表示等待毫秒数,将等待特定的几毫秒,看键盘是否有输入,返回值为ASCII值。如果其参数为0,则表示无限期的等待键盘输入。
cv2.destroyAllWindows():删除建立的全部窗口
cv2.destroyWindows():删除指定的窗口。
cv2.imwrite():保存图片,共两个参数,第一个为保存文件名,第二个为读入图片
Opencv中可以通过函数cv2.pyrDown()和cv2.pyrUp()来构建金字塔。
函数cv2.pyrDown()是从一个高分辨率图像变成低分辨率图像的。cv2.pyrDown()函数接受3个参数:
tmp: 当前图像,初始化为原图像 src 。
dst: 目的图像( 显示图像,为输入图像的一半)
Size( tmp.cols/2, tmp.rows/2 ) :目的图像大小, 既然我们是向下采样
凸轮廓与Douglas-Peucker算法
大多数处理轮廓的时候,物体的形状(包括凸形状)都是变化多样的。凸形状内部的任意两点之间的连线都在该形状里面。
cv2.approxPloyDP是一个OpenCV函数,它用来计算近似的多边形框。该函数有三个参数:
第一个参数为“轮廓”;
第二个参数为“ε值”,它表示源轮廓与近似多边形的最大差值(这个值越小,近似多边形与源轮廓越接近);
第三个参数为“布尔标记”,它表示这个多边形是否闭合。
ε值对获取有用的轮廓非常重要。是为所得到的近似多边形周长与源轮廓周长之间的最大差值,这个差值越小,近似多边形与源轮廓就越相似。
可通过OpenCV的cv2.arcLength函数来得到轮廓的周长信息。
epsilon = 0.01 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
可通过OpenCV来有效地计算一个近似多边形,多边形周长与源轮廓周长之比就为ε。
为了计算凸形状,需要用OpenCV的cv2.convexHull函数来获取处理过的轮廓信息,代码为:
hull = cv2.convexHull(cnt)
3.8、直线和圆检测
Hough变换是直线和形状检测背后的理论基础。
绘制直线
函数为:cv2.line(img,Point pt1,Point pt2,color,thickness=1,line_type=8 shift=0)
有值的代表有默认值,不用给也行。可以看到这个函数主要接受参数为两个点的坐标,线的颜色(彩色图像的话颜色就是一个1*3的数组)
import cv2 import numpy as np import os from matplotlib import pyplot as plt img = np.zeros((512,512),np.uint8)#生成一个空灰度图像 cv2.line(img,(0,200),(511,511),255,5) plt.imshow(img,'gray') plt.show()
import cv2 import numpy as np import os from matplotlib import pyplot as plt img = np.zeros((512,512,3),np.uint8)#生成一个空彩色图像 cv2.line(img,(200,0),(511,511),(0,255,0),5) plt.imshow(img,'brg') plt.show()
3.9、直线检测
直线检测可通过HoughLines和HoughLinesP函数来完成,它们仅有的差别是:第一个函数使用标准的Hough变换,第二个函数使用概率Hough变换。HoughLinesP函数之所以成为
概率版本的Hough变换是因为它只通过分析点的子集并估计这些点都属于一条直线的概率,这是标准Hough变换的优化版本,该函数的计算代价会少一些,执行会变得更快。
import cv2 import numpy as np img = cv2.imread("lines.png") gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 120) minLineLength = 20 maxLineGap = 5 lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength, maxLineGap) for x1, y1, x2, y2 in lines[0]: cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow("edges", edges) cv2.imshow("lines", img) cv2.waitKey() cv2.destroyAllWindows()
HoughLines函数会接收一个由Canny边缘检测滤波器处理过的单通道二值图像,不一定需要Canny滤波器,但是一个经过去噪并只有边缘的图像当作Hough变换的输入会很不错。
HoughLinesP的参数:
需要处理的参数;
线段的几何表示rho和theta,一般分别取1和np.pi/180;
阈值,低于该阈值的直线会被忽略,Hough变换可以理解为投票箱和投票数之间的关系,每一个投票箱代表一个直线,投票数达到阈值的直线会被保留,其他的会被删除。
minLineLength和maxLineGap
3.10、绘制圆
绘制圆形只需要确定圆心与半径,函数: cv2.circle (img,(380,0),63,(255,0,0),3),
import cv2 import numpy as np from matplotlib import pyplot as plt img = np.zeros((512,512,3),np.uint8)#生成一个空彩色图像 cv2.circle(img,(100,100),50,(55,255,155),5) plt.imshow(img,'brg') plt.show(
3.11、圆检测
OpenCV的HoughCircles函数可用来检测圆,它与使用HoughLines函数类似。像用来决定删除或保留直线的两个参数minLineLength和maxLineGap一样,
HoughCircles有一个圆心间的最小距离和圆的最小及最大半径。
import cv2 import numpy as np planets = cv2.imread("planet_glow.png") gray_img = cv2.cvtColor(planets, cv2.COLOR_BGR2GRAY) img = cv2.medianBlur(gray_img, 5) cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 120, param1=100, param2 = 30, minRadius = 0, maxRadius = 0) circles = np.uint16(np.around(circles)) for i in circles[0,:]: # draw the outer circle cv2.circle(planets, (i[0], i[1]), i[2],(0, 255, 0),2) # draw the center of the circle cv2.circle(planets, (i[0], i[1]), 2, (0, 0,255), 3) cv2.imwrite("planets_circles.jpg",planets) cv2.imshow("HoughCircles", planets) cv2.waitKey() cv2.destroyAllWindows()
3.12、绘制椭圆
椭圆涉及到长轴短轴,椭圆圆心,旋转角度等。
import cv2 import numpy as np from matplotlib import pyplot as plt img = np.zeros((512,512,3),np.uint8)#生成一个空彩色图像 cv2.ellipse(img,(256,256),(150,100),0,0,180,250,-1) #注意最后一个参数-1,表示对图像进行填充,默认是不填充的,如果去掉,只有椭圆轮廓了 plt.imshow(img,'brg') plt.show()
3.13、检测其他形状
Hough变换能检测的形状仅限于圆,前面提到过检测任何形状的方法,特别是用approxPloyDP函数来检测,该函数提供多边形的近似,所以如果你的图像有多边形,再结合cv2.findContours函数和
cv2.approxPloyDP函数,就能相当准确地检测出来。