行业知识
Opencv图像处理(全)
文章目录
- 博主精品专栏导航
- 备注:以下源码均可运行,不同项目涉及的函数均有详细分析说明。
- 11、图像项目实战
- (一)银行卡号识别 —— sort_contours()、resize()
- (二)文档扫描OCR识别 —— cv2.getPerspectiveTransform() + cv2.warpPerspective()、np.argmin()、np.argmax()、np.diff()
- (三)全景拼接 —— detectAndDescribe()、matchKeypoints()、cv2.findHomography()、cv2.warpPerspective()、drawMatches()
- (四)停车场车位检测(基于Keras的CNN分类) —— pickle.dump()、pickle.load()、cv2.fillPoly()、cv2.bitwise_and()、cv2.circle()、cv2.HoughLinesP()、cv2.line()
- (五)答题卡识别与判卷 —— cv2.putText()、cv2.countNonZero()
- (六)背景建模(动态目标识别) —— cv2.getStructuringElement()、cv2.createBackgroundSubtractorMOG2()
- (七)光流估计(轨迹点跟踪)—— cv2.goodFeaturesToTrack()、cv2.calcOpticalFlowPyrLK()
- (八)DNN模块的分类 —— cv2.dnn.blobFromImage()
- (九)矩形涂鸦画板 —— cv.namedWindow()、cv.setMouseCallback()
- (十)创建轨迹条 —— createTrackbar()、cv2.getTrackbarPos()
- 10.1、创建一个轨迹条,用于阈值化图像
- 10.2、创建一个轨迹条,用于画板调色
- (十一)基于二值化实现人像抠图与背景替换 —— np.where()、np.uint8()
- 22、图像基本操作
- (一)图像的读取、保存和显示 —— cv2.imread(),cv2.imwrite(),cv2.imshow()
- (1.1)图窗设置:cv2.namedWindow()、cv2.resizeWindow()、cv2.moveWindow()、cv2.setWindowProperty()。
- (1.2)图窗关闭:cv2.waitKey()、cv2.destroyAllWindows()
- (二)视频读取与处理 —— cv2.VideoCapture()
- (三)图像的三色图 —— cv2.split() + cv.merge()
- (四)图像的边缘填充 —— cv2.copyMakeBorder()
- (五)图像融合 —— cv2.addWeighted()
- (六)颜色空间转换 —— cv2.cvtColor()
- (七)阈值处理 —— cv2.threshold() + cv2.adaptiveThreshold()
- (八)均值/高斯/方框/中值滤波 —— cv2.blur() + cv2.boxFilter() + cv2.GaussianBlur() + cv2.medianBlur()
- (九)腐蚀与膨胀 —— cv2.erode() 与 cv2.dilate() + np.zeros() 与 np.ones()
- (十)形态学变化 —— cv2.morphologyEx()
- (十一)边缘检测算子 —— cv2.sobel()、cv2.Scharr()、cv2.Laplacian()、cv2.Canny()
- (十二)图像金字塔 —— cv2.pyrUp()、cv2.pyrDown()
- (十三)图像轮廓检测 —— cv2.findContours()、cv2.drawContours()、cv2.arcLength()、cv2.approxPolyDP()、cv2.rectangle()
- (十四)模板匹配 —— cv2.matchTemplate()、cv2.minMaxLoc()
- (十五)直方图(均衡化) —— cv2.calcHist()、img.ravel()、cv2.bitwise_and()、cv2.equalizeHist()、cv2.createCLAHE()
- (十六)傅里叶变换 + 低通/高通滤波 —— cv2.dft()、cv2.idft()、np.fft.fftshift()、np.fft.ifftshift()、cv2.magnitud()
- (十七)Harris角点检测 —— cv2.cornerHarris()、np.float32()
- (十八)SIFT尺度不变特征检测 —— cv2.xfeatures2d.SIFT_create()、sift.detectAndCompute()、sift.detect()、sift.compute()、cv2.drawKeypoints
- (十九)暴力特征匹配 —— cv2.BFMatcher_create()、bf.match()、bf_knn.knnMatch()、cv2.drawMatches()
- (二十)��像缩放+镜像+平移+旋转+仿射变换+透视变换 —— cv2.resize()、cv2.getRotationMatrix2D()、cv2.getAffineTransform()、cv2.getPerspectiveTransform()、cv2.warpPerspective()、cv2.warpAffine()
博主精品专栏导航
- 专栏:Pytorch项目实战
- Opencv 图像处理(全)
- Opencv C++图像处理(全)
- Pillow 图像处理(PIL.Image)
- Pytorch基础(全)
- Python常用内置函数(全)
- 卷积神经网络CNN的经典模型
- 卷积神经网络CNN的实战知识
- 三万字硬核详解:yolov1、yolov2、yolov3、yolov4、yolov5、yolov7
备注:以下源码均可运行,不同项目涉及的函数均有详细分析说明。
11、图像项目实战
(一)银行卡号识别 —— sort_contours()、resize()
【信用卡检测流程详解】
11、提取模板的每个数字
1111、读取模板图像、转换成灰度图、转换成二值图
1122、轮廓检测、绘制轮廓、对得到的所有轮廓进行排序(编号)
1133、提取模板的所有轮廓 - 每一个数字
22、提取信用卡的所有轮廓
2211、读取待检测图像、转换为灰度图、顶帽操作、sobel算子操作、闭操作、二值化、二次膨胀+腐蚀
2222、轮廓检测、绘制轮廓
33、提取银行卡《四个数字一组》轮廓,然后每个轮廓与模板的每一个数字进行匹配,得到最大匹配结果
3311、在所有轮廓中,识别出《四个数字一组》的轮廓(共有四个),并进行阈值化、轮廓检测和轮廓排序
3322、在《四个数字一组》中,提取每个数字的轮廓以及坐标,并进行模板匹配得到最大匹配结果
44、在原图像上,用矩形画出《四个数字一组》,并在原图上显示所有的匹配结果
import cv2 # opencv读取的格式是BGRimport matplotlib.pyplot as plt # Matplotlib是RGBimport numpy as np def sort_contours(cnt_s, method="left-to-right"): reverse = False ii_myutils = 0 if method == "right-to-left" or method == "bottom-to-top": reverse = True if method == "top-to-bottom" or method == "bottom-to-top": ii_myutils = 1 bounding_boxes = [cv2.boundingRect(cc_myutils) for cc_myutils in cnt_s] # 用一个最小的矩形,把找到的形状包起来x,y,h,w (cnt_s, bounding_boxes) = zip(*sorted(zip(cnt_s, bounding_boxes), key=lambda b: b[1][ii_myutils], reverse=reverse)) return cnt_s, bounding_boxesdef resize(image, width=None, height=None, inter=cv2.INTER_AREA): dim_myutils = None (h_myutils, w_myutils) = image.shape[:2] if width is None and height is None: return image if width is None: r_myutils = height / float(h_myutils) dim_myutils = (int(w_myutils * r_myutils), height) else: r_myutils = width / float(w_myutils) dim_myutils = (width, int(h_myutils * r_myutils)) resized = cv2.resize(image, dim_myutils, interpolation=inter) return resized####################################################################### 11、提取模板的每个数字####################################################################### 读取模板图像(银行卡对应0~9的数字模板)img = cv2.imread(r'ocr_a_reference.png')ref_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 转换成灰度图ref_BINARY = cv2.threshold(ref_gray, 10, 255, cv2.THRESH_BINARY_INV)[1]# 转换成二值图像"""######################################## 轮廓检测:contours, hierarchy = cv2.findContours(img, mode, method)# 输入参数 mode: 轮廓检索模式# (1)RETR_EXTERNAL: 只检索最外面的轮廓;# (2)RETR_LIST: 检索所有的轮廓,但检测的轮廓不建立等级关系,将其保存到一条链表当中,# (3)RETR_CCOMP: 检索所有的轮廓,并建立两个等级的轮廓。顶层是各部分的外部边界,内层是的边界信息;# (4)RETR_TREE: 检索所有的轮廓,并建立一个等级树结构的轮廓;(最常用)# method: 轮廓逼近方法# (1)CHAIN_APPROX_NONE: 存储所有的轮廓点,相邻的两个点的像素位置差不超过1。 例如:矩阵的四条边。(最常用)# (2)CHAIN_APPROX_SIMPLE: 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标。 例如:矩形的4个轮廓点。# 输出参数 contours:所有的轮廓# hierarchy:每条轮廓对应的属性# 备注0:轮廓就是将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。# 备注1:函数输入图像是二值图,即黑白的(不是灰度图)。所以读取的图像要先转成灰度的,再转成二值图。# 备注2:函数在opencv2只返回两个值:contours, hierarchy。# 备注3:函数在opencv3会返回三个值:img, countours, hierarchy#######################################"""refCnts, hierarchy = cv2.findContours(ref_BINARY.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)"""######################################## 绘制轮廓:v2.drawContours(image, contours, contourIdx, color, thickness) ———— (在图像上)画出图像的轮廓# 输入参数 image: 需要绘制轮廓的目标图像,注意会改变原图# contours: 轮廓点,上述函数cv2.findContours()的第一个返回值# contourIdx: 轮廓的索引,表示绘制第几个轮廓。-1表示绘制所有的轮廓# color: 绘制轮廓的颜色(RGB)# thickness: (可选参数)轮廓线的宽度,-1表示填充# 备注:图像需要先复制一份copy(), 否则(赋值操作的图像)与原图会随之一起改变。#######################################"""img_Contours = img.copy()cv2.drawContours(img_Contours, refCnts, -1, (0, 0, 255), 3)# print(np.array(refCnts).shape)# 画图(图像处理并得到轮廓的图形化显示)plt.subplot(221), plt.imshow(img, 'gray'), plt.title('(0)ref')plt.subplot(222), plt.imshow(ref_gray, 'gray'), plt.title('(1)ref_gray')plt.subplot(223), plt.imshow(ref_BINARY, 'gray'), plt.title('(2)ref_BINARY')plt.subplot(224), plt.imshow(img_Contours, 'gray'), plt.title('(3)img_Contours')plt.show()######################################## 对得到的所有轮廓进行排序(编号):从左到右,从上到下refCnts = sort_contours(refCnts, method="left-to-right")[0]######################################## 提取(模板的)所有轮廓 - 数字digits = {}# 保存每个模板的数字 - 元组初始化for (i, c) in enumerate(refCnts): (x, y, w, h) = cv2.boundingRect(c)# 得到轮廓(数字)的外接矩形的左上角的(x, y)坐标、宽度和长度 roi = ref_BINARY[y:y + h, x:x + w]# 获得外接矩形的坐标 roi = cv2.resize(roi, (57, 88))# 将感兴趣区域的图像(数字)resize相同的大小 digits[i] = roi# 保存每个模板(数字)####################################################################### 22、提取信用卡的所有轮廓"""####################################################################### 初始化卷积核:getStructuringElement(shape, ksize)# 输入参数:shape:形状# (1) MORPH_RECT矩形# (2) MORPH_CROSS十字型# (3) MORPH_ELLIPSE椭圆形# ksize:卷积核大小。例如:(3, 3)表示3*3的卷积核######################################"""rect_Kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))square_Kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))####################################### 读取输入图像(待检测信用卡图像),并进行预处理image_card = cv2.imread(r'images\credit_card_01.png')image_resize = resize(image_card, width=300)image_gray = cv2.cvtColor(image_resize, cv2.COLOR_BGR2GRAY)"""######################################## 形态学变化函数:cv2.morphologyEx(src, op, kernel)# 参数说明:src传入的图片,op进行变化的方式, kernel表示方框的大小# op变化的方式有五种:# 开运算(open): cv2.MORPH_OPEN 先腐蚀,再膨胀。 开运算可以用来消除小黑点。# 闭运算(close): cv2.MORPH_CLOSE 先膨胀,再腐蚀。 闭运算可以用来突出边缘特征。# 形态学梯度(morph-grad): cv2.MORPH_GRADIENT 膨胀后图像(减去)腐蚀图像。 可以突出团块(blob)的边缘,保留物体的边缘轮廓。# 顶帽(top-hat): cv2.MORPH_TOPHAT 原始输入(减去)开运算结果。 将突出比原轮廓亮的部分。# 黑帽(black-hat): cv2.MORPH_BLACKHAT 闭运算结果(减去)原始输入 将突出比原轮廓暗的部分。#######################################"""image_tophat = cv2.morphologyEx(image_gray, cv2.MORPH_TOPHAT, rect_Kernel)# 礼帽操作,突出更明亮的区域"""######################################## Sobel算子是一种常用的边缘检测算子。对噪声具有平滑作用,提供较为精确的边缘方向信息,但是边缘定位精度不够高。# 边缘就是像素对应的灰度值快速变化的地方。如:黑到白的边界# 图像是二维的。Sobel算子在x,y两个方向求导,故有不同的两个卷积核(Gx, Gy),且Gx的转置等于Gy。分别反映了每一点像素在水平方向和在垂直方向上的亮度变换情况.######################################### dst = cv2.Sobel(src, ddepth, dx, dy, ksize)# 输入参数src 输入图像# ddepth 图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;# dx和dy 表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。# ksize 卷积核大小,一般为3、5。# 同时对x和y进行求导,会导致部分信息丢失。(不建议)- 分别计算x和y,再求和(效果好)######################################### (1)cv2.CV_16S的说明# (1)Sobel函数求完导数后会有负值,还有会大于255的值。# (2)而原图像是uint8,即8位无符号数。所以Sobel建立图像的位数不够,会有截断。# (3)因此要使用16位有符号的数据类型,即cv2.CV_16S。# (2)cv2.convertScaleAbs(): 给图像的所有像素加一个绝对值# 通过该函数将其转回原来的uint8形式。否则将无法显示图像,而只是一副灰色的窗口。########################################"""# 进行sobel算子操作 ksize=-1相当于用3*3的卷积核进行筛选ll(内置的卷积核)image_gradx = cv2.Sobel(image_tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1) # 检索图像的边界,gradx是经过Sobel算子处理后的图像的像素点矩阵image_gradx = np.absolute(image_gradx) # 对数组中每一个元素求绝对值(minVal, maxVal) = (np.min(image_gradx), np.max(image_gradx)) # 找到最大边界差值和最小边界插值image_gradx = (255 * ((image_gradx - minVal) / (maxVal - minVal))) # 归一化公式,将图像像素数据限制在0-1之间,便于后续的操作image_gradx = image_gradx.astype("uint8") # 将gradx的矩阵元素改为数据类型uint8,一般图像的像素点类型都是uint8"""sobel_Gx1 = cv2.Sobel(image_tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=3)# 3*3卷积核sobel_Gx_Abs1 = cv2.convertScaleAbs(sobel_Gx1)# (1)左边减右边(2)白到黑是正数,黑到白就是负数,且所有的负数会被截断成0,所以要取绝对值。sobel_Gy1 = cv2.Sobel(image_tophat, cv2.CV_64F, 0, 1, ksize=3)sobel_Gy_Abs1 = cv2.convertScaleAbs(sobel_Gy1)sobel_Gx_Gy_Abs1 = cv2.addWeighted(sobel_Gx_Abs1, 0.5, sobel_Gy_Abs1, 0.5, 0) # 权重值x + 权重值y +偏置b"""######################################### 闭操作(先膨胀,再腐蚀)将银行卡分成四个部分,每个部分的四个数字连在一起image_CLOSE = cv2.morphologyEx(image_gradx, cv2.MORPH_CLOSE, square_Kernel)"""######################################### 图像阈值 ret, dst = cv2.threshold(src, thresh, max_val, type)# 输入参数 dst: 输出图# src: 输入图,只能输入单通道图像,通常来说为灰度图# thresh: 阈值# max_val: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值# type: 二值化操作的类型,包含以下5种类型:# (1) cv2.THRESH_BINARY 超过阈值部分取max_val(最大值),否则取0# (2) cv2.THRESH_BINARY_INV THRESH_BINARY的反转# (3) cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变# (4) cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0# (5) cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转########################################"""# THRESH_OTSU 会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0。image_thresh = cv2.threshold(image_CLOSE, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]# (二次)闭操作,将四个连在一起的数字进行填充形成一个整体。image_2_dilate = cv2.dilate(image_thresh, square_Kernel, iterations=2)# 膨胀(迭代次数2次)image_1_erode = cv2.erode(image_2_dilate, square_Kernel, iterations=1) # 腐蚀(迭代次数1次)image_2_CLOSE = image_1_erode# 计算轮廓threshCnts, hierarchy = cv2.findContours(image_2_CLOSE.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# cnts = threshCnts # 代表图像轮廓的点集image_Contours = image_resize.copy()cv2.drawContours(image_Contours, threshCnts, -1, (0, 0, 255), 3)plt.subplot(241), plt.imshow(image_card, 'gray'), plt.title('(0)image_card')plt.subplot(242), plt.imshow(image_gray, 'gray'), plt.title('(1)image_gray')plt.subplot(243), plt.imshow(image_tophat, 'gray'), plt.title('(2)image_tophat')plt.subplot(244), plt.imshow(image_gradx, 'gray'), plt.title('(3)image_gradx')plt.subplot(245), plt.imshow(image_CLOSE, 'gray'), plt.title('(4)image_CLOSE')plt.subplot(246), plt.imshow(image_thresh, 'gray'), plt.title('(5)image_thresh')plt.subplot(247), plt.imshow(image_2_CLOSE, 'gray'), plt.title('(6)image_2_CLOSE')plt.subplot(248), plt.imshow(image_Contours, 'gray'), plt.title('(7)image_Contours')plt.show()####################################################################### 33、提取银行卡" 四个数字一组 "轮廓,然后每个轮廓与模板的每一个数字进行匹配,得到最大匹配结果####################################################################### 3311、识别出四个数字一组的所有轮廓(理论上是四个)########################################locs = []# 保存四个数字一组的轮廓坐标 - 列表初始化for (i, c) in enumerate(threshCnts):# 遍历轮廓(x, y, w, h) = cv2.boundingRect(c)# 计算矩形ar = w / float(h)# (四个数字一组)的长宽比# 匹配(四个数字为一组)轮廓的大小 —— 以实际图像大小进行调整if (2.0深究 Pycharm shadows name ‘xxxx’ from outer scope 警告
(二)文档扫描OCR识别 —— cv2.getPerspectiveTransform() + cv2.warpPerspective()、np.argmin()、np.argmax()、np.diff()
计算轮廓的长度:cv2.arcLength(curve, closed)
找出轮廓的多边形拟合曲线:approxPolyDP(contourMat, 10, true)
求最小值对应的索引:np.argmin()
求最大值对应的索引:np.argmax()
求(同一行)列与列之间的差值:np.diff()
import numpy as npimport cv2import matplotlib.pyplot as plt # Matplotlib是RGB"""####################################################################### 计算齐次变换矩阵:cv2.getPerspectiveTransform(rect, dst)# 输入参数rect输入图像的四个点(四个角)# dst输出图像的四个点(方方正正的图像对应的四个角)####################################################################### 仿射变换:cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)# 透视变换:cv2.warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)# src:输入图像 dst:输出图像# M:2×3的变换矩阵# dsize:变换后输出图像尺寸# flag:插值方法# borderMode:边界像素外扩方式# borderValue:边界像素插值,默认用0填充## (Affine Transformation)可实现旋转,平移,缩放,变换后的平行线依旧平行。# (Perspective Transformation)即以不同视角的同一物体,在像素坐标系中的变换,可保持直线不变形,但是平行线可能不再平行。## 备注:cv2.warpAffine需要与cv2.getPerspectiveTransform搭配使用。######################################################################"""def order_points(pts):rect = np.zeros((4, 2), dtype="float32")# 一共4个坐标点# 按顺序找到对应坐标0123分别是 左上,右上,右下,左下# 计算左上,右下s = pts.sum(axis=1)rect[0] = pts[np.argmin(s)]# np.argmin()求最小值对应的索引rect[2] = pts[np.argmax(s)]# np.argmax()求最大值对应的索引# 计算右上和左下diff = np.diff(pts, axis=1)# np.diff 求(同一行)列与列之间的差值rect[1] = pts[np.argmin(diff)]rect[3] = pts[np.argmax(diff)]return rectdef four_point_transform(image, pts):rect = order_points(pts)# 获取输入坐标点(tl, tr, br, bl) = rect# 获取四边形的四个点,每个点有两个值,对应(x, y)坐标# 计算输入的w和h值widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))maxWidth = max(int(widthA), int(widthB))# 取四边形上下两边中,最大的宽度heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA), int(heightB))# 取四边形左右两边中,最大的高度# 变换后对应坐标位置dst = np.array([[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype="float32")"""################################################################################ 计算齐次变换矩阵:cv2.getPerspectiveTransform(rect, dst)###############################################################################"""M = cv2.getPerspectiveTransform(rect, dst)"""################################################################################ 透视变换(将输入矩形乘以(齐次变换矩阵),得到输出矩阵)###############################################################################"""warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))return warpeddef resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / float(w)dim = (width, int(h * r))resized = cv2.resize(image, dim, interpolation=inter)return resized##############################################image = cv2.imread(r'images\receipt.jpg')ratio = image.shape[0] / 500.0# resize之后坐标也会相同变化,故记录图像的比率orig = image.copy()image = resize(orig, height=500)##############################################gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 转换为灰度图gray = cv2.GaussianBlur(gray, (5, 5), 0)# 高斯滤波操作edged = cv2.Canny(gray, 75, 200)# Canny算法(边缘检测)##############################################print("STEP 1: 边缘检测")cv2.imshow("Image", image)cv2.imshow("Edged", edged)cv2.waitKey(0)cv2.destroyAllWindows()############################################### 轮廓检测cnts, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)# 轮廓检测cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]# 选定所有轮廓中前五个轮廓,并进行排序for c in cnts:peri = cv2.arcLength(c, True)# 计算轮廓近似approx = cv2.approxPolyDP(c, 0.02 * peri, True)# 找出轮廓的多边形拟合曲线if len(approx) == 4:# 如果当前轮廓是四个点(矩形),表示当前轮廓是所需求目标screenCnt = approxbreak##############################################print("STEP 2: 获取轮廓")cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)# 在原图上画出检测得到的轮廓cv2.imshow("Outline", image)cv2.waitKey(0)cv2.destroyAllWindows()############################################### 透视变换warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)# 得到的轮廓要乘以图像的缩放尺寸warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)# 转换为灰度图ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]# 二值化处理ref = resize(ref, height=500)##############################################print("STEP 3: 齐次变换")cv2.imshow("Scanned", ref)cv2.waitKey(0)cv2.destroyAllWindows()############################################### 轮廓点绘制的颜色通道是BGR; 但是Matplotlib是RGB; 故在绘图时,(0, 0, 255)会由BGR转换为RGB(红 - 蓝)orig = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)# BGR转换为RGB格式edged = cv2.cvtColor(edged, cv2.COLOR_BGR2RGB)image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)ref = cv2.cvtColor(ref, cv2.COLOR_BGR2RGB)plt.subplot(2, 2, 1), plt.imshow(orig), plt.title('orig')plt.subplot(2, 2, 2), plt.imshow(edged), plt.title('edged')plt.subplot(2, 2, 3), plt.imshow(image), plt.title('contour')plt.subplot(2, 2, 4), plt.imshow(ref), plt.title('rectangle')plt.show()"""####################################################################### 计算轮廓的长度:retval = cv2.arcLength(curve, closed)# 输入参数: curve 轮廓(曲线)。# closed 若为true,表示轮廓是封闭的;若为false,则表示打开的。(布尔类型)# 输出参数: retval 轮廓的长度(周长)。####################################################################### 找出轮廓的多边形拟合曲线:approxCurve = approxPolyDP(contourMat, 10, true)# 输入参数: contourMat: 轮廓点矩阵(集合)# epsilon: (double类型)指定的精度, 即原始曲线与近似曲线之间的最大距离。# closed: (bool类型)若为true, 则说明近似曲线是闭合的; 反之, 若为false, 则断开。# 输出参数: approxCurve: 轮廓点矩阵(集合);当前点集是能最小包容指定点集的。画出来即是一个多边形;######################################################################"""(三)全景拼接 —— detectAndDescribe()、matchKeypoints()、cv2.findHomography()、cv2.warpPerspective()、drawMatches()
函数功能:利用sift算法,实现全景拼接算法,将给定的两幅图片拼接为一幅.
11:从输入的两张图片里检测关键点、提取(sift)局部不变特征。
22:匹配的两幅图像之间的特征(Lowe’s算法:比较最近邻距离与次近邻距离)
33:使用RANSAC算法(随机抽样一致算法),利用匹配特征向量估计单映射变换矩阵(homography:单应性)。
44:利用33得到的单映矩阵应用透视变换。
import cv2import numpy as np"""########################################################## 预定义框架说明# 定义一个Stitcher类:stitch()、detectAndDescribe()、matchKeypoints()、drawMatches()# stitch() 拼接函数# detectAndDescribe() 检测图像的SIFT关键特征点,并计算特征描述子# matchKeypoints() 匹配两张图片的所有特征点# cv2.findHomography() 计算单映射变换矩阵# cv2.warpPerspective() 透视变换(作用:缝合图像)# drawMatches() 建立直线关键点的匹配可视化## 备注:cv2.warpPerspective()需要与cv2.findHomography()搭配使用。#########################################################"""class Stitcher: ################################################################################## def stitch(self, images, ratio=0.75, reprojThresh=4.0, showMatches=False): (imageB, imageA) = images # 获取输入图片 (kpsA, featuresA) = self.detectAndDescribe(imageA) # 检测A、B图片的SIFT关键特征点,并计算特征描述子 (kpsB, featuresB) = self.detectAndDescribe(imageB) M = self.matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh) # 匹配两张图片的所有特征点,返回匹配结果。 if M is None: # 如果返回结果为空,没有匹配成功的特征点,退出算法 return None # 否则,提取匹配结果 # (matches, H, status) = M # H是3x3视角变换矩阵 result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0])) # 将图片A进行视角变换,result是变换后图片 result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB # 将图片B传入result图片最左端 if showMatches: # 检测是否需要显示图片匹配 vis = self.drawMatches(imageA, imageB, kpsA, kpsB, matches, status) # 生成匹配图片 return (result, vis) return result ################################################################################## def detectAndDescribe(self, image): # gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 将彩色图片转换成灰度图 descriptor = cv2.xfeatures2d.SIFT_create() # 建立SIFT生成器 """##################################################### # 如果是OpenCV3.X,则用cv2.xfeatures2d.SIFT_create方法来实现DoG关键点检测和SIFT特征提取。 # 如果是OpenCV2.4,则用cv2.FeatureDetector_create方法来实现关键点的检测(DoG)。 #####################################################""" (kps, features) = descriptor.detectAndCompute(image, None) # 检测SIFT特征点,并计算描述子 kps = np.float32([kp.pt for kp in kps]) # 将结果转换成NumPy数组 return (kps, features) # 返回特征点集,及对应的描述特征 ################################################################################## def matchKeypoints(self, kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh): matcher = cv2.BFMatcher() # 建立暴力匹配器 rawMatches = matcher.knnMatch(featuresA, featuresB, 2) # 使用KNN检测来自A、B图的SIFT特征匹配对,K=2 matches = [] for m in rawMatches: if len(m) == 2 and m[0].distance 4: # 当筛选后的匹配对大于4时,计算视角变换矩阵 # 投影变换矩阵:3*3。有八个参数对应八个方程,其中一个为1用于归一化。对应四对,每对(x, y) ptsA = np.float32([kpsA[i] for (_, i) in matches]) # 获取匹配对的点坐标 ptsB = np.float32([kpsB[i] for (i, _) in matches]) (H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh) # 使用RANSAC算法利用匹配特征向量估计单映矩阵(homography:单应性) return (matches, H, status) return None # 如果匹配对小于4时,返回None ################################################################################## def drawMatches(self, imageA, imageB, kpsA, kpsB, matches, status): (hA, wA) = imageA.shape[:2] (hB, wB) = imageB.shape[:2] vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8") vis[0:hA, 0:wA] = imageA # 将A、B图左右连接到一起 vis[0:hB, wA:] = imageB for ((trainIdx, queryIdx), s) in zip(matches, status): if s == 1: # 当点对匹配成功时,画到可视化图上 ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1])) ptB = (int(kpsB[trainIdx][0]) + wA, int(kpsB[trainIdx][1])) cv2.line(vis, ptA, ptB, (0, 255, 0), 1) return vis # 返回可视化结果##################################################################################if __name__ == '__main__': # 读取拼接图片 imageA = cv2.imread("left_01.png") imageB = cv2.imread("right_01.png") # 把图片拼接成全景图 stitcher = Stitcher() # 调用拼接函数 (result, vis) = stitcher.stitch([imageA, imageB], showMatches=True) # 显示所有图片 cv2.imshow("Image A", imageA) cv2.imshow("Image B", imageB) cv2.imshow("Keypoint Matches", vis) cv2.imshow("Result", result) cv2.waitKey(0) cv2.destroyAllWindows()基于OpenCV全景拼接(Python)SIFT/SURF
(四)停车场车位检测(基于Keras的CNN分类) —— pickle.dump()、pickle.load()、cv2.fillPoly()、cv2.bitwise_and()、cv2.circle()、cv2.HoughLinesP()、cv2.line()
该项目共分为三个py文件:Parking.py(定义所有的功能函数)、train.py(训练神经网络)、park_test.py(开始检测停车位状态)
(1)Parking.py
###################################################### Parking.py#####################################################import matplotlib.pyplot as pltimport cv2import osimport globimport numpy as npclass Parking: def show_images(self, images, cmap=None): cols = 2 rows = (len(images)+1)//cols plt.figure(figsize=(15, 12)) for i, image in enumerate(images): plt.subplot(rows, cols, i+1) cmap = 'gray' if len(image.shape) == 2 else cmap plt.imshow(image, cmap=cmap) plt.xticks([]) plt.yticks([]) plt.tight_layout(pad=0, h_pad=0, w_pad=0) plt.show() def cv_show(self, name, img): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows() def select_rgb_white_yellow(self, image): # 图像背景信息过滤(即截取图像中,指定范围的颜色) lower = np.uint8([120, 120, 120]) upper = np.uint8([255, 255, 255]) # (1)lower_red和高于upper_red的部分分别变成0 # (2)lower_red~upper_red之间的值变成255 white_mask = cv2.inRange(image, lower, upper) self.cv_show('white_mask', white_mask) masked = cv2.bitwise_and(image, image, mask=white_mask) self.cv_show('masked', masked) return masked def convert_gray_scale(self, image): return cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) def detect_edges(self, image, low_threshold=50, high_threshold=200): return cv2.Canny(image, low_threshold, high_threshold) def filter_region(self, image, vertices): # 功能:剔除掉不需要的地方,提取出多边形(所有车位的位置),纯白显示 ##################################################### # zeros_like(array, dtype=float, order='C') # 作用:返回一个给定形状和类型的数据,填充全为0。 # 输入参数 (1)array:输入数据 # (2)dtype:返回数组的数据类型(可选参数,默认float) # (3)order:C代表行优先;F代表列优先(可选参数) ##################################################### mask = np.zeros_like(image) # 新建模板(0:纯黑色) if len(mask.shape) == 2: ##################################################### # 填充任意多边形:cv2.fillPoly(img, ppt, Scalar); # 输入参数 (1)img 在该图像上绘图。 # (2)ppt 多边形的顶点集 # (5)Scarlar 填充多边形的颜色(255, 255, 255),即RGB的值为白色 ##################################################### cv2.fillPoly(mask, vertices, 255) # 填充多边形(255:纯白色) self.cv_show('mask', mask) ##################################################### # cv2.bitwise_and() # 按位与 # cv2.bitwise_or() # 或 # cv2.bitwise_not() # 非 # cv2.bitwise_xor() # 异或 ################################### # dst = cv2.bitwise_and(src1, src2, mask=mask) # src1/src2是相同类型和大小的图像 # mask=mask表示要提取的区域(可选参数) # (1&1=1,1&0=0,0&1=0,0&0=0) ##################################################### return cv2.bitwise_and(image, mask) # 按位与操作 def select_region(self, image): # 功能:(手动)选择区域 —— 在原图中,筛选出所有车位的位置,并框成一个多边形。 # 多边形的关键点位置由自定义设置,详细如下: rows, cols = image.shape[:2] # 获得图像的长和宽 pt_1 = [cols*0.05, rows*0.90] # 位置点1 pt_2 = [cols*0.05, rows*0.70] # 位置点2 pt_3 = [cols*0.30, rows*0.55] # 位置点3 pt_4 = [cols*0.6, rows*0.15] # 位置点4 pt_5 = [cols*0.90, rows*0.15] # 位置点5 pt_6 = [cols*0.90, rows*0.90] # 位置点6 vertices = np.array([[pt_1, pt_2, pt_3, pt_4, pt_5, pt_6]], dtype=np.int32) # 将数据转换为numpy数组 point_img = image.copy() point_img = cv2.cvtColor(point_img, cv2.COLOR_GRAY2RGB) # 转换为灰度图 for point in vertices[0]: # 遍历所有的位置点 """##################################################### # 绘制圆型:cv2.circle(图像, 圆心, 半径, 颜色, 厚度) # 输入参数 (1)图像:在该图像上绘图。 # (2)圆心:圆的中心坐标。坐标表示为两个值的元组, 即(X坐标值, Y坐标值)。 # (3)半径:圆的半径。 # (4)颜色:圆的边界线颜色。对于BGR, 我们传递一个元组。例如:(255, 0, 0)为蓝色。 # (5)厚度:正数表示线的粗细。其中:-1表示实心圆。 #####################################################""" cv2.circle(point_img, (point[0], point[1]), 10, (0, 0, 255), 4) # 在每一个位置点绘制一个空心圆 self.cv_show('point_img', point_img) return self.filter_region(image, vertices) def hough_lines(self, image): """##################################################### # 检测图像中所有的线:cv2.HoughLinesP(image, rho=0.1, theta=np.pi / 10, threshold=15, minLineLength=9, maxLineGap=4) # image 输入的图像需要是边缘检测后的结果 # minLineLengh (线的最短长度,比这个短的都被忽略) # MaxLineCap (两条直线之间的最大间隔,小于此值,认为是一条直线) # rho 距离精度 # theta 角度精度 # threshod 超过设定阈值才被检测出线段 #####################################################""" return cv2.HoughLinesP(image, rho=0.1, theta=np.pi/10, threshold=15, minLineLength=9, maxLineGap=4) def draw_lines(self, image, lines, color=[255, 0, 0], thickness=2, make_copy=True): # 功能:在原图上画出满足条件的所有线 if make_copy: image = np.copy(image) cleaned = [] for line in lines: for x1, y1, x2, y2 in line: # 一条线由两个点组成,每个点的坐标为(x, y) # abs(y2-y1) = 25 and abs(x2-x1) } # 找到同一列的所有线 dIndex = 0 clus_dist = 10 # 列与最近一列的距离(依据实际情况设置) for i in range(len(list1) - 1): distance = abs(list1[i+1][0] - list1[i][0]) if distance } i = 0 for key in clusters: # 12列 all_list = clusters[key] cleaned = list(set(all_list)) if len(cleaned) 5: # 如果个数大于5,则定义为一列 cleaned = sorted(cleaned, key=lambda tup: tup[1]) avg_y1 = cleaned[0][1] # 提取每一列第一条线。 [0]表示第一条线 avg_y2 = cleaned[-1][1] # 提取每一列最后一条线 [-1]表示最后一条线 avg_x1 = 0 # 由于不同线的 x 层次不齐,故取均值 avg_x2 = 0 for tup in cleaned: avg_x1 += tup[0] avg_x2 += tup[2] avg_x1 = avg_x1/len(cleaned) # x1是矩形的起始点 avg_x2 = avg_x2/len(cleaned) # x2是矩形的终止点 rects[i] = (avg_x1, avg_y1, avg_x2, avg_y2) # 得到每一列矩形的四点坐标 i += 1 print("Num Parking Lanes: ", len(rects)) # 共有12个矩形 # Step 5: 把列矩形画出来 buff = 7 for key in rects: # key表示第几列 tup_topLeft = (int(rects[key][0] - buff), int(rects[key][1])) tup_botRight = (int(rects[key][2] + buff), int(rects[key][3])) cv2.rectangle(new_image, tup_topLeft, tup_botRight, (0, 255, 0), 3) return new_image, rects def draw_parking(self, image, rects, make_copy=True, color=[255, 0, 0], thickness=2, save=True): if make_copy: new_image = np.copy(image) gap = 15.5 # 固定每两个停车位之间的距离间隔(y轴) spot_dict = {} # 字典:一个车位对应一个位置 tot_spots = 0 # 微调 ——— 由于检测得到的矩形具有一定的误差,所以进行人为操作,达到精度化 adj_y1 = {0: 20, 1: -10, 2: 0, 3: -11, 4: 28, 5: 5, 6: -15, 7: -15, 8: -10, 9: -30, 10: 9, 11: -32} adj_y2 = {0: 30, 1: 50, 2: 15, 3: 10, 4: -15, 5: 15, 6: 15, 7: -20, 8: 15, 9: 15, 10: 0, 11: 30} adj_x1 = {0: -8, 1: -15, 2: -15, 3: -15, 4: -15, 5: -15, 6: -15, 7: -15, 8: -10, 9: -10, 10: -10, 11: 0} adj_x2 = {0: 0, 1: 15, 2: 15, 3: 15, 4: 15, 5: 15, 6: 15, 7: 15, 8: 10, 9: 10, 10: 10, 11: 0} for key in rects: # key表示第几列 tup = rects[key] x1 = int(tup[0] + adj_x1[key]) x2 = int(tup[2] + adj_x2[key]) y1 = int(tup[1] + adj_y1[key]) y2 = int(tup[3] + adj_y2[key]) cv2.rectangle(new_image, (x1, y1), (x2, y2), (0, 255, 0), 2) # 在图像上画出微调后的矩形 num_splits = int(abs(y2-y1)//gap) # 计算每一列可以平均停放多少辆车(由于误差不能识别出精确的停车位,故进行平均计算估计) for i in range(0, num_splits+1): # 切六刀,则有七个停车位 y = int(y1 + i*gap) ##################################################### # 画直线段:cv2.line(img, pt1, pt2, color, thickness) # 输入参数 img 要划的线所在的图像; # pt1 直线起点 # pt2 直线终点 # color 直线的颜色 # thickness=1 线条粗细 ##################################################### # 画出所有停车位的横线 cv2.line(new_image, (x1, y), (x2, y), color, thickness) if 0mog2算法
opencv 8 --背景减除 – BackgroundSubtractorMOG2
OpenCV 中 getStructuringElement() 与 morphologyEx() 函数用法
(七)光流估计(轨迹点跟踪)—— cv2.goodFeaturesToTrack()、cv2.calcOpticalFlowPyrLK()
"""########################################################################### 光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的相应关系,从而实现目标跟踪。# 三要素(必要条件) (1)亮度恒定:同一点随着时间的变化(在连续帧之间),其亮度(像素强度)不会发生改变。# (2)小运动:相邻像素具有相似的运动。# 因为只有小运动情况下,才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。# (3)空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。# 因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。# - cv2.goodFeaturesToTrack() 确定要追踪的特征点# - cv2.calcOpticalFlowPyrLK() 追踪视频中的特征点##########################################################################"""# 如果跟踪图像中的目标丢失或被遮掩,则后续图像将始终不再现实轨迹角点(待优化)。import numpy as npimport cv2cap = cv2.VideoCapture('test.avi')feature_params = dict(maxCorners=150, qualityLevel=0.3, minDistance=12) # ShiTomasi角点检测的参数lk_params = dict(winSize=(15, 15), maxLevel=2) # Lucas Kanada光流检测的参数color = np.random.randint(0, 255, (100, 3)) # 构建随机颜色#################################count = 0while True: ret, old_frame = cap.read() # 获取帧图像 count = count + 1 if count == 235: # 挑选视频中指定的第N帧为第一帧图像 breakold_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) # 转化为灰度图"""################################################################## 确定要追踪的特征点:cv2.goodFeaturesToTrack( image, maxCorners, qualityLevel, minDistance, mask=noArray(),# blockSize=3, bool useHarrisDetector=false, double k=0.04 );# 输入参数 image: 输入图像,是八位的或者32位浮点型,单通道图像,所以有时候用灰度图# maxCorners: 返回最大的角点数,是最有可能的角点数,如果这个参数不大于0,那么表示没有角点数的限制。# qualityLevel: 图像角点的最小可接受参数,质量测量值乘以这个参数就是最小特征值,小于这个数的会被抛弃。# minDistance: 返回的角点之间最小的欧式距离。# mask: 检测区域。如果图像不是空的(它需要具有CV_8UC1类型和与图像相同的大小),它指定检测角的区域。# blockSize: 用于计算每个像素邻域上的导数协变矩阵的平均块的大小。# useHarrisDetector: 选择是否采用Harris角点检测,默认是false.# k: Harris检测的自由参数。# 输出参数 corners: 输出为角点。# 备注:角点最大数量(数量越多,效率慢),品质因子(品质因子越大,角点越少,但越大越好)、角点距离(在角点距离范围内,取N个角点中最好的一个角点)#################################################################"""p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params) # 传入字典类型时需要两个**mask = np.zeros_like(old_frame) # 为绘制光流追踪图,构建一个Maskwhile True: ret, frame = cap.read() # 循环获取帧图像 if not ret: break frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) """################################################################# # 追踪视频中的特征点:p1, status, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, winSize=(15, 15), maxLevel=2) # 输入参数 old_gray 前一帧图像 # frame_gray 当前帧图像 # p0 待跟踪的特征点向量 # nextPts None # winSize 搜索窗口的大小 # maxLevel 最大的金字塔层数 # 输出参数 p1 跟踪特征点向量 # status 特征点是否找到,找到的状态为1,未找到的状态为0 #################################################################""" p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params) # 选择轨迹点 good_new = p1[st == 1] # st == 1表示目标锁定,如果目标丢失,则后续都将不再找到目标。(因为后续帧图像是依据第一帧图像识别检测的) good_old = p0[st == 1] # 绘制轨迹 for i, (new, old) in enumerate(zip(good_new, good_old)): a, b = new.ravel() c, d = old.ravel() a = int(a); b = int(b); c = int(c); d = int(d) mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2) """##################################################### # 画直线段:cv2.line(img, pt1, pt2, color, thickness) # 输入参数 img 要划的线所在的图像; # pt1 直线起点 # pt2 直线终点 # color 直线的颜色 # thickness=1 线条粗细 #####################################################""" frame = cv2.circle(frame, (a, b), 5, color[i].tolist(), -1) """##################################################### # 绘制圆型:cv2.circle(图像, 圆心, 半径, 颜色, 厚度) # 输入参数 (1)图像:在该图像上绘图。 # (2)圆心:圆的中心坐标。坐标表示为两个值的元组, 即(X坐标值, Y坐标值)。 # (3)半径:圆的半径。 # (4)颜色:圆的边界线颜色。对于BGR, 我们传递一个元组。例如:(255, 0, 0)为蓝色。 # (5)厚度:正数表示线的粗细。其中:-1表示实心圆。 #####################################################""" img = cv2.add(frame, mask) # 将轨迹线与帧图像叠加 cv2.imshow('frame', img) k = cv2.waitKey(50) & 0xff if k == 27: # 退出键 break old_gray = frame_gray.copy() # 实时更新前一帧 p0 = good_new.reshape(-1, 1, 2)cv2.destroyAllWindows()cap.release()图文详解 OpenCV中光流以及视频特征点追踪(稀疏光流追踪+ 优化版稀疏光流追踪+密集光流追踪)
(八)DNN模块的分类 —— cv2.dnn.blobFromImage()
import utils_pathsimport numpy as npimport cv2################################################################### 提取标签文件中每一行的内容# (1)训练模型标签文件:"synset_words.txt"# (2)使用open().read():打开并读取txt文件中所有的字符串# (3)strip():删除字符串两端的空格# (4)split('\n'):提取每一行的内容rows = open("synset_words.txt").read().strip().split("\n")################################################################### 提取出每行第一个空格后的字符串# (1)对每一行的内容进行遍历,遍历之后找每一行的空格(r.find(' '))。# (2)找到位置后让位置+1,然后r提取到+1的位置一直到最后# (3)以所有逗号为分隔符然后删除他们,取分割的第一个值classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows]################################################################### 加载Caffe所需文件# (1)配置文件:"bvlc_googlenet.prototxt"# (2)训练好的权重参数:"bvlc_googlenet.caffemodel"net = cv2.dnn.readNetFromCaffe("bvlc_googlenet.prototxt", "bvlc_googlenet.caffemodel")################################################################### 读取图像路径# (1)utils_paths.py中的list_images()是提取images文件夹中所有图片的绝对路径,# (2)将所有绝对路径作为元素组成迭代器# (3)使用sorted()进行排序。这个是字符串之间的排序:先比首字母,再比第二个字母,都相同时比长度。imagePaths = sorted(list(utils_paths.list_images("images/")))################################################################### (单个)图像预测image = cv2.imread(imagePaths[0])# 先读取第0张图片resized = cv2.resize(image, (224, 224))# 保持训练模型与测试模型数据大小相同blob = cv2.dnn.blobFromImage(resized, 1, (224, 224), (104, 117, 123))print("First Blob: {}".format(blob.shape))net.setInput(blob)# 输入数据preds = net.forward()# 前向传播得到结果(向量形式)# 排序,取分类可能性最大的 ———— 该Imagenet是一个千分类模型,它会有1000个值对应1000个分类的概率# np.argsort()是从小到大排序,故逆序[::-1],然后取第一个值(最大值的索引)idx = np.argsort(preds[0])[::-1][0]# 获取要写的内容:(1)该索引对应的标签值(2)pred[0]的值,乘以100,然后保留其两位小数,最后在后面加个百分号text = "Label: {}, {:.2f}%".format(classes[idx], preds[0][idx] * 100)# 将要写的内容写在未经处理的图片上cv2.putText(image, text, (5, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 显示预测结果cv2.imshow("Image", image)cv2.waitKey(0)##################################################################################################################################### 预测(其余的所有图) ———— 方法与上述一样,但数据是一个batch。images = []# 定义一个空列表存图# 处理除第0张外的所有图片for p in imagePaths[1:]:image = cv2.imread(p)image = cv2.resize(image, (224, 224))images.append(image)blob = cv2.dnn.blobFromImages(images, 1, (224, 224), (104, 117, 123))print("Second Blob: {}".format(blob.shape))net.setInput(blob)# 输入数据preds = net.forward()# 前向传播得到结果(向量形式)# 首先读进来图,之后找到对应预测结果中最大的,然后写上标签与概率for (i, p) in enumerate(imagePaths[1:]):# i是序号,p是图片路径image = cv2.imread(p)idx = np.argsort(preds[i])[::-1][0]text = "Label: {}, {:.2f}%".format(classes[idx], preds[i][idx] * 100)cv2.putText(image, text, (5, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)cv2.imshow("Image", image)cv2.waitKey(0)"""################################################################### blob = cv2.dnn.blobFromImage() ———— 改变图像的大小,然后令R,G,B三个通道分别减去均值(去除光照影响)。# 输出参数:Blob例如:(1,3,224,224) 分别表示:图片数量、图片通道数、图像的宽、图像的高# 输入参数 resized 要改变的图像# 缩放系数我们当前用的时1,所以不变# (224,224) 图像大小# (104,117,123) 图像三通道均值。这三个均值是Imagenet提供的。# 预测一个结果:cv2.dnn.blobFromImage()处理单张图像# 预测多个结果:cv2.dnn.blobFromImages()处理多张图像##################################################################"""opencv的DNN模块(详细过程)
DNN常用模块简介
(九)矩形涂鸦画板 —— cv.namedWindow()、cv.setMouseCallback()
"""########################################################################## 编写一个矩形涂鸦画板# 功能:鼠标左键按下拖动绘制矩形,鼠标左键弹起时完成绘制# (1)按' c '键清空画板# (2)按' ESC '键退出#########################################################################"""import numpy as npimport cv2from random import randintclass Painter: def __init__(self) -> None: self.mouse_is_pressed = False self.last_pos = (-1, -1) self.width = 300 self.height = 512 self.img = np.zeros((self.width, self.height, 3), np.uint8) self.window_name = 'painter' self.color = None def run(self): print('画板,拖动鼠标绘制矩形框,按ESC退出,按c键清空画板') cv2.namedWindow(self.window_name) cv2.setMouseCallback(self.window_name, lambda event, x, y, flags, param: self.on_draw(event, x, y, flags, param)) while True: cv2.imshow(self.window_name, self.img) k = cv2.waitKey(1) & 0xFF if k == ord('c'): # 按' c '键清空画板 self.clean() # (调用自定义函数):清除画板 elif k == 27: # 按' ESC '键退出 break cv2.destroyAllWindows() def on_draw(self, event, x, y, flags, param): # TODO(You): 请正确实现画板事件响应,完成功能 # 触发左键按下 -> 触发鼠标移动 -> 开始画矩形 -> 触发左键抬起 -> 终止画矩形 pos = (x, y) # 鼠标按下的位置坐标 if event == cv2.EVENT_LBUTTONDOWN: # 触发左键按下 self.mouse_is_pressed = True self.last_pos = pos elif event == cv2.EVENT_MOUSEMOVE: # 触发鼠标移动 if self.mouse_is_pressed == True: # 判断鼠标是否按下 self.begin_draw_rectangle(self.last_pos, pos) # (调用自定义函数):开始画矩形 elif event == cv2.EVENT_LBUTTONUP: # 触发左键抬起 self.end_draw_rectangle(self.last_pos, pos) # (调用自定义函数):终止画矩形 self.mouse_is_pressed = False def clean(self): cv2.rectangle(self.img, (0, 0), (self.height, self.width), (0, 0, 0), -1) def begin_draw_rectangle(self, pos1, pos2): if self.color is None: # 设置颜色(每个矩形的颜色都随机) self.color = (randint(0, 256), randint(0, 256), randint(0, 256)) # 随机生成三通道颜色 cv2.rectangle(self.img, pos1, pos2, self.color, -1) def end_draw_rectangle(self, pos1, pos2): self.color = Noneif __name__ == '__main__': p = Painter()# 类的实例化 p.run()# 调用类函数"""########################################################################## 11、创建鼠标回调函数:cv2.setMouseCallback(windowName, MouseCallback, param=None)# 输入参数 windowName: 窗口名称# MouseCallback: 鼠标响应回调函数# param: 响应函数传递的的参数########################################################################## 22、MouseCallback(int event, int x, int y, int flags, * userdata)# 输入参数 x: 鼠标的x坐标# y: 鼠标的y坐标# userdata: 可选参数# event: 一个MouseEventTypes常量# (1)cv.EVENT_FLAG_LBUTTON= 1, 左键拖拽# (2)cv.EVENT_FLAG_RBUTTON= 2, 右键拖拽# (3)cv.EVENT_FLAG_MBUTTON= 4, 中键不放# (4)cv.EVENT_FLAG_CTRLKEY= 8, 按住ctrl不放# (5)cv.EVENT_FLAG_SHIFTKEY= 16, 按住shift不放# (6)cv.EVENT_FLAG_ALTKEY= 32, 按住alt不放# flags: 一个MouseEventFlags常量# (1)cv.EVENT_MOUSEMOVE= 0, 鼠标移动# (2)cv.EVENT_LBUTTONDOWN= 1, 左键按下# (3)cv.EVENT_RBUTTONDOWN= 2, 右键按下# (4)cv.EVENT_MBUTTONDOWN= 3, 中键按下# (5)cv.EVENT_LBUTTONUP= 4, 左键释放# (6)cv.EVENT_RBUTTONUP= 5, 右键释放# (7)cv.EVENT_MBUTTONUP= 6, 中键释放# (8)cv.EVENT_LBUTTONDBLCLK= 7, 左键双击# (9)cv.EVENT_RBUTTONDBLCLK= 8, 右键双击# (10)cv.EVENT_MBUTTONDBLCLK= 9, 中健双击# (11)cv.EVENT_MOUSEWHEEL= 10, 滚轮滑动# (12)cv.EVENT_MOUSEHWHEEL= 11 横向滚轮滑动#########################################################################"""(十)创建轨迹条 —— createTrackbar()、cv2.getTrackbarPos()
10.1、创建一个轨迹条,用于阈值化图像
import cv2img = cv2.imread('1.png') # 加载图像cv2.imshow('Image', img) # 显示图像cv2.createTrackbar('Threshold', 'Image', 0, 255, lambda x: None) # 创建阈值滑动条while True: threshold_value = cv2.getTrackbarPos('Threshold', 'Image') # 获取滑动条的阈值 threshold_img = cv2.threshold(img, threshold_value, 255, cv2.THRESH_BINARY)[1] # 阈值化图像 cv2.imshow('Threshold Image', threshold_img) # 显示图像 if cv2.waitKey(1) == 27: # Esc退出 breakcv2.destroyAllWindows() # 关闭所有窗口10.2、创建一个轨迹条,用于画板调色
Python OpenCV 使用滑动条(调色)
python -opencv 使用滑动条(调色画板)
OpenCV学习——实现滑动条式调色板
python -opencv 使用滑动条(图像膨胀)
共有三个功能:(1)创建轨迹条(2)打开控制开关,根据RGB设置背景色(3)打开控制开关,根据RGB设置画笔颜色,在画板上画画。
"""########################################################################## (1)滑动条控制R、G、B的值# (2)开关按钮switch,用于确认是否使用自定义RGB改变原图。# 0:不改变原图1:调色2:调色画板#########################################################################"""import cv2import numpy as np# 定义回调函数,此程序无需回调,所以Pass即可def nothing(x): passdef Mouseback(event, x, y, flags, param): if flags == cv2.EVENT_FLAG_LBUTTON and event == cv2.EVENT_MOUSEMOVE: cv2.circle(img, (x, y), 1, [b, g, r], 1)img = np.zeros((300, 512, 3), np.uint8)cv2.namedWindow('image', cv2.WINDOW_NORMAL)cv2.createTrackbar('R', 'image', 0, 255, nothing)cv2.createTrackbar('G', 'image', 0, 255, nothing)cv2.createTrackbar('B', 'image', 0, 255, nothing)switch = 'OFF ON'cv2.createTrackbar(switch, 'image', 0, 2, nothing)while (1): cv2.imshow('image', img) k = cv2.waitKey(1) if k == ord('q'): break r = cv2.getTrackbarPos('R', 'image') g = cv2.getTrackbarPos('G', 'image') b = cv2.getTrackbarPos('B', 'image') s = cv2.getTrackbarPos(switch, 'image') if s == 0:# 不改变原图 img[:] = 0 elif s == 1: # 调色 img[:] = [b, g, r] elif s == 2: # 调色画板 cv2.setMouseCallback('image', Mouseback)cv2.destroyAllWindows()"""########################################################################## 11、创建一个滑动条: cv2.createTrackbar(Track_name, img, min, max, TrackbarCallback)# 输入参数:# Track_name:滑动条的名字。# img:滑动条所在画布。# min:滑动条的最小值。# max:滑动条的最大值。# TrackbarCallback:滑动条的回调函数。# 22、获取滑动条的值: value = cv2.getTrackbarPos(Track_name, img)# 输入参数:# Track_name:滑动条的名字。# img:滑动条所在画布。# 输出参数:滑动条当前所在的位置(值)。#########################################################################"""(十一)基于二值化实现人像抠图与背景替换 —— np.where()、np.uint8()
图像处理实战–Opencv实现人像迁移
# 检测原理:# (1)将前景图像通过二值化进行抠图,去除白色背景,得到人像位置。# (2)将人像在背景图像中的位置置为0。# (3)将两张图像进行像素值累加。# 只适用于前景图像的物体背景为白色的图像,背景图像随意。# 因此采用二值化抠图,故只能指定唯一像素,而白色背景为常用背景。import cv2import numpy as npimport matplotlib.pyplot as pltq_img = cv2.imread('black.png')# (1)前景图像b_img = cv2.imread('starry_night.jpg')# (2)背景图像# 将风景图片尺寸调整为与人像图片一致, 需要进行图像像素累加计算。q_img = cv2.resize(q_img, (b_img.shape[1], b_img.shape[0]))print(q_img.shape)print(b_img.shape)# (3)二值化抠图img = q_img.copy()for i in range(img.shape[0]): # 高for j in range(img.shape[1]):# 宽# 颜色接近背景颜色的像素点置为1,其余部分置为0if 255 == img[i][j][0] and 255 == img[i][j][1] and 255 == img[i][j][2]: img[i][j] = 255else:img[i][j] = 0# (4)人像迁移# 黑白反转(前景图像:人像位置置为1,其余位置置为0)img_t = np.where(img == 0, 1, 0)img3 = np.uint8(q_img * img_t)# 白黑反转(背景图像:人像位置置为0,其余位置置为1)img_t = np.where(img_t == 1, 2, img_t)img_t = np.where(img_t == 0, 1, img_t)img_t = np.where(img_t == 2, 0, img_t)img4 = np.uint8(b_img * img_t) # 图像像素值累加(尺寸必须相同)img5 = img4 + img3# (5)绘图titles = ['q_img', 'b_img', 'img', 'img3', 'img4', 'img5']images = [q_img, b_img, img, img3, img4, img5]for ii in range(6):images[ii] = cv2.cvtColor(images[ii], cv2.COLOR_BGR2RGB)plt.subplot(2, 3, ii+1)plt.imshow(images[ii], 'gray')plt.axis('off')plt.xticks([]), plt.yticks([])plt.show()22、图像基本操作
(一)图像的读取、保存和显示 —— cv2.imread(),cv2.imwrite(),cv2.imshow()
(1)多张图(在同一个窗口)同时显示 plt.subplot()
(2)cv2.imshow()和plt.imshow()的区别
"""###################################################################### cv2是opencv在python中的缩写;# Matplotlib 是一个 Python 库,可以通过 python 脚本创建二维图形和图表。# Matplotlib 中的 pyplot 模块,可以控制线条样式,字体属性,格式化轴等功能。且支持各种各样的图形绘制,如直方图,条形图,功率谱,误差图等。#####################################################################"""import cv2 # opencv读取图像的格式BGR(图像的格式RGB)import matplotlib.pyplot as plt# plt默认RGB通道"""###################################################################### 11、读取图像:cv2.imread(img_path, flag)# 输入参数#img_path: 图像的路径(若路径错误,则返回None。但不会报错)# flag:cv2.IMREAD_COLOR(也可以传入1) (默认)加载彩色图像RGB# cv2.IMREAD_GRAYSCALE(也可以传入0) 将图像转换为灰度图,# cv2.IMREAD_UNCHANGED(也可以传入-1) 加载原图# 备注1:OpenCV 支持JPG、PNG、TIFF等常见格式图像文件加载(默认读取的格式是BGR)(图像的格式是RGB)# 备注2:转义字符\可以转义很多字符,比如:'\n'表示换行,'\t'表示制表符,'\'表示\。当然如果不需要转义,可以使用(r'cat.hpg');#################################### 路径说明(路径中不能出现中文,否则系统异常提示。)cat_path = r'C:\Users\my\Desktop\py_test\cat.jpg'# 绝对路径cat_path = r'cat.jpg'# 相对路径(即当前同级目录):不指定路径,则默认当前.py文件的路径下。cat_path = r'./cat.jpg'# 相对路径(即当前同级目录):与上同效。cat_path = r'../py_test/cat.jpg'# 相对路径(上级目录):py_test表示存放当前.py文件的文件夹#####################################################################"""cat_path = r'C:\Users\my\Desktop\py_test\cat.jpg'img0 = cv2.imread(cat_path)img1 = cv2.imread(cat_path, cv2.IMREAD_COLOR)img2 = cv2.imread(cat_path, cv2.IMREAD_GRAYSCALE)img3 = cv2.imread(cat_path, cv2.IMREAD_UNCHANGED)"""###################################################################### 22、保存图像:cv2.imwrite(img_path_name, img)# 输入参数img_path_name: 自定义待保存图像的路径+名字# img: 待保存图像#####################################################################"""cv2.imwrite('gray_cat.png', img2)"""###################################################################### 33、显示图像:cv2.imshow(window_title, img)# 输入参数window_title:自定义窗口的名字# img:待显示图像# 备注1:窗口会自适应图像大小# 备注2:指定多个窗口名称,可以显示多幅图像# 备注3:显示多幅图像的时候,若cv2.imshow()指定相同的窗口名,这样后面显示的图像会覆盖前面的图像,从而只产生一个(连续)窗口。如:视频#####################################################################"""cv2.imshow('raw_img', img0)cv2.imshow('cv2.IMREAD_COLOR', img1)cv2.imshow('cv2.IMREAD_GRAYSCALE', img2)cv2.imshow('cv2.IMREAD_UNCHANGED', img3)cv2.waitKey(1000) # 延迟一秒后自动关闭图像cv2.destroyAllWindows() # (同时)关闭所有图窗"""###################################################################### 键盘绑定函数:cv2.waitKey()# (1)cv2.waitKey(0): 表示无限期的等待键盘输入,按任意键继续。如:空格键# (2)cv2.waitKey(delay): 当 delay>0 (单位:ms)时使用,表示等待一定时间。1秒(s) = 1000毫秒#####################################################################"""###################################################################### 44、(在同一个窗口)同时显示多张图# plt.subplot(231)或者plt.subplot(2,3,1) # 该图指定了(row)2*3(col)的子图区域,并且同一个坐标轴1,2,3,4,5,6内分别画图。# plt.plot() # 直接在一张大的画布中画图,相当于获取当前活跃的axes然后在上面作图。# 备注1:在jupyter notebook上,单是plt.imshow()就可显示图片,同时也显示其格式。# 备注2:在pycharm上,单是plt.imshow()不显示图像,需搭配plt.show()。而加上plt.show()后,结果:仅显示图片,不显示格式。##################################plt.subplot(141), plt.imshow(img0, 'gray'), plt.title('raw_img')plt.subplot(142), plt.imshow(img1, 'gray'), plt.title('cv2.IMREAD_COLOR')plt.subplot(143), plt.imshow(img2, 'gray'), plt.title('cv2.IMREAD_GRAYSCALE')plt.subplot(144), plt.imshow(img3, 'gray'), plt.title('cv2.IMREAD_UNCHANGED')plt.show()###################################################################### 55、cv2.imshow()和plt.imshow()的区别# cv2.imshow():常用于对读入图像进行一系列图像处理后的绘图;# plt.imshow():常用语绘制热图; 热图即通过色差、亮度来展示数据的差异。# 备注:两者都可以,但要注意图片的格式是RGB格式,而opencv是BGR格式,plt是RGB格式;##################################plt.imshow(img0)plt.colorbar()plt.show()(1.1)图窗设置:cv2.namedWindow()、cv2.resizeWindow()、cv2.moveWindow()、cv2.setWindowProperty()。
import cv2window_name = 'projector'# 图窗标题cv2.namedWindow(window_name, cv2.WINDOW_KEEPRATIO)# 创建命名窗口cv2.resizeWindow(window_name, 10, 20)# 自定义窗口大小cv2.moveWindow(window_name, 100, 200)# 设置窗口的位置cv2.setWindowProperty(window_name, cv2.WND_PROP_TOPMOST, 1)# 设置窗口显示在最前面im = cv2.imread('test01.png')# 读取图像cv2.imshow(window_name, im)# 显示图像cv2.waitKey(0)# 等待输入任意键cv2.destroyAllWindows()# 摧毁所有图窗参数 cv2.namedWindow(winname, flags) 创建命名窗口 1 winname 窗口名称,用作窗口的标识符。 2 flags 窗口属性设置标志。 flags=cv2.WINDOW_NORMAL 用户可以手动改变窗口大小 flags=cv2.WINDOW_AUTOSIZE 窗口大小自动适应图片大小,并且不可手动更改。 flags=cv2.WINDOW_FREERATIO 自适应比例 flags=cv2.WINDOW_KEEPRATIO 保持比例 flags=cv2.WINDOW_OPENGL 窗口创建的时候会支持OpenGL flags=cv2.WINDOW_GUI_EXPANEDE 创建的窗口允许添加工具栏和状态栏。 flags=cv2.WINDOW_GUI_NORMAL 创建没有状态栏和工具栏窗口。 flags=cv2.WINDOW_AUTOSIZE 窗口大小自动适应图片大小,并且不可手动更改。 参数 cv2.resizeWindow(winname, width, height) 改变窗口大小 1 winname 窗口名 2 width 窗口宽度 3 height 窗口高度 参数 cv2.moveWindow(winname, x, y) 设置窗口位置 1 winname 窗口名 2 x 窗口x轴位置 3 y 窗口y轴位置 参数 cv2.setWindowProperty(winname, prop_id, prop_value) 设置窗口属性 1 winname 窗口名 2 prop_id 要编辑的窗口属性。如cv2.WINDOW_NORMAL、cv2.WINDOW_KEEPRATIO、cv2.WINDOW_FULLSCREEN等。 3 prop_value 窗口属性的新值。如cv2.WND_PROP_FULLSCREEN, cv2.WND_PROP_AUTOSIZE, cv2.WND_PROP_ASPECT_RATIO等。 (1.2)图窗关闭:cv2.waitKey()、cv2.destroyAllWindows()
参数 cv2.waitKey() 键盘绑定函数 1 cv2.waitKey(0) 表示无限期的等待键盘输入,按任意键继续。如:空格键 2 cv2.waitKey(delay) 当 delay>0 (单位:ms)时使用,表示等待一定时间。1秒(s) = 1000毫秒 参数 cv2.destroyAllwindows() 销毁窗口 1 cv2.destroyAllwindows() 摧毁所有窗口 2 cv2.destroyWindow(winname) 摧毁指定的窗口 (二)视频读取与处理 —— cv2.VideoCapture()
OpenCV 保存视频
读取视频 + 检查视频是否可以打开 + 循环读取视频的每一帧(图像)并显示
import cv2 # cv2是opencv在python中的缩写; opencv读取图像的格式BGR(图像的格式RGB)# (1)视频读取与处理 —— 读取视频vc = cv2.VideoCapture(r'picture\test.mp4')# (2)视频读取与处理 —— 检查视频是否可以打开if vc.isOpened(): open, frame = vc.read()else: open = False# (3)视频读取与处理 —— 循环读取视频的每一帧(图像)while open: ret, frame = vc.read() # 如果读到的帧数不为空,那么就继续读取,如果为空,就退出 if frame is None: break if ret == True: gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 将读取到视频的每一帧(图像)转换成灰度图 cv2.imshow('result', gray) # 使用 waitKey 可以控制视频的播放速度,数值越小,播放速度越快 if cv2.waitKey(10) & 0xFF == 27: # 27 表示退出键(Esc) breakvc.release()cv2.destroyAllWindows()"""#################################################################### 11、cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备。# 0:表示调用电脑自带摄像头。# 1:表示调用外接USB摄像头。# 22、vc.isOpened(): 检查视频是否可以打开(返回值:True/False)# 33、vc.read(): 读取视频的每一帧(图像)(返回值:True/False,图像彩色图)# 44、cv2.imshow('frame',frame)将每一帧(图像)显示在一个叫frame的窗口上。# 为什么会产生视频的效果:通过while循环,将图像固定显示在'frame'图窗上,每一帧会覆盖上一帧,就产生了视频的效果。###################################################################"""(三)图像的三色图 —— cv2.split() + cv.merge()
(1)图像分割得到三色图(BGR)
(2)将分割的三色图还原为彩色图
(3)保留R通道 + 保留G通道 + 保留B通道
import cv2 # cv2是opencv在python中的缩写; opencv读取图像的格式BGR(图像的格式RGB)import matplotlib.pyplot as plt # 绘图展示# 截取部分图像cat_address = r'C:\Users\my\Desktop\pythonProject\picture\cat.jpg'img = cv2.imread(cat_address)cat = img[0:50, 0:200]"""############################################## 图像分割得到三色图: b, g, r = cv2.split(img)# 功能:将多通道的图像分离成若干个单通道的图像,分割后的单通道图像尺寸大小相同。# 注意:分割后任意单通道都属于灰度图,而不是对应的颜色通道图;#############################################"""b, g, r = cv2.split(img)"""############################################## 将分割的三色图还原为彩色图: img = cv2.merge((b, g, r))# 功能:将多幅图像合并成一幅多通道图像,合并后的通道数是所有输入图像通道数的总和。# 注意:所有输入图像的通道数可以不相同,但是所有图像需要具有相同的尺寸和数据类型#############################################"""img = cv2.merge((b, g, r))# img.copy():复制原图进行图像处理,可以避免改变原图# 只保留红色(R)单通道(G and B通道全部置0即可)cur_img_R = img.copy()cur_img_R[:, :, 0] = 0cur_img_R[:, :, 1] = 0# 只保留绿色(G)单通道(R and B通道全部置0即可)cur_img_G = img.copy()cur_img_G[:, :, 0] = 0cur_img_G[:, :, 2] = 0# 只保留蓝色(B)单通道(R and G通道全部置0即可)cur_img_B = img.copy()cur_img_B[:, :, 1] = 0cur_img_B[:, :, 2] = 0plt.subplot(131), plt.imshow(cur_img_B), plt.title('Red')plt.subplot(132), plt.imshow(cur_img_G), plt.title('Green')plt.subplot(133), plt.imshow(cur_img_R), plt.title('Blue')plt.show()(四)图像的边缘填充 —— cv2.copyMakeBorder()
import cv2 # cv2是opencv在python中的缩写;opencv读取图像的格式是BGR(图像的格式RGB)import matplotlib.pyplot as plt # 绘图展示"""######################################################### cv2.copyMakeBorder():用于在像相框一样的图像周围创建边框。# cv2.copyMakeBorder(src, top, bottom, left, right, borderType, value)# 输入参数: src 原图像。# top 顶部方向的边框宽度, 以像素数为单位。# bottom 底部方向的边框宽度(以像素数为单位)。# left 沿左方向的像素数量的边框宽度。# right 沿右方向的像素数的边框宽度。# borderType 描述要添加哪种边框。# (1)BORDER_REPLICATE :复制法,即用复制最边缘像素。# (2)BORDER_REFLECT :反射法,对感兴趣的图像中的像素在两边进行复制。例如:fedcba | abcdefgh | hgfedcb# (3)BORDER_REFLECT_101 :反射法,即用以最边缘像素为轴,对称。gfedcb | abcdefgh | gfedcba# (4)BORDER_WRAP :外包装法。abcdefgh | abcdefgh | abcdefgh# (5)BORDER_CONSTANT :常量法,即用常数值填充。# value: 可选参数, 如果边界类型为cv2.BORDER_CONSTANT, 则描述边界的颜色。########################################################"""cat_address = r'C:\Users\my\Desktop\pythonProject\picture\cat.jpg'img = cv2.imread(cat_address)top_size, bottom_size, left_size, right_size = (50, 50, 50, 50) # 指定边缘(上下左右)需要填充的宽度replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT)reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_CONSTANT, value=0)plt.subplot(231), plt.imshow(img, 'gray'), plt.title('ORIGINAL')plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('BORDER_REPLICATE')plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('BORDER_REFLECT')plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('BORDER_REFLECT_101')plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('BORDER_WRAP')plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('BORDER_CONSTANT')plt.show()(五)图像融合 —— cv2.addWeighted()
(1)图像截取
(2)图像加/减常数
(3)图像加/减另一个图像
(4)图像相加 cv2.add()
import cv2 # cv2是opencv在python中的缩写;opencv读取图像的格式是BGR(图像的格式RGB)import matplotlib.pyplot as plt # 绘图展示img_cat = cv2.imread(r'picture\cat.jpg')img_dog = cv2.imread(r'picture\dog.jpg')print(img_cat.shape)print(img_dog.shape)######################################### 图像截取cat_piece = img_cat[100:300, 0:200]######################################### 图像加/减一个常数 ———— 即图像的每个像素都加常数值img_plus = img_cat - 50######################################### 图像相加/相减 ———— 两个图像的尺寸要一样。# 方式1:在numpy中,如果两个图像相加超过255,将自动减去255,然后得到剩下的值。相当于取余【% 256】# 方式2:cv2.add():如果两个图像相加超过255,则等于255,否则当前值保留不变;img_cat = cv2.resize(img_cat, (400, 480))img_dog = cv2.resize(img_dog, (400, 480))print(img_cat.shape)print(img_dog.shape)res_add1 = img_cat + img_dogres_add2 = cv2.add(img_cat, img_dog)########################################plt.subplot(231), plt.imshow(img_cat, 'gray'), plt.title('img_cat_resize')plt.subplot(232), plt.imshow(img_dog, 'gray'), plt.title('img_dog_resize')plt.subplot(233), plt.imshow(cat_piece, 'gray'), plt.title('cat_piece')plt.subplot(234), plt.imshow(img_plus, 'gray'), plt.title('fig1 - 50')plt.subplot(235), plt.imshow(res_add1, 'gray'), plt.title('fig1 + fig2')plt.subplot(236), plt.imshow(res_add2, 'gray'), plt.title('cv2.add')plt.show()"""###################################################################### 图像融合:cv2.addWeighted(src1, alpha, src2, beta, gamma)# 功能:将两张相同shape的图像按权重进行融合# 输入参数 src1/src2 图像1与图像2# alpha/beta 图像1与图像2对应的权重(融合后的图像偏向于权重高的一边)# gamma 相当于(y=a*x+b)中的截距。用于调节亮度# 权重融合公式:dst = src1 * alpha + src2 * beta + gamma#####################################################################"""img_cat = cv2.resize(img_cat, (500, 414)) # 将图像裁剪到指定尺寸img_dog = cv2.resize(img_dog, (500, 414)) # 将图像裁剪到指定尺寸res = cv2.addWeighted(img_cat, 0.35, img_dog, 0.65, 2)plt.imshow(res)plt.show()(六)颜色空间转换 —— cv2.cvtColor()
import cv2 # opencv读取的格式是BGRimport matplotlib.pyplot as plt # Matplotlib是RGB"""###################################################################### opencv中颜色空间转换函数:cv2.cvtColor()# opencv中有多种色彩空间,包括 RGB、HSI、HSL、HSV、HSB、YCrCb、CIE XYZ、CIE Lab8种。# 在opencv中默认的颜色空间是BGR。#####################################################################"""img_BGR = cv2.imread(r'picture/cat.jpg') # BGRplt.subplot(3, 3, 1), plt.imshow(img_BGR), plt.axis('off'), plt.title('BGR')img_RGB = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2RGB)plt.subplot(3, 3, 2), plt.imshow(img_RGB), plt.axis('off'), plt.title('RGB')img_GRAY = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2GRAY)plt.subplot(3, 3, 3), plt.imshow(img_GRAY), plt.axis('off'), plt.title('GRAY')img_HSV = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2HSV)plt.subplot(3, 3, 4), plt.imshow(img_HSV), plt.axis('off'), plt.title('HSV')img_YcrCb = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2YCrCb)plt.subplot(3, 3, 5), plt.imshow(img_YcrCb), plt.axis('off'), plt.title('YcrCb')img_HLS = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2HLS)plt.subplot(3, 3, 6), plt.imshow(img_HLS), plt.axis('off'), plt.title('HLS')img_XYZ = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2XYZ)plt.subplot(3, 3, 7), plt.imshow(img_XYZ), plt.axis('off'), plt.title('XYZ')img_LAB = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2LAB)plt.subplot(3, 3, 8), plt.imshow(img_LAB), plt.axis('off'), plt.title('LAB')img_YUV = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2YUV)plt.subplot(3, 3, 9), plt.imshow(img_YUV), plt.axis('off'), plt.title('YUV')plt.show()(七)阈值处理 —— cv2.threshold() + cv2.adaptiveThreshold()
(1)全局阈值分割:cv2.threshold()
(2)自适应阈值分割:cv2.adaptiveThreshold()
import cv2 # opencv读取的格式是BGRimport matplotlib.pyplot as plt # Matplotlib是RGBimg_BGR = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)_, thresh1 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_BINARY)_, thresh2 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_BINARY_INV)_, thresh3 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_TRUNC)_, thresh4 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_TOZERO)_, thresh5 = cv2.threshold(img_BGR, 127, 255, cv2.THRESH_TOZERO_INV)titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']images = [img_BGR, thresh1, thresh2, thresh3, thresh4, thresh5]for ii in range(6): plt.subplot(2, 3, ii + 1), plt.imshow(images[ii], 'gray') plt.title(titles[ii]) plt.xticks([]), plt.yticks([])plt.show()"""############################################################## 函数介绍:全局阈值分割 ———— 根据阈值将图像的像素分为两个类别:高于阈值的像素和低于阈值的像素。# 函数说明:ret, dst = cv2.threshold(src, thresh, max_val, type)# 输入参数: # src: 输入灰度图像# thresh: 阈值# max_val: 阈值上限。通常为255(8-bit)。# type: 二值化操作的类型,包含以下5种类型:# (1) cv2.THRESH_BINARY 超过阈值部分取max_val(最大值),否则取0# (2) cv2.THRESH_BINARY_INV THRESH_BINARY的反转# (3) cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变# (4) cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0# (5) cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转# 输出参数: # ret 浮点数,表示最终使用的阈值。# dst 经过阈值分割操作后的二值图像#############################################################"""image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)thresh1 = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)thresh2 = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 2)thresh3 = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)thresh4 = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)titles = ['gray Image', 'mean + binary', 'mean + binary_inv', 'gray Image', 'gaussian + binary', 'gaussian + binary_inv']images = [image, thresh1, thresh2, image, thresh3, thresh4]for ii in range(6): plt.subplot(2, 3, ii + 1), plt.imshow(images[ii], 'gray') plt.title(titles[ii]) plt.xticks([]), plt.yticks([])plt.show()"""########################################################################### 函数介绍:自适应阈值分割 ———— 根据图像不同区域的像素值来自动确定阈值,从而实现更好的图像分割效果。# 函数说明:adaptive_threshold = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)# 输入参数:# src: 输入灰度图像。# maxValue: 阈值上限。通常为255(8-bit)。# adaptiveMethod: 自适应方法,包含以下2种:# (1)cv2.ADAPTIVE_THRESH_MEAN_C(局部均值)# (2)cv2.ADAPTIVE_THRESH_GAUSSIAN_C(局部高斯加权均值)# thresholdType: 阈值类型。# (1)cv2.THRESH_BINARY 超过阈值部分取max_val(最大值),否则取0# (2)cv2.THRESH_BINARY_INV THRESH_BINARY的反转# blockSize: 区域大小,用于计算局部阈值。通常是一个奇数,例如 3、5、7 等。# C: 从均值中减去的常数,用于调整阈值的灵敏度。# 输出参数:# adaptive_threshold 自适应阈值##########################################################################"""(八)均值/高斯/方框/中值滤波 —— cv2.blur() + cv2.boxFilter() + cv2.GaussianBlur() + cv2.medianBlur()
import cv2 # opencv读取的格式是BGRimport matplotlib.pyplot as plt # Matplotlib是RGBimport numpy as npimg = cv2.imread(r'picture/cat.jpg')"""####################################### 均值滤波:cv2.blur(img, ksize) ———— 取卷积核所有元素的均值# 输入参数 ksize 表示卷积核大小。 例如:(3, 3)# 作用:对于椒盐噪声的滤除效果比较好。######################################"""blur = cv2.blur(img, (3, 3))"""####################################### 方框滤波:cv2.boxFilter(img, -1, (3, 3), normalize=True)# 输入参数 normalize=True 选择归一化 即取所有元素之和除以卷积核大小(与均值滤波等同)# normalize=False 不选择归一化 【容易越界】且当“元素之和>255”,则等于255;######################################"""box_T = cv2.boxFilter(img, -1, (3, 3), normalize=True)box_F = cv2.boxFilter(img, -1, (3, 3), normalize=False)"""####################################### 高斯滤波:cv2.GaussianBlur()# 特点:高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的######################################"""aussian = cv2.GaussianBlur(img, (5, 5), 1)"""####################################### 中值滤波:cv2.medianBlur() ———— 取卷积核所有元素(从小到大排序)的中间值# 作用:中值滤波对消除椒盐噪声非常有效,能够克服线性滤波器带来的图像细节模糊等弊端,能够有效保护图像边缘信息;######################################"""median = cv2.medianBlur(img, 5)plt.subplot(2, 3, 1), plt.imshow(img), plt.title('raw')plt.subplot(2, 3, 2), plt.imshow(blur), plt.title('blur')plt.subplot(2, 3, 3), plt.imshow(box_T), plt.title('box_T')plt.subplot(2, 3, 4), plt.imshow(box_F), plt.title('box_F')plt.subplot(2, 3, 5), plt.imshow(aussian), plt.title('aussian')plt.subplot(2, 3, 6), plt.imshow(median), plt.title('median')plt.show()"""####################################### np.hstack(img1, img2, img3) 在水平方向上平铺# np.vstack(img1, img2, img3) 在竖直方向上堆叠######################################"""res = np.hstack((blur, aussian, median))cv2.imshow('median - Gaussian - average', res)cv2.waitKey(0)cv2.destroyAllWindows()res = np.vstack((blur, aussian, median))cv2.imshow('median - Gaussian - average', res)cv2.waitKey(0)cv2.destroyAllWindows()(九)腐蚀与膨胀 —— cv2.erode() 与 cv2.dilate() + np.zeros() 与 np.ones()
import cv2 # opencv读取的格式是BGRimport numpy as npimport matplotlib.pyplot as plt # Matplotlib是RGB"""############################################################ 腐蚀操作:cv2.erode(src, kernel, iteration)# 膨胀操作:cv2.dilate(src, kernel, iteration)# 两者参数说明相同: src表示输入的图片, kernel表示方框的大小, iteration表示迭代的次数###########################################################"""img = cv2.imread(r'picture/dige.png')kernel = np.ones((3, 3), np.uint8) # 初始化卷积核大小dilate_1 = cv2.dilate(img, kernel, iterations=1) # 膨胀(迭代次数1次)dilate_2 = cv2.dilate(img, kernel, iterations=2) # 膨胀dilate_3 = cv2.dilate(img, kernel, iterations=3) # 膨胀erosion_1 = cv2.erode(img, kernel, iterations=1) # 腐蚀(迭代次数1次)erosion_2 = cv2.erode(img, kernel, iterations=2)# 腐蚀erosion_3 = cv2.erode(img, kernel, iterations=3)# 腐蚀plt.subplot(2, 3, 1), plt.imshow(dilate_1), plt.title('erode-1')plt.subplot(2, 3, 2), plt.imshow(dilate_2), plt.title('erode-2')plt.subplot(2, 3, 3), plt.imshow(dilate_3), plt.title('erode-3')plt.subplot(2, 3, 4), plt.imshow(erosion_1), plt.title('dilate-1')plt.subplot(2, 3, 5), plt.imshow(erosion_2), plt.title('dilate-2')plt.subplot(2, 3, 6), plt.imshow(erosion_3), plt.title('dilate-3')plt.show()"""############################################################ np.zeros()与np.ones():分别创建全0与全1的数组 —— 需导入numpy模块# 两者输入参数相同,创建数组的方式也相同。# 下面以np.zeros()为例# np.zeros(shape, dtype=float, order='C')# 输入参数(1)shape:生成numpy数组# (2)dtype:指定生成的数据类型数据类型,可选参数# (3)order:表示在内存中是以行为主存储还是以列为主存储,可选参数,c代表行优先(默认);F代表列优先# 创建一维数组: np.zeros(5)# 创建多维数组: np.zeros((5,2))# 创建int类型的数组: np.zeros((5,2),dtype=int)# 创建x为int类型,y为float类型的数组:np.zeros((5,2),dtype=[('x','int'),('y','float')]###########################################################"""python 中的np.zeros()和np.ones()函数
(十)形态学变化 —— cv2.morphologyEx()
主要内容:开运算 + 闭运算 + 梯度计算 + 顶帽 + 黑帽
import cv2 # opencv读取的格式是BGRimport numpy as npimport matplotlib.pyplot as plt # Matplotlib是RGB"""######################################## morphology# n. (生物)形态学;(语言学中的)词法,形态学;结构,形态######################################## morph# n. 形素,语素;形态;图像变换# v. (使)图像变形;将(图像)进行合成处理;改变,变化,变形######################################## 形态学变化函数:cv2.morphologyEx(src, op, kernel) # 参数说明:src传入的图片,op进行变化的方式, kernel表示方框的大小# op变化的方式有五种:# 开运算(open): cv2.MORPH_OPEN 先腐蚀,再膨胀。 开运算可以用来消除小黑点。# 闭运算(close): cv2.MORPH_CLOSE 先膨胀,再腐蚀。 闭运算可以用来突出边缘特征。# 形态学梯度(morph-grad): cv2.MORPH_GRADIENT 膨胀后图像(减去)腐蚀图像。 可以突出团块(blob)的边缘,保留物体的边缘轮廓。# 顶帽(top-hat): cv2.MORPH_TOPHAT 原始输入(减去)开运算结果。 将突出比原轮廓亮的部分。# 黑帽(black-hat): cv2.MORPH_BLACKHAT 闭运算结果(减去)原始输入 将突出比原轮廓暗的部分。#######################################"""img = cv2.imread(r'picture/dige.png')kernel = np.ones((5, 5), np.uint8) # 卷积核初始化img_open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 开运算img_close = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 闭运算img_grad = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) # 梯度计算img_top = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel) # 顶帽img_black = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel) # 黑帽plt.subplot(2, 3, 1), plt.imshow(img), plt.title('RAW')plt.subplot(2, 3, 2), plt.imshow(img_open), plt.title('MORPH_OPEN')plt.subplot(2, 3, 3), plt.imshow(img_close), plt.title('MORPH_CLOSE')plt.subplot(2, 3, 4), plt.imshow(img_grad), plt.title('MORPH_GRADIENT')plt.subplot(2, 3, 5), plt.imshow(img_top), plt.title('MORPH_TOPHAT')plt.subplot(2, 3, 6), plt.imshow(img_black), plt.title('MORPH_BLACKHAT')plt.show()(十一)边缘检测算子 —— cv2.sobel()、cv2.Scharr()、cv2.Laplacian()、cv2.Canny()
(1) 不同算子的差异:Sobel算子、Scharr算子、Laplacian算子
(2) Canny 不同阈值的区别
>
import cv2import matplotlib.pyplot as pltimport numpy as npimg = cv2.imread(r'picture\lena.jpg')############################################################################################# (1)左边减右边(2)白到黑是正数,黑到白就是负数,且所有的负数会被截断成0,所以要取绝对值。sobel_Gx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)sobel_Gx_Abs = cv2.convertScaleAbs(sobel_Gx)########################################sobel_Gy = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)sobel_Gy_Abs = cv2.convertScaleAbs(sobel_Gy)# 分别计算x和y,再求和sobel_Gx_Gy_Abs = cv2.addWeighted(sobel_Gx_Abs, 0.5, sobel_Gy_Abs, 0.5, 0) # 权重值x + 权重值y +偏置b# 同时对x和y进行求导,会导致部分信息丢失。(不建议)# sobel_Gx = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=3)########################################plt.subplot(2, 3, 1), plt.imshow(img), plt.title('RAW')plt.subplot(2, 3, 2), plt.imshow(sobel_Gx), plt.title('sobel_Gx')plt.subplot(2, 3, 3), plt.imshow(sobel_Gx_Abs), plt.title('sobel_Gx_Abs')plt.subplot(2, 3, 4), plt.imshow(sobel_Gy), plt.title('sobel_Gy')plt.subplot(2, 3, 5), plt.imshow(sobel_Gy_Abs), plt.title('sobel_Gy_Abs')plt.subplot(2, 3, 6), plt.imshow(sobel_Gx_Gy_Abs), plt.title('sobel_Gx_Gy_Abs')plt.show()"""######################################### Sobel算子: 是一种常用的边缘检测算子。对噪声具有平滑作用,提供较为精确的边缘方向信息,但是边缘定位精度不够高。# Sobel算子: 是离散微分算子(discrete differentiation operator),它结合了高斯平滑和微分求导,用来计算图像灰度的近似梯度,梯度越大越有可能是边缘。# 边缘就是像素对应的灰度值快速变化的地方。如:黑到白的边界# 图像是二维的。Sobel算子在x,y两个方向求导,故有不同的两个卷积核(Gx, Gy),且Gx的转置等于Gy。分别反映了每一点像素在水平方向和在垂直方向上的亮度变换情况.######################################### dst = cv2.Sobel(src, ddepth, dx, dy, ksize)# 输入参数src 输入图像# ddepth 图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度;# dx和dy 表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。# ksize 卷积核大小,一般为3、5。# 同时对x和y进行求导,会导致部分信息丢失。(不建议)- 分别计算x和y,再求和(效果好)######################################### (1)cv2.CV_16S的说明# (1)Sobel函数求完导数后会有负值,还有会大于255的值。# (2)而原图像是uint8,即8位无符号数。所以Sobel建立图像的位数不够,会有截断。# (3)因此要使用16位有符号的数据类型,即cv2.CV_16S。# (2)cv2.convertScaleAbs(): 给图像的所有像素加一个绝对值# 通过该函数将其转回原来的uint8形式。否则将无法显示图像,而只是一副灰色的窗口。############################################################################################"""# 不同算子的差异:Sobel算子、Scharr算子、laplacian算子img = cv2.imread(r'picture\lena.jpg', cv2.IMREAD_GRAYSCALE)sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)sobelx = cv2.convertScaleAbs(sobelx)sobely = cv2.convertScaleAbs(sobely)sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)scharrx = cv2.convertScaleAbs(scharrx)scharry = cv2.convertScaleAbs(scharry)scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)laplacian = cv2.Laplacian(img, cv2.CV_64F)laplacian = cv2.convertScaleAbs(laplacian)res = np.hstack((sobelxy, scharrxy, laplacian))cv2.imshow('Sobel, Scharr, Laplacian', res)cv2.waitKey(0)cv2.destroyAllWindows()"""############################################################################################# 边缘检测Canny算子# (1)使用高斯滤波器,以平滑图像,滤除噪声。# (2)计算图像中每个像素点的梯度强度和方向。# (3)应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。保留大值,去除小值。# (4)应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。# (5)通过抑制孤立的弱边缘最终完成边缘检测。############################################################################################"""img = cv2.imread(r'picture\lena.jpg', cv2.IMREAD_GRAYSCALE)v1 = cv2.Canny(img, 80, 150) # minVal越小,检测出来的特征越多(可能是假边界值);maxVal越大,检测出来的特征越少v2 = cv2.Canny(img, 50, 100)res = np.hstack((v1, v2))cv2.imshow('Canny', res)cv2.waitKey(0)cv2.destroyAllWindows()opencv边缘检测sobel算子
opencv边缘检测Canny
(十二)图像金字塔 —— cv2.pyrUp()、cv2.pyrDown()
import cv2import matplotlib.pyplot as pltimg = cv2.imread(r'picture\AM.png')img_up = cv2.pyrUp(img) # 高斯金字塔:图像上采样(放大一倍)img_up2 = cv2.pyrUp(img_up) # 高斯金字塔:二次图像上采样(放大两倍)############################img_down = cv2.pyrDown(img) # 高斯金字塔:图像下采样(缩小一倍)img_down2 = cv2.pyrDown(img_down) # 高斯金字塔:二次图像下采样(缩小两倍)############################img_up_down = cv2.pyrDown(img_up) # 高斯金字塔:先上采样,然后下采样img_down_up = cv2.pyrUp(img_down) # 高斯金字塔:先下采样,然后上采样############################img_laplacian = img - img_down_up # 拉普拉斯金字塔:(1)源图像先缩小后再放大(2)源图像减去(1)操作后的图像。plt.subplot(2, 4, 1), plt.imshow(img), plt.title('RAW')plt.subplot(2, 4, 2), plt.imshow(img_up), plt.title('img_up')plt.subplot(2, 4, 3), plt.imshow(img_up2), plt.title('img_up2')plt.subplot(2, 4, 4), plt.imshow(img_down), plt.title('img_down')plt.subplot(2, 4, 5), plt.imshow(img_down2), plt.title('img_down2')plt.subplot(2, 4, 6), plt.imshow(img_up_down), plt.title('img_up_down')plt.subplot(2, 4, 7), plt.imshow(img_down_up), plt.title('img_down_up')plt.subplot(2, 4, 8), plt.imshow(img_laplacian), plt.title('img_laplacian')plt.show()"""############################################################################################# 高斯金字塔: cv2.pyrUp 与 cv2.pyrDown# cv2.pyrDown: 向下采样(缩小一倍)。 (1)对图像进行高斯内核卷积;(2)将所有偶数行和列去除;# cv2.pyrUp: 向上采样(放大一倍),分辨率降低。(1)将图像每隔“一行与一列”,全部填充0;(2)使用先前同样的高斯内核(乘以4)与放大后的图像卷积,获得近似值;# 形成过程大致: (1)对原图像进行低通滤波和降采样得到一个粗尺度的近似图像,即分解得到的低通近似图像,# (2)对近似图像经过插值(即上采样)和低通滤波# (3)计算它和原图像的差值,得到分解的带通分量。# ——— 第一步:源图像先缩小后再放大; 第二步:源图像减去第一步操作后得到新图像。# 图像缩放: (1)图像金字塔(2)resize()函数; 后者效果更好,不会降低分辨率。############################################################################################"""OpenCV计算机视觉学习 —— 图像金字塔(高斯金字塔,拉普拉斯金字塔,图像缩放resize函数)
(十三)图像轮廓检测 —— cv2.findContours()、cv2.drawContours()、cv2.arcLength()、cv2.approxPolyDP()、cv2.rectangle()
(1) 轮廓的多边形拟合曲线:cv2.approxPolyDP()
(2) 用矩形画出轮廓的边界:cv2.boundingRect()、cv2.rectangle()
(3) 用外接圆画出轮廓的边界:cv2.minEnclosingCircle()、cv2.circle()
import cv2import matplotlib.pyplot as plt # Matplotlib是RGB#################################### 图像二值化 ———— 图像轮廓检测的输入图像是二值图,即黑白的(不是灰度图)img = cv2.imread(r'picture\contours2.png')img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图ret, thresh = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY) # 二值化#################################### (1)图像轮廓检测draw_img1 = img.copy()contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)# 画出图像的轮廓(在图像上) —— 注意:图像需要copy(), 否则原图会随之一起改变。res1 = cv2.drawContours(draw_img1, contours, -1, (0, 0, 255), 2)#################################### (2)轮廓的多边形拟合曲线draw_img2 = img.copy()contours1 = contours[0]epsilon = 0.21*cv2.arcLength(contours1, True) # 系数k:越小越接近于真实轮廓,越大拟合越粗糙approx = cv2.approxPolyDP(contours1, epsilon, True)# 画出图像的轮廓(在图像上) —— 注意:图像需要copy(), 否则原图会随之一起改变。res2 = cv2.drawContours(draw_img2, [approx], -1, (0, 0, 255), 2)#################################### (3)用矩形画出轮廓的边界draw_img3 = img.copy()x, y, w, h = cv2.boundingRect(contours1)img_rectangle = cv2.rectangle(draw_img3, (x, y), (x+w, y+h), (0, 255, 0), 2)#################################### (4)用外接圆画出轮廓的边界draw_img4 = img.copy()(x, y), radius = cv2.minEnclosingCircle(contours1)center = (int(x), int(y))radius = int(radius)img_circle = cv2.circle(draw_img4, center, radius, (0, 255, 0), 2)###################################plt.subplot(2, 3, 1), plt.imshow(img), plt.title('RAW') # 轮廓点绘制的颜色通道是BGR; 但是Matplotlib是RGB;plt.subplot(2, 3, 2), plt.imshow(res1), plt.title('findContours') # 故在绘图时,(0, 0, 255)会由BGR转换为RGB(红 - 蓝)plt.subplot(2, 3, 3), plt.imshow(res2), plt.title('approxPolyDP')plt.subplot(2, 3, 4), plt.imshow(draw_img3), plt.title('rectangle')plt.subplot(2, 3, 5), plt.imshow(draw_img4), plt.title('circle')plt.show()"""####################################################################### (1)轮廓检测:contours, hierarchy = cv2.findContours(img, mode, method)# 输入参数 mode: 轮廓检索模式# (1)RETR_EXTERNAL: 只检索最外面的轮廓;# (2)RETR_LIST: 检索所有的轮廓,但检测的轮廓不建立等级关系,将其保存到一条链表当中,# (3)RETR_CCOMP: 检索所有的轮廓,并建立两个等级的轮廓。顶层是各部分的外部边界,内层是的边界信息;# (4)RETR_TREE: 检索所有的轮廓,并建立一个等级树结构的轮廓;(最常用)# method: 轮廓逼近方法# (1)CHAIN_APPROX_NONE: 存储所有的轮廓点,相邻的两个点的像素位置差不超过1。 例如:矩阵的四条边。(最常用)# (2)CHAIN_APPROX_SIMPLE: 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标。 例如:矩形的4个轮廓点。# 输出参数 contours:所有的轮廓# hierarchy:每条轮廓对应的属性# 备注0:轮廓就是将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。# 备注1:函数输入图像是二值图,即黑白的(不是灰度图)。所以读取的图像要先转成灰度的,再转成二值图。# 备注2:函数在opencv2只返回两个值:contours, hierarchy。# 备注3:函数在opencv3会返回三个值:img, countours, hierarchy####################################################################### (2)绘制轮廓:v2.drawContours(image, contours, contourIdx, color, thickness) ———— (在图像上)画出图像的轮廓# 输入参数 image: 需要绘制轮廓的目标图像,注意会改变原图# contours: 轮廓点,上述函数cv2.findContours()的第一个返回值# contourIdx: 轮廓的索引,表示绘制第几个轮廓。-1表示绘制所有的轮廓# color: 绘制轮廓的颜色(RGB)# thickness: (可选参数)轮廓线的宽度,-1表示填充# 备注:图像需要先复制一份copy(), 否则(赋值操作的图像)与原图会随之一起改变。####################################################################### (3)计算轮廓的长度:retval = cv2.arcLength(curve, closed)# 输入参数: curve 轮廓(曲线)。# closed 若为true,表示轮廓是封闭的;若为false,则表示打开的。(布尔类型)## 输出参数: retval 轮廓的长度(周长)。####################################################################### (4)找出轮廓的多边形拟合曲线:approxCurve = approxPolyDP(contourMat, epsilon, closed);# 输入参数: contourMat: 轮廓点矩阵(集合)# epsilon: (double类型)指定的精度, 即原始曲线与近似曲线之间的最大距离。# closed: (bool类型)若为true, 则说明近似曲线是闭合的; 反之, 若为false, 则断开。# # 输出参数: approxCurve: 轮廓点矩阵(集合);当前点集是能最小包容指定点集的。画出来即是一个多边形;####################################################################### (5)绘制矩形边框:cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)# (x, y): 矩形定点# (x+w, y+h): 矩形的宽高# (0,0,225): 矩形的边框颜色;# 2: 矩形边框宽度######################################################################"""(十四)模板匹配 —— cv2.matchTemplate()、cv2.minMaxLoc()
############################################################################################# 模板匹配和卷积原理很相似:模板在原图像上从原点开始滑动,并计算模板与(图像被模板覆盖的地方)的相似程度;# 【假如】原图形大小: AxB; 而模板大小: axb; 则输出结果的矩阵大小: (A-a+1)x(B-b+1)############################################################################################import cv2import matplotlib.pyplot as plt # Matplotlib是RGBimg = cv2.imread(r'picture/lena.jpg', 0) # 读取目标图片(0/1/2,表示RGB单通道)template = cv2.imread(r'picture/face.jpg', 0) # 读取模板图片h, w = template.shape[::1] # 获得模板图片的高宽尺寸############################### 如果模板方法是平方差或者归一化平方差,要用min_loc; 其余用max_loc##############################res1 = cv2.matchTemplate(img, template, cv2.TM_SQDIFF) # 执行模板匹配,采用的匹配方式cv2.TM_SQDIFF_NORMEDmin_val1, max_val1, min_loc1, max_loc1 = cv2.minMaxLoc(res1) # 寻找矩阵(一维数组当做向量,用Mat定义)中的最大值和最小值的匹配结果及其位置top_left1 = min_loc1bottom_right1 = (top_left1[0] + w, top_left1[1] + h)##############################res2 = cv2.matchTemplate(img, template, cv2.TM_CCORR)min_val2, max_val2, min_loc2, max_loc2 = cv2.minMaxLoc(res2)top_left2 = max_loc2bottom_right2 = (top_left2[0] + w, top_left2[1] + h)##############################res3 = cv2.matchTemplate(img, template, cv2.TM_CCOEFF)min_val3, max_val3, min_loc3, max_loc3 = cv2.minMaxLoc(res3)top_left3 = max_loc3bottom_right3 = (top_left3[0] + w, top_left3[1] + h)##############################res4 = cv2.matchTemplate(img, template, cv2.TM_SQDIFF_NORMED)min_val4, max_val4, min_loc4, max_loc4 = cv2.minMaxLoc(res4)top_left4 = min_loc4bottom_right4 = (top_left4[0] + w, top_left4[1] + h)##############################res5 = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED)min_val5, max_val5, min_loc5, max_loc5 = cv2.minMaxLoc(res5)top_left5 = max_loc5bottom_right5 = (top_left5[0] + w, top_left5[1] + h)##############################res6 = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)min_val6, max_val6, min_loc6, max_loc6 = cv2.minMaxLoc(res6)top_left6 = max_loc6bottom_right6 = (top_left6[0] + w, top_left6[1] + h)# 画矩形img1 = img.copy(); img2 = img.copy(); img3 = img.copy()img4 = img.copy(); img5 = img.copy(); img6 = img.copy()cv2.rectangle(img1, top_left1, bottom_right1, 255, 2)cv2.rectangle(img2, top_left2, bottom_right2, 255, 2)cv2.rectangle(img3, top_left3, bottom_right3, 255, 2)cv2.rectangle(img4, top_left4, bottom_right4, 255, 2)cv2.rectangle(img5, top_left5, bottom_right5, 255, 2)cv2.rectangle(img6, top_left6, bottom_right6, 255, 2)############################################### plt.imshow(img1) 彩图显示# plt.imshow(img1, cmap='gray') 灰色图plt.subplot(231), plt.imshow(img1, cmap='gray'), plt.axis('off'), plt.title('cv2.TM_SQDIFF')plt.subplot(232), plt.imshow(img2, cmap='gray'), plt.axis('off'), plt.title('cv2.TM_CCORR')plt.subplot(233), plt.imshow(img3, cmap='gray'), plt.axis('off'), plt.title('cv2.TM_CCOEFF')plt.subplot(234), plt.imshow(img4, cmap='gray'), plt.axis('off'), plt.title('cv2.TM_SQDIFF_NORMED')plt.subplot(235), plt.imshow(img5, cmap='gray'), plt.axis('off'), plt.title('cv2.TM_CCORR_NORMED')plt.subplot(236), plt.imshow(img6, cmap='gray'), plt.axis('off'), plt.title('cv2.TM_CCOEFF_NORMED')plt.show()"""############################################### 模板匹配:cv2.matchTemplate(image, template, method)# 输入图像:检测对象的图像# 模板图像:待检测的对象特征# 模板匹配方法:# (1)cv2.TM_SQDIFF: 计算平方差。 计算出来的值越接近0,越相关# (2)cv2.TM_CCORR: 计算相关性。 计算出来的值越大,越相关# (3)cv2.TM_CCOEFF: 计算相关系数。 计算出来的值越大,越相关# (4)cv2.TM_SQDIFF_NORMED: 计算(归一化)平方差。 计算出来的值越接近0,越相关# (5)cv2.TM_CCORR_NORMED: 计算(归一化)相关性。 计算出来的值越接近1,越相关# (6)cv2.TM_CCOEFF_NORMED: 计算(归一化)相关系数。 计算出来的值越接近1,越相关# (最好选择有归一化操作,效果好)############################################### 获取匹配结果函数:min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(ret)# 其中: ret是cv2.matchTemplate函数返回的矩阵;# min_val, max_val, min_loc, max_loc分别表示最小值,最大值,最小值与最大值在图像中的位置# 如果模板方法是平方差或者归一化平方差,要用min_loc; 其余用max_loc##############################################"""opencv 实现模板匹配、特征点匹配
(十五)直方图(均衡化) —— cv2.calcHist()、img.ravel()、cv2.bitwise_and()、cv2.equalizeHist()、cv2.createCLAHE()
import cv2 # opencv读取的格式是BGRimport matplotlib.pyplot as plt # Matplotlib是RGBimport numpy as np##############################################img = cv2.imread(r'picture\cat.jpg', 0) # 0表示灰度图hist = cv2.calcHist([img], [0], None, [256], [0, 256])plt.hist(img.ravel(), 256) # img.ravel() 将图片转化成一维数组; 256 是BIN的数目plt.show()# hist(): 用于绘制直方图。############################################### 不同颜色通道的直方图img1 = cv2.imread(r'picture\cat.jpg', 1) # 0表示灰度图color = ('blue', 'green', 'red')for i, col in enumerate(color): hist1 = cv2.calcHist([img1], [i], None, [256], [0, 256]) # img必须是单通道图像,否则报错:error: (-215) plt.plot(hist1, color=col)plt.show()"""############################################### 统计每个像素的众数(备注:输入参数需用方括号[]表示)# 直方图: cv2.calcHist(images,channels,mask,histSize,ranges)# 输入参数: images: 原图像图像格式必须为 uint8 或 float32。# channels: (1)灰度图:[0]; (2)彩色图像:[0] [1] [2],分别对应着BGR。# mask: 掩模图像。(1)统计整幅图像的直方图,则设置为 None。(2)统计图像某一区域的直方图,则制作一个掩模图像。# histSize: BIN的数目。可以理解为迭代范围。如:[1]表示0,1,2...256;如[10]表示0~10,11~20...;如[256]表示0~256。# ranges: 统计像素值范围。通常为[0~256]。##############################################"""# 创建掩膜:maskmask = np.zeros(img.shape[:2], np.uint8) # np.uint8:八位无符号整型:0~255mask[100:300, 100:400] = 255 # 掩膜区域显示255masked_img = cv2.bitwise_and(img, img, mask=mask) # 与操作hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])plt.subplot(221), plt.imshow(img, 'gray'), plt.title('Raw')plt.subplot(222), plt.imshow(mask, 'gray'), plt.title('mask')plt.subplot(223), plt.imshow(masked_img, 'gray'), plt.title('cv2.bitwise_and')plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask), plt.title('hist_full + hist_mask')plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask), plt.title('hist_full + hist_mask')plt.xlim([0, 256])plt.show()############################################### 直方图均衡化img = cv2.imread(r'picture\cat.jpg', 0) # 0表示灰度图equ = cv2.equalizeHist(img) # 将0~255两端的像素进行整幅图像的均匀化。即去掉:极黑/极白像素# 自适应直方图均衡化clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) # 将图像切分成8*8份,分别进行直方图均衡化(有效果降低过度处理)res_clahe = clahe.apply(img)plt.subplot(131), plt.imshow(img, 'gray'), plt.title('Raw')plt.subplot(132), plt.imshow(equ, 'gray'), plt.title('cv2.equalizeHist')plt.subplot(133), plt.imshow(res_clahe, 'gray'), plt.title('cv2.createCLAHE')plt.show()##############################################matplotlib.pyplot.hist()函数
像素点直方图统计、掩膜图像
错误提示:cv::binary_op
错误提示:cv::histPrepareImages
(十六)傅里叶变换 + 低通/高通滤波 —— cv2.dft()、cv2.idft()、np.fft.fftshift()、np.fft.ifftshift()、cv2.magnitud()
傅里叶变换
以时间作为参照来观察动态世界的方法我们称其为时域分析。
世间万物都在随着时间不停的改变,并且永远不会静止下来。但在频域中,你会发现世界是静止的、永恒不变的。
傅里叶告诉我们:任何周期函数,都可以看作是不同振幅,不同相位正弦波的叠加。
举例:利用对不同琴键不同力度,不同时间点的敲击,可以组合出任何一首乐曲。
傅里叶分析可分为傅里叶级数(Fourier Serie)和傅里叶变换(Fourier Transformation)。
傅里叶变换的作用
(1)高频:变化剧烈的灰度分量,例如:边界/图像的轮廓
(2)低频:变化缓慢的灰度分量,例如:一片大海
滤波器
(1)低通滤波器:只保留低频,会使得图像模糊
(2)高通滤波器:只保留高频,会使得图像细节增强
"""############################################### 傅里叶变换:cv2.dft(np.float32, cv2.DFT_COMPLEX_OUTPUT)# 输入参数: (1)输入图像需要先转换成np.float32 格式。# (2)转换标识 - cv2.DFT_COMPLEX_OUTPUT - 用来输出一个复数阵列# 逆傅里叶变换:cv2.idft(dft_shift)# 输入参数: (1)傅里叶变换后并位置转换后的频谱图像。# 在OpenCV中,我们通过cv2.dft()来实现傅里叶变换,使用cv2.idft()来实现逆傅里叶变换。# 注意1:变换后得到原始图像的频谱信息。其中:频率为0的部分(零分量)会在左上角,需要使用numpy.fft.fftshift()函数,将其移动到中间位置。# 注意2:变换后的频谱图像是双通道的(实部,虚部)。需要使用cv2.magnitude函数,将幅度映射到灰度空间[0,255]内,使其以灰度图像显示出来。# cv2.magnitude(x-实部,y-虚部)# 输入参数: (1)浮点型x坐标值(实部)# (2)浮点型y坐标值(虚部)# 备注:两个参数的必须具有相同的大小(size)##############################################"""# 频谱图像设计img = cv2.imread(r'picture\lena.jpg', 0) # 0表示灰度图dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT) # 傅里叶变换(np.float32 格式)dft_shift = np.fft.fftshift(dft) # 移动到中心位置magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:, :, 0],dft_shift[:, :, 1])) # 理解为固定公式即可# 频谱:最中心的频率最小,像圆一样向外扩散,越来越大。# 备注:20*np.log(cv2.magnitude())############################################### 低通滤波设计rows, cols = img.shapecrow, ccol = int(rows/2), int(cols/2) # 获取图像中心位置mask_low = np.zeros((rows, cols, 2), np.uint8)mask_low[crow-30:crow+30, ccol-30:ccol+30] = 1fshift_low = dft_shift * mask_lowf_ishift_low = np.fft.ifftshift(fshift_low)img_low = cv2.idft(f_ishift_low) # 逆傅里叶变换img_low = cv2.magnitude(img_low[:, :, 0], img_low[:, :, 1]) # 频谱图像转灰度图像############################################### 高通滤波设计mask_high = np.ones((rows, cols, 2), np.uint8)mask_high[crow-30:crow+30, ccol-30:ccol+30] = 0fshift_high = dft_shift * mask_highf_ishift_high = np.fft.ifftshift(fshift_high)img_high = cv2.idft(f_ishift_high) # 逆傅里叶变换img_high = cv2.magnitude(img_high[:, :, 0], img_high[:, :, 1]) # 频谱图像转灰度图像##############################################plt.subplot(141), plt.imshow(img, cmap='gray'), plt.title('Input Image'), plt.xticks([]), plt.yticks([])plt.subplot(142), plt.imshow(magnitude_spectrum, cmap='gray'), plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])plt.subplot(143), plt.imshow(img_low, cmap='gray'), plt.title('Low pass filter'), plt.xticks([]), plt.yticks([])plt.subplot(144), plt.imshow(img_high, cmap='gray'), plt.title('High pass filter'), plt.xticks([]), plt.yticks([])plt.show()傅里叶分析之详细剖析(完整版-强烈推荐)
(十七)Harris角点检测 —— cv2.cornerHarris()、np.float32()
import cv2import numpy as npimg = cv2.imread(r'picture\Black_and_white_chess.jpg')print('img.shape:', img.shape)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # OpenCV读取图像的颜色是BGRgray = np.float32(gray)"""################################################## Harris角点检测:res = cv2.cornerHarris(img_gray, blockSize, k_size, k)# 输入参数 img_gray 数据类型为 float32 的输入图像。# blockSize 角点检测中要考虑的领域大小 (一般等于2)# k_size Sobel求导中使用的窗口大小 (一般等于3)# k 方程中检测器的自由参数, 取值参数为 [0,04,0.06].# 角点定义: 角点是一个无论框框往哪边移动 框框内像素值都会变化很大的情况而定下来的点。################################################### float16半精度浮点数,包括:1 个符号位,5 个指数位,10 个尾数位# float32单精度浮点数,包括:1 个符号位,8 个指数位,23 个尾数位# float64双精度浮点数,包括:1 个符号位,11 个指数位,52 个尾数位##################################################"""Harris_dst = cv2.cornerHarris(gray, 2, 3, 0.04)print('dst.shape:', Harris_dst.shape)# 最佳阈值因图而异(如果角点>角点最大值*0.01,就判断为角点)img[Harris_dst > 0.01 * Harris_dst.max()] = [0, 0, 255] # [0, 0, 255] 表示角点用红色表示cv2.imshow('Harris_dst', img)cv2.waitKey(0)cv2.destroyAllWindows()OpenCV特征提取与检测之Harris角点检测
(十八)SIFT尺度不变特征检测 —— cv2.xfeatures2d.SIFT_create()、sift.detectAndCompute()、sift.detect()、sift.compute()、cv2.drawKeypoints
"""########################################################## SIFT 图像特征检测算法# (Scale-invariant feature transform,SIFT)尺度不变特征变换。# 特点:具有尺度不变性。即对图片进行放缩、变形、模糊、明暗变化、光照变化、添加噪声,甚至是使用不同的相机拍摄不同角度的照片的情况下,SIFT都能检测到稳定的特征点,并建立对应关系。# 缺点:计算量比较大,很难实时# 对比:Harris角点检测算法最大的缺陷是不具有尺度不变性。当图片放大后,原来能检测到的角点就变成了边线,就检测不到了########################################################## 在Opencv中,SIFT函数从3.4.3版本以上已经涉及到专利保护。顾Opencv需要降版本使用。# 卸载旧版本:pip uninstall opencv-python# pip uninstall opencv-contrib-python# 安装新版本:pip install opencv-python==3.4.1.15# pip install opencv-contrib-python==3.4.1.15#########################################################"""import cv2 # OpenCV读取图像的颜色是BGRimg = cv2.imread('test_1.jpg')gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为灰度图sift = cv2.xfeatures2d.SIFT_create() # 实例化一个sift()函数"""########################################################## 检测关键点并计算描述子:key_points, des = sift.detectAndCompute(img, None)# 输出参数:key_points为图像关键点;dst为sift特征向量,通常是128维。## 其中,sift.detectAndCompute()可以拆分为以下两个函数:sift.detect()和sift.compute()。# key_points = sift.detect(gray, None) # 找出图像中的关键点# kp, dst = sift.compute(key_points) # 计算关键点对应的sift特征向量#########################################################"""key_points, des = sift.detectAndCompute(gray, None) # 找出图像中的关键点"""########################################################## 在图中画出关键点:ret = cv2.drawKeypoints(gray, key_points, img)# 输入参数:gray表示输入图片;kp表示关键点;img表示输出的图片#########################################################"""img = cv2.drawKeypoints(gray, key_points, img) # 在图中画出关键点cv2.imshow('key_points', img)cv2.waitKey(0)cv2.destroyAllWindows()(十九)暴力特征匹配 —— cv2.BFMatcher_create()、bf.match()、bf_knn.knnMatch()、cv2.drawMatches()
"""######################################################### 暴力特征检测主要流程 # (1)先在查询描述符中取一个关键点的描述符,将其与训练描述符中的所有关键点描述符进行比较。# (2)每次比较后会计算出一个距离值,距离最小的值对应最佳匹配结果。# (3)所有描述符比较完后,匹配器返回匹配结果列表。######################################################### 在Opencv中,SIFT函数从3.4.3版本以上已经涉及到专利保护。顾Opencv需要降版本使用。# 卸载旧版本:pip uninstall opencv-python# pip uninstall opencv-contrib-python# 安装新版本:pip install opencv-python==3.4.1.15# pip install opencv-contrib-python==3.4.1.15########################################################"""import cv2 # opencv读取的格式是BGRimport matplotlib.pyplot as plt # Matplotlib是RGBimg1 = cv2.imread(r'picture\box.png')img2 = cv2.imread(r'picture\box_in_scene.png')#####################################sift = cv2.xfeatures2d.SIFT_create() # 创建SIFT特征检测器##########################kp1, des1 = sift.detectAndCompute(img1, None) # 获取第一张图关键点kp1与特征向量des1kp2, des2 = sift.detectAndCompute(img2, None) # 获取第二张图关键点kp2与特征向量des2###################################### 方法1:match()匹配最佳结果bf = cv2.BFMatcher_create(cv2.NORM_L1, crossCheck=False)matches = bf.match(des1, des2)matches = sorted(matches, key=lambda x: x.distance)result = cv2.drawMatches(img1, kp1, img2, kp2, matches[:15], None)###################################### 方法二:knnMatch()匹配指定数量的最佳结果bf_knn = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=False)ms_knn = bf_knn.knnMatch(des1, des2, k=2)# 应用比例测试选择要使用的匹配结果good = []for m, n in ms_knn: if m.distance
OpenCV特征匹配 SIFT demo(图解)
OpenCV特征检测之SIFT算法(原理详解)
OpenCV特征检测之特征匹配方式详解
(二十)图像缩放+镜像+平移+旋转+仿射变换+透视变换 —— cv2.resize()、cv2.getRotationMatrix2D()、cv2.getAffineTransform()、cv2.getPerspectiveTransform()、cv2.warpPerspective()、cv2.warpAffine()
import cv2import numpy as npimport matplotlib.pyplot as plt # 绘图展示################################################img = cv2.imread(r"picture/1.jpg", 1)imgInfo = img.shapeheight = imgInfo[0]; width = imgInfo[1]; deep = imgInfo[2]################################################################################################# 图像缩放# (1)直接进行缩放操作dstHeight = int(height/2)dstWidth = int(width/2)dst_resize_dir = cv2.resize(img, (dstWidth, dstHeight))# (2)使用邻域插值法进行缩放dst_resize_linear = np.zeros([dstHeight, dstWidth, 3], np.uint8)for i in range(dstHeight): for j in range(dstWidth): iNew = i * (height * 1.0 / dstHeight) jNew = j * (width * 1.0 / dstWidth) dst_resize_linear[i, j] = img[int(iNew), int(jNew)]################################################################################################# 镜像dst_mirror = np.zeros([height * 2, width, deep], np.uint8)for i in range(height): for j in range(width): dst_mirror[i, j] = img[i, j] dst_mirror[height * 2 - i - 1, j] = img[i, j]for i in range(width): dst_mirror[height, i] = (0, 0, 255)################################################################################################# 图像平移dst_trans = np.zeros(imgInfo, np.uint8)for i in range(height): for j in range(width - 200): # 平移大小:100 dst_trans[i, j+100] = img[i, j] # 新图像截取原图像部分数据################################################################################################# 图像旋转matRotate = cv2.getRotationMatrix2D((height*0.5, width*0.5), 45, 0.7) # 获取旋转矩阵dst_rotate = cv2.warpAffine(img, matRotate, (height, width)) # 仿射变换################################################################################################# 仿射变换matSrc = np.float32([[0, 0], [0, height-1], [width-1, 0]]) # 原图的三个点坐标matDst = np.float32([[50, 50], [100, height-50], [width-200, 100]]) # 仿射的三个点坐标matAffine = cv2.getAffineTransform(matSrc, matDst) # 获取仿射变换矩阵dst_affine = cv2.warpAffine(img, matAffine, (height, width)) # 仿射变换################################################################################################# 透视变换matDst1 = np.float32([[50, 50], [100, height-50], [width-200, 100], [width-200, height-50]]) # 仿射的三个点坐标matSrc1 = np.float32([[0, 0], [0, height-1], [width-1, 0], [width-1, height-1]]) # 原图的三个点坐标matwarp = cv2.getPerspectiveTransform(matDst1, matSrc1) # 参数1:输入图像的四个点坐标 参数2:输出图像的四个点坐标(方方正正的图像)dst_Perspective = cv2.warpPerspective(img, matwarp, (height, width))dst_Perspective = np.hstack((dst_affine, dst_Perspective))################################################################################################plt.subplot(241), plt.imshow(img, 'gray'), plt.title('img')plt.subplot(242), plt.imshow(dst_resize_dir, 'gray'), plt.title('dst_resize_dir (coordinates)')plt.subplot(243), plt.imshow(dst_resize_linear, 'gray'), plt.title('dst_resize_linear (coordinates)')plt.subplot(244), plt.imshow(dst_mirror, 'gray'), plt.title('dst_mirror')plt.subplot(245), plt.imshow(dst_trans, 'gray'), plt.title('dst_trans')plt.subplot(246), plt.imshow(dst_rotate, 'gray'), plt.title('dst_rotate')plt.subplot(247), plt.imshow(dst_affine, 'gray'), plt.title('dst_affine')plt.subplot(248), plt.imshow(dst_Perspective, 'gray'), plt.title('dst_Perspective')plt.show()"""################################################# 图像缩小或放大:cv2.resize(src, dsize, fx=0, fy=0, interpolation=INTER_LINEAR)# 输入参数: src 输入图片# dsize (1)矩阵参数缩放到指定大小(width,height); 例如:cv2.resize(img_dog, (500, 414))# (2)矩阵参数为(0,0),原图像缩放倍数通过fx, fy来控制; 例如:cv2.resize(img_dog, (0, 0), fx=4, fy=4)# fx, fy 沿x轴,y轴的缩放系数# interpolation 插值方法,有以下五种(可选参数)# (1)INTER_NEAREST 最近邻插值 (2)INTER_LINEAR 双线性插值(默认设置)# (3)INTER_AREA 使用像素区域关系进行重采样。 (4)INTER_CUBIC 4x4像素邻域的双三次插值# (5)INTER_LANCZOS4 8x8像素邻域的Lanczos插值################################################# 获取旋转矩阵:rot_mat = cv2.getRotationMatrix2D(center, angle, scale)# 输入参数 center 旋转的中心点。一般是图像的中心,取图像长宽的一半# angle 旋转的角度。正值是顺时针旋转,负值是逆时针旋转# scale 缩放比例。################################################# 获取仿射变换矩阵:matAffine = cv2.getAffineTransform(matSrc, matDst)# 输入参数 matSrc 原图的三个点坐标# matDst 仿射的三个点坐标# 输出参数 matAffine 仿射变换矩阵################################################# 计算齐次变换矩阵:cv2.getPerspectiveTransform(rect, dst)# 输入参数rect输入图像的四个点(四个角)# dst输出图像的四个点(方方正正的图像对应的四个角)################################################# 仿射变换:cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)# 透视变换:cv2.warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)# src:输入图像 dst:输出图像# M:2×3的变换矩阵# dsize:变换后输出图像尺寸# flag:插值方法# borderMode:边界像素外扩方式# borderValue:边界像素插值,默认用0填充## (Affine Transformation)可实现旋转,平移,缩放,变换后的平行线依旧平行。# (Perspective Transformation)即以不同视角的同一物体,在像素坐标系中的变换,可保持直线不变形,但是平行线可能不再平行。## 备注:cv2.warpAffine需要与cv2.getRotationMatrix2D/cv2.getAffineTransform/cv2.getPerspectiveTransform搭配使用。################################################"""