OpenCV 教程

什么是 OpenCV?

OpenCV(Open Source Computer Vision Library)是开源的计算机视觉库,包含 2500+ 优化算法,广泛应用于图像处理、视频分析、人脸识别、物体检测、机器学习等领域。支持 Python、C++、Java 等多种语言。

核心特性

  • 跨平台:Windows、Linux、macOS、Android、iOS
  • 多语言:Python、C++、Java、JavaScript 等接口
  • 功能全面:图像处理、特征检测、视频分析、3D 重建、机器学习
  • 硬件加速:支持 CUDA、OpenCL GPU 加速
  • 活跃社区:全球最流行的 CV 库,文档与教程丰富

典型应用

  • 人脸检测与识别
  • 自动驾驶中的车道线、障碍物检测
  • 医学图像分析
  • 安防监控、运动检测
  • 图像拼接(全景图)、OCR 文字识别

Python 快速示例

import cv2

# 读取并显示图像
img = cv2.imread("photo.jpg")
cv2.imshow("窗口", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

版本说明

OpenCV 4.x 是目前的主流版本,Python 包名是 opencv-python。旧项目可能还在用 3.x,API 基本兼容。

OpenCV 简介

OpenCV 简介

OpenCV 起源于 1999 年 Intel 的研究项目,2000 年发布第一个版本,2012 年由 OpenCV 基金会接手维护,目前最新版本为 4.x。

模块结构

core         核心数据结构(Mat 矩阵等)
imgproc      图像处理(滤波、形态学、几何变换)
imgcodecs    图像读写
video        视频分析与目标跟踪
calib3d      相机标定与 3D 重建
features2d   特征检测与描述
objdetect    物体检测(人脸级联检测器)
dnn          深度学习模块
ml           机器学习(传统算法)
stitching    图像拼接
photo        图像修复与去噪

图像在 OpenCV 中的表示

# 图像本质是 NumPy 数组
# 灰度图: (height, width) 单通道,值 0-255
# 彩色图: (height, width, 3) BGR 三通道
import cv2
import numpy as np

img = cv2.imread("photo.jpg")
print(img.shape)   # (1080, 1920, 3) → 高、宽、通道数
print(img.dtype)   # uint8

BGR 与 RGB

# 注意!OpenCV 使用 BGR 顺序,不是 RGB
# 转换函数
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

图像读写

img = cv2.imread("input.jpg")          # 读取
cv2.imwrite("output.jpg", img)         # 保存
# 读取参数
img_gray = cv2.imread("a.jpg", cv2.IMREAD_GRAYSCALE)
img_color = cv2.imread("a.jpg", cv2.IMREAD_COLOR)

显示图像

cv2.imshow("窗口名", img)
cv2.waitKey(0)          # 等待按键
cv2.destroyAllWindows() # 关闭所有窗口

OpenCV 安装

OpenCV 安装(Python)

Python 环境下安装 OpenCV 非常简单,使用 pip 即可。

1. 安装

# 基础版本(推荐)
pip install opencv-python

# 含额外算法模块(如 SIFT)
pip install opencv-contrib-python

# 指定版本
pip install opencv-python==4.9.0.80

2. 验证安装

python -c "import cv2; print(cv2.__version__)"
# 输出: 4.9.0

3. 常用依赖

# NumPy 是核心依赖(图像即数组)
pip install numpy
# 数据处理
pip install matplotlib   # 显示图像
pip install pillow       # 图像处理辅助

4. 第一个程序

import cv2
import numpy as np

# 创建一张纯色图像
img = np.zeros((300, 300, 3), dtype=np.uint8)
img[:] = (0, 0, 255)   # BGR:红色

cv2.imshow("Red", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

5. 常见问题

  • import cv2 失败:确认已安装 opencv-python
  • 版本冲突:先卸载再装 pip uninstall opencv-python
  • conda 环境:conda install -c conda-forge opencv
  • GUI 无法显示:Linux 无桌面环境时改用 imwrite 保存查看

OpenCV 安装(C++)

OpenCV 安装(C++)

C++ 环境安装 OpenCV 通常通过包管理器或源码编译。

Ubuntu/Debian(apt)

sudo apt update
sudo apt install libopencv-dev

# 验证
pkg-config --modversion opencv4

macOS(Homebrew)

brew install opencv

# 或指定版本
brew install opencv@4

Windows(预编译包)

1. 从官网/Releases 下载 Windows 预编译包
2. 解压到如 D:\\opencv
3. 配置环境变量:
   PATH 添加 D:\\opencv\\build\\x64\\vc15\\bin
4. Visual Studio 中配置:
   包含目录: D:\\opencv\\build\\include
   库目录:   D:\\opencv\\build\\x64\\vc15\\lib
   附加依赖: opencv_world490.lib(Release)

CMake 使用示例

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyCV)

find_package(OpenCV REQUIRED)

add_executable(main main.cpp)
target_link_libraries(main ${OpenCV_LIBS})
mkdir build && cd build
cmake ..
make
./main

C++ 示例代码

#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat img = cv::imread("photo.jpg");
    if (img.empty()) {
        std::cout << "图像加载失败" << std::endl;
        return -1;
    }
    cv::imshow("Image", img);
    cv::waitKey(0);
    return 0;
}

源码编译(自定义选项)

git clone https://github.com/opencv/opencv.git
cd opencv && mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j8
sudo make install

OpenCV 入门实例

OpenCV 入门实例

通过几个简单实例快速上手 OpenCV 的基本操作。

1. 读取、显示、保存图像

import cv2

# 读取
img = cv2.imread("photo.jpg")

# 显示
cv2.imshow("原图", img)
cv2.waitKey(0)

# 保存副本
cv2.imwrite("copy.jpg", img)
cv2.destroyAllWindows()

2. 灰度转换与阈值

import cv2

img = cv2.imread("photo.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化(阈值 127)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

cv2.imshow("灰度", gray)
cv2.imshow("二值", binary)
cv2.waitKey(0)
cv2.destroyAllWindows()

3. 图像缩放与旋转

import cv2

img = cv2.imread("photo.jpg")
h, w = img.shape[:2]

# 缩放
resized = cv2.resize(img, (w // 2, h // 2))

# 旋转 45 度
M = cv2.getRotationMatrix2D((w // 2, h // 2), 45, 1.0)
rotated = cv2.warpAffine(img, M, (w, h))

cv2.imshow("缩放", resized)
cv2.imshow("旋转", rotated)
cv2.waitKey(0)
cv2.destroyAllWindows()

4. 绘制图形

import cv2
import numpy as np

canvas = np.zeros((400, 400, 3), dtype=np.uint8)

# 画线
cv2.line(canvas, (50, 50), (350, 50), (0, 255, 0), 2)
# 画矩形
cv2.rectangle(canvas, (50, 100), (150, 200), (255, 0, 0), 2)
# 画圆
cv2.circle(canvas, (250, 150), 50, (0, 0, 255), -1)   # -1 填充
# 写字
cv2.putText(canvas, "OpenCV", (50, 300), cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 255, 255), 3)

cv2.imshow("绘制", canvas)
cv2.waitKey(0)
cv2.destroyAllWindows()

5. 摄像头读取

import cv2

cap = cv2.VideoCapture(0)   # 0 表示默认摄像头
while True:
    ret, frame = cap.read()
    if not ret:
        break
    cv2.imshow("摄像头", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):   # 按 q 退出
        break

cap.release()
cv2.destroyAllWindows()

OpenCV 基础模块

OpenCV 基础模块(core)

core 模块是 OpenCV 的核心数据结构与基础工具。

Mat 与 NumPy

import cv2
import numpy as np

# 创建单通道矩阵
mat = np.zeros((5, 5), dtype=np.uint8)
print(mat)

# 创建彩色矩阵
color = np.full((100, 100, 3), 255, dtype=np.uint8)  # 白色

像素访问

img = cv2.imread("photo.jpg")

# 访问像素 (y, x) → BGR
b, g, r = img[50, 100]
print(b, g, r)

# 修改像素
img[50, 100] = (0, 0, 255)   # 变红

# 区域操作
img[0:100, 0:100] = (0, 255, 0)   # 左上角 100x100 变绿

图像属性

img = cv2.imread("photo.jpg")
print(img.shape)      # (高, 宽, 通道)
print(img.size)       # 总像素数(高×宽×通道)
print(img.dtype)      # 数据类型 uint8

图像通道操作

b, g, r = cv2.split(img)     # 分离通道
merged = cv2.merge([b, g, r])  # 合并

# 提取单个通道
blue_channel = img[:, :, 0]

创建特殊矩阵

# 全黑/全白
black = np.zeros((200, 200), dtype=np.uint8)
white = np.ones((200, 200), dtype=np.uint8) * 255

# 单位矩阵
identity = np.eye(5, dtype=np.float32)

图像数据类型转换

img = cv2.imread("photo.jpg")
# uint8 → float32(便于计算)
img_float = img.astype(np.float32) / 255.0
# 转回 uint8
img_back = (img_float * 255).astype(np.uint8)

OpenCV 图像处理基础

OpenCV 图像处理基础(imgproc)

imgproc 是图像处理的核心模块,包含滤波、几何变换、形态学等大量算法。

颜色空间转换

import cv2

img = cv2.imread("photo.jpg")

gray   = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)     # 灰度
hsv    = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)      # HSV
lab    = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)      # LAB
rgb    = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)      # RGB

