CV2库基本函数笔记
目录
1. 图像基本属性
1. 图像尺寸: 可以用image.shape函数获取到图片的width height
2. 颜色通道:RGB(三通道)、灰度图(单通道)
3. 数据类型:常见的一般有uint8(0~255)、float32
2. 处理图像的基本函数
读取图像:
import cv2
image = cv2.imread("image.jpg") # ""内填入文件名称
* 返回的是Numpy数组,如果访问不到返回"None"
显示图像:
import cv2
cv2.imread("file_name", image) # 打开图片
cv2.WaitKey(0) # 等待按键输入
cv2.destroyAllWindows() # 清空窗口
保存图片
import cv2
cv2.imwrite("output_image.jpg", image)
* “”里面写的是保存路径
访问和修改像素值
# 获取像素值 (BGR 格式)
pixel_value = image[100, 100] # 获取 (100, 100) 处的像素值
# 修改像素值
image[100, 100] = [255, 255, 255] # 将 (100, 100) 处的像素设置为白色
图像 ROI ->本质是一个切片操作
import cv2
# 获取 ROI
roi = image[50:150, 50:150] # 获取 (50,50) 到 (150,150) 的区域
# 修改 ROI
image[50:150, 50:150] = [0, 255, 0] # 将 ROI 区域设置为绿色
图片通道的分离和合并
import cv2
# 分离通道
b, g, r = cv2.split(image)
# 合并通道
merged_image = cv2.merge([b, g, r])
注意merge传入的是一个list[b, g, r]
图片的缩放
import cv2
width, height = image.shape
width_scale = 50 # 范围为0~100
height_scale = 50 # 范围为0~100
resized_image = cv2.resize(image, ((width_scale / 100) * width, (height_scale / 100) * height)
resized_image = cv2.resize(image, (0, 0), fx = 2, fy = 4)
图片旋转
# 旋转
rotation_matrix = cv2.getRotationMatrix2D((center_x, center_y), angle, scale)
# (center_x, center_y) 旋转中心的坐标(便捷的求解方法为 width // 2, height // 2)
# angle 旋转角度
# scale 旋转缩放因子
# 返回值 “旋转配方”
rotation_image = cv2.warpAffine(image, rotation_matrix, (output_image_width, output_image_height)[, flags[, borderMode[, borderValue]]])
# flags 插值方法,默认cv2.INTER_LINEAR
# cv2.INTER_NEAREST :最近邻(快,质量低)
# cv2.INTER_LINEAR :双线性(默认,兼顾速度与质量)
# cv2.INTER_CUBIC :双三次(更平滑,较慢)
图像平移
import cv2
# 生成一个平移矩阵
translation_matrix = np.float32([[1, 0, tx], [0, 1, ty]]) # tx, ty 为平移距离
translated_image = cv2.warpAffine(image, translation_matrix, (width, height))
# 反射变化函数,和上面一样
图像翻转
import cv2
flipped_image = cv2.flip(image, flip_code)
# flip_code: 0 (垂直翻转), 1 (水平翻转), -1 (双向翻转)
图像算术运算
import cv2
result = cv2.add(image1, image2)
result = cv2.subtract(image1, image2)
result = cv2.addWeighted(image1, alpha, image2, beta, gamma)
# alpha 和 beta 是权重
# gamma 是标量值,添加到结果上的标量值(亮度调节)
# result = alpha * image1 + beta * image2 + gamma
注意事项:通道数和图片尺寸要相同
可以利用cv2.resize(image, (resize_width, resize_height))
3.阈值和平滑处理函数
ret, dst = cv2.threshold(src, thresh, maxval, ty2pe)
# src: 输入图,只能为单通道(一般为灰度图)
# dst: 输出图
# thresh: 阈值
# maxval: 当像素值超过了阈值(或小于阈值,由type来决定)所赋予的值
# type: 二值操作的类型
# cv2.THRESH_BINARY: 超过阈值取maxval,否则取0
# cv2.THRESH_BINARY_INV: THRESH_BINARY取反
# cv2.THRESH_TRUNC: 大于阈值的取阈值,否则不变
# cv2.THRESH_TOZERO: 大于阈值不改变,否则取0
# cv2.THRESH_TOZERO_INV: cv2.THRESH_TOZERO取反
# THRESH_OTSU会自动寻找合适的阈值,适合双峰,需要把阈值设置为0
threhold = cv2.threshold(closing, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 均值滤波
blur = cv2.blur(image, (3, 3)) #(3, 3)表示卷积内核
# 方框滤波
# 可以选择性使用均值滤波(归一化)
blur = cv2.boxFilter(image, -1, (3, 3), normalize = True)
# -1: 表示颜色通道数目相同,一般不用改
# normalize: True表示归一化(和均值滤波一样,归一化)
# False表示非归一化(此时和均值滤波差在:只是求和没取均值)
# 高斯滤波(加权平均值)
# 高斯滤波卷积核满足高斯分布,离中心越近权重越大
blur = cv2.GaussianBlur(image, (3, 3), 1)
# 我们需要调节的只有卷积核
# 中值滤波
blur = cv2.medianBlur(image, 5)
4.形态学操作函数
# 腐蚀操作
# 取核(结构单元)
kernel = np.ones((3, 3), np.uint8)
# 腐蚀操作
# 取邻域内的 最小值
# 效果:白色(高值)区域缩小,黑色(低值)区域扩大
erosion = cv2.erode(image, kernel, iteration = 1)
# iteration: 表示腐蚀次数,膨胀函数同理
# 膨胀操作
kernel = np.ones((3, 3), np.uint8)
dilation = cv2.dilate(image, kernel, iteration = 1)
# 开运算:先腐蚀(毛刺像素点),在膨胀(目标像素点)
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
# 闭运算
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
# 梯度运算
# ->可以用在粗略读取图像边界
# 梯度 = 膨胀 - 腐蚀
gradient = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)
# 礼帽 -> 突出一些明亮的区域
# 礼帽 = 原始输入 - 开运算结果 ->其实得出来的是毛刺像素
tophat = cv2.morphologyEx(image, cv2.MORPH_TOPHAT, kernel)
# 黑帽
# 黑帽 = 闭运算 - 原始输入
blackhat = cv2.morphologyEx(image, cv2.MORPH_BLACKHAT, kernel)
# Sobel算子
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
# ddpeth 图像的深度(通常指定-1即可)
# dx,dy 水平和数值方向(用1表示要启用的,0表示不启用)
# ksize Sobel算子的大小
# Sobel算子应用
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize = 3)# 此时保留右 - 左的值(有正有负)
sobelx = cv2.converScaleAbs(sobelx) # 取绝对值操作
# y操作类似
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize = 3)
sobely = cv2.converScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
# 不建议一下操作(直接计算)
sobelxy = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize = 3)
# 类似的还有Scharr算子和laplacian算子
# laplacian算子对于噪声干扰严重
# Scharr算子其实就是Sobel算子的plus版,加大了间隔之间加权差距
- 图片拼接:np.hstack np.vstack
5.轮廓和边缘检测
# 第一步:读取图像
img = cv2.imread("image")
# 第二步:二值化处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 第三步:边缘检测
canny = cv2.Canny(thresh, 80, 150) # 80和150为双阈值处理参数
# Canny检测
# 1. 高斯滤波器,滤除噪音
# 2. 计算梯度和方向
# 3. 非极大值抑制
# 4. 双阈值判断(检测阈值的真实性和其他阈值的潜在可能)
# 5. 抑制孤立边缘完成检测
# 轮廓检测
cv2.findContours(img, mode, method)
mode: RETR_EXTERNAL :只检测最外面的轮廓
RETR_LIST :检测所有的轮廓,并将其保存到链表当中
RETR_CCOMP :检测所有轮廓,并分为两层,顶层是外部边界,第二层是空洞的边界
RETR_TREE(最常用) :检索所有的轮廓,并且重构嵌套轮廓的整个层次
method:CHAIN_APPROX_NONE :输出多边形
CHAIN_APPROX_SIMPLE :保留他们的终点部分
img = cv2.imread("image")
# 二值化处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 边缘检测
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 绘制轮廓
draw_img = img.copy()
# 函数说明:cv2.drawContours(传入绘制图像,轮廓(列表形式[]),轮廓索引(注:-1表示所有轮廓), 颜色模式( , , ), 线条厚度)
cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2)
cv2.imshow(draw_img)
# 如果想要实现轮廓排序的话,用myutils库
contours = imutils.contours.sort_contours(contours, method="left-to-right")[0]
# 轮廓其他操作
cnt = contours[0]
# 周长,True表示闭合的
cv2.arcLength(cnt, True)
# 面积
cv2.contourArea(cnt)
# 1. 轮廓近似处理
epsilon = 0.1*cv2.arcLength(cnt, True) # 这个系数越小,对应的轮廓和原轮廓越近似
approx = cv2.approxPloyDP(cnt, epsilon, True)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
# 2. 外接矩形
cnt = contours[0]
draw_img = img.copy()
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(draw_img, (x,y), (x + w, y + h), (0, 0, 255), 2)
# 3. 外接圆
cnt = contours[0]
draw_img = img.copy()
(x, y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y))
cv2.circle(draw_img, center, radius, (0, 0, 255), 2)
6.模板匹配
img = cv2.imread("image", 0) # 比较对象的灰度图
template = cv2.imread("template", 0) # 比较模板的灰度图
h1, w1 = img.shape[:2]
h2, w2 = template.shape[:2]
# 模板匹配
# return:如果原图形的是AxB,模板是axb,返回的矩阵是(A-a+1)x(B-b+1),每一个矩阵元素都是匹配结果
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF_NORMED)
# 寻找模板匹配结果的最值
# max_loc和min_loc 直接对应原图上匹配区域左上角的像素位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = min_loc
bottom_right = (top_left[0] + w2, top_left[1] + h2)
# 画矩形
cv2.rectangle(img, top_left, bottom_righ, 255, 2)
# 多个模板匹配
res = cv2.matchTemplate(img, template, cv2.TM_CCOFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold) # 传回的是x坐标的array+y坐标的array
# zip:比如array(50, 100, 150) array(150, 100, 50) ---> (50, 150) (100, 100) (150, 50)
for pt in zip(*loc[::-1]):
bottom_right = (pt[0] + w2, pt[1] + h2)
cv2.rectangle(img, pt, bottom_right, (0, 0, 255), 2)
cv.matchTemplate(比较对象, 比较模板, 比较方法)
method:
TM_SQDIFF : 计算平方不同,越小越相关
TM_CCORR : 计算相关性,越大越相关
TM_CCOEFF : 计算相关系数,越大越相关
# 归一化操作(一般用有带归一化的操作)
TM_SQDIFF_NORMED
TM_CCORR_NORMED
TM_CCOEFF_NORMED
7.金字塔
- 一般模板匹配的时候,可以将图像分为多个层(整体构成类似金字塔结构),分别进行模板匹配
- 高斯金字塔:


