Skip to content

OpenCV 基础入门

引言

OpenCV(开源计算机视觉库)是一个强大的图像处理和计算机视觉库。对于 LeBot 来说,OpenCV 用于处理摄像头图像、检测障碍物、识别环境特征等视觉任务。本章将介绍 OpenCV 的基础知识和在 LeBot 中的应用。

OpenCV 安装

Python 环境

bash
# 使用 pip 安装
pip install opencv-python

# 安装完整版本(包括额外模块)
pip install opencv-contrib-python

# 验证安装
python -c "import cv2; print(cv2.__version__)"

C++ 环境

bash
# Ubuntu/Debian
sudo apt-get install libopencv-dev

# macOS
brew install opencv

# 或从源代码编译
git clone https://github.com/opencv/opencv.git
cd opencv
mkdir build
cd build
cmake ..
make -j4
sudo make install

基本图像操作

读取和显示图像

python
import cv2
import numpy as np

# 读取图像
image = cv2.imread('robot_camera.jpg')

# 检查是否成功读取
if image is None:
    print("错误:无法读取图像")
else:
    # 显示图像
    cv2.imshow('Original Image', image)
    
    # 获取图像信息
    height, width, channels = image.shape
    print(f"图像尺寸:{width}x{height}, 通道数:{channels}")
    
    # 等待按键(0 表示无限等待)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# 保存图像
cv2.imwrite('output.jpg', image)

图像缩放

python
import cv2

image = cv2.imread('large_image.jpg')

# 方法 1:指定缩放比例
scaled = cv2.resize(image, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)

# 方法 2:指定输出尺寸
scaled = cv2.resize(image, (640, 480))

# 显示对比
cv2.imshow('Original', image)
cv2.imshow('Scaled', scaled)
cv2.waitKey(0)
cv2.destroyAllWindows()

图像裁剪

python
import cv2

image = cv2.imread('image.jpg')

# 使用数组切片裁剪(ROI - Region of Interest)
x, y, w, h = 100, 50, 200, 150  # x, y, 宽度, 高度
cropped = image[y:y+h, x:x+w]

cv2.imshow('Cropped', cropped)
cv2.waitKey(0)
cv2.destroyAllWindows()

颜色空间转换

python
import cv2

image = cv2.imread('image.jpg')

# BGR 是 OpenCV 的默认格式
# 转换为 RGB(显示正确的颜色)
rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 转换为灰度图
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 转换为 HSV(色调、饱和度、亮度)
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# 转换为 LAB 颜色空间
lab_image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)

# 显示不同的颜色空间
cv2.imshow('Gray', gray_image)
cv2.imshow('HSV', hsv_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

图像处理基础

阈值处理

python
import cv2

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 二值阈值处理
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 自适应阈值(更好地处理光照不均匀)
adaptive_thresh = cv2.adaptiveThreshold(
    gray, 255, 
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY, 
    11, 2
)

# 显示结果
cv2.imshow('Binary', binary)
cv2.imshow('Adaptive', adaptive_thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

模糊处理

python
import cv2

image = cv2.imread('image.jpg')

# 高斯模糊(最常用)
blurred = cv2.GaussianBlur(image, (5, 5), 0)

# 均值模糊
mean_blur = cv2.blur(image, (5, 5))

# 中值模糊(去噪)
median_blur = cv2.medianBlur(image, 5)

# 双边滤波(保持边界清晰)
bilateral = cv2.bilateralFilter(image, 9, 75, 75)

cv2.imshow('Gaussian', blurred)
cv2.imshow('Bilateral', bilateral)
cv2.waitKey(0)
cv2.destroyAllWindows()

边缘检测

python
import cv2

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Sobel 边缘检测
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=5)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=5)

# Canny 边缘检测(最常用)
edges = cv2.Canny(gray, 100, 200)

# Laplacian 边缘检测
laplacian = cv2.Laplacian(gray, cv2.CV_64F)

cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

形态学操作

python
import cv2
import numpy as np

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 创建核(内核)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))

# 腐蚀(缩小白色区域)
eroded = cv2.erode(binary, kernel, iterations=1)

# 膨胀(扩大白色区域)
dilated = cv2.dilate(binary, kernel, iterations=1)

# 开运算(先腐蚀后膨胀,去除小噪点)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)

# 闭运算(先膨胀后腐蚀,填补小孔洞)
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

# 梯度(膨胀减去腐蚀,边界检测)
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)