几何变换

h, w = img.shape[:2]

# 缩放
resized = cv2.resize(img, (w // 2, h // 2))

# 平移
M = np.float32([[1, 0, 100], [0, 1, 50]])
shifted = cv2.warpAffine(img, M, (w, h))

# 旋转
M2 = cv2.getRotationMatrix2D((w / 2, h / 2), 45, 1.0)
rotated = cv2.warpAffine(img, M2, (w, h))

# 翻转
flip_h = cv2.flip(img, 1)   # 水平翻转
flip_v = cv2.flip(img, 0)   # 垂直翻转

图像拼接与 ROI

# ROI(感兴趣区域)
roi = img[100:300, 200:400]

# 水平拼接
hconcat = cv2.hconcat([img, img])
# 垂直拼接
vconcat = cv2.vconcat([img, img])

像素值统计

import numpy as np

img = cv2.imread("photo.jpg")
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(img[:, :, 0])
print("最小值:", min_val, "最大值:", max_val)
print("均值:", np.mean(img))

图像质量评价(PSNR)

def psnr(img1, img2):
    mse = np.mean((img1 - img2) ** 2)
    if mse == 0:
        return 100
    return 20 * np.log10(255.0 / np.sqrt(mse))

OpenCV 图像基本操作

OpenCV 图像基本操作

掌握图像的读写、显示、像素操作等基本技能是学习 OpenCV 的第一步。

图像读取模式

import cv2

# 默认彩色 BGR
img1 = cv2.imread("a.jpg")
# 灰度
img2 = cv2.imread("a.jpg", cv2.IMREAD_GRAYSCALE)
# 带透明通道
img3 = cv2.imread("a.png", cv2.IMREAD_UNCHANGED)
# 原样读取
img4 = cv2.imread("a.jpg", cv2.IMREAD_COLOR)

创建空白图像

import numpy as np

# 黑色图像
black = np.zeros((300, 300, 3), dtype=np.uint8)
# 白色图像
white = np.ones((300, 300, 3), dtype=np.uint8) * 255
# 自定义颜色(BGR)
red = np.zeros((300, 300, 3), dtype=np.uint8)
red[:] = (0, 0, 255)

像素读取与修改

# 读取单个像素
pixel = img[100, 200]        # 返回 [B, G, R]
blue = img[100, 200, 0]      # 单独蓝色分量

# 修改像素
img[100, 200] = [0, 255, 0]  # 变绿

# 批量修改(性能更好)
img[100:200, 100:200] = (0, 0, 255)  # 区域变红

图像裁剪

# 裁剪 ROI: img[y1:y2, x1:x2]
crop = img[50:200, 100:300]
cv2.imshow("裁剪结果", crop)

图像算术

# 加亮/减暗
brighter = cv2.add(img, np.array([50]))   # 每像素 +50
darker = cv2.subtract(img, np.array([50]))

# 像素值截断(防止溢出)
result = cv2.addWeighted(img, 0.7, img2, 0.3, 0)

图像显示的高级用法

cv2.namedWindow("窗口", cv2.WINDOW_NORMAL)   # 可调整大小
cv2.imshow("窗口", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV 图像算术运算

OpenCV 图像算术运算

图像算术运算包括加减乘除、位运算与混合,是图像合成的基础。

图像加法

import cv2
import numpy as np

img1 = cv2.imread("a.jpg")
img2 = cv2.imread("b.jpg")   # 尺寸需一致

# 两种加法
add1 = cv2.add(img1, img2)          # 饱和加法(上限 255)
add2 = img1 + img2                  # NumPy 加法(模运算 256)

图像混合

# addWeighted:按权重混合
# 结果 = a1 * img1 + a2 * img2 + gamma
blend = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)
cv2.imshow("混合 70%+30%", blend)

位运算

# 创建掩码
mask = np.zeros(img1.shape[:2], dtype=np.uint8)
cv2.rectangle(mask, (100, 100), (300, 300), 255, -1)

# 与运算(提取 ROI)
roi = cv2.bitwise_and(img1, img1, mask=mask)

# 或/异或/非
or_result = cv2.bitwise_or(img1, img2)
xor_result = cv2.bitwise_xor(img1, img2)
not_result = cv2.bitwise_not(img1)

图像减除(背景差分)

# 图像相减,提取差异区域
diff = cv2.absdiff(img1, img2)
gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 30, 255, cv2.THRESH_BINARY)
cv2.imshow("差异区域", thresh)

图像乘法(调节亮度)

# 乘法可以缩放像素
bright = cv2.multiply(img1, np.array([1.5]))   # 提高亮度
dark = cv2.multiply(img1, np.array([0.5]))     # 降低亮度

与掩码配合

# 用掩码只处理指定区域
mask = np.zeros(img1.shape[:2], dtype=np.uint8)
cv2.circle(mask, (200, 200), 100, 255, -1)

# 只对圆形区域做模糊
blurred = cv2.GaussianBlur(img1, (15, 15), 0)
result = np.where(mask[:, :, np.newaxis] == 255, blurred, img1)

OpenCV 图像阈值处理

OpenCV 图像阈值处理

阈值处理将灰度图转换为二值图,是图像分割与目标提取的常用手段。

基本阈值

import cv2

img = cv2.imread("photo.jpg", cv2.IMREAD_GRAYSCALE)

# 固定阈值
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

# 反二值
_, binary_inv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)

cv2.imshow("二值", binary)
cv2.imshow("反二值", binary_inv)
cv2.waitKey(0)
cv2.destroyAllWindows()

阈值类型

cv2.THRESH_BINARY      二值:大于阈值为 255,否则 0
cv2.THRESH_BINARY_INV  反二值:大于阈值为 0,否则 255
cv2.THRESH_TRUNC       截断:大于阈值的变为阈值
cv2.THRESH_TOZERO      零化:小于阈值的变为 0
cv2.THRESH_TOZERO_INV  反零化:大于阈值的变为 0