- 拉普拉斯金字塔

# 高斯金字塔
img = imread("image")
up = cv2.pyrUp(img)
down = cv2.pyrDown(img)
# 注意如果先进行up操作,再进行down操作,得到的图像和原来的不一样
up_donw = cv2.pyrDown(up)
# 拉普拉斯金字塔
down = cv2.pyrDown(img)
down_up = cv2.pyrUp(down)
laplas = img - down_up
8.直方图
cv2.calcHist(image, channels, mask, histSize, range)
# image : 传入时需要用[],eg.[img]
# channels : 灰度图:[0], BGR图像:[0]:blue、[1]:green、[2]:red
# 可以传入多个通道,eg. [0, 1]
# mask : 见下图
# histSize : 直方图的大小,传入的是直方图柱子(BIN)的数量(一般用256),记得传入参数# 要加入[],eg. [256]
# range : 像素的范围,一般就取[0, 256]
# 灰度图的直方图
img = cv2.imread("image", 0)
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
plt.plot(hist, color = 'b')
plt.xlim([0, 256])
plt.show()
# 彩色图的直方图
img = cv.imread("image")
color = ('b','g','r')
for i, col in enumerate(color):
histr = cv2.calcHist([img], [i], None, [256], [0, 256])
plt.plot(histr, color = col)
plt.xlim([0, 256])
plt.show()
# 两种绘制直方图
# 第一种
# 第一种是只绘制,不参与计算,依赖原始数据hist(通过cv2.calcHist获取)
plt.plot(hist, color = '')
plt.xlim([0, 256])
plt.show()
# 第二种
# 先计算再绘制
plt.hist(img.ravel(), 256) # 256是BIN的数量
plt.show()
# 直方图均衡化
equ = cv2.equalizeHist(img)
# 自适应直方图均衡化
# [避坑指南:OpenCV CLAHE参数调优那些事儿(tileGridSize/clipLimit详解)-CSDN博客](https://blog.csdn.net/coffee/article/details/154059535)
clathe = cv2.creatCLAHE(clipLimit = 2.0, tileGridSize = (8, 8))
res_clathe = clathe.apply(img)
- mask的说明:

