# opencv-python机器视觉快速入门 **Repository Path**: levijia/eazy_cv_py ## Basic Information - **Project Name**: opencv-python机器视觉快速入门 - **Description**: 面向初学者的OpenCV-Python机器视觉项目,涵盖基础教程与实战案例,助您快速掌握图像处理与计算机视觉技术。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-28 - **Last Updated**: 2025-09-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # OpenCV初学者教程 @珠海科技学院电子设计协会 ## 1. 简介![logo](images/opencv_logo.jpg) OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库,它包含了数百种计算机视觉算法。本教程专为初学者设计,将带你从零开始学习OpenCV。 ## 项目结构 ``` 教程/ ├── images/ # 图片素材 └── rec/ # 文档资源 ``` ## 2. 安装OpenCV ### 2.1 安装前的准备 在开始之前,请确保你已经安装了Python(建议3.7版本或更高)。你可以通过以下命令检查Python版本: ```bash python --version ``` ### 2.2 使用pip安装 最简单的安装方式是使用pip: ```bash pip install opencv-python ``` 如果你需要完整版本(包含额外模块): ```bash pip install opencv-contrib-python ``` ### 2.3 验证安装 安装完成后,在Python中验证: ```python import cv2 print(cv2.__version__) ``` 如果没有报错并显示了版本号,说明安装成功。 ```bash >>> import cv2 >>> print(cv2.__version__) 4.12.0 ``` ### 2.4 安装其他必要的库 OpenCV通常与NumPy一起使用,如果你还没有安装NumPy: ```bash pip install numpy ``` 还有matplotlib也比较常用 ```bash pip install matplotlib ``` ## 3. OpenCV与NumPy的关系 ### 3.1 基本概念 OpenCV是基于NumPy进行图像处理的。 在OpenCV中,图像被表示为NumPy数组,这使得图像处理变得非常简单和高效。为什么是数组?因为相机感光元件是有很多个感光点构成的,比如有640x480个点,每个点就是一个像素,把每个点的像素收集整理起来,就是一副图片,那么这张图片的分辨率就是640x480 ![像素示意图](rec/pixel.jpg) ### 3.2 NumPy数组与图像 在OpenCV中,一张彩色图片实际上是一个三维的NumPy数组: - **维度**:(高度, 宽度, 通道数) - **数据类型**:通常是uint8(0-255的无符号8位整数) - **通道顺序**:BGR(蓝、绿、红),而不是常见的RGB ![彩图三维数组](rec/rgb.png) ### 3.3 彩色图片的数据结构 让我们看一个具体的例子: ```python import cv2 import numpy as np # 读取一张彩色图片 img = cv2.imread('opencv_logo.jpg') # 显示图片的基本信息 print(f"图片形状: {img.shape}") print(f"图片数据类型: {img.dtype}") print(f"最小像素值: {img.min()}") print(f"最大像素值: {img.max()}") ``` 假设输出结果为: ``` 图片形状: (480, 640, 3) 图片数据类型: uint8 最小像素值: 0 最大像素值: 255 ``` 这表示: - **图片高度**:480像素 - **图片宽度**:640像素 - **颜色通道**:3个(蓝、绿、红) - **每个像素值**:0-255之间 ### 3.4 像素与数组索引 在NumPy数组中,像素的位置和值可以通过数组索引来访问: ```python # 访问位于(100, 200)位置的像素 # 注意:OpenCV中坐标是(y, x),即(行, 列) pixel = img[100, 200] print(f"位置(200, 100)的像素值: {pixel}") # 输出示例: [255 248 198] (BGR格式) # 访问特定通道的值 blue = img[100, 200, 0] # 蓝色通道 green = img[100, 200, 1] # 绿色通道 red = img[100, 200, 2] # 红色通道 print(f"蓝色: {blue}, 绿色: {green}, 红色: {red}") # 修改像素值 img[100, 200] = [0, 255, 0] # 将该像素设为绿色 ``` ### 3.5 图像区域的表示 你可以通过NumPy的切片操作来获取图像的特定区域: ```python # 获取图像左上角100x100的区域 top_left = img[0:100, 0:100] # 获取图像的特定区域(ROI - Region of Interest) roi = img[100:300, 200:400] # 修改区域像素值 img[0:100, 0:100] = [255, 255, 255] # 将左上角设为白色 ``` ## 4. 图片与像素的关系 ### 4.1 什么是像素 像素(Pixel)是图像的最小单位,是图像中的一个点。每个像素都有: - **位置**:在图像中的坐标 - **颜色值**:在彩色图像中,每个像素由三个值组成(BGR) - **亮度值**:在灰度图像中,每个像素只有一个值表示亮度 ### 4.2 图像分辨率 图像分辨率表示图像中包含的像素数量: - **总像素数**:宽度 × 高度 - **常见分辨率**:1920×1080(Full HD)、2560×1440(2K)、3840×2160(4K) ### 4.3 颜色深度 颜色深度表示每个像素可以显示的颜色数量: - **8位色深**:每个通道256个级别(0-255) - **24位色深**:3个通道×8位 = 16,777,216种颜色 ### 4.4 实际操作示例 让我们通过一个完整的例子来理解这些概念: ```python import cv2 import numpy as np import matplotlib.pyplot as plt # 创建一张简单的彩色图片 # 创建一个300x400x3的数组,初始值为0(黑色) img = np.zeros((300, 400, 3), dtype=np.uint8) # 在图片上绘制不同颜色的矩形 # 红色矩形 (BGR: [0, 0, 255]) img[50:100, 50:150] = [0, 0, 255] # 绿色矩形 (BGR: [0, 255, 0]) img[50:100, 200:300] = [0, 255, 0] # 蓝色矩形 (BGR: [255, 0, 0]) img[150:200, 50:150] = [255, 0, 0] # 白色矩形 (BGR: [255, 255, 255]) img[150:200, 200:300] = [255, 255, 255] # 显示图片信息 print("图像基本信息:") print(f"形状: {img.shape}") print(f"总像素数: {img.shape[0] * img.shape[1]}") print(f"数据类型: {img.dtype}") # 保存图片 cv2.imwrite('simple_image.jpg', img) # 显示图片 # 由于OpenCV使用BGR而matplotlib使用RGB,需要转换颜色通道 img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(img_rgb) plt.title('Simple Color Image') plt.axis('off') plt.show() ``` ## 5. 基础图像操作 ### 5.1 读取和显示图像 ```python import cv2 # 读取图像 img = cv2.imread('opencv_logo.jpg') # 检查图像是否成功加载 if img is None: print("无法加载图像") else: # 显示图像 cv2.imshow('Image', img) cv2.waitKey(0) # 等待任意键按下 cv2.destroyAllWindows() ``` ### 5.2 图像的基本属性 ```python # 获取图像属性 height, width, channels = img.shape size = img.size # 总像素数 dtype = img.dtype print(f"高度: {height}") print(f"宽度: {width}") print(f"通道数: {channels}") print(f"总像素数: {size}") print(f"数据类型: {dtype}") ``` ### 5.3 颜色空间转换 ```python # 转换为灰度图像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为RGB(用于matplotlib显示) rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转换为HSV(色调、饱和度、明度) hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) ``` ### 5.4 像素级操作 ```python # 创建图像副本 img_copy = img.copy() # 遍历并修改像素(不推荐用于大图像,效率低) for i in range(100): # 只修改前100行 for j in range(img.shape[1]): img_copy[i, j] = [255, 255, 255] # 设为白色 # 更高效的向量化操作 img_copy[:100, :] = [255, 255, 255] # 将前100行设为白色 ``` ## 6. 实践练习 ### 练习1:创建并保存简单图像 ```python import cv2 import numpy as np # 创建一个500x500的彩色图像 image = np.zeros((500, 500, 3), dtype=np.uint8) # 绘制彩色条纹 for i in range(0, 500, 100): image[:, i:i+50] = [i%255, (i+85)%255, (i+170)%255] # 保存图像 cv2.imwrite('colorful_stripes.jpg', image) print("图像已保存为 colorful_stripes.jpg") ``` ### 练习2:图像信息提取 ```python import cv2 # 读取图像 img = cv2.imread('opencv_logo.jpg') if img is not None: # 提取基本信息 h, w, c = img.shape # 获取中心像素的颜色 center_pixel = img[h//2, w//2] # 计算平均颜色 mean_color = np.mean(img, axis=(0, 1)) print(f"图像尺寸: {w}×{h}") print(f"通道数: {c}") print(f"中心像素(BGR): {center_pixel}") print(f"平均颜色(BGR): {mean_color}") else: print("无法加载图像") ``` ## 7. 常见问题解答 ### Q1: 为什么我的图像显示颜色不正常? A: OpenCV使用BGR格式,而大多数其他库(如matplotlib)使用RGB格式。使用`cv2.cvtColor(img, cv2.COLOR_BGR2RGB)`进行转换。 ### Q2: 如何检查图像是否成功加载? A: 使用`img is None`来检查。如果返回True,说明图像路径错误或文件损坏。 ### Q3: 为什么像素值是0-255? A: 这是8位无符号整数的范围,每个颜色通道可以有256个不同的强度级别。 ### Q4: 如何访问图像的特定区域? A: 使用NumPy数组切片:`region = img[y1:y2, x1:x2]`。 ## 8. OpenCV绘图基础操作 OpenCV提供了丰富的绘图函数,可以在图像上绘制各种形状、文字和线条。这些操作对于图像标注、可视化和创建示例图像非常有用。 ### 8.1 基本绘图函数 #### 8.1.1 绘制直线 ```python import cv2 import numpy as np # 创建黑色背景 img = np.zeros((400, 400, 3), dtype=np.uint8) # 绘制直线 # 参数:图像,起点,终点,颜色(BGR),线条粗细 cv2.line(img, (50, 50), (350, 350), (0, 255, 0), 5) # 绘制虚线(通过多次绘制短线段) for i in range(0, 400, 20): cv2.line(img, (i, 50), (i+10, 50), (255, 0, 0), 2) # 显示结果 cv2.imshow('Lines', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` #### 8.1.2 绘制矩形和圆形 ```python import cv2 import numpy as np # 创建画布 img = np.zeros((400, 400, 3), dtype=np.uint8) # 绘制矩形 # 参数:图像,左上角,右下角,颜色,线条粗细(-1表示填充) cv2.rectangle(img, (50, 50), (150, 150), (255, 0, 0), 2) cv2.rectangle(img, (200, 50), (300, 150), (0, 255, 0), -1) # 填充绿色 # 绘制圆形 # 参数:图像,圆心,半径,颜色,线条粗细(-1表示填充) cv2.circle(img, (100, 250), 40, (0, 0, 255), 3) cv2.circle(img, (250, 250), 40, (255, 255, 0), -1) # 填充青色 # 显示结果 cv2.imshow('Shapes', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` #### 8.1.3 绘制椭圆和多边形 ```python import cv2 import numpy as np # 创建画布 img = np.zeros((400, 400, 3), dtype=np.uint8) # 绘制椭圆 # 参数:图像,中心,(长轴,短轴),旋转角度,起始角度,结束角度,颜色,粗细 cv2.ellipse(img, (200, 100), (100, 50), 0, 0, 360, (255, 255, 255), 2) cv2.ellipse(img, (200, 250), (80, 80), 45, 0, 180, (0, 255, 255), -1) # 绘制多边形 # 定义三角形的顶点 pts = np.array([[100, 300], [150, 350], [200, 300]], np.int32) pts = pts.reshape((-1, 1, 2)) cv2.polylines(img, [pts], True, (255, 255, 255), 2) # 绘制填充多边形 pts2 = np.array([[250, 300], [300, 350], [350, 300]], np.int32) cv2.fillPoly(img, [pts2], (0, 150, 255)) # 显示结果 cv2.imshow('Advanced Shapes', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` #### 8.1.4 添加文字 ```python import cv2 import numpy as np # 创建画布 img = np.zeros((300, 500, 3), dtype=np.uint8) # 添加文字 # 参数:图像,文字,位置,字体,字体大小,颜色,粗细,线条类型 cv2.putText(img, 'Hello OpenCV!', (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) # 不同字体样式 cv2.putText(img, 'SIMPLEX', (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.putText(img, 'PLAIN', (50, 150), cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 255, 255), 2) cv2.putText(img, 'DUPLEX', (50, 200), cv2.FONT_HERSHEY_DUPLEX, 1, (255, 0, 255), 2) cv2.putText(img, 'COMPLEX', (50, 250), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 255, 0), 2) # 显示结果 cv2.imshow('Text', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` ### 8.2 鼠标交互绘图 ```python import cv2 import numpy as np # 全局变量 drawing = False # 是否正在绘制 ix, iy = -1, -1 # 起始点坐标 # 鼠标回调函数 def draw_circle(event, x, y, flags, param): global ix, iy, drawing if event == cv2.EVENT_LBUTTONDOWN: drawing = True ix, iy = x, y elif event == cv2.EVENT_MOUSEMOVE: if drawing: cv2.circle(img, (x, y), 5, (0, 255, 0), -1) elif event == cv2.EVENT_LBUTTONUP: drawing = False cv2.circle(img, (x, y), 5, (0, 0, 255), -1) # 创建黑色图像 img = np.zeros((512, 512, 3), np.uint8) cv2.namedWindow('Drawing') cv2.setMouseCallback('Drawing', draw_circle) print("按住鼠标左键并移动来绘制绿色圆点") print("松开鼠标左键绘制红色圆点") print("按ESC键退出") while True: cv2.imshow('Drawing', img) if cv2.waitKey(1) & 0xFF == 27: # ESC键 break cv2.destroyAllWindows() ``` ### 8.3 轨迹条控制绘图 ```python import cv2 import numpy as np # 回调函数(什么也不做) def nothing(x): pass # 创建窗口和轨迹条 cv2.namedWindow('Drawing') img = np.zeros((300, 512, 3), np.uint8) cv2.createTrackbar('R', 'Drawing', 0, 255, nothing) cv2.createTrackbar('G', 'Drawing', 0, 255, nothing) cv2.createTrackbar('B', 'Drawing', 0, 255, nothing) cv2.createTrackbar('Shape', 'Drawing', 0, 2, nothing) # 0:圆, 1:矩形, 2:线 while True: cv2.imshow('Drawing', img) if cv2.waitKey(1) & 0xFF == 27: break # 获取轨迹条值 r = cv2.getTrackbarPos('R', 'Drawing') g = cv2.getTrackbarPos('G', 'Drawing') b = cv2.getTrackbarPos('B', 'Drawing') shape = cv2.getTrackbarPos('Shape', 'Drawing') # 根据形状类型绘制 if shape == 0: img[:] = 0 # 清空画布 cv2.circle(img, (256, 150), 100, (b, g, r), -1) elif shape == 1: img[:] = 0 cv2.rectangle(img, (156, 50), (356, 250), (b, g, r), -1) else: img[:] = 0 cv2.line(img, (50, 150), (462, 150), (b, g, r), 10) cv2.destroyAllWindows() ``` ## 9. 调用电脑摄像头操作 ### 9.1 摄像头基础操作 #### 9.1.1 打开和显示摄像头 ```python import cv2 # 打开默认摄像头(通常是0) # 如果有多个摄像头,可以尝试1、2等 cap = cv2.VideoCapture(0) # 检查摄像头是否成功打开 if not cap.isOpened(): print("无法打开摄像头") exit() print("摄像头已打开,按ESC键退出") while True: # 读取一帧 ret, frame = cap.read() if not ret: print("无法获取画面") break # 显示画面 cv2.imshow('Camera', frame) # 按ESC键退出 if cv2.waitKey(1) & 0xFF == 27: break # 释放资源 cap.release() cv2.destroyAllWindows() ``` #### 9.1.2 获取摄像头属性 ```python import cv2 cap = cv2.VideoCapture(0) # 获取摄像头属性 width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = cap.get(cv2.CAP_PROP_FPS) brightness = cap.get(cv2.CAP_PROP_BRIGHTNESS) contrast = cap.get(cv2.CAP_PROP_CONTRAST) saturation = cap.get(cv2.CAP_PROP_SATURATION) print(f"分辨率: {width}x{height}") print(f"帧率: {fps}") print(f"亮度: {brightness}") print(f"对比度: {contrast}") print(f"饱和度: {saturation}") cap.release() ``` #### 9.1.3 修改摄像头设置 ```python import cv2 cap = cv2.VideoCapture(0) # 设置分辨率(不一定所有摄像头都支持) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # 设置亮度 cap.set(cv2.CAP_PROP_BRIGHTNESS, 0.5) # 设置对比度 cap.set(cv2.CAP_PROP_CONTRAST, 0.8) while True: ret, frame = cap.read() if not ret: break # 在画面上显示当前设置 text = f"Resolution: {int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))}x{int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))}" cv2.putText(frame, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) cv2.imshow('Camera Settings', frame) if cv2.waitKey(1) & 0xFF == 27: break cap.release() cv2.destroyAllWindows() ``` ### 9.2 摄像头图像处理 #### 9.2.1 实时灰度转换 ```python import cv2 cap = cv2.VideoCapture(0) print("实时灰度摄像头,按ESC键退出") while True: ret, frame = cap.read() if not ret: break # 转换为灰度图像 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转换为3通道以便显示 gray_3channel = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR) # 并排显示原图和灰度图 combined = np.hstack([frame, gray_3channel]) cv2.imshow('Original vs Gray', combined) if cv2.waitKey(1) & 0xFF == 27: break cap.release() cv2.destroyAllWindows() ``` #### 9.2.2 实时边缘检测 ```python import cv2 cap = cv2.VideoCapture(0) print("实时边缘检测,按ESC键退出") while True: ret, frame = cap.read() if not ret: break # 转换为灰度图像 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 应用高斯模糊 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 边缘检测 edges = cv2.Canny(blurred, 50, 150) # 转换为3通道以便显示 edges_3channel = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR) # 并排显示 combined = np.hstack([frame, edges_3channel]) cv2.imshow('Original vs Edges', combined) if cv2.waitKey(1) & 0xFF == 27: break cap.release() cv2.destroyAllWindows() ``` #### 9.2.3 实时人脸检测 ```python import cv2 # 加载人脸检测器 face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') cap = cv2.VideoCapture(0) print("实时人脸检测,按ESC键退出") while True: ret, frame = cap.read() if not ret: break # 转换为灰度图像 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 检测人脸 faces = face_cascade.detectMultiScale(gray, 1.1, 4) # 绘制人脸矩形框 for (x, y, w, h) in faces: cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) cv2.putText(frame, f'Face: {w}x{h}', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) # 显示人脸数量 cv2.putText(frame, f'Faces: {len(faces)}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow('Face Detection', frame) if cv2.waitKey(1) & 0xFF == 27: break cap.release() cv2.destroyAllWindows() ``` ### 9.3 摄像头视频录制 #### 9.3.1 保存视频到文件 ```python import cv2 import datetime cap = cv2.VideoCapture(0) # 获取默认的分辨率 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 定义视频编码器和输出文件 fourcc = cv2.VideoWriter_fourcc(*'XVID') out = cv2.VideoWriter(f'recording_{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}.avi', fourcc, 20.0, (frame_width, frame_height)) print("开始录制,按ESC键停止") while True: ret, frame = cap.read() if not ret: break # 添加时间戳 timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") cv2.putText(frame, timestamp, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2) # 写入视频文件 out.write(frame) cv2.imshow('Recording', frame) if cv2.waitKey(1) & 0xFF == 27: break # 释放资源 cap.release() out.release() cv2.destroyAllWindows() print("录制完成") ``` #### 9.3.2 拍照保存 ```python import cv2 import datetime cap = cv2.VideoCapture(0) print("按空格键拍照,按ESC键退出") while True: ret, frame = cap.read() if not ret: break # 显示画面 cv2.imshow('Camera', frame) key = cv2.waitKey(1) & 0xFF if key == 32: # 空格键 filename = f'photo_{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}.jpg' cv2.imwrite(filename, frame) print(f"照片已保存: {filename}") elif key == 27: # ESC键 break cap.release() cv2.destroyAllWindows() ``` ### 9.4 多摄像头操作 #### 9.4.1 同时打开多个摄像头 ```python import cv2 def open_camera(index): """尝试打开指定索引的摄像头""" cap = cv2.VideoCapture(index) if cap.isOpened(): return cap return None # 尝试打开多个摄像头 cameras = [] for i in range(3): # 尝试0,1,2号摄像头 cap = open_camera(i) if cap: cameras.append(cap) print(f"摄像头{i}已打开") if not cameras: print("没有找到可用的摄像头") exit() print("按ESC键退出所有摄像头") while True: frames = [] for i, cap in enumerate(cameras): ret, frame = cap.read() if ret: # 添加摄像头编号 cv2.putText(frame, f'Camera {i}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) frames.append(frame) if frames: # 水平拼接所有画面 combined = np.hstack(frames) cv2.imshow('Multi-Camera', combined) if cv2.waitKey(1) & 0xFF == 27: break # 释放所有摄像头 for cap in cameras: cap.release() cv2.destroyAllWindows() ``` ## 10. 综合实践项目 ### 10.1 实时绘图应用 ```python import cv2 import numpy as np class DrawingApp: def __init__(self): self.drawing = False self.ix, self.iy = -1, -1 self.color = (0, 255, 0) self.thickness = 2 self.mode = 'line' # line, circle, rectangle # 创建画布 self.img = np.zeros((480, 640, 3), dtype=np.uint8) cv2.namedWindow('Drawing App') cv2.setMouseCallback('Drawing App', self.draw) def draw(self, event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: self.drawing = True self.ix, self.iy = x, y elif event == cv2.EVENT_MOUSEMOVE: if self.drawing: img_copy = self.img.copy() if self.mode == 'line': cv2.line(img_copy, (self.ix, self.iy), (x, y), self.color, self.thickness) elif self.mode == 'circle': radius = int(np.sqrt((x-self.ix)**2 + (y-self.iy)**2)) cv2.circle(img_copy, (self.ix, self.iy), radius, self.color, self.thickness) elif self.mode == 'rectangle': cv2.rectangle(img_copy, (self.ix, self.iy), (x, y), self.color, self.thickness) cv2.imshow('Drawing App', img_copy) elif event == cv2.EVENT_LBUTTONUP: self.drawing = False if self.mode == 'line': cv2.line(self.img, (self.ix, self.iy), (x, y), self.color, self.thickness) elif self.mode == 'circle': radius = int(np.sqrt((x-self.ix)**2 + (y-self.iy)**2)) cv2.circle(self.img, (self.ix, self.iy), radius, self.color, self.thickness) elif self.mode == 'rectangle': cv2.rectangle(self.img, (self.ix, self.iy), (x, y), self.color, self.thickness) cv2.imshow('Drawing App', self.img) def run(self): print("绘图应用") print("左键拖动绘制") print("按键:") print(" 'r': 红色") print(" 'g': 绿色") print(" 'b': 蓝色") print(" 'c': 圆形") print(" 'l': 直线") print(" 't': 矩形") print(" 's': 保存") print(" 'ESC': 退出") while True: cv2.imshow('Drawing App', self.img) key = cv2.waitKey(1) & 0xFF if key == 27: # ESC break elif key == ord('r'): self.color = (0, 0, 255) elif key == ord('g'): self.color = (0, 255, 0) elif key == ord('b'): self.color = (255, 0, 0) elif key == ord('c'): self.mode = 'circle' elif key == ord('l'): self.mode = 'line' elif key == ord('t'): self.mode = 'rectangle' elif key == ord('s'): cv2.imwrite('drawing_result.jpg', self.img) print("图片已保存为 drawing_result.jpg") cv2.destroyAllWindows() # 运行应用 if __name__ == "__main__": app = DrawingApp() app.run() ``` ## 11. 下一步学习 完成本教程后,你可以继续学习: - 图像滤波和边缘检测 - 图像变换(缩放、旋转、平移) - 特征检测和匹配 - 人脸检测和识别 - 目标跟踪 - 深度学习与OpenCV ## 12. 总结 通过本教程,你学习了: - 如何安装OpenCV和相关库 - OpenCV与NumPy的关系 - 彩色图片在NumPy中的数据结构 - 像素的基本概念和操作方法 - 基础的图像读写和属性获取 - **OpenCV绘图基础操作**(直线、矩形、圆形、文字等) - **电脑摄像头操作**(打开摄像头、实时显示、视频录制、拍照保存) - 实时图像处理(灰度转换、边缘检测、人脸检测) 记住,OpenCV的强大之处在于它与NumPy的紧密集成,理解这一点将帮助你更好地进行图像处理和计算机视觉开发。绘图和摄像头操作是计算机视觉项目的基础,掌握这些技能将为你后续的学习打下坚实基础。 --- *本教程基于OpenCV 4.x版本编写,适用于Python 3.7+环境*