自适应阈值

# 光照不均时用自适应阈值效果更好
adaptive = cv2.adaptiveThreshold(
    img, 255,
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C,   # 高斯加权
    cv2.THRESH_BINARY,
    11,     # 邻域大小(奇数)
    2       # 常量减数
)

Otsu 自动阈值

# Otsu 算法自动寻找最佳阈值(适合双峰直方图)
_, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print("自动阈值:", otsu)

大津法完整流程

# 1. 灰度
gray = cv2.cvtColor(cv2.imread("doc.jpg"), cv2.COLOR_BGR2GRAY)
# 2. 高斯模糊去噪
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# 3. Otsu 二值化
_, binary = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imshow("文档二值化", binary)

阈值应用:抠图

# 提取亮色区域
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, mask = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
# 用掩码抠出亮区
result = cv2.bitwise_and(img, img, mask=mask)

OpenCV 图像平滑处理

OpenCV 图像平滑处理(滤波)

平滑(滤波)用于去除噪声、模糊图像,是预处理的关键步骤。

均值滤波

import cv2

img = cv2.imread("noisy.jpg")

# 均值滤波:邻域平均
blur = cv2.blur(img, (5, 5))

高斯滤波

# 高斯滤波:加权平均,中心权重高
gaussian = cv2.GaussianBlur(img, (5, 5), 0)
# (核大小, sigmaX) sigma=0 自动计算

中值滤波

# 中值滤波:取中位数,对椒盐噪声效果最好
median = cv2.medianBlur(img, 5)

双边滤波

# 双边滤波:保边去噪(保留边缘细节)
bilateral = cv2.bilateralFilter(img, 9, 75, 75)

对比效果

import numpy as np

# 添加噪声对比
noise = np.random.normal(0, 25, img.shape).astype(np.uint8)
noisy = cv2.add(img, noise)

blur = cv2.blur(noisy, (5, 5))
median = cv2.medianBlur(noisy, 5)

cv2.imshow("噪声", noisy)
cv2.imshow("均值滤波", blur)
cv2.imshow("中值滤波", median)

滤波核大小

# 核越大,模糊越强,细节丢失越多
blur3 = cv2.GaussianBlur(img, (3, 3), 0)
blur9 = cv2.GaussianBlur(img, (9, 9), 0)
blur15 = cv2.GaussianBlur(img, (15, 15), 0)

自定义核(卷积)

kernel = np.ones((5, 5), np.float32) / 25
custom = cv2.filter2D(img, -1, kernel)   # 等价均值滤波

# 锐化核
sharp_kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
sharpened = cv2.filter2D(img, -1, sharp_kernel)

OpenCV 图像形态学操作

OpenCV 图像形态学操作

形态学操作基于形状处理二值图像,常用于去噪、连接断点、分离物体。

腐蚀与膨胀

import cv2
import numpy as np

img = cv2.imread("binary.jpg", cv2.IMREAD_GRAYSCALE)
# 核(结构元素)
kernel = np.ones((5, 5), np.uint8)

# 腐蚀:消除边缘,缩小白色区域(去小噪声)
eroded = cv2.erode(img, kernel, iterations=1)

# 膨胀:扩大白色区域(连接断点)
dilated = cv2.dilate(img, kernel, iterations=1)

开运算(腐蚀+膨胀)

# 开运算:先腐蚀后膨胀 → 去除小物体、分离粘连
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

闭运算(膨胀+腐蚀)

# 闭运算:先膨胀后腐蚀 → 填充小洞、连接断裂
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

形态学梯度

# 梯度:膨胀 - 腐蚀 → 提取物体边缘
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)

顶帽与黑帽

# 顶帽:原图 - 开运算 → 提取比背景亮的区域
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

# 黑帽:闭运算 - 原图 → 提取比背景暗的区域
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)

不同核形状

kernel_rect = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))    # 矩形
kernel_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))  # 椭圆
kernel_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))  # 十字

实战:去噪流程

# 1. 二值化
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 2. 开运算去小噪点
opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# 3. 闭运算填充空洞
cleaned = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel)

OpenCV 图像边缘检测

OpenCV 图像边缘检测

边缘检测是图像分析的基础,常见算法有 Sobel、Laplacian、Canny。

Canny 边缘检测(最常用)

import cv2

img = cv2.imread("photo.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Canny:双阈值
edges = cv2.Canny(gray, 100, 200)
# 阈值越小,检测到的边缘越多(越敏感)

cv2.imshow("Canny 边缘", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

Sobel 算子

# 水平方向边缘
sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
# 垂直方向边缘
sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)

# 合并
sobel_xy = cv2.bitwise_or(
    cv2.convertScaleAbs(sobel_x),
    cv2.convertScaleAbs(sobel_y)
)

Laplacian 算子

# 二阶导数,对噪声敏感
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)

Scharr 算子

# Scharr 比 Sobel 更精确(3x3)
scharr_x = cv2.Scharr(gray, cv2.CV_64F, 1, 0)
scharr_y = cv2.Scharr(gray, cv2.CV_64F, 0, 1)

Canny 参数调优

# 低阈值 高阈值
edges1 = cv2.Canny(gray, 50, 150)     # 边缘较多
edges2 = cv2.Canny(gray, 150, 300)    # 边缘较少但更清晰

# 先模糊再检测效果更好
blur = cv2.GaussianBlur(gray, (5, 5), 0)
edges_best = cv2.Canny(blur, 100, 200)

应用:轮廓粗提取

# 边缘 → 轮廓
edges = cv2.Canny(gray, 100, 200)
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 在原图绘制轮廓
result = img.copy()
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)
cv2.imshow("轮廓", result)

OpenCV 图像轮廓检测

OpenCV 图像轮廓检测

轮廓检测用于找到图像中物体的边界,是目标识别与测量(面积、周长)的基础。

查找轮廓

import cv2
import numpy as np

img = cv2.imread("shapes.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化(轮廓检测需要二值图)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 查找轮廓
contours, hierarchy = cv2.findContours(
    binary,
    cv2.RETR_EXTERNAL,      # 只取外部轮廓
    cv2.CHAIN_APPROX_SIMPLE # 压缩轮廓点
)
print("找到轮廓数:", len(contours))

绘制轮廓

# 绘制所有轮廓
result = img.copy()
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)
# -1 绘制全部;也可以指定索引绘制单个

# 用轮廓外接矩形
for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    cv2.rectangle(result, (x, y), (x + w, y + h), (255, 0, 0), 2)

轮廓特征

for cnt in contours:
    area = cv2.contourArea(cnt)          # 面积
    perimeter = cv2.arcLength(cnt, True) # 周长
    moments = cv2.moments(cnt)           # 矩(可用于质心)
    print(f"面积: {area}, 周长: {perimeter}")

    # 质心
    if moments["m00"] != 0:
        cx = int(moments["m10"] / moments["m00"])
        cy = int(moments["m01"] / moments["m00"])
        cv2.circle(img, (cx, cy), 3, (0, 0, 255), -1)

轮廓近似

# 用多边形近似轮廓
for cnt in contours:
    epsilon = 0.02 * cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, epsilon, True)
    print("顶点数:", len(approx))   # 3=三角形, 4=四边形, 6=六边形

形状识别

for cnt in contours:
    approx = cv2.approxPolyDP(cnt, 0.02 * cv2.arcLength(cnt, True), True)
    x, y, w, h = cv2.boundingRect(approx)
    vertices = len(approx)

    if vertices == 3:
        shape = "三角形"
    elif vertices == 4:
        ratio = w / float(h)
        shape = "正方形" if 0.95 <= ratio <= 1.05 else "矩形"
    elif vertices == 5:
        shape = "五边形"
    else:
        shape = "圆/椭圆"
    cv2.putText(img, shape, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)