img = cv2.imread("image") mask = np.zeros(img.shape[:2], np.uint8) mask[100:300, 100:400] = 255
cv2.creatCLAHE(clipLimit = 2.0, tileGridSize = (8, 8))CLATHE调参说明: - 省流版:clipLimit 值越大,对比度增强越强,但噪声也可能被放大;值越小,效果越柔和 tileGridSize 决定将图像分割成多少个小块。数值越大,网格越细,局部适应性越强,但计算量也越大,且可能带来不平滑的块状效应
- clipLimit值的范围一般是:2.0~4.0 避坑指南:OpenCV CLAHE参数调优那些事儿(tileGridSize/clipLimit详解)-CSDN博客
9. 傅里叶变化
滤波:
- 高通滤波器:只保留高频->图像细节增强
- 低通滤波器:只保留低频->图像变得模糊
opencv中傅里叶变化和反变化:==
cv2.dft()和cv2.idft()==,输入图像要先转为np.float32格式得到的频域中频率为0的点会在左上角,因此需要shift变化来移到中间
cv2.dft得到的结果是双通道的,通常还需要转为图像格式才能显示
img_back = cv2.magnitude(img_back[::0], img_back[::1])
img = cv2.imread("image", 0) # 读取灰度图
# 转为float32格式
img_float32 = np.float32(img)
# dft变化
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
# 将0频率点移到中间
dft_shift = np.fft.fftshift(dft)
# 采集中间像素点位置(滤波操作需要用到)
rows, cols = img.shape
center_crow , cnter_ccol = (int)(rows / 2), (int)(cols / 2)
mask_len = 30
# 低通滤波:把低频点保留 -> 对应需要把dft中间部分保留
# 取mask
mask = np.zeros((rows, cols, 2), np.uint8) # 这里的2表示双通道
mask[center_crow - mask_len:crow_center + mask_len, center_ccol - mask_len:center_ccol + mask_len] = 1
# IDFT
fshift = dft_shift * mask;
# 将0频率点重新移回左上角
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1])
# 高通滤波:把高频点保留 -> 对应需要把dft周围部分保留
# 取mask
mask = np.ones((rows, cols, 2), np.uint8) # 这里的2表示双通道
mask[center_crow - mask_len:crow_center + mask_len, center_ccol - mask_len:center_ccol + mask_len] = 0
# IDFT
fshift = dft_shift * mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1])
- 改变尺寸大小好用的函数:
card_gray = imutils.resize(card_gray, width = 300)
10.透视变化
from imutils.perspective import four_point_transform
import spicy
warped = four_point_transform(image, pts)
# image :输入图像(彩色或灰度均可)
# pts :形状为(4, 2)的数组,包含原始图像中四边形区域的四个顶点坐标,顺序应为 左上 → 右上 → 右下 → 左下。
# 应用说明
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = cv2.imread("xiaopiao.png")
orig = img.copy()
# 转灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 高斯滤波
blur = cv2.GaussianBlur(gray, (5, 5), 0)
cv_show("blur", blur)
# 边界检测
canny = cv2.Canny(blur, 70, 150)
cv_show("canny", canny)
# 轮廓检测
contours = cv2.findContours(canny, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[0]
contours = sorted(contours, key= cv2.contourArea, reverse=True)[:5]
draw_img = img.copy()
# cv2.drawContours(draw_img, contours, -1, (0, 255, 0), 2)
# cv_show("draw", draw_img)
# 透视变换
# 第一步:把边缘近似转为四点矩形
for c in contours:
lenth = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * lenth, True)
if len(approx) == 4:
screencnt = approx
break
# 第二步:四点透视变化
warped = four_point_transform(orig, approx.reshape(4, 2))
cv_show("warped", warped)
11.角点检测
- 角点:沿着x和y方向变化量均很大

