1. opencv图像读取与显示
# 导入 OpenCV 库
import cv2
# 1. 读取图像
# 替换为实际的图像路径,这里是当前目录下的 "bird.jpg"
image_path = "./bird.jpg"
image = cv2.imread(image_path)
# 检查图像是否成功读取
if image is None:
print("错误:无法加载图像,请检查路径是否正确。")
exit()
# 2. 显示图像
# 创建一个名为 "Display Image" 的窗口,并在其中显示图像
cv2.imshow("Display Image", image)
# 3. 等待用户按键
# 参数 0 表示无限等待,直到用户按下任意键
key = cv2.waitKey(0)
# 4. 根据用户按键执行操作
if key == ord('s'): # 如果按下 's' 键
# 保存图像
output_path = "saved_image.jpg"
cv2.imwrite(output_path, image)
print(f"图像已保存为 {output_path}")
else: # 如果按下其他键
print("图像未保存。")
# 5. 关闭所有窗口
cv2.destroyAllWindows()
扩展练习
接下来我们可以尝试做一些代码的修改。
1、修改窗口名称:将 "Display Image" 改为其他名称,例如 "My Image"。
cv2.imshow("My Image", image)
2、保存为不同格式:将 "saved_image.jpg" 改为 "saved_image.png",观察保存结果。
# 4. 根据用户按键执行操作
if key == ord('s'): # 如果按下 's' 键
# 保存图像为PNG格式
output_path = "saved_image.png"
cv2.imwrite(output_path, image)
print(f"图像已保存为 {output_path}")
3、添加更多交互:例如,按下 'q' 键直接退出程序。
elif key == ord('q'): # 如果按下 'q' 键直接退出
2. opencv基本操作
常用方法如下:
2.1 图像变换
通过对图像进行变换,我们可以实现图像的旋转、缩放、平移等操作,从而达到不同的视觉效果和分析目的。在OpenCV中,图像变换操作具有灵活性和高效性,本章节将介绍一些基础的图像变换操作。
2.1.1 图像旋转
图像旋转的基本原理
旋转操作是将图像绕着其中心点旋转一定的角度。这个旋转的过程可以通过一个矩阵来表示,即旋转矩阵。对于2D图像,旋转矩阵的表达式为:
对于图像旋转,我们通常使用 cv2.getRotationMatrix2D 函数来获取旋转矩阵。 在常规的坐标系中,逆时针旋转被认为是正方向。因此,正的旋转角度 θ 表示逆时针旋转,而负的旋转角度表示顺时针旋转。
import cv2
# 读取图像
img = cv2.imread('tulips1.jpg')
# 获取图像的高度和宽度
height, width = img.shape[:2]
# 定义旋转角度
angle = 45
# 计算旋转矩阵
rotation_matrix = cv2.getRotationMatrix2D((width/2, height/2), angle, 1)
# 进行图像旋转
rotated_img = cv2.warpAffine(img, rotation_matrix, (width, height))
# 显示旋转后的图像
cv2.imshow('Rotated Image', rotated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.1.2 图像缩放
缩放是调整图像大小的基本操作,通过改变图像的宽度和高度,我们可以实现图像的缩小或放大。在图像处理中,缩放操作经常用于调整图像大小以适应不同的显示需求或分析环境。
图像缩放基本原理
插值:在缩放过程中,为了得到新的像素位置和值,需要使用插值方法。插值是一种估算未知数据点的方法,基于已知数据点的值。
缩放公式:
import cv2
import numpy as np
# 读取图像
img = cv2.imread('tulips1.jpg')
# 定义缩放比例
scale_percent = 30
# 计算缩放后的宽度和高度
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
# 进行图像缩放
resized_img = cv2.resize(img, (width, height))
# 使用不同的插值方法放大图像
resized_nearest = cv2.resize(resized_img, (img.shape[1], img.shape[0]), interpolation=cv2.INTER_NEAREST)
resized_linear = cv2.resize(resized_img, (img.shape[1], img.shape[0]), interpolation=cv2.INTER_LINEAR)
resized_cubic = cv2.resize(resized_img, (img.shape[1], img.shape[0]), interpolation=cv2.INTER_CUBIC)
# 创建黑色背景图像
black_background = np.zeros((max(height, img.shape[0]), max(width, img.shape[1]), 3), dtype=np.uint8)
# 将缩放后的图像放置在黑色背景上
resized_img_bg = black_background.copy()
resized_img_bg[:resized_img.shape[0], :resized_img.shape[1], :] = resized_img
# 水平拼接原图和缩小的图像
hconcat_img = cv2.hconcat([img, resized_img_bg, black_background])
# 垂直拼接不同放大方法的图像
vconcat_img = cv2.hconcat([resized_nearest, resized_linear, resized_cubic])
# 最终水平拼接
final_result = cv2.vconcat([hconcat_img, vconcat_img])
# 显示图像
cv2.imshow('Resized Image', final_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.1.3 图像平移
平移是将图像沿着x和y轴移动的操作,通过改变图像的位置,我们可以实现图像的平移。在图像处理中,平移操作常用于调整图像在画布上的位置,以便进一步的处理或显示。在OpenCV中,可以使用 cv2.warpAffine 函数实现图像的平移。
平移操作原理
平移操作的原理是通过构建一个平移矩阵,将图像中的每个像素移动到新的位置。平移矩阵的形式如下: 其中,tx是沿x轴的平移量,ty是沿y轴的平移量。通过 cv2.warpAffine 函数应用这个平移矩阵,即可完成图像的平移操作。
import cv2
import numpy as np
# 读取图像
img = cv2.imread('tulips1.jpg')
# 定义平移矩阵
translation_matrix = np.float32([[1, 0, 50], [0, 1, 30]]) # 沿x轴平移50个像素,沿y轴平移30个像素
# 进行图像平移
translated_img = cv2.warpAffine(img, translation_matrix, (img.shape[1], img.shape[0]))
# 显示平移后的图像
cv2.imshow('Translated Image', translated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.1.4 图像翻转
水平翻转
import cv2
# 读取图像
img = cv2.imread('tulips1.jpg')
# 进行水平翻转
horizontal_flip = cv2.flip(img, 1)
# 显示原始图像和水平翻转后的图像
cv2.imshow('Horizontal Flip', cv2.hconcat([img, horizontal_flip]))
cv2.waitKey(0)
cv2.destroyAllWindows()
垂直翻转
import cv2
# 读取图像
img = cv2.imread('tulips1.jpg')
# 进行垂直翻转
vertical_flip = cv2.flip(img, 0)
# 显示原始图像和垂直翻转后的图像
cv2.imshow('Vertical Flip', cv2.vconcat([img, vertical_flip]))
cv2.waitKey(0)
cv2.destroyAllWindows()
2.1.5 两个图像之间的操作
# 图像加法
import cv2
# 读取两张图像
img1 = cv2.imread('tulips1.jpg')
img2 = cv2.imread('tulips2.jpg')
# 确保两张图像具有相同的尺寸
img2_resized = cv2.resize(img2, (img1.shape[1], img1.shape[0]))
# 图像加法
result = cv2.add(img1, img2_resized)
# 显示结果
cv2.imshow('Image Addition', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
除了简单的图像加法,OpenCV还支持图像的加权混合。这种混合是通过为每个图像分配一个权重,然后将它们相加得到的。具体而言,对于两个图像 A 和 B,其加权混合操作可以表示为:
# 图像加权混合
import cv2
# 读取两张图像
img1 = cv2.imread('tulips1.jpg')
img2 = cv2.imread('tulips2.jpg')
# 确保两张图像具有相同的尺寸
img2_resized = cv2.resize(img2, (img1.shape[1], img1.shape[0]))
# 设置加权混合的权重
alpha = 0.7
beta = 0.3
gamma = 0
# 图像加权混合
result = cv2.addWeighted(img1, alpha, img2_resized, beta, gamma)
# 显示结果
cv2.imshow('Image Blend', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
通过调整 alpha 和 beta 的值,可以控制两张图像在混合中的权重。通常情况下,alpha + beta 应该等于 1。 gamma 用于调整亮度。
3. 图像边缘检测
图像边缘检测是计算机视觉和图像处理中的一项基本任务,它用于识别图像中亮度变化明显的区域,这些区域通常对应于物体的边界。
是 OpenCV 中常用的边缘检测函数及其说明:
3.1 Canny边缘检测
Canny 边缘检测算法主要包括以下几个步骤:
噪声抑制:使用高斯滤波器对图像进行平滑处理,以减少噪声的影响。
计算梯度:使用 Sobel 算子计算图像的梯度幅值和方向。
非极大值抑制:沿着梯度方向,保留局部梯度最大的像素点,抑制其他像素点。
双阈值检测:使用两个阈值(低阈值和高阈值)来确定真正的边缘。高于高阈值的像素点被认为是强边缘,低于低阈值的像素点被抑制,介于两者之间的像素点如果与强边缘相连则保留。
边缘连接:通过滞后阈值处理,将弱边缘与强边缘连接起来,形成完整的边缘。
import cv2
import numpy as np
# 读取图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 应用 Canny 边缘检测
edges = cv2.Canny(image, 100, 200)
# 显示结果
cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
# 读取图像
image = cv2.imread("tulips.jpg")
# 应用Canny算子
edges_low = cv2.Canny(image, 0, 50)
edges_mid = cv2.Canny(image, 100, 150)
edges_high = cv2.Canny(image, 200, 250)
# 共享的参数
shared_params = {
"org": (10, 30),
"fontFace": cv2.FONT_HERSHEY_SIMPLEX,
"fontScale": 1,
"thickness": 2,
"color": (0, 255, 0),
"lineType": cv2.LINE_AA,
}
# 创建包含三个相同通道的图像
edges_low_bgr = cv2.merge([edges_low, edges_low, edges_low])
edges_mid_bgr = cv2.merge([edges_mid, edges_mid, edges_mid])
edges_high_bgr = cv2.merge([edges_high, edges_high, edges_high])
# 添加文字
original_image = cv2.putText(image.copy(), "Original Image", **shared_params)
edges_low_image = cv2.putText(edges_low_bgr, "Low Canny Edges", **shared_params)
edges_mid_image = cv2.putText(edges_mid_bgr, "Mid Canny Edges", **shared_params)
edges_high_image = cv2.putText(edges_high_bgr, "High Canny Edges", **shared_params)
# 显示原始图像和Canny算子的结果
cv2.imshow("Canny Edges",
cv2.vconcat(
[cv2.hconcat([original_image, edges_low_image]),
cv2.hconcat([edges_mid_image, edges_high_image])]
))
cv2.waitKey(0)
cv2.destroyAllWindows()
3.2 Sobel 算子
Sobel 算子是一种基于梯度的边缘检测算子,它通过计算图像在水平和垂直方向上的梯度来检测边缘。
Sobel 算子结合了高斯平滑和微分操作,因此对噪声具有一定的抑制作用。
原理:
Sobel算子是一种基于卷积操作的边缘检测算子,常用于图像处理中。它的基本原理是通过对图像进行卷积操作,计算每个像素点的梯度,从而突出图像中的边缘信息。Sobel算子分为水平方向和垂直方向两种,分别用于检测图像中的水平边缘和垂直边缘。
import cv2
import numpy as np
# 读取图像
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 计算 x 方向的梯度
sobel_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
# 计算 y 方向的梯度
sobel_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)
# 计算梯度幅值
sobel_combined = np.sqrt(sobel_x**2 + sobel_y**2)
# 显示结果
cv2.imshow('Sobel X', sobel_x)
cv2.imshow('Sobel Y', sobel_y)
cv2.imshow('Sobel Combined', sobel_combined)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
# 读取图像
image = cv2.imread("tulips.jpg")
# 应用Sobel算子
sobel_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)
# 计算梯度幅值和方向
gradient_magnitude = np.sqrt(sobel_x**2 + sobel_y**2)
gradient_direction = np.arctan2(sobel_y, sobel_x)
# # 分别显示原始图像和Sobel算子的结果
# cv2.imshow("Original Image", image)
# cv2.imshow("Gradient Magnitude", cv2.convertScaleAbs(gradient_magnitude))
# cv2.imshow("Gradient Direction", gradient_direction)
# cv2.imshow("Sobel X", cv2.convertScaleAbs(sobel_x))
# cv2.imshow("Sobel Y", cv2.convertScaleAbs(sobel_y))
# cv2.imshow("Sobel X+Y", cv2.convertScaleAbs(sobel_x)+cv2.convertScaleAbs(sobel_y))
# 将浮点数图像缩放到0到255的范围
normal_gradient_direction = cv2.normalize(gradient_direction, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
# 共享的参数
shared_params = {
"org": (10, 30),
"fontFace": cv2.FONT_HERSHEY_SIMPLEX,
"fontScale": 1,
"thickness": 2,
"color": (0, 255, 0),
"lineType": cv2.LINE_AA,
}
# 添加文字
original_image = cv2.putText(image.copy(), "Original Image",**shared_params)
gradient_magnitude_image = cv2.putText(cv2.convertScaleAbs(gradient_magnitude.copy()), "Gradient Magnitude", **shared_params)
gradient_direction_image = cv2.putText(normal_gradient_direction, "Gradient Direction", **shared_params)
sobel_x_image = cv2.putText(cv2.convertScaleAbs(sobel_x.copy()), "Sobel X", **shared_params)
sobel_y_image = cv2.putText(cv2.convertScaleAbs(sobel_y.copy()), "Sobel Y", **shared_params)
sobel_xy_image = cv2.putText(cv2.convertScaleAbs(sobel_x + sobel_y), "Sobel X+Y", **shared_params)
# 水平拼接
row1 = cv2.hconcat([original_image, gradient_magnitude_image, gradient_direction_image])
row2 = cv2.hconcat([sobel_x_image, sobel_y_image, sobel_xy_image])
# 垂直拼接
sobel_image = cv2.vconcat([row1, row2])
# 显示合并后的图像
cv2.imshow("Sobel Images", sobel_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
常用边缘检测函数对比
作业
使用OpenCV读取一张图像,并将其缩放到原来的30%大小,读取一张图像,并将其绕中心旋转45度,将图像向右移动80个像素,向下移动160个像素,读取一张图像,并对其进行水平翻转
import cv2
import numpy as np
# 公共参数设置
image_path = "./01_cat.jpg"
def resize_image():
# 读取原始图像
image = cv2.imread(image_path)
if image is None:
print("错误:无法加载图像")
return
# 计算新尺寸(宽高的30%)
height, width = image.shape[:2]
new_size = (int(width * 0.3), int(height * 0.3))
# 使用INTER_AREA插值法缩小图像
resized = cv2.resize(image, new_size, interpolation=cv2.INTER_AREA)
# 显示结果对比
cv2.imshow("Original", image)
cv2.imshow("Resized 30%", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
def rotate_image():
image = cv2.imread(image_path)
if image is None:
print("错误:无法加载图像")
return
# 获取图像尺寸
(h, w) = image.shape[:2]
# 计算旋转中心
center = (w // 2, h // 2)
# 构建旋转矩阵(逆时针45度)
M = cv2.getRotationMatrix2D(center, 45, 1.0)
# 执行仿射变换
rotated = cv2.warpAffine(image, M, (w, h))
# 显示结果
cv2.imshow("Rotated 45 degrees", rotated)
cv2.waitKey(0)
cv2.destroyAllWindows()
def translate_image():
image = cv2.imread(image_path)
if image is None:
print("错误:无法加载图像")
return
# 定义平移矩阵
# [1, 0, tx] : tx控制水平平移(正数右移)
# [0, 1, ty] : ty控制垂直平移(正数下移)
M = np.float32([[1, 0, 80], [0, 1, 160]])
# 执行平移操作
translated = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
# 显示结果
cv2.imshow("Translated Image", translated)
cv2.waitKey(0)
cv2.destroyAllWindows()
def flip_image():
image = cv2.imread(image_path)
if image is None:
print("错误:无法加载图像")
return
# 参数1表示水平翻转
# 参数0表示垂直翻转
# 负数表示同时水平垂直翻转
flipped = cv2.flip(image, 1)
# 显示结果对比
cv2.imshow("Original", image)
cv2.imshow("Horizontally Flipped", flipped)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 执行所有操作(可单独调用)
if __name__ == "__main__":
resize_image() # 缩放演示
rotate_image() # 旋转演示
translate_image() # 平移演示
flip_image() # 翻转演示