轮廓层级

# RETR_TREE 返回层级关系
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# hierarchy[i] = [next, prev, first_child, parent]

OpenCV 图像直方图

OpenCV 图像直方图

直方图统计图像中每个灰度值(或颜色)的像素数量,用于分析亮度分布与对比度。

计算并绘制直方图

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread("photo.jpg", cv2.IMREAD_GRAYSCALE)

# 计算直方图
hist = cv2.calcHist([img], [0], None, [256], [0, 256])

# 绘制
plt.plot(hist)
plt.title("灰度直方图")
plt.xlabel("灰度值")
plt.ylabel("像素数")
plt.show()

彩色直方图

img = cv2.imread("photo.jpg")
colors = ("b", "g", "r")
for i, color in enumerate(colors):
    hist = cv2.calcHist([img], [i], None, [256], [0, 256])
    plt.plot(hist, color=color)
plt.show()

直方图均衡化

# 增强对比度(暗图变清晰)
gray = cv2.imread("dark.jpg", cv2.IMREAD_GRAYSCALE)
equalized = cv2.equalizeHist(gray)

# 彩色图像分通道均衡
img = cv2.imread("dark.jpg")
b, g, r = cv2.split(img)
b_eq = cv2.equalizeHist(b)
g_eq = cv2.equalizeHist(g)
r_eq = cv2.equalizeHist(r)
equalized_color = cv2.merge([b_eq, g_eq, r_eq])

CLAHE 自适应均衡

# CLAHE:局部对比度增强,避免过度放大噪声
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
clahe_img = clahe.apply(gray)

直方图反向投影

# 用直方图匹配找目标颜色区域
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 目标区域(如肤色)
roi = hsv[100:200, 100:200]
roi_hist = cv2.calcHist([roi], [0, 1], None, [180, 256], [0, 180, 0, 256])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
# 反向投影
dst = cv2.calcBackProject([hsv], [0, 1], roi_hist, [0, 180, 0, 256], 1)

用掩码统计区域直方图

mask = np.zeros(gray.shape, np.uint8)
mask[100:300, 100:300] = 255
hist = cv2.calcHist([gray], [0], mask, [256], [0, 256])

OpenCV 视频处理

OpenCV 视频处理

OpenCV 可以读取视频文件、摄像头流,并逐帧处理、保存输出视频。

读取视频

import cv2

cap = cv2.VideoCapture("video.mp4")

while True:
    ret, frame = cap.read()   # ret:是否成功
    if not ret:
        break
    cv2.imshow("视频", frame)
    if cv2.waitKey(30) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

视频属性

cap = cv2.VideoCapture("video.mp4")
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)) # 高
total = cap.get(cv2.CAP_PROP_FRAME_COUNT)        # 总帧数
print(f"fps={fps}, 尺寸={width}x{height}, 总帧数={total}")

保存视频

cap = cv2.VideoCapture("video.mp4")
fourcc = cv2.VideoWriter_fourcc(*"mp4v")   # 编码
out = cv2.VideoWriter("output.mp4", fourcc, 30.0, (width, height))

while True:
    ret, frame = cap.read()
    if not ret:
        break
    # 处理:灰度化后写回彩色(便于保存)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    processed = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
    out.write(processed)

cap.release()
out.release()
print("保存完成")

摄像头实时处理

cap = cv2.VideoCapture(0)   # 0 = 默认摄像头

while True:
    ret, frame = cap.read()
    if not ret:
        break
    # 反转镜像
    frame = cv2.flip(frame, 1)
    # 画帧率
    fps = cap.get(cv2.CAP_PROP_FPS)
    cv2.putText(frame, f"FPS: {fps:.0f}", (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    cv2.imshow("摄像头", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

跳转到指定帧

cap.set(cv2.CAP_PROP_POS_FRAMES, 100)   # 跳到第 100 帧
ret, frame = cap.read()
# 或按时间跳转(毫秒)
cap.set(cv2.CAP_PROP_POS_MSEC, 5000)    # 跳到 5 秒处

OpenCV 视频目标跟踪

OpenCV 视频目标跟踪

目标跟踪在视频中持续定位选定目标,常用跟踪器包括 KCF、CSRT、MIL 等。

选择跟踪器

import cv2

def create_tracker():
    # 按 OpenCV 版本选择
    tracker = cv2.TrackerCSRT_create()   # 精度高(推荐)
    # tracker = cv2.TrackerKCF_create()  # 速度快
    return tracker

完整跟踪示例

import cv2

cap = cv2.VideoCapture("video.mp4")
tracker = cv2.TrackerCSRT_create()

# 第一帧
ret, frame = cap.read()
# 手动选择目标框
bbox = cv2.selectROI("选择目标", frame, False)
tracker.init(frame, bbox)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    # 更新跟踪
    ok, bbox = tracker.update(frame)
    if ok:
        x, y, w, h = [int(v) for v in bbox]
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
    else:
        cv2.putText(frame, "跟踪丢失", (10, 30),
                    cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)

    cv2.imshow("跟踪", frame)
    if cv2.waitKey(30) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

跟踪器对比

cv2.TrackerBoosting_create()   # 最慢,旧
cv2.TrackerMIL_create()        # 精度一般
cv2.TrackerKCF_create()        # 速度快,精度尚可
cv2.TrackerCSRT_create()       # 精度最高(推荐)
cv2.TrackerMOSSE_create()      # 极快,精度低

多目标跟踪

# 多个目标用多个跟踪器管理
trackers = []
for bbox in initial_boxes:
    t = cv2.TrackerKCF_create()
    t.init(frame, bbox)
    trackers.append(t)

for t in trackers:
    ok, bbox = t.update(frame)
    if ok:
        x, y, w, h = [int(v) for v in bbox]
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

注意事项

  • 目标离开画面后跟踪会丢失,需重新初始化
  • 目标大小剧烈变化或遮挡会降低准确率
  • CSRT 精度高但较慢,KCF 适合实时
  • OpenCV 4.5+ 用 TrackerCSRT_create() 等 API

OpenCV 视频背景减除

OpenCV 视频背景减除

背景减除(Background Subtraction)从视频中分离前景移动物体,常用于运动检测、人流统计。

MOG2 背景减除器

import cv2

cap = cv2.VideoCapture("video.mp4")
# 创建背景减除器
fgbg = cv2.createBackgroundSubtractorMOG2()

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 应用背景减除 → 前景掩码
    fgmask = fgbg.apply(frame)

    # 后处理:去噪
    fgmask = cv2.medianBlur(fgmask, 5)

    cv2.imshow("原视频", frame)
    cv2.imshow("前景", fgmask)
    if cv2.waitKey(30) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

KNN 背景减除器

# KNN 算法,速度与效果均衡
fgbg = cv2.createBackgroundSubtractorKNN()

参数调节

# MOG2 参数
fgbg = cv2.createBackgroundSubtractorMOG2(
    history=500,        # 历史帧数(背景学习长度)
    varThreshold=16,    # 阈值(越大越不敏感)
    detectShadows=True  # 检测阴影
)

运动检测完整流程

import cv2
import numpy as np

cap = cv2.VideoCapture("video.mp4")
fgbg = cv2.createBackgroundSubtractorMOG2()

while True:
    ret, frame = cap.read()
    if not ret:
        break

    fgmask = fgbg.apply(frame)
    # 二值化
    _, thresh = cv2.threshold(fgmask, 200, 255, cv2.THRESH_BINARY)
    # 形态学去噪
    kernel = np.ones((3, 3), np.uint8)
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
    thresh = cv2.dilate(thresh, kernel, iterations=2)

    # 找轮廓画框
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area > 500:   # 过滤小区域
            x, y, w, h = cv2.boundingRect(cnt)
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)

    cv2.imshow("运动检测", frame)
    if cv2.waitKey(30) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

适用场景

  • 固定摄像头下的运动物体检测
  • 入侵报警、区域监控
  • 人流/车流统计
  • 不适合:摄像头移动或背景快速变化

OpenCV 人脸检测

OpenCV 人脸检测

使用 Haar 级联分类器进行人脸检测,是 OpenCV 最经典的应用之一。

加载级联分类器

import cv2

# 加载预训练模型(OpenCV 自带)
face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

图片人脸检测

import cv2

face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

img = cv2.imread("group.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 检测人脸
faces = face_cascade.detectMultiScale(
    gray,
    scaleFactor=1.1,    # 缩放步长(越小越慢越准)
    minNeighbors=5,     # 邻居数(越大误检越少)
    minSize=(30, 30)    # 最小人脸尺寸
)

print("检测到人脸数:", len(faces))

for (x, y, w, h) in faces:
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imshow("人脸检测", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

摄像头实时检测

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.1, 5)

    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.putText(frame, "Face", (x, y - 5),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)

    cv2.imshow("实时人脸检测", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

眼睛检测

eye_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_eye.xml"
)

# 在人脸区域内检测眼睛
for (x, y, w, h) in faces:
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = frame[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_gray)
    for (ex, ey, ew, eh) in eyes:
        cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)

参数调优

# scaleFactor:1.05~1.2,越小检测越细但更慢
# minNeighbors:3~6,越大误检越少但可能漏检
faces = face_cascade.detectMultiScale(
    gray, scaleFactor=1.05, minNeighbors=3, minSize=(20, 20)
)

其他预训练模型

haarcascade_frontalface_default.xml   默认人脸
haarcascade_frontalface_alt.xml      另一种人脸
haarcascade_eye.xml                  眼睛
haarcascade_smile.xml                微笑
haarcascade_profileface.xml          侧脸
haarcascade_fullbody.xml             全身

局限性

  • 对侧脸、遮挡、光照变化敏感
  • Haar 特征较传统,精度有限
  • 更精确的方案:OpenCV DNN 模块或深度学习模型

OpenCV 物体识别

OpenCV 物体识别

物体识别指识别图像中特定物体并定位。OpenCV 提供传统方法(特征匹配)与深度学习(DNN)两条路线。

1. 模板匹配(简单场景)

import cv2
import numpy as np

img = cv2.imread("scene.jpg")        # 场景图
template = cv2.imread("template.jpg") # 模板图

# 模板匹配
result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

# 最佳匹配位置
top_left = max_loc
h, w = template.shape[:2]
bottom_right = (top_left[0] + w, top_left[1] + h)

cv2.rectangle(img, top_left, bottom_right, (0, 255, 0), 2)
cv2.imshow("模板匹配", img)

2. 颜色物体识别(HSV)

import cv2
import numpy as np

img = cv2.imread("balls.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 红色范围(HSV)
lower_red = np.array([0, 100, 100])
upper_red = np.array([10, 255, 255])
mask = cv2.inRange(hsv, lower_red, upper_red)

# 找轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
    area = cv2.contourArea(cnt)
    if area > 500:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)

3. 特征点匹配(SIFT/ORB)

import cv2

# 创建特征检测器
orb = cv2.ORB_create()

img1 = cv2.imread("object1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("object2.jpg", cv2.IMREAD_GRAYSCALE)

# 检测特征点与描述子
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)

# 匹配
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)

# 绘制前 20 个匹配
result = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None)
cv2.imshow("特征匹配", result)