cv2.imshow('Opening', opening)
cv2.imshow('Closing', closing)
cv2.imshow('Gradient', gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()

特征检测和匹配

角点检测(Harris)

python
import cv2
import numpy as np

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Harris 角点检测
corners = cv2.cornerHarris(gray, 2, 3, 0.04)

# 将角点标记为红色
image_copy = image.copy()
image_copy[corners > 0.01 * corners.max()] = [0, 0, 255]

cv2.imshow('Harris Corners', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

ORB 特征检测和描述

python
import cv2

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 创建 ORB 检测器
orb = cv2.ORB_create(nfeatures=500)

# 检测关键点和计算描述符
keypoints, descriptors = orb.detectAndCompute(gray, None)

# 绘制关键点
image_with_keypoints = cv2.drawKeypoints(
    image, keypoints, None,
    color=(0, 255, 0),
    flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
)

cv2.imshow('ORB Features', image_with_keypoints)
cv2.waitKey(0)
cv2.destroyAllWindows()

SIFT 特征匹配

python
import cv2

# 读取两张图像
img1 = cv2.imread('scene.jpg')
img2 = cv2.imread('object.jpg')

gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建 SIFT 检测器
sift = cv2.SIFT_create()

# 检测关键点和描述符
kp1, des1 = sift.detectAndCompute(gray1, None)
kp2, des2 = sift.detectAndCompute(gray2, None)

# 特征匹配
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)

# Lowe's 比率测试
good_matches = []
for m, n in matches:
    if m.distance < 0.75 * n.distance:
        good_matches.append(m)

# 绘制匹配结果
result = cv2.drawMatches(
    img1, kp1,
    img2, kp2,
    good_matches, None,
    flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS
)

cv2.imshow('Feature Matching', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

物体检测

轮廓检测

python
import cv2

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 检测轮廓
contours, hierarchy = cv2.findContours(
    binary, 
    cv2.RETR_TREE,
    cv2.CHAIN_APPROX_SIMPLE
)

# 绘制轮廓
image_copy = image.copy()
cv2.drawContours(image_copy, contours, -1, (0, 255, 0), 2)

# 分析轮廓
for cnt in contours:
    # 轮廓面积
    area = cv2.contourArea(cnt)
    
    # 轮廓周长
    perimeter = cv2.arcLength(cnt, True)
    
    # 最小包围矩形
    rect = cv2.minAreaRect(cnt)
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    
    # 最小包围圆
    (x, y), radius = cv2.minEnclosingCircle(cnt)
    
    # 拟合椭圆
    if len(cnt) > 5:
        ellipse = cv2.fitEllipse(cnt)
        cv2.ellipse(image_copy, ellipse, (255, 0, 0), 2)

cv2.imshow('Contours', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

圆形检测

python
import cv2

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 高斯模糊
blurred = cv2.GaussianBlur(gray, (9, 9), 2)

# Hough 圆形检测
circles = cv2.HoughCircles(
    blurred,
    cv2.HOUGH_GRADIENT,
    dp=1,
    minDist=20,
    param1=50,
    param2=30,
    minRadius=10,
    maxRadius=100
)

image_copy = image.copy()

if circles is not None:
    circles = np.uint16(np.around(circles))
    
    for i in circles[0, :]:
        # 外圆
        cv2.circle(image_copy, (i[0], i[1]), i[2], (0, 255, 0), 2)
        
        # 中心
        cv2.circle(image_copy, (i[0], i[1]), 2, (0, 0, 255), 3)

cv2.imshow('Circles', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

直线检测

python
import cv2
import numpy as np

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)

# Hough 直线检测
lines = cv2.HoughLines(edges, 1, np.pi/180, 100)

image_copy = image.copy()

if lines is not None:
    for line in lines:
        rho, theta = line[0]
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a * rho
        y0 = b * rho
        
        x1 = int(x0 + 1000 * (-b))
        y1 = int(y0 + 1000 * a)
        x2 = int(x0 - 1000 * (-b))
        y2 = int(y0 - 1000 * a)
        
        cv2.line(image_copy, (x1, y1), (x2, y2), (0, 255, 0), 2)

cv2.imshow('Lines', image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

实时视频处理

基本视频处理框架

python
import cv2

def process_video(video_path=None):
    """
    处理视频文件或实时摄像头
    
    参数:
        video_path: 视频文件路径,None 表示使用摄像头
    """
    # 打开视频源
    if video_path is None:
        cap = cv2.VideoCapture(0)  # 使用默认摄像头
    else:
        cap = cv2.VideoCapture(video_path)
    
    # 检查视频是否打开成功
    if not cap.isOpened():
        print("错误:无法打开视频源")
        return
    
    # 获取视频属性
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    
    print(f"视频信息:{width}x{height}, {fps} FPS")
    
    # 定义视频编码器和创建视频写入器
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter('output.mp4', fourcc, fps, (width, height))
    
    frame_count = 0
    
    while True:
        ret, frame = cap.read()
        
        if not ret:
            break
        
        # 处理帧
        processed = process_frame(frame)
        
        # 显示帧
        cv2.imshow('Video', processed)
        
        # 写入输出视频
        out.write(processed)
        
        frame_count += 1
        if frame_count % 30 == 0:
            print(f"处理了 {frame_count} 帧")
        
        # 按 'q' 退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    # 释放资源
    cap.release()
    out.release()
    cv2.destroyAllWindows()
    print(f"处理完成,总共 {frame_count} 帧")

def process_frame(frame):
    """处理单个帧"""
    # 应用各种处理
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    edges = cv2.Canny(blurred, 100, 200)
    
    # 转换回 BGR 用于显示
    processed = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
    
    # 添加文本
    cv2.putText(
        processed,
        f"Frame: {processed.shape}",
        (10, 30),
        cv2.FONT_HERSHEY_SIMPLEX,
        1,
        (0, 255, 0),
        2
    )
    
    return processed

# 使用示例
process_video()  # 摄像头
# process_video('video.mp4')  # 视频文件

在 LeBot 中的应用

障碍物检测

python
import cv2
import numpy as np

class ObstacleDetector:
    """障碍物检测器"""
    
    def __init__(self):
        self.lower_obstacle = np.array([0, 0, 0])
        self.upper_obstacle = np.array([180, 255, 100])
    
    def detect_obstacles(self, frame):
        """检测障碍物"""
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        
        # 创建掩码
        mask = cv2.inRange(hsv, self.lower_obstacle, self.upper_obstacle)
        
        # 形态学操作
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
        mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
        
        # 检测轮廓
        contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        
        obstacles = []
        for cnt in contours:
            area = cv2.contourArea(cnt)
            if area > 500:  # 最小面积阈值
                x, y, w, h = cv2.boundingRect(cnt)
                obstacles.append((x, y, w, h))
        
        return obstacles, mask

# 使用示例
detector = ObstacleDetector()
cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    obstacles, mask = detector.detect_obstacles(frame)
    
    # 绘制检测到的障碍物
    for x, y, w, h in obstacles:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)
    
    cv2.imshow('Obstacle Detection', frame)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

性能优化

多线程处理

python
import cv2
import threading
from queue import Queue

class VideoProcessor:
    """多线程视频处理器"""
    
    def __init__(self):
        self.frame_queue = Queue(maxsize=10)
        self.result_queue = Queue(maxsize=10)
        
    def capture_frames(self):
        """捕获帧的线程"""
        cap = cv2.VideoCapture(0)
        
        while True:
            ret, frame = cap.read()
            if not ret:
                break
            
            if not self.frame_queue.full():
                self.frame_queue.put(frame)
        
        cap.release()
    
    def process_frames(self):
        """处理帧的线程"""
        while True:
            if not self.frame_queue.empty():
                frame = self.frame_queue.get()
                
                # 处理帧
                processed = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                processed = cv2.GaussianBlur(processed, (5, 5), 0)
                
                if not self.result_queue.full():
                    self.result_queue.put(processed)
    
    def display_results(self):
        """显示结果的线程"""
        while True:
            if not self.result_queue.empty():
                frame = self.result_queue.get()
                cv2.imshow('Result', frame)
                
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break

# 使用示例
processor = VideoProcessor()

capture_thread = threading.Thread(target=processor.capture_frames, daemon=True)
process_thread = threading.Thread(target=processor.process_frames, daemon=True)
display_thread = threading.Thread(target=processor.display_results)

capture_thread.start()
process_thread.start()
display_thread.start()

display_thread.join()

总结

OpenCV 在 LeBot 中的关键应用:

  1. 图像采集和显示:处理摄像头输入
  2. 图像处理:增强、过滤、变换
  3. 特征检测:识别重要特征
  4. 物体检测:发现和跟踪对象
  5. 视觉导航:基于视觉的路径规划
  6. 实时处理:低延迟的视觉反馈

正确使用 OpenCV 对于 LeBot 的视觉系统至关重要。


参考资源

由 LeBot 开发团队编写