OpenCV实战教程 第一部分:基础入门
第一部分:基础入门
1. OpenCV简介
什么是OpenCV及其应用领域
OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库,于1999年由Intel公司发起,现在由非营利组织OpenCV.org维护。OpenCV旨在提供一个共同的基础设施,加速计算机视觉在商业产品中的应用,并推动计算机视觉研究的发展。
OpenCV的主要特点:
- 完全开源和跨平台
- 支持C++、Python、Java等多种编程语言
- 包含超过2500个优化算法
- 全球拥有超过1800万下载量
主要应用领域:
- 工业自动化:缺陷检测、零件识别、尺寸测量
- 医疗影像:病变检测、医学图像分析
- 安防监控:人脸识别、行为分析、车牌识别
- 增强现实(AR):实时图像处理、特征追踪
- 自动驾驶:道路标记识别、障碍物检测
- 机器人导航:环境感知、路径规划
- 文档分析:OCR(光学字符识别)、文本检测
- 人机交互:手势识别、姿态检测
- 移动应用:照片处理、实时滤镜
开发环境搭建
Windows系统
安装Python和pip(如果尚未安装)
- 访问 Python官网,下载并安装最新版Python(推荐Python 3.7以上版本)
- 安装时勾选"Add Python to PATH"选项
- 打开命令提示符,输入以下命令确认安装成功:
bash
python --version
pip --version
安装OpenCV
- 在命令提示符中运行以下命令:
bash
pip install opencv-python
pip install opencv-contrib-python # 包含扩展模块
安装IDE(推荐)
- 下载并安装Visual Studio Code: Visual Studio Code - Code Editing. Redefined
- 安装Python扩展
MacOS系统
使用Homebrew安装Python(如果尚未安装)
- 安装Homebrew(如果尚未安装):
bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- 安装Python:
bash
brew install python
安装OpenCV
bash
pip3 install opencv-python
pip3 install opencv-contrib-python # 包含扩展模块
安装IDE(推荐)
- 下载并安装Visual Studio Code: Visual Studio Code - Code Editing. Redefined
- 安装Python扩展
Linux系统(Ubuntu示例)
安装Python和pip(如果尚未安装)
bash
sudo apt update
sudo apt install python3 python3-pip
安装OpenCV依赖项
bash
sudo apt install libgl1-mesa-glx
安装OpenCV
bash
pip3 install opencv-python
pip3 install opencv-contrib-python # 包含扩展模块
安装IDE(推荐)
bash
sudo snap install code --classic # 安装Visual Studio Code
安装配置和第一个程序"Hello OpenCV"
验证安装
创建一个Python文件,例如verify_opencv.py
,输入以下代码:
python
import cv2# 打印OpenCV版本
print(f"OpenCV 版本: {cv2.__version__}")# 检查是否可以创建窗口
try:cv2.namedWindow("Test", cv2.WINDOW_NORMAL)cv2.destroyAllWindows()print("OpenCV GUI功能正常!")
except Exception as e:print(f"OpenCV GUI功能异常: {e}")
运行此脚本验证OpenCV安装是否成功。
第一个程序:"Hello OpenCV"
我们将创建一个简单的程序,实现以下功能:
- 读取一张图片
- 在图片上显示"Hello OpenCV"文字
- 显示图片
- 保存结果
创建一个Python文件,例如hello_opencv.py
:
python
import cv2
import numpy as np# 创建一个纯色背景图像(500x300的蓝色背景)
# OpenCV中颜色顺序是BGR(蓝、绿、红)而不是RGB
image = np.zeros((300, 500, 3), dtype=np.uint8)
image[:] = (255, 127, 0) # 橙色背景# 添加文字
text = "Hello OpenCV!"
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1.5
font_thickness = 3
text_color = (255, 255, 255) # 白色文字# 获取文本大小,以便居中
(text_width, text_height), baseline = cv2.getTextSize(text, font, font_scale, font_thickness)
x = (image.shape[1] - text_width) // 2 # 计算文本起始x坐标(居中)
y = (image.shape[0] + text_height) // 2 # 计算文本起始y坐标(居中)# 在图像上绘制文本
cv2.putText(image, text, (x, y), font, font_scale, text_color, font_thickness)# 在窗口中显示图像
window_name = "First OpenCV Program"
cv2.namedWindow(window_name)
cv2.imshow(window_name, image)# 保存图像
cv2.imwrite("hello_opencv.jpg", image)
print("图像已保存为 'hello_opencv.jpg'")# 等待直到按下任意键
print("按任意键关闭窗口...")
cv2.waitKey(0)
cv2.destroyAllWindows()
代码讲解:
import cv2
:导入OpenCV库。import numpy as np
:导入NumPy库,用于图像矩阵操作。image = np.zeros((300, 500, 3), dtype=np.uint8)
:创建一个黑色图像,大小为300x500像素,3个颜色通道。image[:] = (255, 127, 0)
:将图像背景设为橙色。注意OpenCV中颜色顺序是BGR(蓝、绿、红)。cv2.putText()
:在图像上绘制文字。cv2.namedWindow()
:创建一个命名窗口。cv2.imshow()
:在窗口中显示图像。cv2.imwrite()
:保存图像到文件。cv2.waitKey(0)
:等待按键输入,参数0表示无限等待。cv2.destroyAllWindows()
:关闭所有窗口。
运行结果:
- 一个带有"Hello OpenCV!"文字的橙色窗口
- 生成的图像将保存为
hello_opencv.jpg
这是你的第一个OpenCV程序!恭喜!现在我们已经完成了OpenCV的安装和基本配置,接下来将深入学习图像基础知识。
2. 图像基础
图像的数字表示方式
在计算机中,图像以二维或三维矩阵(数组)的形式存储:
灰度图像:
- 二维矩阵,每个元素代表一个像素
- 像素值范围通常为0-255(8位),0表示黑色,255表示白色
- 例如:100×100的灰度图像是一个100×100的二维矩阵
彩色图像:
- 三维矩阵,其中第三个维度表示颜色通道
- 常见的RGB图像有3个通道:红、绿、蓝
- 在OpenCV中,颜色通道顺序为BGR(蓝、绿、红)
- 像素值范围通常为每通道0-255(8位)
- 例如:100×100的彩色RGB图像是一个100×100×3的三维矩阵
图像数据类型: 在OpenCV中,图像通常使用NumPy数组表示,常见的数据类型有:
uint8
:无符号8位整数(0-255),最常用float32
:32位浮点数,用于高精度计算float64
:64位浮点数
让我们创建一个程序来理解图像的数字表示:
python
import cv2
import numpy as np
import matplotlib.pyplot as plt# 创建一个简单的灰度渐变图像
gradient = np.linspace(0, 255, 256, dtype=np.uint8)
gradient = np.tile(gradient, (100, 1))# 创建一个简单的彩色图像
color_channels = np.zeros((100, 256, 3), dtype=np.uint8)
color_channels[:, :, 0] = np.linspace(0, 255, 256, dtype=np.uint8) # 蓝色通道
color_channels[:, :, 1] = np.linspace(0, 255, 256, dtype=np.uint8) # 绿色通道
color_channels[:, :, 2] = np.linspace(0, 255, 256, dtype=np.uint8) # 红色通道# 显示图像
plt.figure(figsize=(10, 6))plt.subplot(2, 1, 1)
plt.title('灰度渐变图像')
plt.imshow(gradient, cmap='gray')
plt.axis('off')plt.subplot(2, 1, 2)
plt.title('RGB渐变图像')
plt.imshow(cv2.cvtColor(color_channels, cv2.COLOR_BGR2RGB)) # 转换BGR为RGB用于正确显示
plt.axis('off')plt.tight_layout()
plt.savefig('gradients.png')
plt.show()# 打印图像形状和数据类型
print(f"灰度图像形状: {gradient.shape}, 数据类型: {gradient.dtype}")
print(f"彩色图像形状: {color_channels.shape}, 数据类型: {color_channels.dtype}")# 查看特定位置的像素值
position = (50, 150) # 第50行,第150列
print(f"灰度图像在位置{position}的像素值: {gradient[position]}")
print(f"彩色图像在位置{position}的像素值: {color_channels[position]}")
代码讲解:
- 创建了一个灰度渐变图像(从黑到白)
- 创建了一个RGB渐变图像(从黑到白)
- 使用matplotlib显示这些图像
- 打印图像的形状和数据类型
- 查看特定位置的像素值
注意:我们使用
cv2.cvtColor()
函数将BGR转换为RGB,因为OpenCV使用BGR顺序,而matplotlib使用RGB顺序。
色彩空间(RGB、HSV、灰度图)
颜色空间是表示颜色的不同方法。最常见的色彩空间包括:
RGB(红-绿-蓝):
- 最常见的色彩空间,使用红、绿、蓝三个通道的组合表示颜色
- 每个通道值范围为0-255(8位)
- 在OpenCV中,默认颜色顺序是BGR而不是RGB
HSV(色调-饱和度-明度):
- 色调(H):颜色种类,范围0-179(在OpenCV中,对应0-360度)
- 饱和度(S):颜色的纯度/鲜艳程度,范围0-255
- 明度(V):颜色的亮度,范围0-255
- 比RGB更接近人类感知颜色的方式
- 常用于颜色追踪和分割
灰度(Grayscale):
- 单通道,只有亮度信息,没有颜色信息
- 值范围通常为0-255,0表示黑色,255表示白色
- 计算量小,常用于不需要颜色信息的任务
让我们编写一个程序来实现不同色彩空间的转换和比较:
python
import cv2
import numpy as np
import matplotlib.pyplot as plt# 创建一个彩色测试图像(彩虹渐变)
def create_rainbow():# 创建一个大小为200x300的彩虹图像rainbow = np.zeros((200, 300, 3), dtype=np.uint8)# 为了简化,我们创建一个水平的彩虹渐变for i in range(300):# 将位置映射到0-360的色调值(对应OpenCV中的0-179)hue = int(i / 300 * 180)# 创建一个满饱和度、满亮度的HSV颜色rainbow_hsv = np.ones((200, 1, 3), dtype=np.uint8) * np.array([hue, 255, 255], dtype=np.uint8)# 转换为BGRrainbow[:, i:i+1] = cv2.cvtColor(rainbow_hsv, cv2.COLOR_HSV2BGR)return rainbow# 创建彩虹图像
rainbow = create_rainbow()# 转换为不同的色彩空间
rainbow_rgb = cv2.cvtColor(rainbow, cv2.COLOR_BGR2RGB) # BGR到RGB
rainbow_hsv = cv2.cvtColor(rainbow, cv2.COLOR_BGR2HSV) # BGR到HSV
rainbow_gray = cv2.cvtColor(rainbow, cv2.COLOR_BGR2GRAY) # BGR到灰度# 显示不同色彩空间的图像
plt.figure(figsize=(12, 10))plt.subplot(4, 1, 1)
plt.title('原始图像 (BGR转RGB显示)')
plt.imshow(rainbow_rgb)
plt.axis('off')plt.subplot(4, 1, 2)
plt.title('HSV色彩空间 - 色调(H)通道')
plt.imshow(rainbow_hsv[:, :, 0], cmap='hsv')
plt.axis('off')plt.subplot(4, 1, 3)
plt.title('HSV色彩空间 - 饱和度(S)和亮度(V)通道')
plt.imshow(np.dstack([np.zeros_like(rainbow_hsv[:, :, 1]), rainbow_hsv[:, :, 1], rainbow_hsv[:, :, 2]]), cmap='gray')
plt.axis('off')plt.subplot(4, 1, 4)
plt.title('灰度图像')
plt.imshow(rainbow_gray, cmap='gray')
plt.axis('off')plt.tight_layout()
plt.savefig('color_spaces.png')
plt.show()# 展示H、S、V三个独立通道
plt.figure(figsize=(12, 8))plt.subplot(3, 1, 1)
plt.title('HSV - 色调(H)通道')
plt.imshow(rainbow_hsv[:, :, 0], cmap='hsv')
plt.axis('off')plt.subplot(3, 1, 2)
plt.title('HSV - 饱和度(S)通道')
plt.imshow(rainbow_hsv[:, :, 1], cmap='gray')
plt.axis('off')plt.subplot(3, 1, 3)
plt.title('HSV - 亮度(V)通道')
plt.imshow(rainbow_hsv[:, :, 2], cmap='gray')
plt.axis('off')plt.tight_layout()
plt.savefig('hsv_channels.png')
plt.show()
代码讲解:
create_rainbow()
函数创建了一个彩虹渐变图像,展示从红到紫的完整色彩范围- 使用
cv2.cvtColor()
函数将图像从BGR转换到其他色彩空间 - 使用matplotlib显示不同色彩空间的图像和HSV的各个通道
cv2.COLOR_BGR2RGB
、cv2.COLOR_BGR2HSV
、cv2.COLOR_BGR2GRAY
是OpenCV中的色彩空间转换常量
OpenCV中常用的色彩空间转换常量:
cv2.COLOR_BGR2RGB
:BGR→RGBcv2.COLOR_RGB2BGR
:RGB→BGRcv2.COLOR_BGR2GRAY
:BGR→灰度cv2.COLOR_GRAY2BGR
:灰度→BGRcv2.COLOR_BGR2HSV
:BGR→HSVcv2.COLOR_HSV2BGR
:HSV→BGRcv2.COLOR_BGR2Lab
:BGR→Labcv2.COLOR_Lab2BGR
:Lab→BGR
图像读取、显示与保存
现在,让我们学习如何使用OpenCV读取、显示和保存图像,这是计算机视觉应用的基本操作。
图像读取
OpenCV提供了cv2.imread()
函数来读取图像:
python
image = cv2.imread('path/to/image.jpg')
imread()
有两个参数:
- 文件路径
- 读取标志(可选),常用的有:
cv2.IMREAD_COLOR
:以彩色模式读取图像(默认)cv2.IMREAD_GRAYSCALE
:以灰度模式读取图像cv2.IMREAD_UNCHANGED
:读取图像,包括alpha通道(如果有)
图像显示
OpenCV使用cv2.imshow()
函数显示图像:
python
cv2.imshow('Window Name', image)
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows() # 关闭所有窗口
图像保存
OpenCV使用cv2.imwrite()
函数保存图像:
python
cv2.imwrite('output.jpg', image)
让我们编写一个完整的程序来演示这些操作:
python
import cv2
import os
import sysdef process_image(image_path):"""读取、显示和保存图像的基本演示"""# 检查文件是否存在if not os.path.isfile(image_path):print(f"错误: 文件 '{image_path}' 不存在")return# 读取图像(以彩色模式)color_image = cv2.imread(image_path, cv2.IMREAD_COLOR)if color_image is None:print(f"错误: 无法读取图像 '{image_path}'")return# 读取图像(以灰度模式)gray_image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)# 获取图像信息height, width = color_image.shape[:2]channels = 1 if len(color_image.shape) == 2 else color_image.shape[2]# 打印图像信息print(f"图像信息:")print(f"- 路径: {image_path}")print(f"- 宽度 x 高度: {width} x {height} 像素")print(f"- 通道数: {channels}")print(f"- 数据类型: {color_image.dtype}")print(f"- 文件大小: {os.path.getsize(image_path) / 1024:.2f} KB")# 创建输出文件夹output_dir = "output"if not os.path.exists(output_dir):os.makedirs(output_dir)# 保存灰度图像gray_output_path = os.path.join(output_dir, "gray_" + os.path.basename(image_path))cv2.imwrite(gray_output_path, gray_image)print(f"灰度图像已保存到: {gray_output_path}")# 显示图像cv2.namedWindow("彩色图像", cv2.WINDOW_NORMAL)cv2.namedWindow("灰度图像", cv2.WINDOW_NORMAL)cv2.imshow("彩色图像", color_image)cv2.imshow("灰度图像", gray_image)print("按 'Esc' 键关闭窗口...")while True:key = cv2.waitKey(0) & 0xFFif key == 27: # Esc键breakcv2.destroyAllWindows()if __name__ == "__main__":# 如果提供了命令行参数,使用它作为图像路径# 否则,使用默认图像if len(sys.argv) > 1:image_path = sys.argv[1]else:# 使用内置的示例图像或当前目录中的示例图像# 如果没有图像,将提示用户sample_images = ["sample.jpg", "test.jpg", "image.jpg"]found = Falsefor img in sample_images:if os.path.isfile(img):image_path = imgfound = Truebreakif not found:print("请提供图像路径作为命令行参数,或确保当前目录有示例图像。")print("用法: python image_io.py <图像路径>")sys.exit(1)process_image(image_path)
代码讲解:
cv2.imread()
函数读取图像,可以选择彩色或灰度模式- 我们获取并打印图像的基本信息(尺寸、通道数、数据类型等)
cv2.imwrite()
函数将灰度图像保存到输出目录cv2.namedWindow()
创建窗口,指定cv2.WINDOW_NORMAL
允许调整窗口大小cv2.imshow()
在各自的窗口中显示彩色和灰度图像cv2.waitKey(0)
等待按键,& 0xFF
确保在不同平台上正确工作cv2.destroyAllWindows()
关闭所有OpenCV窗口
运行程序:
- 将代码保存为
image_io.py
- 运行命令
python image_io.py path/to/your/image.jpg
- 如果没有提供图像路径,程序将尝试在当前目录中查找示例图像
实用技巧:使用matplotlib显示图像
OpenCV的imshow()
函数很强大,但有时使用matplotlib可能更方便,特别是当你想比较多个图像时:
python
import cv2
import matplotlib.pyplot as plt# 读取图像
image = cv2.imread('path/to/image.jpg')# 将BGR转换为RGB(matplotlib使用RGB顺序)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 使用matplotlib显示图像
plt.figure(figsize=(10, 8))
plt.imshow(image_rgb)
plt.title('使用Matplotlib显示图像')
plt.axis('off') # 隐藏坐标轴
plt.show()
3. 基本图像操作
像素访问与修改
在OpenCV中,图像被表示为NumPy数组,因此可以直接使用NumPy的索引操作来访问和修改像素值。
访问像素值
python
# 访问(y, x)位置的像素值(注意:先是y行,再是x列)
pixel = image[y, x]# 对于灰度图像,pixel是一个标量
# 对于彩色图像,pixel是一个含有B,G,R值的数组
修改像素值
python
# 修改(y, x)位置的像素值
image[y, x] = new_value# 对于灰度图像,new_value是一个标量
# 对于彩色图像,new_value是[B, G, R]
让我们编写一个程序来演示像素访问和修改:
python
import cv2
import numpy as np
import matplotlib.pyplot as plt# 创建一个简单的图像
def create_test_image():# 创建白色背景image = np.ones((300, 400, 3), dtype=np.uint8) * 255# 绘制一个蓝色矩形cv2.rectangle(image, (50, 50), (200, 150), (255, 0, 0), thickness=-1)# 绘制一个绿色圆形cv2.circle(image, (300, 150), 80, (0, 255, 0), thickness=-1)# 绘制红色文字font = cv2.FONT_HERSHEY_SIMPLEXcv2.putText(image, "OpenCV", (100, 250), font, 2, (0, 0, 255), 3)return image# 创建测试图像
original_image = create_test_image()# 复制原始图像以便修改
modified_image = original_image.copy()# 1. 单个像素修改 - 创建一个小点
for i in range(-3, 4):for j in range(-3, 4):if i*i + j*j <= 9: # 创建一个小圆点y, x = 150 + i, 250 + jmodified_image[y, x] = [0, 0, 0] # 黑色点# 2. 区域修改 - 反转一个矩形区域内的颜色
x1, y1, x2, y2 = 100, 50, 150, 100
roi = modified_image[y1:y2, x1:x2]
modified_image[y1:y2, x1:x2] = 255 - roi# 3. 像素值分析
# 获取某一点的像素值
pixel_point = (300, 150) # x=300, y=150
pixel_value = original_image[pixel_point[1], pixel_point[0]]
print(f"点 {pixel_point} 处的像素值 (BGR): {pixel_value}")# 获取某一区域的像素值统计
roi = original_image[50:100, 150:200]
mean_value = np.mean(roi, axis=(0, 1))
min_value = np.min(roi, axis=(0, 1))
max_value = np.max(roi, axis=(0, 1))print(f"选定区域的平均像素值 (BGR): {mean_value}")
print(f"选定区域的最小像素值 (BGR): {min_value}")
print(f"选定区域的最大像素值 (BGR): {max_value}")# 在图像上标记分析的位置
analysis_image = original_image.copy()
cv2.circle(analysis_image, pixel_point, 5, (0, 0, 0), -1)
cv2.rectangle(analysis_image, (150, 50), (200, 100), (0, 0, 0), 2)# 显示结果
plt.figure(figsize=(15, 5))plt.subplot(1, 3, 1)
plt.imshow(cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB))
plt.title('原始图像')
plt.axis('off')plt.subplot(1, 3, 2)
plt.imshow(cv2.cvtColor(modified_image,