4. DNN 深度学习检测

# 需要模型文件(如 YOLO、SSD)
import cv2

net = cv2.dnn.readNet("yolov8n.onnx")

# 预处理
blob = cv2.dnn.blobFromImage(img, 1/255.0, (416, 416), (0, 0, 0), swapRB=True)
net.setInput(blob)
outputs = net.forward()

方法选择建议

模板匹配:目标固定、无旋转缩放
颜色检测:目标颜色特征明显
特征匹配:物体可能有旋转/遮挡(SIFT/ORB)
DNN:    通用场景、精度要求高(YOLO/SSD)

OpenCV 图像拼接

OpenCV 图像拼接(全景图)

图像拼接把多张有重叠区域的图片合成一张全景图,核心是特征匹配 + 透视变换。

使用 Stitcher 模块

import cv2

# 读取多张图片
img1 = cv2.imread("left.jpg")
img2 = cv2.imread("right.jpg")

# 创建拼接器
stitcher = cv2.Stitcher_create()
# 拼接
status, panorama = stitcher.stitch([img1, img2])

if status == cv2.Stitcher_OK:
    cv2.imwrite("panorama.jpg", panorama)
    cv2.imshow("全景图", panorama)
    cv2.waitKey(0)
else:
    print("拼接失败,状态码:", status)

手动拼接流程

import cv2
import numpy as np

def stitch_images(img1, img2):
    # 1. 转为灰度
    gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

    # 2. 检测特征点(SIFT)
    sift = cv2.SIFT_create()
    kp1, des1 = sift.detectAndCompute(gray1, None)
    kp2, des2 = sift.detectAndCompute(gray2, None)

    # 3. 特征匹配
    bf = cv2.BFMatcher()
    matches = bf.knnMatch(des1, des2, k=2)
    good = []
    for m, n in matches:
        if m.distance < 0.75 * n.distance:   # 比率测试
            good.append(m)

    # 4. 计算单应矩阵
    if len(good) > 10:
        src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
        dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
    else:
        return None

    # 5. 透视变换拼接
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]
    result = cv2.warpPerspective(img1, M, (w1 + w2, h1))
    result[0:h2, 0:w2] = img2
    return result

拼接注意事项

  • 相邻图片需要有 30%+ 的重叠区域
  • 光照尽量一致,避免明显接缝
  • 图片数量多时逐对拼接,最后合成全景
  • 运动物体在重叠区会产生鬼影

简单水平拼接(无透视)

# 图片尺寸相同时直接拼接(无重叠)
result = cv2.hconcat([img1, img2])

OpenCV 简单滤镜效果

OpenCV 简单滤镜效果

利用 OpenCV 可以轻松实现各种照片滤镜,如灰度、怀旧、漫画、锐化等。

灰度与暖色滤镜

import cv2
import numpy as np

img = cv2.imread("photo.jpg")

# 灰度滤镜
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 暖色滤镜(增加红黄)
warm = img.copy()
warm[:, :, 2] = cv2.add(warm[:, :, 2], 30)   # R+
warm[:, :, 1] = cv2.add(warm[:, :, 1], 15)   # G+

# 冷色滤镜(增加蓝)
cool = img.copy()
cool[:, :, 0] = cv2.add(cool[:, :, 0], 30)   # B+

怀旧(复古)滤镜

# 通过调整通道比例实现
sepia = img.copy()
b, g, r = cv2.split(sepia)
sepia = cv2.merge([
    np.clip(b * 0.2 + g * 0.3 + r * 0.1, 0, 255).astype(np.uint8),
    np.clip(b * 0.15 + g * 0.3 + r * 0.2, 0, 255).astype(np.uint8),
    np.clip(b * 0.1 + g * 0.2 + r * 0.3, 0, 255).astype(np.uint8),
])

漫画/卡通滤镜

# 1. 双边滤波(平滑颜色)
color = cv2.bilateralFilter(img, 9, 75, 75)