img = cv2.imread("image")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
img[dst > 0.01 * dst.max()] = [0, 0, 255] # 角点标红处理
cv2.imshow("image", img)
12.特征匹配
import cv2
import numpy as np
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img1 = cv2.imread("book1.png")
img2 = cv2.imread("book2.png")
cv_show("book1", img1)
cv_show("book2", img2)
shift = cv2.SIFT_create()
kp1, des1 = shift.detectAndCompute(img1, None)
kp2, des2 = shift.detectAndCompute(img2, None)
# 创建暴力匹配器
bf = cv2.BFMatcher(crossCheck=True)
# 执行匹配
matches = bf.match(des1, des2)
matches = sorted(matches, key= lambda x:x.distance)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:15], None, flags = 2)
cv_show("res", img3)
cv2.SIFT_create(img, None)
# 传参:图像(一般是灰度图)
# 输出:kp :关键点列表(包含关键点位置(x, y)、尺寸、方向等)
# dest :描述子矩阵((关键点数, 128),每个关键点都对应着128个特征向量)
cv2.BFMatcher(crossCheck = True)
# 创建暴力匹配器:对于第一张图的每个关键点描述子,计算它与第二张图**所有**描述子之间的距离(默认欧氏距离),找出距离最近的那个作为匹配候选
# crossCheck :开启“双向校验”。意思是:只有当第一张图的点A匹配到第二张图的点B,且B反向匹配回去也选中A时,这对匹配才被认为是有效的
bf.match(des1, des2)
# des1, des2 :描述子矩阵
# 传回:一个 DMatch 对象的列表,每个对象包含:
# queryIdx:第一张图中关键点的索引。
# trainIdx:第二张图中关键点的索引。
# distance:两个描述子之间的欧氏距离,值越小表示越相似