第四部分:实用应用开发
本部分将涵盖以下关键主题:
- 视频处理基础
- 视频读取与保存
- 视频帧处理
- 实战:视频中运动目标追踪
- 条形码与二维码识别
- 条形码检测原理(概念)
- QR 码识别
- 实战:制作二维码扫描器
- 文本识别入门 (OCR)
- 图像预处理
- 使用 Tesseract 与 OpenCV 结合
- 实战:简单 OCR 应用
让我们开始将 OpenCV 应用于实际场景!
OpenCV 实用应用开发 (第四部分)
欢迎来到 OpenCV 教程的第四部分!在前三部分,我们主要处理静态图像,学习了如何操作像素、增强图像、提取特征。现在,我们将把目光转向视频和从图像中提取特定类型的信息,如条形码、二维码和文本。
为什么要学习这些应用?
许多计算机视觉任务并非只处理单张图片。监控、自动化、内容分析等都涉及到视频处理。同时,从图像中自动读取条形码、二维码或文本是零售、物流、文档处理等领域的常见需求。掌握这些技能将使你能够构建更贴近实际的应用。
本部分将通过代码实践来学习这些实用技术。
1. 视频处理基础
视频可以看作是一系列连续播放的图像帧。OpenCV 提供了强大的功能来读取视频文件、捕获摄像头画面以及保存处理后的视频。
1.1 视频读取与保存
处理视频的第一步是能够读取它。OpenCV 使用 cv2.VideoCapture
对象来处理视频输入。它可以打开视频文件或连接到摄像头。
Python
import cv2
import numpy as np# --- 练习 1.1.1: 读取视频 ---# 1. 创建 VideoCapture 对象
# 参数可以是视频文件路径 (如 'my_video.mp4') 或设备索引 (如 0 表示默认摄像头)
# video_source = 'your_video.mp4' # 替换成你的视频文件路径
video_source = 0 # 使用摄像头,如果你有多个摄像头,可以尝试1, 2等cap = cv2.VideoCapture(video_source)# 检查 VideoCapture 是否成功打开
if not cap.isOpened():print(f"错误: 无法打开视频源 {video_source}")exit()print(f"成功打开视频源: {video_source}")# 2. 获取视频的一些属性 (可选)
# cap.get() 方法可以获取视频属性,参数是属性ID
# cv2.CAP_PROP_FRAME_WIDTH: 帧宽度
# cv2.CAP_PROP_FRAME_HEIGHT: 帧高度
# cv2.CAP_PROP_FPS: 帧率
# cv2.CAP_PROP_FRAME_COUNT: 总帧数 (对于摄像头通常返回0或很大的值)
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(cap.get(cv2.CAP_PROP_FPS))
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 注意:对于摄像头,这个值通常不准确print(f"视频宽度: {frame_width}, 视频高度: {frame_height}, 帧率: {fps}")
if frame_count > 0: # 只有对于文件才可能有准确的总帧数print(f"总帧数: {frame_count}")# 3. 逐帧读取并显示视频
while True:# cap.read() 返回一个元组: (ret, frame)# ret 是布尔值,表示是否成功读取帧 (True/False)# frame 是读取到的帧图像 (numpy数组)ret, frame = cap.read()# 如果未成功读取帧,说明视频结束或出现错误if not ret:print("视频读取结束或出现错误")break# 显示当前帧cv2.imshow('Video Frame', frame)# 按 'q' 键退出循环# cv2.waitKey(delay): 等待按键,delay 单位是毫秒# 如果 delay 为 0,则无限等待直到按键# 如果 delay > 0,则等待 delay 毫秒# 播放视频通常需要等待一个适当的 delay,例如 1000ms / fpskey = cv2.waitKey(max(1, int(1000 / fps))) & 0xFF # 确保 delay 至少为 1msif key == ord('q'):break# 4. 释放 VideoCapture 对象和关闭窗口
cap.release()
cv2.destroyAllWindows()print("\n--- 练习 1.1.1 完成 ---")
练习提示:
- 尝试使用视频文件路径或摄像头索引作为
cv2.VideoCapture()
的参数。 - 注意
cap.read()
的返回值,确保循环能够正常退出。 - 调整
cv2.waitKey()
中的delay
值,观察视频播放速度的变化。
保存视频则需要创建一个 cv2.VideoWriter
对象,并指定输出文件名、编码器、帧率和帧大小。
Python
import cv2
import numpy as np# --- 练习 1.1.2: 保存视频 ---# 1. 创建 VideoCapture 对象 (从摄像头或文件)
# video_source = 'your_video.mp4'
video_source = 0 # 使用摄像头cap = cv2.VideoCapture(video_source)if not cap.isOpened():print(f"错误: 无法打开视频源 {video_source}")exit()# 2. 获取视频属性,用于 VideoWriter
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = 20 # 设置输出视频的帧率 (可以与输入不同)
# fps = int(cap.get(cv2.CAP_PROP_FPS)) # 如果希望与输入帧率相同# 3. 定义编码器和创建 VideoWriter 对象
# FourCC 是用于指定视频编解码器的4字符代码
# 常见的 FourCC 代码:
# 'XVID': MPEG-4 codec (avi文件常用)
# 'MJPG': Motion-JPEG (avi文件常用)
# 'mp4v': MPEG-4 codec (mp4文件常用)
# 'DIVX': DivX codec
# 注意: 某些编码器可能需要安装额外的库或在你的系统上可用
fourcc = cv2.VideoWriter_fourcc(*'XVID') # 使用 XVID 编码器,保存为 .avi 文件
# fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 使用 mp4v 编码器,保存为 .mp4 文件 (并非所有系统都支持)output_filename = 'output_video.avi' # 输出文件名
# output_filename = 'output_video.mp4'out = cv2.VideoWriter(output_filename, fourcc, fps, (frame_width, frame_height))# 检查 VideoWriter 是否成功创建
if not out.isOpened():print(f"错误: 无法创建 VideoWriter 对象或打开文件 {output_filename}")print("请检查文件路径、编码器是否正确,以及是否有写入权限。")cap.release()exit()print(f"成功创建 VideoWriter 对象,将保存到 {output_filename}")# 4. 逐帧读取、处理 (可选) 并保存视频
while True:ret, frame = cap.read()if not ret:print("视频读取结束或出现错误")break# 在这里可以添加你想要对每一帧进行的处理# 例如: frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转换为灰度# 将处理后的帧写入输出视频文件out.write(frame)# 显示当前帧 (可选)cv2.imshow('Recording Video', frame)# 按 'q' 键退出循环if cv2.waitKey(1) & 0xFF == ord('q'): # 等待1毫秒break# 5. 释放对象
cap.release()
out.release()
cv2.destroyAllWindows()print("\n--- 练习 1.1.2 完成 ---")
练习提示:
- 尝试使用不同的
fourcc
编码器和输出文件扩展名 (.avi
,.mp4
)。如果遇到编码问题,可能需要尝试不同的编码器或安装相应的编解码器。 - 注意
cv2.VideoWriter
需要指定输出帧的宽度和高度。
1.2 视频帧处理
一旦你能够读取视频的每一帧,就可以像处理静态图像一样处理每一帧了。你可以将前面学到的图像增强、滤波、边缘检测、特征提取等技术应用到视频的每一帧上。
Python
import cv2
import numpy as np# --- 练习 1.2: 视频帧处理 ---# 1. 创建 VideoCapture 对象
# video_source = 'your_video.mp4'
video_source = 0 # 使用摄像头cap = cv2.VideoCapture(video_source)if not cap.isOpened():print(f"错误: 无法打开视频源 {video_source}")exit()# 2. 逐帧读取并处理
while True:ret, frame = cap.read()if not ret:print("视频读取结束或出现错误")break# --- 对每一帧进行图像处理 ---# 示例1: 转换为灰度图gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 示例2: 应用高斯模糊blurred_frame = cv2.GaussianBlur(frame, (15, 15), 0)# 示例3: 应用 Canny 边缘检测 (需要在灰度图上)canny_edges = cv2.Canny(gray_frame, 50, 150)# --- 显示处理后的帧 ---cv2.imshow('Original Frame', frame)cv2.imshow('Grayscale Frame', gray_frame)cv2.imshow('Blurred Frame', blurred_frame)cv2.imshow('Canny Edges', canny_edges)# 按 'q' 键退出循环if cv2.waitKey(1) & 0xFF == ord('q'):break# 3. 释放对象
cap.release()
cv2.destroyAllWindows()print("\n--- 练习 1.2 完成 ---")
练习提示:
- 尝试将你在第二部分和第三部分学到的其他图像处理技术应用到视频帧上,如调整亮度对比度、进行形态学操作、检测角点等。
- 注意处理的计算量。如果处理太复杂,可能会导致视频播放卡顿。
1.3 实战:视频中运动目标追踪
一个简单的运动目标追踪方法是检测连续帧之间的差异。如果某个区域的像素值在两帧之间变化很大,说明这个区域可能发生了运动。
Python
import cv2
import numpy as np# --- 实战练习: 视频中运动目标追踪 ---# 1. 创建 VideoCapture 对象 (摄像头通常更适合演示运动检测)
# video_source = 'your_video_with_motion.mp4'
video_source = 0 # 使用摄像头cap = cv2.VideoCapture(video_source)if not cap.isOpened():print(f"错误: 无法打开视频源 {video_source}")exit()# 2. 读取第一帧并转换为灰度图,作为背景帧
ret, frame1 = cap.read()
if not ret:print("无法读取第一帧")cap.release()exit()gray_frame1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
# 对第一帧进行高斯模糊,减少噪声影响
gray_frame1 = cv2.GaussianBlur(gray_frame1, (21, 21), 0)print("准备就绪,开始检测运动...")# 3. 逐帧处理,检测运动
while True:# 读取下一帧ret, frame2 = cap.read()if not ret:print("视频结束或出现错误")break# 转换为灰度图并进行高斯模糊gray_frame2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)gray_frame2 = cv2.GaussianBlur(gray_frame2, (21, 21), 0)# 计算两帧之间的绝对差# cv2.absdiff(src1, src2)frame_diff = cv2.absdiff(gray_frame1, gray_frame2)# 对差异图像进行阈值处理,将变化较大的区域标记出来# 超过 thresholdValue 的像素设为 maxValue (255)# cv2.THRESH_BINARYthreshold_value = 30 # 调整这个值来控制对变化敏感度thresh_diff = cv2.threshold(frame_diff, threshold_value, 255, cv2.THRESH_BINARY)[1] # [1]获取二值化后的图像# 对阈值化后的图像进行膨胀,连接相邻的运动区域thresh_diff = cv2.dilate(thresh_diff, None, iterations=2)# 在膨胀后的差异图像中查找轮廓 (运动区域的轮廓)# 注意: findContours 会修改输入图像,所以使用 thresh_diff.copy()contours, hierarchy = cv2.findContours(thresh_diff.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 在原始彩色帧上绘制运动区域的边界框output_frame = frame2.copy() # 在当前帧上绘制min_motion_area = 500 # 忽略小于这个面积的轮廓 (可能是噪声)for contour in contours:# 计算轮廓面积area = cv2.contourArea(contour)# 如果轮廓面积足够大,则认为是一个运动区域if area > min_motion_area:# 获取轮廓的外接矩形(x, y, w, h) = cv2.boundingRect(contour)# 在原始彩色帧上绘制矩形框cv2.rectangle(output_frame, (x, y), (x+w, y+h), (0, 255, 0), 2) # 绿色框# 可以在框旁边添加文本提示 "Motion"# cv2.putText(output_frame, "Motion", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)# 显示结果cv2.imshow('Original Frame with Motion', output_frame)# 可以显示中间结果,帮助理解# cv2.imshow('Frame Difference', frame_diff)# cv2.imshow('Thresholded Difference', thresh_diff)# 更新背景帧为当前帧,用于下一次比较# 注意: 直接使用 gray_frame2 可能导致运动区域被“记住”# 更准确的做法是根据实际需求,定期更新完全静止的背景帧,或者使用更复杂的背景建模算法# gray_frame1 = gray_frame2 # 简单的更新方式,可能导致静止物体一段时间后不再被检测为运动# 按 'q' 键退出循环if cv2.waitKey(1) & 0xFF == ord('q'):break# 4. 释放对象
cap.release()
cv2.destroyAllWindows()print("\n--- 实战练习: 视频中运动目标追踪 完成 ---")
实战提示:
- 调整
threshold_value
来控制检测的敏感度。较低的值会检测到更微小的运动,但也可能引入更多噪声。 - 调整
min_motion_area
来过滤掉小的运动区域(如噪声)。 gray_frame1 = gray_frame2
这种背景更新方式非常简单,适用于检测临时运动。如果需要更长时间的运动检测,或者背景本身会发生变化,需要使用更高级的背景建模技术(如cv2.createBackgroundSubtractorMOG2()
)。- 尝试在不同光照条件和背景下运行代码,观察效果。
2. 条形码与二维码识别
条形码和二维码是存储信息的常用方式。OpenCV 提供了识别二维码的功能,对于条形码,虽然没有直接读取内容的函数,但可以利用图像处理技术进行检测,或者结合第三方库进行识别。
2.1 条形码检测原理 (概念)
条形码是一系列平行线和空隙,其宽度和间距编码了信息。检测条形码通常涉及:
- 图像预处理: 灰度化,可能的直方图均衡化,降噪。
- 边缘/梯度检测: 突出条形码的高对比度变化区域。
- 寻找平行线: 在梯度图像中寻找具有特定方向和间距的平行线模式。
- 分组: 将找到的平行线分组到候选的条形码区域。
- 验证: 对候选区域进行进一步检查,确认是否符合条形码的结构特征。
- 定位: 确定条形码的精确位置和方向。
请注意,OpenCV 内置的功能主要集中在检测条形码的位置,而不是直接读取其包含的数据。读取条形码数据通常需要额外的解码库(如 pyzbar
)。
2.2 QR 码识别
QR 码 (Quick Response Code) 是一种二维矩阵码,可以存储比传统条形码更多的信息。QR 码具有定位图案(方块)帮助识别其位置和方向。OpenCV 提供了一个专门用于检测和解码 QR 码的功能。
Python
import cv2
import numpy as np# --- 练习 2.2: QR 码识别 ---# 1. 创建 QRCodeDetector 对象
qr_detector = cv2.QRCodeDetector()# 2. 加载包含 QR 码的图像 (或从视频帧获取)
image_path = 'your_qrcode_image.png' # 替换成你的QR码图片路径
try:image = cv2.imread(image_path)if image is None:raise FileNotFoundError(f"图片文件未找到: {image_path}")print(f"成功加载图像: {image_path}")
except FileNotFoundError as e:print(e)print("请确保你的QR码图片文件存在并位于正确路径。")print("将使用模拟图像代替,无法演示实际识别效果。")# 创建模拟图像,无法包含真实QR码image = np.zeros((300, 300, 3), dtype=np.uint8)cv2.putText(image, "QR Code Image Needed", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)# 3. 检测和解码 QR 码
# qr_detector.detectAndDecode(img) 返回:
# decoded_data: 解码的字符串 (如果未检测到或解码失败,则为空字符串)
# points: QR码的四个顶点坐标 (如果未检测到,则为None)
# straight_qrcode: 矫正后的QR码图像 (如果未检测到,则为None)
decoded_data, points, straight_qrcode = qr_detector.detectAndDecode(image)# 4. 处理检测结果
if decoded_data:print(f"\n成功检测并解码 QR 码:")print(f" 数据: {decoded_data}")# 如果检测到QR码,points 是一个包含四个顶点坐标的 numpy 数组if points is not None:# 在原始图像上绘制 QR 码的边界points = np.int0(points) # 将顶点坐标转换为整数# points[0] 包含所有检测到的 QR 码的顶点,通常只有一个# 如果有多个QR码,points[0]是第一个QR码的顶点# 如果需要处理多个QR码,需要遍历 points 列表中的每个元素cv2.polylines(image, [points[0]], True, (255, 0, 0), 3) # 绘制蓝色多边形else:print("\n未检测到 QR 码或解码失败。")# 5. 显示结果
cv2.imshow('QR Code Recognition', image)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 练习 2.2 完成 ---")
练习提示:
- 尝试使用不同内容的 QR 码图片进行测试。
- 如果 QR 码倾斜、模糊或有遮挡,识别可能会失败。
2.3 实战:制作二维码扫描器
结合视频读取和 QR 码识别,我们可以制作一个实时的二维码扫描器。
Python
import cv2
import numpy as np
import time # 用于显示扫描速率# --- 实战练习: 制作二维码扫描器 ---# 1. 创建 VideoCapture 对象 (通常从摄像头获取视频流)
video_source = 0 # 使用默认摄像头
cap = cv2.VideoCapture(video_source)if not cap.isOpened():print(f"错误: 无法打开视频源 {video_source}")exit()# 2. 创建 QRCodeDetector 对象
qr_detector = cv2.QRCodeDetector()print("二维码扫描器启动,请将二维码对准摄像头...")last_scan_time = time.time()
scan_interval = 1 # 每隔1秒尝试扫描一次,避免过度占用资源 (可选优化)
decoded_data_display = "" # 用于在屏幕上显示解码结果# 3. 逐帧读取视频并进行 QR 码检测和解码
while True:ret, frame = cap.read()if not ret:print("视频流结束或出现错误")breakcurrent_time = time.time()# 每隔一定时间或在特定条件下进行扫描 (可选优化)# 简单的实现是每一帧都扫描# if current_time - last_scan_time > scan_interval:# 尝试检测和解码 QR 码decoded_data, points, _ = qr_detector.detectAndDecode(frame)if decoded_data:print(f"检测到 QR 码: {decoded_data}")decoded_data_display = decoded_data # 更新要显示的数据last_scan_time = current_time # 更新扫描时间# 如果检测到QR码,绘制边界框if points is not None:points = np.int0(points)# 绘制检测到的QR码的四个顶点cv2.polylines(frame, [points[0]], True, (0, 255, 0), 3) # 绿色多边形# 在屏幕上显示上次成功扫描到的数据cv2.putText(frame, f"Scanned: {decoded_data_display}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 显示当前帧cv2.imshow('QR Code Scanner', frame)# 按 'q' 键退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 4. 释放对象
cap.release()
cv2.destroyAllWindows()print("\n--- 实战练习: 二维码扫描器 完成 ---")
实战提示:
- 准备好一些包含不同信息的二维码,用摄像头扫描测试。
- 确保摄像头光照良好,QR 码清晰且尽量平整。
- 可以尝试在
cv2.QRCodeDetector()
创建时设置参数,虽然对于简单的 QR 码默认参数通常足够。
3. 文本识别入门 (OCR)
光学字符识别 (OCR) 是将图像中的文本转换为机器可编辑文本的技术。OpenCV 本身不包含完整的 OCR 引擎,但它在图像预处理阶段非常有用。通常,我们会结合第三方 OCR 引擎(如 Tesseract)来完成文本识别任务。
3.1 图像预处理
良好的图像质量对于 OCR 至关重要。常见的预处理步骤包括:
- 灰度化: 将彩色图像转换为灰度图,减少数据量并突出文本。
- 二值化: 将灰度图转换为只有黑白两色的二值图,进一步分离文本和背景。自适应阈值通常比全局阈值效果好。
- 降噪: 使用模糊或其他滤波方法去除图像中的噪声。
- 倾斜校正 (Deskewing): 修正倾斜的文本行。
- 去除边框或线条: 移除可能干扰文本识别的非文本元素。
<!-- end list -->
Python
import cv2
import numpy as np# --- 练习 3.1: 图像预处理用于 OCR ---# 1. 加载包含文本的图像
image_path = 'your_text_image.png' # 替换成你的文本图片路径
try:image = cv2.imread(image_path)if image is None:raise FileNotFoundError(f"图片文件未找到: {image_path}")print(f"成功加载图像: {image_path}")
except FileNotFoundError as e:print(e)print("请确保你的文本图片文件存在并位于正确路径。")print("将使用模拟图像代替,无法演示实际预处理效果。")# 创建模拟图像,包含一些文本image = np.zeros((200, 400, 3), dtype=np.uint8)cv2.putText(image, "OpenCV OCR Preprocessing", (20, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)cv2.putText(image, "Hello World!", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)# 2. 灰度化
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 3. 二值化 (使用自适应阈值)
# cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
# adaptiveMethod: 计算阈值的方法 (cv2.ADAPTIVE_THRESH_MEAN_C 或 cv2.ADAPTIVE_THRESH_GAUSSIAN_C)
# thresholdType: 阈值类型 (cv2.THRESH_BINARY 或 cv2.THRESH_BINARY_INV)
# blockSize: 用于计算阈值的邻域大小 (必须是奇数)
# C: 从平均值或加权平均值中减去的常数
binary_image = cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)# 4. (可选) 降噪 (使用中值滤波,适合去除椒盐噪声)
# median_blurred = cv2.medianBlur(binary_image, 5)# 5. 显示原始、灰度图和二值化图像
cv2.imshow('Original Image', image)
cv2.imshow('Grayscale Image', gray_image)
cv2.imshow('Binary Image (Adaptive Threshold)', binary_image)
# cv2.imshow('Median Blurred', median_blurred) # 如果使用了降噪cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 练习 3.1 完成 ---")
练习提示:
- 尝试使用不同字体、背景的文本图片进行测试。
- 调整
adaptiveThreshold
的参数 (blockSize
,C
),观察二值化效果。 - 尝试使用全局阈值 (
cv2.threshold
) 与自适应阈值进行比较。
3.2 使用 Tesseract 与 OpenCV 结合
Tesseract 是由 Google 开发的一款强大的开源 OCR 引擎。我们可以使用 Python 库 pytesseract
作为 Tesseract 的包装器,方便地在 Python 代码中调用 Tesseract。
安装 Tesseract 和 pytesseract:
- 安装 Tesseract OCR 引擎本体: 这是最重要的一步。你需要下载并安装 Tesseract 的可执行程序。安装方法取决于你的操作系统:
- Windows: 可以从 https://www.google.com/search?q=https://github.com/UB-Mannheim/tesseract/wiki 下载安装包。安装时记住安装路径。
- macOS: 使用 Homebrew 安装
brew install tesseract
。 - Linux: 使用包管理器安装,例如
sudo apt install tesseract-ocr
(Debian/Ubuntu)。
- 安装 pytesseract Python 库:
pip install pytesseract
在 Python 代码中,如果 Tesseract 可执行文件不在系统 PATH 中,你需要告诉 pytesseract
Tesseract 的安装路径: pytesseract.pytesseract.tesseract_cmd = r'<full_path_to_your_tesseract_executable>'
例如,在 Windows 上可能是 r'C:\Program Files\Tesseract-OCR\tesseract.exe'
。
Python
import cv2
import numpy as np
import pytesseract
import os # 用于检查文件或设置路径# --- 练习 3.2: 使用 Tesseract ---# 1. 设置 Tesseract 可执行文件的路径 (如果它不在你的系统 PATH 中)
# 根据你的实际安装路径修改
# 例如: pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
# 如果 Tesseract 已经添加到环境变量 PATH 中,可以跳过这一步# 检查 Tesseract 是否可用 (可选)
# try:
# pytesseract.get_tesseract_version()
# print("Tesseract 已成功安装并找到。")
# except pytesseract.TesseractNotFoundError:
# print("错误: Tesseract OCR 引擎未安装或未添加到系统 PATH。")
# print("请按照教程说明安装 Tesseract。")
# # exit() # 如果Tesseract未找到,退出程序# 2. 加载包含文本的图像 (可以使用上一练习的预处理后的图像)
image_path = 'your_text_image.png' # 替换成你的文本图片路径
try:image = cv2.imread(image_path)if image is None:raise FileNotFoundError(f"图片文件未找到: {image_path}")print(f"成功加载图像: {image_path}")
except FileNotFoundError as e:print(e)print("请确保你的文本图片文件存在并位于正确路径。")print("将使用模拟图像代替,无法进行实际OCR。")image = np.zeros((200, 400, 3), dtype=np.uint8)cv2.putText(image, "Placeholder Image", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)cv2.putText(image, "(Need real text image for OCR)", (30, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)# 3. 对图像进行预处理 (通常需要转换为灰度或二值图)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 或者使用二值图,Tesseract 通常对二值图效果更好
ret, binary_image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 4. 使用 pytesseract 从图像中提取文本
# pytesseract.image_to_string() 直接处理 numpy 数组表示的图像
# 参数 lang 可以指定语言,例如 'eng' 表示英语,'chi_sim' 表示简体中文
try:# 尝试识别二值图像text = pytesseract.image_to_string(binary_image, lang='eng')# 或者尝试识别灰度图像# text = pytesseract.image_to_string(gray_image, lang='eng')print("\n--- 识别到的文本 ---")print(text)print("--------------------")except pytesseract.TesseractNotFoundError:print("\nOCR 识别失败: Tesseract OCR 引擎未正确安装或配置。")
except Exception as e:print(f"\nOCR 识别过程中发生错误: {e}")# 5. 显示图像 (可选)
cv2.imshow('Original Image', image)
cv2.imshow('Binary Image for OCR', binary_image)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 练习 3.2 完成 ---")
练习提示:
- 务必先正确安装 Tesseract OCR 引擎本体和 pytesseract 库。
- 如果 Tesseract 可执行文件不在 PATH 中,需要设置
pytesseract.pytesseract.tesseract_cmd
。 - 尝试使用不同语言的文本图片,并修改
lang
参数进行测试(需要安装对应的语言包)。 - 预处理对识别结果影响很大,尝试不同的二值化方法或阈值。
3.3 实战:简单 OCR 应用
结合图像加载、预处理和 Tesseract,我们可以创建一个简单的 OCR 应用程序,从用户指定的图片文件中读取文本。
Python
import cv2
import numpy as np
import pytesseract
import os
import sys # 用于命令行参数# --- 实战练习: 简单 OCR 应用 ---# 1. 设置 Tesseract 可执行文件的路径 (如果不在 PATH 中)
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
# print("请确保 Tesseract 已正确安装并配置路径。")# 2. 获取输入图像文件路径 (可以从命令行参数获取,或直接指定)
# 例如: python your_script_name.py image_with_text.jpg
if len(sys.argv) > 1:image_path = sys.argv[1]print(f"尝试加载命令行参数指定的图像: {image_path}")
else:image_path = 'your_image_for_ocr.png' # 如果没有命令行参数,使用这个默认路径print(f"未指定命令行参数,使用默认图像路径: {image_path}")# 3. 加载图像
try:image = cv2.imread(image_path)if image is None:raise FileNotFoundError(f"图片文件未找到: {image_path}")print(f"成功加载图像: {image_path}")
except FileNotFoundError as e:print(e)print("请确保图片文件存在并位于正确路径。")print("程序退出。")sys.exit(1) # 退出程序并返回错误码# 4. 图像预处理 (灰度化和二值化)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 使用 Otsu's 二值化 + THRESH_BINARY 是一种常用的预处理方法
ret, binary_image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 5. 使用 Tesseract 提取文本
try:# 将二值图像传递给 Tesseractextracted_text = pytesseract.image_to_string(binary_image, lang='eng') # 指定语言为英语print("\n--- 识别到的文本 ---")print(extracted_text)print("--------------------")except pytesseract.TesseractNotFoundError:print("\nOCR 识别失败: Tesseract OCR 引擎未正确安装或未配置 PATH。")print("请按照教程说明安装 Tesseract。")
except Exception as e:print(f"\nOCR 识别过程中发生错误: {e}")# 6. 显示处理后的图像 (可选)
cv2.imshow('Processed Image for OCR', binary_image)
cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 实战练习: 简单 OCR 应用 完成 ---")
实战提示:
- 将脚本保存为
.py
文件(例如simple_ocr.py
)。 - 在命令行中运行脚本,并指定图像路径作为参数:
python simple_ocr.py /path/to/your/text_image.jpg
- 如果没有指定参数,它将尝试加载代码中默认的
your_image_for_ocr.png
文件。 - 尝试使用不同类型的文本图片(打印体、手写体、不同字体大小)进行测试。手写体识别通常需要更复杂的模型和预处理。
- 对于包含多种语言的图片,你可能需要安装多个语言包并尝试指定多种语言进行识别(例如
lang='eng+chi_sim'
)。
总结
在这一部分,我们从理论走向实践,学习了如何使用 OpenCV 进行视频处理、条形码/二维码识别以及文本识别入门:
- 掌握了视频的读取、保存和逐帧处理的基本方法。
- 通过帧间差异检测实现了一个简单的运动目标追踪应用。
- 学习了如何使用 OpenCV 的
QRCodeDetector
进行 QR 码的检测和解码,并制作了一个实时扫描器。 - 了解了 OCR 的基本流程,学习了使用 OpenCV 进行图像预处理,并结合 Tesseract 实现了简单的文本识别。
相关文章:
第四部分:实用应用开发
本部分将涵盖以下关键主题: 视频处理基础 视频读取与保存视频帧处理实战:视频中运动目标追踪 条形码与二维码识别 条形码检测原理(概念)QR 码识别实战:制作二维码扫描器 文本识别入门 (OCR) 图像预处理使用 Tesseract…...
目标检测YOLO实战应用案例100讲-基于多级特征融合的小目标深度检测网络
目录 知识储备 基于多级特征融合的小目标深度检测网络实现 一、环境配置 二、核心代码实现 1. 多级特征融合模块(models/fpn.py ) 2. 主干网络(models/backbone.py ) 3. 检测头(models/detector.py ) 三、完整网络架构(models/net.py ) 四、训练代码(train.p…...
单片机-89C51部分:11、IIC 、传感器温湿度
飞书文档https://x509p6c8to.feishu.cn/wiki/Cczpw4oBeiyK71kFhKfcXkQmnad 一、简介 IIC协议,又称I2C协议,是由PHILP公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备,IIC属于半双工同步通信方式。 C IIC是一种同…...
Java从入门到精通 - Java入门
Java 入门 此笔记参考黑马教程,仅学习使用,如有侵权,联系必删 文章目录 Java 入门01 Java快速入门1. Java 是什么?能干什么?1.1 Java 背景知识1.2 Java 能做什么?1.3 Java 技术体系 2. 如何使用 Java&…...
SLAM中的状态估计理论:从基础到前沿的完整解析
SLAM中的状态估计理论:从基础到前沿的完整解析 一、SLAM状态估计基础与问题建模 1.状态估计问题的数学描述 在SLAM(Simultaneous Localization and Mapping,同时定位与地图构建)中,状态估计问题的核心在…...
Android 自带的分享功能分享到三方应用
1. 分享视频到三方应用 var shareIntent Intent(Intent.ACTION_SEND)shareIntent.setType("video/*")shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(path))startActivity(Intent.createChooser(shareIntent, "")) 2. 分享音频到三方应用 var sha…...
宇树科技开启“人形机器人格斗盛宴”
2025年5月至6月,一场备受瞩目的全球性科技盛事——全球首届“人形机器人格斗大赛”将由杭州宇树科技隆重开启。赛事将带来前所未有的机器人格斗视觉冲击,吸引全球目光聚焦。 为打造顶级参赛队伍,宇树科技的技术精英团队已连续多周开展密集的算…...
K8S - 命名空间实战 - 从资源隔离到多环境管理
引言 在传统的物理机或虚拟机环境中,不同业务应用共享资源,容易导致权限冲突、资源争用和管理混乱。Kubernetes 通过 命名空间(Namespace)实现资源逻辑隔离,将集群划分为多个虚拟子集群,从而解决以下问题&…...
【安全扫描器原理】基于协议的服务扫描器
【安全扫描器原理】基于协议的服务扫描器 1.概述2.服务扫描的基本原理3.WWW服务扫描4.FTP服务扫描5.Telnet服务扫描1.概述 一台计算机逻辑上可以提供多项服务,每安装一个服务,即打开了一个或多个端口。从这个角度上看,每个服务对应一个或几个指定端口,反之,如果能检测到某…...
第十六届蓝桥杯 2025 C/C++组 数列差分
目录 题目: 题目描述: 题目链接: 思路: 核心算法: 思路详解: 代码: 代码详解: 题目: 题目描述: 题目链接: P12342 [蓝桥杯 2025 省 B/Py…...
模式识别的基本概念与理论体系
前面在讨论专家系统时曾经说过,为了使计算机具有自动获取知识的能力,除了应使它具有学习能力外,还应使它具有能识别诸如文字、图形、图象、声音等的能力,计算机的这种识别能力是模式识别研究的主要内容。当然,模式识别…...
机器学习,深度学习
定义(非正式):不进行明确编程的情况下,提供大量数据让计算机进行自我学习分类:监督(supervised)学习,无监督(unsupervised)学习监督学习:提供的数据中包含了问题到正确答案(x到y)的映射…...
smolagents - Guided tour
https://colab.research.google.com/github/huggingface/notebooks/blob/main/smolagents_doc/en/pytorch/guided_tour.ipynbhttps://colab.research.google.com/github/huggingface/notebooks/blob/main/smolagents_doc/en/pytorch/guided_tour.ipynb...
【keil使用】无法打开keil工程,只有空白界面的解决方法
【keil使用】无法打开keil工程,只有空白界面的解决方法 一、最常见的原因 在我们新建keil工程或下载其他大佬的keil工程的时候可能会出现工程无法正常打开的情况,如图所示: 其中最常见的一个原因就是keil工程的目录路径太长了,W…...
openEuler 22.03 安装 Nginx,支持离线安装
目录 一、环境检查1.1 必要环境检查1.2 在线安装(有网络)1.3 离线安装(无网络) 二、下载Nginx2.1 在线下载2.2 离线下载 三、安装Nginx四、开机自启服务五、开放防火墙端口六、常用命令 一、环境检查 1.1 必要环境检查 # 查看 g…...
Excel 数组功能及应用示例
Excel表格中的数组(Array)是一个可以同时存储和操作多个数据的结构。数组可以是单行、单列(一维数组)或多行多列(二维数组)。在Excel中,数组公式或动态数组功能可以一次性处理多个值,…...
C++后端服务器开发:侵入式与非侵入式程序结构解析
在C后端服务器开发中,架构设计是决定系统性能、可扩展性和可维护性的关键因素之一。尽管不同的业务需求会导致服务器架构的多样化,但网络通信模块作为所有服务的通用部分,为我们提供了一个抽象和讨论的基础。基于此,我们可以将服务…...
「Mac畅玩AIGC与多模态09」开发篇05 - 使用自定义天气查询插件开发智能体应用
一、概述 本篇介绍如何在 macOS 环境下,通过编写自定义 OpenAPI Schema,将天气查询服务接入 Dify 平台,并开发基于实时天气信息的智能体应用。本案例培养路径参数与查询参数结合的插件开发技巧,实现智能体和外部实时数据的动态联动。 二、环境准备 1. 确认本地开发环境 …...
Maven插件学习(五)—— 将项目构建生成的 OSGi Bundles(或 Features)发布到一个 P2 仓库
发布OSGi Bundles到一个 P2 仓库 读取项目中properties文件中的属性 <plugin><groupId>org.codehaus.mojo</groupId><artifactId>properties-maven-plugin</artifactId><version>1.0-alpha-2</version><executions><exec…...
欧拉计划 Project Euler61(循环的多边形数)题解
欧拉计划 Project Euler 61 题解 题干思路code 题干 思路 先生成所有四位数的多边形数集合分类保存,然后dfs找即可 code // 2512 1281 8128 2882 8256 5625 // 28684 #include <bits/stdc.h>using namespace std;using ll long long;typedef vector<i…...
C语言与Unix的传奇起源
C语言与Unix的传奇起源 背景:Multics项目的困境 这段历史要从20世纪60年代中叶的美国说起。当时,三大技术巨头——麻省理工学院(MIT)、AT&T贝尔实验室和通用电气(GE)——联手为GE-645大型机开发一个名…...
C#扩展方法与Lambda表达式基本用法
C# 扩展方法与 Lambda 表达式详解 一、扩展方法详解 1. 基本概念 扩展方法允许为现有类型"添加"方法,而无需修改原始类型或创建派生类型。 定义条件: 必须在静态类中定义方法本身必须是静态的第一个参数使用this修饰符指…...
C#规避内存泄漏的编码方法
C#规避内存泄漏的编码方法 内存泄漏是C#开发中常见的问题,尽管.NET有垃圾回收机制(GC),但不当的编码实践仍可能导致内存无法被及时回收。以下是系统性的规避内存泄漏的方法: 一、理解内存泄漏的常见原因 未释放的事件订阅静态…...
ARM 指令集(ubuntu环境学习) 第一章:ARM 指令集概述
1.1 ARM 架构简介 ARM(Advanced RISC Machine)是一种精简指令集计算机(RISC)架构,最初由英国的 ARM Holdings 公司设计。与复杂指令集计算机(CISC)不同,RISC 架构通过使用简单且高效的指令集,使得处理器能够以更高的速度和更低的功耗执行任务。ARM 架构被广泛应用于各…...
OpenCV实战教程:从零开始的计算机视觉之旅
第一部分:基础入门 OpenCV简介 什么是OpenCV及其应用领域开发环境搭建(Windows/MacOS/Linux)安装配置和第一个程序"Hello OpenCV" 图像基础 图像的数字表示方式色彩空间(RGB、HSV、灰度图)图像读取、显示与…...
零基础做自动驾驶集成测试(仿真)
图 1:使用 GPUDrive 进行极快的多代理模拟。上图:GPUDrive 中 Waymo Open Motion Dataset 场景的鸟瞰图,方框表示受控智能体,圆圈表示其目标。底部:相应的代理视图,以一个代理为中心。可以根据用户的目标轻…...
阿里云dns服务器不可用怎么办?dns可以随便改吗?
阿里云DNS服务器不可用怎么办?dns可以随便改吗? 当DNS服务器不可用时,可能导致无法访问网站或网络服务。以下是常见的解决方法: 1. 检查网络连接 确保设备已连接到互联网(如Wi-Fi或有线网络)。 尝试访问其他网站或服务&#x…...
神经网络用于地震数据时空均匀插值的方法与开源资料
神经网络用于地震数据时空均匀插值的方法与开源资料 地震数据的不均匀采样是一个常见问题,神经网络提供了一种有效的解决方案。以下是关于如何使用神经网络进行地震数据时空均匀插值的概述和可用资源。 主要方法 1. 基于深度学习的插值方法 卷积神经网络(CNN)&a…...
线性微分方程与非线性微分方程
方程一 d x d t x \frac{dx}{dt} x dtdxx 这是一个一阶线性常微分方程,可以直接分离变量求解。 将变量分离: d x x d t \frac{dx}{x} dt xdxdt 两边积分: ∫ 1 x d x ∫ 1 d t ⇒ ln ∣ x ∣ t C \int \frac{1}{x} \, dx \…...
Windows查看和修改IP,IP互相ping通
Windows系统 查看IP地址 winr 输入cmd 打开终端使用 ipconfig 或 ipconfig -all 命令查看当前网络 IPV4地址 Windows系统 修改IP地址 自动获取IP(DHCP): 打开 控制面板,点击 网络和Internet。点击 网络和共享中心。选择 更改适配…...
ESP32开发之freeRTOS的信号量
什么是信号量信号量能干啥信号量的函数实例举例总结什么是信号量 简而言之,就是发出通知,接收通知的任务获得通知后去干啥啥。通知有多有少。自定义通知数量的,叫计数型信号量;只有有无(即“0”,“1”)通知的,叫二进制信号量。 信号量能干啥 资源管理:控制多个任务对…...
CRMEB-PRO系统定时任务扩展开发指南
适用场景 当系统内置定时任务类型无法满足业务需求时,开发者可通过本教程快速掌握自定义定时任务的扩展方法。本指南以"定时检测服务"为例,演示完整开发流程。 我想添加一个定时任务 ,而这里没有我需要的,我怎么来添加 比如我想添加一个定时检…...
单片机不同通信方式的适用场景
一、串口通信 UART 通信双方约定好波特率,每次发送一个字节(8位数据) 这种通信方式一共有2根线,且互相独立不受影响。 串口通信的缺点 二、RS232和RS485 优点是能够远距离传输信号 RS232达到30m RS485达到1000m 同时RS485还具有一对多的功能 三、S…...
【神经网络与深度学习】探索全连接网络如何学习数据的复杂模式,提取高层次特征
引言 全连接网络(Fully Connected Network,FCN)是深度学习中的重要架构,广泛用于模式识别、分类和回归任务。其强大的特征提取能力使其能够自动学习输入数据中的复杂模式,并逐步形成高层次特征。这种能力主要依赖于参…...
股指期货贴水对对冲的影响大吗?
如果你持有股票,又担心股市下跌,可能会想到用股指期货来“对冲风险”——比如买入股票的同时,卖出股指期货合约。但如果股指期货处于贴水状态(期货价格低于现货价格),对冲效果会受影响吗? 一、…...
浙江大学 | DeepSeek系列公开课 | 当艺术遇见AI:科艺融合的前沿探索
今天要给大家分享一份由浙江大学出品的DeepSeek系列公开课第三季第一期,公开课的主题是当艺术遇见AI,科艺融合的新探索。本报告系统展示了浙江大学在艺术与人工智能融合领域的研究成果,涵盖古画修复流程、色彩复原技术、诗画融合模型、图像召…...
(Go Gin)Gin学习笔记(三)数据解析和绑定:结构体分析,包括JSON解析、form解析、URL解析,区分绑定的Bind方法
1. 数据解析和绑定 bind或bindXXX函数(后文中我们统一都叫bind函数)的作用就是将请求体中的参数值绑定到对应的结构体上,以方便后续业务逻辑的处理 1.1 JSON数据解析和绑定 客户端传参,后端接收并解析到结构体 package mainim…...
【JavaEE】网络原理之初识(1.0)
目录 编辑 局域网与广域网 IP地址和端口号 实现简单的服务器客户端交互 简单理解socket TCP和UDP的差别(初识) socket面对udp DatagramSocket API DatagramSocket 构造方法 DatagramSocket 方法: DatagramPacket API Data…...
Go与Cpp的本质区别
这个问题是我们经常听到的问题 常见的观点有 Go 与 C 的差异主要体现在设计哲学、内存管理、并发模型、语法特性及应用场景等方面,以下从多个维度进行详细对比: 一、内存管理机制 C:需手动管理内存(如 new/delete、智能…...
Vulkan 学习(16)---- 使用 VertexBuffer
Vertex Buffer 创建一个 VertexBuffer 存储 Vertex data,代替之前在 Shader 中使用固定顶点值的做法 Vertex Shader 修改 GLSL 的 VertexShader 如下: 注意这里指定了 input Vertex data 的 location 和 格式 #version 450 layout(location 0) in vec2 inPosit…...
论文阅读 2024 arxiv Comprehensive Assessment of Jailbreak Attacks Against LLMs
总目录 大模型安全相关研究:https://blog.csdn.net/WhiffeYF/article/details/142132328 Comprehensive Assessment of Jailbreak Attacks Against LLMs https://arxiv.org/pdf/2402.05668 https://www.doubao.com/chat/4015423571416834 速览 这篇论文是关于大…...
SIFT特征点检测
刚看完了SIFT特征点检测的原理,阅读的是两篇csdn博客,一个全面和一个最全面,不得不说,你俩写的都很全面,这么用心奉献知识的博主是全人类的财富。 现在用我这张笨拙的嘴先说一下我理解的流程 首先先将图像扩大一倍&a…...
开源模型应用落地-qwen模型小试-Qwen3-8B-快速体验-pipeline方式(二)
一、前言 阿里云最新推出的 Qwen3-8B 大语言模型,作为国内首个集成“快思考”与“慢思考”能力的混合推理模型,凭借其 80 亿参数规模及 128K 超长上下文支持,正在重塑 AI 应用边界。该模型既可通过轻量化“快思考”实现低算力秒级响应,也能在复杂任务中激活深度推理模式,以…...
Python os.path.join()路径拼接异常
问题 在使用os.path.join()对两个路径进行拼接,如:/University/School/和/Department/Class/进行拼接,最终的结果为/Department/Class/,而/University/School/却不知所踪。 import osos.path.join(“/University/School/”, “/…...
Flink之DataStream
Apache Flink 的 DataStream API 是用于 处理无限(流)或有限(批)数据流的核心编程模型,适用于事件驱动、实时分析、ETL 等场景。相比 Flink Table API,DataStream API 提供了更强的灵活性和底层控制能力。 …...
WHAT - Tailwind CSS + Antd = MetisUI组件库
文章目录 Tailwind 和 Antd 组件库MetisUI 组件库 Tailwind 和 Antd 组件库 在 WHAT - Tailwind 样式方案(不写任何自定义样式) 中我们介绍了 Tailwind,至于 Antd 组件库,我们应该都耳熟能详,官网地址:htt…...
【LLM】MOE混合专家大模型综述(重要模块原理)
note 当前的 MoE 架构就是一个用显存换训练时长/推理延迟的架构MoE 目前的架构基本集中在于将原先 GPT 每层的 FFN 复制多份作为 n 个 expert,并增加一个 router,用来计算每个 token 对应到哪个 FFN(一般采用每个 token 固定指派 n 个 exper…...
量子机器学习中的GPU加速实践:基于CUDA Quantum的混合编程模型探索
引言:量子机器学习的新范式 在量子计算与经典机器学习交叉融合的前沿领域,量子机器学习(Quantum Machine Learning, QML)正经历着革命性突破。然而,随着量子比特规模的增长和算法复杂度的提升,传统计算架构…...
CentOS Linux 环境二进制方式安装 MySQL 5.7.32
文章目录 安装依赖包新建用户解压初始化配置文件启动服务登录MySQL修改密码停止数据库 安装依赖包 yum -y install libaio perl perl-devel libncurses* autoconf numactl新建用户 useradd mysql解压 tar xf mysql-5.7.32-linux-glibc2.12-x86_64.tar.gz mv mysql-5.7.32-l…...
数学:拉马努金如何想出计算圆周率的公式?
拉马努金(Srinivasa Ramanujan)提出的圆周率(π)计算公式,源于他对数学模式的超凡直觉、对无穷级数和模形式的深刻洞察,以及独特的非传统数学思维方式。尽管他的思考过程带有强烈的个人色彩,甚至…...