# 2. 灰度 + 中值 + 自适应阈值(描边)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 7)
edges = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
                              cv2.THRESH_BINARY, 9, 9)

# 3. 合并:边缘黑色 + 彩色
cartoon = cv2.bitwise_and(color, color, mask=edges)

锐化滤镜

kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
sharpened = cv2.filter2D(img, -1, kernel)

模糊(柔焦)滤镜

blurred = cv2.GaussianBlur(img, (15, 15), 0)

马赛克效果

# 局部马赛克
def mosaic(img, x, y, w, h, size=10):
    roi = img[y:y+h, x:x+w]
    # 缩小再放大 → 马赛克
    small = cv2.resize(roi, (w // size, h // size), interpolation=cv2.INTER_LINEAR)
    mosaic_roi = cv2.resize(small, (w, h), interpolation=cv2.INTER_NEAREST)
    img[y:y+h, x:x+w] = mosaic_roi
    return img

img = cv2.imread("photo.jpg")
img = mosaic(img, 100, 100, 200, 200)   # 打码区域

C++ OpenCV 基础操作

C++ OpenCV 基础操作

本节展示 C++ 环境下 OpenCV 的核心基础操作,与 Python API 逻辑一致。

读取与显示

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取图像
    Mat img = imread("photo.jpg");
    if (img.empty()) {
        cout << "无法读取图像" << endl;
        return -1;
    }

    // 显示
    imshow("原图", img);
    waitKey(0);

    // 保存
    imwrite("copy.jpg", img);
    return 0;
}

灰度与颜色转换

Mat gray, hsv;
cvtColor(img, gray, COLOR_BGR2GRAY);
cvtColor(img, hsv, COLOR_BGR2HSV);

imshow("灰度", gray);

像素操作

// 访问像素
Vec3b pixel = img.at<Vec3b>(50, 100);
cout << (int)pixel[0] << " " << (int)pixel[1] << " " << (int)pixel[2];

// 修改像素
img.at<Vec3b>(50, 100) = Vec3b(0, 0, 255);

// 区域操作
rectangle(img, Rect(50, 50, 200, 200), Scalar(0, 255, 0), 2);

创建空白图像

Mat black = Mat::zeros(300, 300, CV_8UC3);           // 黑色
Mat white = Mat::ones(300, 300, CV_8UC3) * 255;      // 白色
Mat red(300, 300, CV_8UC3, Scalar(0, 0, 255));       // 红色(BGR)

图像缩放与旋转

Mat resized, rotated;
resize(img, resized, Size(img.cols / 2, img.rows / 2));

Point center(img.cols / 2, img.rows / 2);
Mat M = getRotationMatrix2D(center, 45, 1.0);
warpAffine(img, rotated, M, img.size());

摄像头采集

VideoCapture cap(0);
if (!cap.isOpened()) return -1;

Mat frame;
while (true) {
    cap >> frame;
    imshow("摄像头", frame);
    if (waitKey(30) == 27) break;   // Esc 退出
}

C++ Opencv 图像处理

C++ OpenCV 图像处理

本节演示 C++ 中的常见图像处理操作:滤波、阈值、形态学、边缘检测。

滤波

#include <opencv2/opencv.hpp>
using namespace cv;

Mat img = imread("noisy.jpg");
Mat blur, gaussian, median;

// 均值滤波
blur(img, blur, Size(5, 5));
// 高斯滤波
GaussianBlur(img, gaussian, Size(5, 5), 0);
// 中值滤波
medianBlur(img, median, 5);

阈值处理

Mat gray, binary;
cvtColor(img, gray, COLOR_BGR2GRAY);

// 固定阈值
threshold(gray, binary, 127, 255, THRESH_BINARY);

// 自适应阈值
Mat adaptive;
adaptiveThreshold(gray, adaptive, 255,
                  ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11, 2);

// Otsu
Mat otsu;
threshold(gray, otsu, 0, 255, THRESH_BINARY | THRESH_OTSU);

形态学操作

Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
Mat eroded, dilated, opened, closed;

erode(binary, eroded, kernel);
dilate(binary, dilated, kernel);
morphologyEx(binary, opened, MORPH_OPEN, kernel);
morphologyEx(binary, closed, MORPH_CLOSE, kernel);

边缘检测

Mat edges;
// Canny 边缘检测
Canny(gray, edges, 100, 200);

// Sobel
Mat sobel_x, sobel_y;
Sobel(gray, sobel_x, CV_64F, 1, 0, 3);
Sobel(gray, sobel_y, CV_64F, 0, 1, 3);
convertScaleAbs(sobel_x, sobel_x);
convertScaleAbs(sobel_y, sobel_y);

轮廓检测

vector<vector<Point>> contours;
findContours(binary, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

// 绘制
Mat result = img.clone();
drawContours(result, contours, -1, Scalar(0, 255, 0), 2);

// 面积过滤
for (auto& cnt : contours) {
    double area = contourArea(cnt);
    if (area > 500) {
        Rect r = boundingRect(cnt);
        rectangle(result, r, Scalar(255, 0, 0), 2);
    }
}

直方图均衡化

Mat equalized;
equalizeHist(gray, equalized);

C++ OpenCV 基本模块

C++ OpenCV 基本模块

本节介绍 C++ 中 OpenCV 各核心模块的典型用法。

core 模块:Mat 与矩阵运算

#include <opencv2/opencv.hpp>
using namespace cv;

// 创建矩阵
Mat m1 = (Mat_<float>(2, 2) << 1, 2, 3, 4);
Mat m2 = Mat::eye(2, 2, CV_32F);

// 矩阵运算
Mat sum = m1 + m2;
Mat product = m1 * m2;
Mat transposed = m1.t();
double det = determinant(m1);

imgproc 模块:绘制

Mat canvas(400, 400, CV_8UC3, Scalar(255, 255, 255));

// 线、矩形、圆、椭圆、文字
line(canvas, Point(50, 50), Point(350, 50), Scalar(0, 0, 0), 2);
rectangle(canvas, Rect(50, 100, 100, 80), Scalar(255, 0, 0), 2);
circle(canvas, Point(250, 140), 40, Scalar(0, 255, 0), -1);
ellipse(canvas, Point(150, 300), Size(80, 40), 0, 0, 360, Scalar(0, 0, 255), 2);
putText(canvas, "OpenCV C++", Point(200, 300),
        FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 0), 2);

imgcodecs 模块:读写

#include <opencv2/imgcodecs.hpp>

Mat img = imread("a.jpg", IMREAD_COLOR);       // 读
imwrite("out.png", img);                       // 写

// 编码为内存(内存传输用)
vector<uchar> buf;
imencode(".jpg", img, buf);

video 模块:视频读写

#include <opencv2/videoio.hpp>

VideoCapture cap("video.mp4");
double fps = cap.get(CAP_PROP_FPS);
int w = cap.get(CAP_PROP_FRAME_WIDTH);
int h = cap.get(CAP_PROP_FRAME_HEIGHT);

VideoWriter writer("out.mp4", VideoWriter::fourcc('m', 'p', '4', 'v'),
                   fps, Size(w, h));

Mat frame;
while (cap.read(frame)) {
    writer.write(frame);
}
cap.release();
writer.release();

编译命令参考

# 单文件编译
g++ main.cpp -o app $(pkg-config --cflags --libs opencv4)
# 或 cmake 工程见安装章节

C++ OpenCV 特征检测

C++ OpenCV 特征检测

特征检测在 C++ 中实现同样简单,常用 SIFT、ORB、AKAZE 等。

SIFT 特征检测

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;
using namespace std;

Mat img = imread("object.jpg");
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);

