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.金字塔

  • 一般模板匹配的时候,可以将图像分为多个层(整体构成类似金字塔结构),分别进行模板匹配
  • 高斯金字塔: 图片1

图片2

  • 拉普拉斯金字塔 图片3
# 高斯金字塔
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的说明: 图片4 图片5
    img = cv2.imread("image")
    mask = np.zeros(img.shape[:2], np.uint8)
    mask[100:300, 100:400] = 255
    
    图片6 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方向变化量均很大

图片7

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:两个描述子之间的欧氏距离,值越小表示越相似