1. 更改图像颜色空间 使用cv.cvtColor()
,输入:图像,方法(比如cv.COLOR_BGR2HSV)
1 hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
2. 几何转换操作 调整图像大小
1 2 3 4 5 6 7 8 9 10 11 12 import numpy as npimport cv2 as cv img = cv.imread('ml.png' ) assert img is not None , "file could not be read, check with os.path.exists()" res = cv.resize(img,None ,fx=2 , fy=2 , interpolation = cv.INTER_CUBIC) height, width = img.shape[:2 ] res = cv.resize(img,(2 *width, 2 *height), interpolation = cv.INTER_CUBIC)
使用cv.warpAffine()
平移图片; 使用cv.getRotationMatrix2D()
获取2x3旋转矩阵,如旋转90度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import numpy as npimport cv2 as cv img = cv.imread('ml.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" rows,cols = img.shape M = cv.getRotationMatrix2D(((cols-1 )/2.0 ,(rows-1 )/2.0 ),90 ,1 ) dst = cv.warpAffine(img,M,(cols,rows)) cv.imshow('img' ,dst) cv.waitKey(0 ) cv.destroyAllWindows()
3. 仿射变换 在仿射变换中,原图中平行线在变换后仍然平行。 方法为使用cv.getAffineTransform()
获取2x3的转换矩阵后传入cv.warpAffine()
生成操作矩阵需要3组对应坐标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import numpy as npimport cv2 as cvimport matplotlib.pyplot as plt img = cv.imread('ml.png' ) assert img is not None , "file could not be read, check with os.path.exists()" rows,cols,ch = img.shape pts1 = np.float32([[50 ,50 ],[200 ,50 ],[50 ,200 ]]) pts2 = np.float32([[10 ,100 ],[200 ,50 ],[100 ,250 ]]) M = cv.getAffineTransform(pts1,pts2) dst = cv.warpAffine(img,M,(cols,rows)) plt.subplot(121 ),plt.imshow(img),plt.title('Input' ) plt.subplot(122 ),plt.imshow(dst),plt.title('Output' ) plt.show()
4. 视角变换 视角变换后直线还是直线。变换矩阵是3x3的,需要4组对应已知的坐标,其中不能存在3点共线,这样便能使用cv.getPerspectiveTransform()
获得转换矩阵,然后矩阵传入cv.warpPespective()
即可
1 2 3 4 5 6 pts1 = np.float32([[56 ,65 ],[368 ,52 ],[28 ,387 ],[389 ,390 ]]) pts2 = np.float32([[0 ,0 ],[300 ,0 ],[0 ,300 ],[300 ,300 ]]) M = cv.getPerspectiveTransform(pts1,pts2) dst = cv.warpPerspective(img,M,(300 ,300 ))
5. 阈值处理 使用cv.threshold()
,输入:图像(灰度),阈值,最大值(超阈值的设定为此),方法 返回:使用阈值, 阈值处理的图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import cv2 as cvimport numpy as npfrom matplotlib import pyplot as plt img = cv.imread('ml.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" ret,thresh1 = cv.threshold(img,127 ,255 ,cv.THRESH_BINARY) ret,thresh2 = cv.threshold(img,127 ,255 ,cv.THRESH_BINARY_INV) ret,thresh3 = cv.threshold(img,127 ,255 ,cv.THRESH_TRUNC) ret,thresh4 = cv.threshold(img,127 ,255 ,cv.THRESH_TOZERO) ret,thresh5 = cv.threshold(img,127 ,255 ,cv.THRESH_TOZERO_INV) titles = ['Original Image' ,'BINARY' ,'BINARY_INV' ,'TRUNC' ,'TOZERO' ,'TOZERO_INV' ] images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] for i in range (6 ): plt.subplot(2 ,3 ,i+1 ),plt.imshow(images[i],'gray' ,vmin=0 ,vmax=255 ) plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
自适应阈值cv.ADAPTIVE_THRESH_MEAN_C
阈值 = 邻域均值 - Ccv.ADAPTIVE_THRESH_GAUSSIAN_C
阈值 = 邻域高斯加权和均值 - C blockSize决定领域大小,C是从邻域均值或加权均值中减去的常数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import cv2 as cvimport numpy as npfrom matplotlib import pyplot as plt img = cv.imread('ml.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" img = cv.medianBlur(img,5 ) ret,th1 = cv.threshold(img,127 ,255 ,cv.THRESH_BINARY) th2 = cv.adaptiveThreshold(img,255 ,cv.ADAPTIVE_THRESH_MEAN_C,\ cv.THRESH_BINARY,11 ,2 ) th3 = cv.adaptiveThreshold(img,255 ,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\ cv.THRESH_BINARY,11 ,2 ) titles = ['Original Image' , 'Global Thresholding (v = 127)' , 'Adaptive Mean Thresholding' , 'Adaptive Gaussian Thresholding' ] images = [img, th1, th2, th3] for i in range (4 ): plt.subplot(2 ,2 ,i+1 ),plt.imshow(images[i],'gray' ) plt.title(titles[i]) plt.xticks([]),plt.yticks([]) plt.show()
大津二值化 Otsu’s Binarization 可以不需要选择一个数值作为阈值,从图像直方图确定全局最优阈值,该值通过最小化加权组内方差求得
1 2 ret2,th2 = cv.threshold(img,0 ,255 ,cv.THRESH_BINARY+cv.THRESH_OTSU)
6. 图像平滑(卷积) cv.filter2D()
实现卷积
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt img = cv.imread('ml.png' ) assert img is not None , "file could not be read, check with os.path.exists()" kernel = np.ones((5 ,5 ),np.float32)/25 dst = cv.filter2D(img,-1 ,kernel) blr = cv.blur() plt.subplot(121 ),plt.imshow(img),plt.title('Original' ) plt.xticks([]), plt.yticks([]) plt.subplot(122 ),plt.imshow(dst),plt.title('Averaging' ) plt.xticks([]), plt.yticks([]) plt.show()
图像过滤模糊的一些常用函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt img = cv.imread('ml.png' ) assert img is not None , "file could not be read, check with os.path.exists()" kernel = np.ones((5 ,5 ),np.float32)/25 dst = cv.filter2D(img,-1 ,kernel) blr = cv.blur(img,(5 ,5 )) g_blr = cv.GaussianBlur(img, (5 ,5 ),0 ) m_blr = cv.medianBlur(img, 5 ) bi_blr = cv.bilateralFilter(img,9 , 75 , 75 ) plt.subplot(321 ),plt.imshow(img),plt.title('Original' ) plt.xticks([]), plt.yticks([]) plt.subplot(322 ),plt.imshow(dst),plt.title('Averaging' ) plt.xticks([]), plt.yticks([]) plt.subplot(323 ),plt.imshow(blr),plt.title('Bluring' ) plt.xticks([]), plt.yticks([]) plt.subplot(324 ),plt.imshow(g_blr),plt.title('gaussian' ) plt.xticks([]), plt.yticks([]) plt.subplot(325 ),plt.imshow(m_blr),plt.title('medianBlur' ) plt.xticks([]), plt.yticks([]) plt.subplot(326 ),plt.imshow(bi_blr),plt.title('Bilaterial filtering' ) plt.xticks([]), plt.yticks([]) plt.show()
7. 形态学操作 腐蚀:使用一个kernel扫过二值图片每一个角落,其下所有像素都1时候保留中心位置在原图对应的像素,否则被“腐蚀”为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import cv2 as cvimport numpy as np img = cv.imread('ml.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" kernel = np.ones((5 ,5 ),np.uint8) erosion = cv.erode(img,kernel,iterations = 1 ) dilation = cv.dilate(img,kernel,iterations = 1 ) opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel) closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel) gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel) tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel) blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)
使用cv.getStructuringElement()
生成结构化kernel,输入形状和尺寸元组即可
1 2 3 4 kernel = cv.getStructuringElement(cv.MORPH_RECT,(5 ,5 )) print (kernel)cv.getStructuringElement(cv.MORPH_ELLIPSE,(5 ,5 )) cv.getStructuringElement(cv.MORPH_CROSS,(5 ,5 ))
8. 图像梯度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt img = cv.imread('ml.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" laplacian = cv.Laplacian(img,cv.CV_64F) sobelx = cv.Sobel(img,cv.CV_64F,1 ,0 ,ksize=5 ) sobely = cv.Sobel(img,cv.CV_64F,0 ,1 ,ksize=5 ) plt.subplot(2 ,2 ,1 ),plt.imshow(img,cmap = 'gray' ) plt.title('Original' ), plt.xticks([]), plt.yticks([]) plt.subplot(2 ,2 ,2 ),plt.imshow(laplacian,cmap = 'gray' ) plt.title('Laplacian' ), plt.xticks([]), plt.yticks([]) plt.subplot(2 ,2 ,3 ),plt.imshow(sobelx,cmap = 'gray' ) plt.title('Sobel X' ), plt.xticks([]), plt.yticks([]) plt.subplot(2 ,2 ,4 ),plt.imshow(sobely,cmap = 'gray' ) plt.title('Sobel Y' ), plt.xticks([]), plt.yticks([]) plt.show()
特别注意:将输出的数据类型设定高一些,如cv.CV_64F等,再转为cv.CV_8U,可避免丢失信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt img = cv.imread('opencv.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" sobelx8u = cv.Laplacian(img,cv.CV_8U) sobelx64f = cv.Laplacian(img,cv.CV_64F) abs_sobel64f = np.absolute(sobelx64f) sobel_8u = np.uint8(abs_sobel64f) plt.subplot(1 ,3 ,1 ),plt.imshow(img,cmap = 'gray' ) plt.title('Original' ), plt.xticks([]), plt.yticks([]) plt.subplot(1 ,3 ,2 ),plt.imshow(sobelx8u,cmap = 'gray' ) plt.title('Sobel CV_8U' ), plt.xticks([]), plt.yticks([]) plt.subplot(1 ,3 ,3 ),plt.imshow(sobel_8u,cmap = 'gray' ) plt.title('Sobel abs(CV_64F)' ), plt.xticks([]), plt.yticks([]) plt.show()
9. 图像金字塔 1 2 3 lower_reso = cv.pyrDown(higher_reso) higher_reso2 = cv.pyrUp(lower_reso)
使用金字塔操作混合两个图片的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import cv2 as cvimport numpy as np,sys A = cv.imread('opencv.png' ) B = cv.imread('opencv_white.png' ) assert A is not None , "file could not be read, check with os.path.exists()" assert B is not None , "file could not be read, check with os.path.exists()" G = A.copy() gpA = [G] for i in range (6 ): G = cv.pyrDown(G) gpA.append(G) G = B.copy() gpB = [G] for i in range (6 ): G = cv.pyrDown(G) gpB.append(G) lpA = [gpA[5 ]] for i in range (5 ,0 ,-1 ): GE = cv.pyrUp(gpA[i]) L = gpA[i-1 ] - GE lpA.append(L) lpB = [gpB[5 ]] for i in range (5 ,0 ,-1 ): GE = cv.pyrUp(gpB[i]) L = gpB[i-1 ] - GE lpB.append(L) LS = [] for la,lb in zip (lpA,lpB): rows,cols,dpt = la.shape ls = np.hstack((la[:,0 :cols//2 ], lb[:,cols//2 :])) LS.append(ls) ls_ = LS[0 ] for i in range (1 ,6 ): ls_ = cv.pyrUp(ls_) ls_ = cv.add(ls_, LS[i]) real = np.hstack((A[:,:cols//2 ],B[:,cols//2 :])) cv.imshow('Pyramid_blending2.jpg' ,ls_) cv.imshow('Direct_blending.jpg' ,real) cv.waitKey(0 )
10. Canny 边缘检测 求梯度,边缘与梯度法线垂直,判断边缘点在梯度方向是否为局部最大值,是则保留,否则置零(非极大值抑制);下一步,小于最小值不认为是边缘,大于最大值是确定边缘,位于其间而又与确定边缘相连的也予以保留,否则舍弃。 使用cv.Canny()
,输入:图像,最小值,最大值,用于算梯度的Sobel kernel 尺寸(默认3),L2gradient(True 全面公式较精确,False 默认的简化公式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt img = cv.imread('ml.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" edges = cv.Canny(img,100 ,180 ) plt.subplot(121 ),plt.imshow(img,cmap = 'gray' ) plt.title('Original Image' ), plt.xticks([]), plt.yticks([]) plt.subplot(122 ),plt.imshow(edges,cmap = 'gray' ) plt.title('Edge Image' ), plt.xticks([]), plt.yticks([]) plt.show()
11. 轮廓检测 使用cv.findCountours()
获取轮廓,输入:源图,轮廓模式,近似方法 源图只能是二值图,白色为目标黑色为背景 近似方法用cv.CHAIN_APPROX_NONE
将保存轮廓所有点,而用cv.CHAIN_APPROX_SIMPLE
可以将轮廓用少量数据描述,如矩形仅用4个焦点等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) cv.drawContours(im, contours, -1 , (0 ,255 ,0 ), 2 ) cnt = countours[2 ] M = cv.moments(cnt) c_x = M['m10' ] / M['m00' ] c_y = M['m01' ] / M['m00' ] area = cv.contourArea(cnt) perimeter = cv.arcLength(cnt,True ) epsilon = 0.1 *cv.arcLength(cnt,True ) approx = cv.approxPolyDP(cnt,epsilon,True )
凸包 Convex Hull 凸曲线都是往外凸的,只少也是平的。cv.convexHull
用来修复凸性缺陷(内凹情况)
1 hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]])
points 传入的轮廓
hull 输出,通常免去
clockwise 方向标志 True顺时针
returnPoints 默认True返回凸点坐标,而False返回凸点坐标对应的轮廓点索引 实际使用hull = cv.convexHull(cnt)
就行了
1 print (cv.isContourConvex(cnt))
轮廓的直边界框
1 2 x, y, w, h = cv.boundingRect(cnt) cv.rectangle(im, (x,y),(x+w, y+h),(0 ,0 ,255 ),1 )
轮廓的旋转边界宽 使用cv.minAreaRect()
绘制轮廓的最小面积边界框,返回(中心坐标(x,y),(宽, 高),旋转角度)。传入cv.boxPoints()
绘制矩形
1 2 3 4 rect = cv.minAreaRect(cnt) box = cv.boxPoints(rect) box = np.int0(box) cv.drawContours(im,[box],0 ,(0 ,0 ,255 ),2 )
最小封闭圆 完全包括轮廓的最小圆
1 2 3 4 (x,y),radius = cv.minEnclosingCircle(cnt) center = (int (x),int (y)) radius = int (radius) cv.circle(im,center,radius,(0 ,255 ,0 ),2 )
拟合椭圆
1 2 ellipse = cv.fitEllipse(cnt) cv.ellipse(im,ellipse,(0 ,255 ,0 ),2 )
拟合直线
1 2 3 4 5 rows,cols = img.shape[:2 ] [vx,vy,x,y] = cv.fitLine(cnt, cv.DIST_L2,0 ,0.01 ,0.01 ) lefty = int ((-x*vy/vx) + y) righty = int (((cols-x)*vy/vx)+y) cv.line(img,(cols-1 ,righty),(0 ,lefty),(0 ,255 ,0 ),2 )
12. 轮廓属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 x,y,w,h = cv.boundingRect(cnt) aspect_ratio = float (w)/h area = cv.contourArea(cnt) x,y,w,h = cv.boundingRect(cnt) rect_area = w*h extent = float (area)/rect_area area = cv.contourArea(cnt) hull = cv.convexHull(cnt) hull_area = cv.contourArea(hull) solidity = float (area)/hull_area area = cv.contourArea(cnt) equi_diameter = np.sqrt(4 *area/np.pi) (x,y),(MA,ma),angle = cv.fitEllipse(cnt) mask = np.zeros(imgray.shape,np.uint8) cv.drawContours(mask,[cnt],0 ,255 ,-1 ) pixelpoints = np.transpose(np.nonzero(mask)) min_val, max_val, min_loc, max_loc = cv.minMaxLoc(imgray,mask = mask) mean_val = cv.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 ]) dist = cv.pointPolygonTest(cnt,(50 ,50 ),True ) ret, thresh = cv.threshold(img1, 127 , 255 ,0 ) ret, thresh2 = cv.threshold(img2, 127 , 255 ,0 ) contours,hierarchy = cv.findContours(thresh,2 ,1 ) cnt1 = contours[0 ] contours,hierarchy = cv.findContours(thresh2,2 ,1 ) cnt2 = contours[0 ] ret = cv.matchShapes(cnt1,cnt2,1 ,0.0 ) print ( ret )
13. 轮廓的层级 我们在使用查找轮廓的时候返回了一个hierarchy,即轮廓可能在另一轮廓之内的这种父子关系在opencv中的表达 ** [Next, Previous, First_Child, Parent] ** Next 表示同一层级的下一个轮廓 Previous 同一层级的上一个轮廓 First_Child 第一个子轮廓 Parent 父轮廓
注:如果没有父子轮廓,该位置设为-1
轮廓检索模式RETR_LIST
直接生成所有轮廓,无父子关系RETR_EXTERNAL
只要最外轮廓RETR_CCOMP
排成2级,外部轮廓为层级1,孔洞轮廓为层级2RETR_TREE
完整的层级
14. 直方图 直方图的x轴是亮度,从0到255的(在8bit图像下,可以更改),纵高是每一亮度像素的总数量。直方图分析只用灰度图 直方图常用术语如下:BINS 用来指定x轴上有多少个区间,如256,或者16DIMS 采集数据的维度,如1RANGE 需要采集数据的范围,通常[0,256]
使用OpenCV获取直方图cv.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
images : uint8或float32的源图,用方括号包含,如 “[img]”。
channels : 准备计算的通道的索引,用方括号包含,灰度图可用[0]。彩色图可用[0], [1] 或 [2] 分别计算蓝色、绿色、红色通道的直方图。
mask : 掩膜图,计算全图直方图时候置”None”即可,否则创建一个mask放到这里。
histSize : 用方括号包含的BINS ,如 [256]。
ranges : 范围,通常 [0,256]。1 2 3 img = cv.imread('ml.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" hist = cv.calcHist([img],[0 ],None ,[16 ],[0 ,256 ])
使用Numpy获取直方图,OpenCV比之快40x
1 hist,bins = np.histogram(img.ravel(),256 ,[0 ,256 ])
绘制直方图
1 2 3 4 5 6 7 8 9 10 11 12 import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt img = cv.imread('bottle.png' ) assert img is not None , "file could not be read, check with os.path.exists()" color = ('b' ,'g' ,'r' ) for i,col in enumerate (color): histr = cv.calcHist([img],[i],None ,[256 ],[0 ,256 ]) plt.plot(histr,color = col) plt.xlim([0 ,256 ]) plt.show()
获取掩膜Mask内的直方图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt img = cv.imread('bottle.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" mask = np.zeros(img.shape[:2 ], np.uint8) mask[400 :800 , 400 :1000 ] = 255 masked_img = cv.bitwise_and(img,img,mask = mask) hist_full = cv.calcHist([img],[0 ],None ,[256 ],[0 ,256 ]) hist_mask = cv.calcHist([img],[0 ],mask,[256 ],[0 ,256 ]) plt.subplot(221 ), plt.imshow(img, 'gray' ) plt.subplot(222 ), plt.imshow(mask,'gray' ) plt.subplot(223 ), plt.imshow(masked_img, 'gray' ) plt.subplot(224 ), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0 ,256 ]) plt.show()
直方图均衡化,提高图像对比度,统一光照条件
1 2 3 4 5 img = cv.imread('wiki.jpg' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" equ = cv.equalizeHist(img) res = np.hstack((img,equ)) cv.imwrite('res.png' ,res)
对比度有限自适应直方图均衡CLAHE 将图像分成小块做常规的直方图均衡,如果任何直方图bin超过了给定的对比度限(默认40),做直方图均衡前会将这些像素剪切均匀分散到其他bins,做完后使用双线性插值去除边界不自然。
1 2 3 4 clahe = cv.createCLAHE(clipLimit=2.0 , tileGridSize=(8 ,8 )) cl1 = clahe.apply(img) cv.imwrite('clahe_2.jpg' ,cl1)
2D直方图 将图像BGR转HSV,对Hue和Saturation进行绘制,还是用cv.calcHist()
channels = [0,1] 因为我们使用H 和 S 平面
bins = [180,256] 180 是 H 平面,256是 S 平面
range = [0,180,0,256] Hue范围从0到180,Saturation从0到2561 2 3 4 5 6 7 8 9 10 11 import numpy as npimport cv2 as cvfrom matplotlib import pyplot as plt img = cv.imread('view.png' ) assert img is not None , "file could not be read, check with os.path.exists()" hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV) hist = cv.calcHist( [hsv], [0 , 1 ], None , [180 , 256 ], [0 , 180 , 0 , 256 ] ) plt.imshow(hist,interpolation = 'nearest' ) plt.show()
直方图反向投射 用于图像分割或者识别图像中物体。通过创建一个与输入同样宽高的单色图,其每个像素点代表输入图片的对应像素输入物体的概率,也就是越亮的地方有目标物体的概率越大。 方法:计算包含目标物体的图像之直方图,该图应经可能全部都是目标物体。做颜色直方图会比灰度直方图效果更佳。然后反向投射这个直方图到需要查找目标的图片,也就是计算目标图片每个像素属于目标物体图片的概率,并显示之。在合适的阈值下,可以达到将目标分割出来的目的。
1 cv.calcBackProject( images, channels, hist, ranges, scale[, dst] ) -> dst
用法类似与cv.calcHist()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import numpy as npimport cv2 as cv roi = cv.imread('trees.png' ) assert roi is not None , "file could not be read, check with os.path.exists()" hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV) target = cv.imread('view.jpg' ) assert target is not None , "file could not be read, check with os.path.exists()" hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV) roihist = cv.calcHist([hsv],[0 , 1 ], None , [180 , 256 ], [0 , 180 , 0 , 256 ] ) cv.normalize(roihist,roihist,0 ,255 ,cv.NORM_MINMAX) dst = cv.calcBackProject([hsvt],[0 ,1 ],roihist,[0 ,180 ,0 ,256 ],1 ) disc = cv.getStructuringElement(cv.MORPH_ELLIPSE,(5 ,5 )) cv.filter2D(dst,-1 ,disc,dst) ret,thresh = cv.threshold(dst,50 ,255 ,0 ) thresh = cv.merge((thresh,thresh,thresh)) res = cv.bitwise_and(target,thresh) res = np.vstack((target,thresh,res)) cv.imwrite('res.jpg' ,res)
15. 图像傅里叶转换 对应正弦信号,振幅剧烈变化表示高频,缓慢变化为低频。图像类似的,边缘和噪音的亮度变化剧烈,因此算是高频信号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import cv2 as cvimport numpy as npfrom matplotlib import pyplot as pltimg = cv.imread('view.jpg' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" f = np.fft.fft2(img) fshift = np.fft.fftshift(f) magnitude_spectrum = 20 *np.log(np.abs (fshift)) plt.subplot(121 ),plt.imshow(img, cmap = 'gray' ) plt.title('Input Image' ), plt.xticks([]), plt.yticks([]) plt.subplot(122 ),plt.imshow(magnitude_spectrum, cmap = 'gray' ) plt.title('Magnitude Spectrum' ), plt.xticks([]), plt.yticks([]) plt.show() rows, cols = img.shape crow, ccol = rows//2 , cols//2 fshift[crow-10 :crow+11 , ccol-10 :ccol+11 ] = 0 f_ishift = np.fft.ifftshift(fshift) img_back = np.fft.ifft2(f_ishift) img_back = np.real(img_back) plt.subplot(131 ),plt.imshow(img, cmap = 'gray' ) plt.title('Input Image' ), plt.xticks([]), plt.yticks([]) plt.subplot(132 ),plt.imshow(img_back, cmap = 'gray' ) plt.title('Image after HPF' ), plt.xticks([]), plt.yticks([]) plt.subplot(133 ),plt.imshow(img_back) plt.title('Result in JET' ), plt.xticks([]), plt.yticks([]) plt.show()
OpenCV的实现要快一些,但是没有Numpy这么直观。
16. 模板匹配 使用cv.matchTemplate()
,如果输入图像尺寸(W,H),目标图(w,h),那么输出(W-h+1,H-h+1) 使用cv.minMaxLoc()
找到极值之所在作为矩形左上角坐标,结合(w,h)绘制包含目标的矩形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import cv2 as cvimport numpy as npfrom matplotlib import pyplot as plt img = cv.imread('view.jpg' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" img2 = img.copy() template = cv.imread('build.jpg' , cv.IMREAD_GRAYSCALE) assert template is not None , "file could not be read, check with os.path.exists()" w, h = template.shape[::-1 ] methods = ['cv.TM_CCOEFF' , 'cv.TM_CCOEFF_NORMED' , 'cv.TM_CCORR' , 'cv.TM_CCORR_NORMED' , 'cv.TM_SQDIFF' , 'cv.TM_SQDIFF_NORMED' ] for meth in methods: img = img2.copy() method = eval (meth) res = cv.matchTemplate(img,template,method) min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res) if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]: top_left = min_loc else : top_left = max_loc bottom_right = (top_left[0 ] + w, top_left[1 ] + h) cv.rectangle(img,top_left, bottom_right, 255 , 2 ) plt.subplot(121 ),plt.imshow(res,cmap = 'gray' ) plt.title('Matching Result' ), plt.xticks([]), plt.yticks([]) plt.subplot(122 ),plt.imshow(img,cmap = 'gray' ) plt.title('Detected Point' ), plt.xticks([]), plt.yticks([]) plt.suptitle(meth) plt.show()
17. 霍夫直线变换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import cv2 as cvimport numpy as np img = cv.imread('opencv.png' ) assert img is not None , "img loading wrong" gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY) edges = cv.Canny(gray,50 ,150 ,apertureSize = 3 ) lines = cv.HoughLines(edges,1 ,np.pi/180 ,100 ) for line in lines: rho,theta = line[0 ] a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int (x0 + 1000 *(-b)) y1 = int (y0 + 1000 *(a)) x2 = int (x0 - 1000 *(-b)) y2 = int (y0 - 1000 *(a)) cv.line(img,(x1,y1),(x2,y2),(0 ,0 ,255 ),2 ) cv.imwrite('houghlines3.jpg' ,img)
霍夫圆变换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import numpy as npimport cv2 as cv img = cv.imread('opencv_white.png' , cv.IMREAD_GRAYSCALE) assert img is not None , "file could not be read, check with os.path.exists()" img = cv.medianBlur(img,5 ) cimg = cv.cvtColor(img,cv.COLOR_GRAY2BGR) circles = cv.HoughCircles(img,cv.HOUGH_GRADIENT,1 ,20 , param1=50 ,param2=30 ,minRadius=0 ,maxRadius=0 ) circles = np.uint16(np.around(circles)) for i in circles[0 ,:]: cv.circle(cimg,(i[0 ],i[1 ]),i[2 ],(0 ,255 ,0 ),2 ) cv.circle(cimg,(i[0 ],i[1 ]),2 ,(0 ,0 ,255 ),3 ) cv.imshow('detected circles' ,cimg) cv.waitKey(0 ) cv.destroyAllWindows()