// SIFT 检测器
Ptr<SIFT> sift = SIFT::create();
vector<KeyPoint> keypoints;
Mat descriptors;
sift->detectAndCompute(gray, noArray(), keypoints, descriptors);

// 绘制特征点
Mat result;
drawKeypoints(img, keypoints, result, Scalar::all(-1));
imshow("SIFT 特征", result);
waitKey(0);

ORB 特征检测

// ORB 更快,适合实时
Ptr<ORB> orb = ORB::create(500);   // 最多 500 个特征点
vector<KeyPoint> kp;
Mat desc;
orb->detectAndCompute(gray, noArray(), kp, desc);

Mat result;
drawKeypoints(img, kp, result, Scalar(0, 0, 255));
imshow("ORB 特征", result);

特征匹配

Mat img1 = imread("obj1.jpg", IMREAD_GRAYSCALE);
Mat img2 = imread("scene.jpg", IMREAD_GRAYSCALE);

// 检测
Ptr<ORB> orb = ORB::create();
vector<KeyPoint> kp1, kp2;
Mat des1, des2;
orb->detectAndCompute(img1, noArray(), kp1, des1);
orb->detectAndCompute(img2, noArray(), kp2, des2);

// 匹配
Ptr<BFMatcher> bf = BFMatcher::create(NORM_HAMMING, true);
vector<DMatch> matches;
bf->match(des1, des2, matches);

// 按距离排序取前 20
sort(matches.begin(), matches.end());
vector<DMatch> good(matches.begin(), matches.begin() + 20);

// 绘制
Mat result;
drawMatches(img1, kp1, img2, kp2, good, result);
imshow("匹配结果", result);
waitKey(0);

单应矩阵求变换

// 从匹配点计算单应矩阵(物体定位)
vector<Point2f> src_pts, dst_pts;
for (auto& m : good) {
    src_pts.push_back(kp1[m.queryIdx].pt);
    dst_pts.push_back(kp2[m.trainIdx].pt);
}
Mat H = findHomography(src_pts, dst_pts, RANSAC);

C++ Opencv 高级图像处理

C++ OpenCV 高级图像处理

本节覆盖 C++ 中的高级图像处理技术:图像修复、去噪、透视变换、图像拼接。

图像修复(inpaint)

#include <opencv2/opencv.hpp>
using namespace cv;

Mat img = imread("damaged.jpg");
Mat mask = imread("mask.jpg", IMREAD_GRAYSCALE);

Mat result;
inpaint(img, mask, result, 3, INPAINT_TELEA);
// INPAINT_TELEA 或 INPAINT_NS

imshow("修复后", result);

非局部均值去噪

Mat img = imread("noisy.jpg");
Mat denoised;
fastNlMeansDenoisingColored(img, denoised, 10, 10, 7, 21);
imshow("去噪后", denoised);

透视变换

// 4 个源点与目标点
vector<Point2f> src, dst;
src.push_back(Point2f(50, 50));
src.push_back(Point2f(300, 50));
src.push_back(Point2f(300, 300));
src.push_back(Point2f(50, 300));

dst.push_back(Point2f(0, 0));
dst.push_back(Point2f(300, 0));
dst.push_back(Point2f(300, 300));
dst.push_back(Point2f(0, 300));

Mat M = getPerspectiveTransform(src, dst);
Mat result;
warpPerspective(img, result, M, Size(300, 300));
imshow("透视变换", result);

色彩增强与对比度

// 亮度/对比度调节
// 公式: dst = alpha * src + beta
Mat adjusted;
double alpha = 1.5;   // 对比度
double beta = 30;     // 亮度
img.convertTo(adjusted, -1, alpha, beta);

// CLAHE 局部增强
Ptr<CLAHE> clahe = createCLAHE(2.0, Size(8, 8));
Mat gray, enhanced;
cvtColor(img, gray, COLOR_BGR2GRAY);
clahe->apply(gray, enhanced);

图像金字塔

// 向下采样(缩小)
Mat lower;
pyrDown(img, lower);

// 向上采样(放大)
Mat higher;
pyrUp(img, higher);

C++ OpenCV 视频处理

C++ OpenCV 视频处理

本节展示 C++ 中的视频读写、帧处理与实时视频分析。

读取视频并处理

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
    VideoCapture cap("video.mp4");
    if (!cap.isOpened()) {
        cout << "视频打开失败" << endl;
        return -1;
    }

    Mat frame, gray, edges;
    while (cap.read(frame)) {
        // 逐帧处理
        cvtColor(frame, gray, COLOR_BGR2GRAY);
        Canny(gray, edges, 100, 200);

        imshow("原视频", frame);
        imshow("边缘", edges);
        if (waitKey(30) == 27) break;   // Esc 退出
    }
    cap.release();
    destroyAllWindows();
    return 0;
}

保存处理后的视频

VideoCapture cap("video.mp4");
int w = cap.get(CAP_PROP_FRAME_WIDTH);
int h = cap.get(CAP_PROP_FRAME_HEIGHT);
double fps = cap.get(CAP_PROP_FPS);

VideoWriter writer("out.avi",
                   VideoWriter::fourcc('M', 'J', 'P', 'G'),
                   fps, Size(w, h));

Mat frame, processed;
while (cap.read(frame)) {
    // 转灰度再转回彩色保存
    cvtColor(frame, processed, COLOR_BGR2GRAY);
    cvtColor(processed, processed, COLOR_GRAY2BGR);
    writer.write(processed);
}
cap.release();
writer.release();

摄像头运动检测

VideoCapture cap(0);
Ptr<BackgroundSubtractor> bg = createBackgroundSubtractorMOG2();

Mat frame, fgmask;
while (cap.read(frame)) {
    bg->apply(frame, fgmask);
    threshold(fgmask, fgmask, 200, 255, THRESH_BINARY);

    // 找轮廓
    vector<vector<Point>> contours;
    findContours(fgmask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

    for (auto& cnt : contours) {
        if (contourArea(cnt) > 500) {
            Rect r = boundingRect(cnt);
            rectangle(frame, r, Scalar(0, 0, 255), 2);
        }
    }
    imshow("运动检测", frame);
    if (waitKey(30) == 27) break;
}

视频帧率控制

// 用定时器控制处理速度
while (true) {
    double t1 = getTickCount();
    cap >> frame;

    // ... 处理 ...

    double t2 = getTickCount();
    double fps = getTickFrequency() / (t2 - t1);
    putText(frame, "FPS: " + to_string((int)fps), Point(10, 30),
            FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
    imshow("FPS 显示", frame);
    if (waitKey(1) == 27) break;
}

C++ OpenCV 机器学习

C++ OpenCV 机器学习

OpenCV 的 ml 模块提供传统机器学习算法:KNN、SVM、决策树、随机森林等。

KNN 数字识别示例

#include <opencv2/opencv.hpp>
#include <opencv2/ml.hpp>
using namespace cv;
using namespace cv::ml;
using namespace std;

int main() {
    // 1. 准备训练数据(简单示例:2 类)
    // 特征:2 维坐标;标签:0/1
    Mat trainData = (Mat_<float>(6, 2) <<
        1, 1,   2, 2,   3, 1,     // 类 0
        10, 10, 8, 9,   9, 11);   // 类 1
    Mat labels = (Mat_<float>(6, 1) <<
        0, 0, 0, 1, 1, 1);

    // 2. 创建并训练 KNN
    Ptr<KNearest> knn = KNearest::create();
    knn->setDefaultK(3);
    knn->train(trainData, ROW_SAMPLE, labels);

    // 3. 预测
    Mat test = (Mat_<float>(1, 2) << 2, 1.5);
    Mat result;
    knn->findNearest(test, 3, result);
    cout << "预测类别: " << result.at<float>(0, 0) << endl;
    return 0;
}

SVM 分类

// 创建 SVM
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::RBF);       // 高斯核
svm->setC(100);                 // 惩罚参数

// 训练
svm->train(trainData, ROW_SAMPLE, labels);

// 预测
float response = svm->predict(test);
cout << "SVM 预测: " << response << endl;

// 保存/加载模型
svm->save("svm_model.yml");
Ptr<SVM> loaded = SVM::load("svm_model.yml");

决策树与随机森林

// 决策树
Ptr<DTrees> tree = DTrees::create();
tree->setMaxDepth(5);
tree->train(trainData, ROW_SAMPLE, labels);

// 随机森林
Ptr<RTrees> forest = RTrees::create();
forest->setMaxDepth(10);
forest->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 0.01));
forest->train(trainData, ROW_SAMPLE, labels);

数据归一化

// 训练前建议归一化特征到 [0,1]
Mat normalized;
normalize(trainData, normalized, 0, 1, NORM_MINMAX);

注意事项

  • 传统 ML 适合中小规模数据与简单特征
  • 复杂任务(图像分类)建议用 DNN 模块或外部框架
  • 模型保存为 .yml/.xml 文件,可加载复用

C++ OpenCV 项目实战

C++ OpenCV 项目实战

综合前面所学,用 C++ 实现一个"实时人脸检测 + 保存截图"的小项目。

项目结构

face_detect/
├── CMakeLists.txt
└── main.cpp

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(FaceDetect)

find_package(OpenCV REQUIRED)

add_executable(face_detect main.cpp)
target_link_libraries(face_detect ${OpenCV_LIBS})

main.cpp

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>

using namespace cv;
using namespace std;

int main() {
    // 加载人脸级联分类器
    CascadeClassifier face_cascade(
        "haarcascade_frontalface_default.xml");
    if (face_cascade.empty()) {
        cout << "级联分类器加载失败" << endl;
        return -1;
    }

    // 打开摄像头
    VideoCapture cap(0);
    if (!cap.isOpened()) {
        cout << "摄像头打开失败" << endl;
        return -1;
    }

    Mat frame, gray;
    int shot_count = 0;

    while (true) {
        cap >> frame;
        if (frame.empty()) break;

        cvtColor(frame, gray, COLOR_BGR2GRAY);
        equalizeHist(gray, gray);   // 提升检测效果

        // 人脸检测
        vector<Rect> faces;
        face_cascade.detectMultiScale(gray, faces,
                                      1.1, 5, 0, Size(40, 40));

        for (const auto& face : faces) {
            rectangle(frame, face, Scalar(0, 255, 0), 2);
        }

        // 显示人脸数
        string text = "Faces: " + to_string(faces.size());
        putText(frame, text, Point(10, 30),
                FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 255, 0), 2);

        imshow("人脸检测", frame);

        char key = waitKey(30);
        if (key == 27) break;                 // Esc 退出
        if (key == 's' && !faces.empty()) {   // s 保存截图
            imwrite("face_" + to_string(shot_count++) + ".jpg", frame);
            cout << "截图已保存" << endl;
        }
    }

    cap.release();
    destroyAllWindows();
    return 0;
}

编译运行

mkdir build && cd build
cmake ..
make
./face_detect

功能说明

1. 实时摄像头画面
2. 检测到的人脸画绿框
3. 左上角显示人脸数量
4. 按 s 保存当前截图
5. 按 Esc 退出程序

C++ OpenCV 性能优化

C++ OpenCV 性能优化

图像处理对性能敏感,掌握优化技巧可显著提升运行速度。

使用性能计时函数

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

// 计时
double t1 = (double)getTickCount();
// ... 要计时的代码 ...
double t2 = (double)getTickCount();
double elapsed = (t2 - t1) / getTickFrequency();  // 秒
cout << "耗时: " << elapsed * 1000 << " ms" << endl;

编译优化选项

// CMake 中开启优化
set(CMAKE_BUILD_TYPE Release)
# 或直接编译
g++ -O3 -march=native main.cpp $(pkg-config --cflags --libs opencv4)

选择合适的数据类型

// CV_8U 比 CV_64F 快很多(整数 vs 浮点)
Mat img8 = imread("a.jpg");            // CV_8UC3
Mat img32;
img.convertTo(img32, CV_32F);          // 需要时再转

// 能整数不浮点,能单通道不三通道

ROI 代替全图处理

// 只处理感兴趣区域
Rect roi(100, 100, 200, 200);
Mat roi_img = img(roi);                // 浅拷贝,不复制数据
// 在 roi_img 上处理,只影响该区域

多线程处理

#include <thread>

void process_chunk(Mat& frame, int start_y, int end_y) {
    // 处理每行的像素
    for (int y = start_y; y < end_y; y++) {
        // ...
    }
}

// 分 4 线程并行
int h = frame.rows;
thread t1(process_chunk, std::ref(frame), 0, h/4);
thread t2(process_chunk, std::ref(frame), h/4, h/2);
thread t3(process_chunk, std::ref(frame), h/2, 3*h/4);
thread t4(process_chunk, std::ref(frame), 3*h/4, h);
t1.join(); t2.join(); t3.join(); t4.join();

使用 OpenCV 并行 API

// OpenCV 内置并行(部分算法自动多线程)
cv::parallel_for_(cv::Range(0, h), [&](const cv::Range& range) {
    for (int y = range.start; y < range.end; y++) {
        // 逐行处理
    }
});

其他优化建议

1. 降低分辨率:处理前 resize 缩小
2. 减少帧内重复计算:缓存中间结果
3. 用灰度图代替彩色图(如果不需要颜色)
4. 避免在循环内创建大 Mat
5. 视频处理时降低 fps 或跳过隔帧
6. 用 OpenCL/CUDA 版本:opencv-world-gpu

OpenCV 官方资源

OpenCV 官方资源与学习路径

学习 OpenCV 除了本教程,官方与社区提供了大量优质资源。

官方资源

官网:      opencv.org
文档:      docs.opencv.org
GitHub:    github.com/opencv/opencv
Python 包: pypi.org/project/opencv-python
模型库:    github.com/opencv/opencv_extra
论坛:      forum.opencv.org

推荐的扩展方向

  • OpenCV DNN:加载 YOLO、SSD、EfficientDet 等深度学习模型
  • OpenCV Zoo:官方模型库,人脸识别、姿态估计、OCR
  • OpenCV.js:浏览器端运行 OpenCV
  • OpenCV Android/iOS:移动端开发
  • MediaPipe:Google 的 CV 管线(与 OpenCV 互补)

经典学习路线

1. 基础:图像读写、像素操作、颜色空间
2. 进阶:滤波、形态学、阈值、边缘、轮廓
3. 特征:直方图、SIFT/ORB、特征匹配
4. 视频:目标跟踪、背景减除、运动分析
5. 实战:人脸检测、物体识别、图像拼接
6. 深度:结合 DNN 与深度学习框架

实践建议

1. 动手写代码比看教程重要 10 倍
2. 用自己拍的图片/摄像头测试
3. 调参是常态:scaleFactor、threshold 多试几组
4. 性能用 getTickCount 量一量
5. 关注 GitHub Trending 的 CV 项目学习实战技巧

OpenCV 4 新特性速览

1. 全面支持 DNN 模块
2. QR 码检测、文本检测
3. 改进的哈尔级联与 ArUco 标记
4. 更多 GPU(CUDA/OpenCL)加速
5. C++11 支持,API 更现代
第 1 / 32 节