欢迎光临
我们一直在努力

OpenCV 学习

1. OpenCV 是什么

OpenCV 是一个开源计算机视觉库,覆盖图像处理、视频处理、特征检测、相机标定、三维重建、目标检测、传统机器学习、DNN 推理等方向。

在 Python 中一般通过 cv2 模块使用:

import cv2 as cv

OpenCV 的优势:

  • API 覆盖面广,适合快速做视觉原型。
  • C++ 实现,Python 调用也能有不错性能。
  • 和 NumPy 深度配合,图像就是数组。
  • 传统视觉算法非常丰富,适合工程落地。
  • 能处理图片、视频流、摄像头、标定、几何变换等完整链路。

2. 安装

官方文档目前推荐大多数 Python 用户优先使用 PyPI 包。建议在项目目录中创建虚拟环境,避免依赖混乱。

2.1 创建虚拟环境

Windows PowerShell:

python m venv .venv
.\\.venv\\Scripts\\activate
python m pip install upgrade pip setuptools wheel

Linux/macOS:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install –upgrade pip setuptools wheel

2.2 选择安装包

四个常见包只能选一个装在同一个环境里:

pip install opencv-python

适合大多数本地开发,有 GUI、图片和视频功能。

pip install opencv-contrib-python

包含额外 contrib 模块,例如一些扩展特征、跟踪、结构光等。

pip install opencv-python-headless

适合服务器、Docker、CI,不需要 imshow 窗口。

pip install opencv-contrib-python-headless

contrib + 无 GUI 版本。

2.3 验证安装

import cv2 as cv
print(cv.__version__)

如果能输出版本号,说明安装成功。

3. OpenCV 的核心心智模型

3.1 图像就是 NumPy 数组

OpenCV 读入图片后得到一个 numpy.ndarray:

img = cv.imread("demo.jpg")
print(type(img))
print(img.shape)
print(img.dtype)

常见输出:

<class 'numpy.ndarray'>
(1080, 1920, 3)
uint8

含义:

  • shape[0] 是高度 height
  • shape[1] 是宽度 width
  • shape[2] 是通道数 channel
  • uint8 表示每个通道范围通常是 0 到 255

3.2 坐标顺序要分清

NumPy 访问像素:

pixel = img[y, x]

OpenCV 画图和几何 API 里点坐标通常是:

(x, y)

这点非常容易写反。

3.3 OpenCV 默认是 BGR,不是 RGB

cv.imread 读入彩色图像时通道顺序是 BGR:

img_bgr = cv.imread("demo.jpg")
img_rgb = cv.cvtColor(img_bgr, cv.COLOR_BGR2RGB)

如果用 Matplotlib 显示 OpenCV 图像,一般要先转 RGB:

import matplotlib.pyplot as plt

plt.imshow(cv.cvtColor(img_bgr, cv.COLOR_BGR2RGB))
plt.axis("off")
plt.show()

4. 图片读取、显示和保存

4.1 读取图片

import cv2 as cv

img = cv.imread("input.jpg")
if img is None:
raise FileNotFoundError("图片读取失败,请检查路径")

读取灰度图:

gray = cv.imread("input.jpg", cv.IMREAD_GRAYSCALE)

保留 alpha 通道:

img = cv.imread("input.png", cv.IMREAD_UNCHANGED)

4.2 显示图片

本地有 GUI 的环境:

cv.imshow("image", img)
cv.waitKey(0)
cv.destroyAllWindows()

服务器或 Notebook 中建议用 Matplotlib 或保存文件。

4.3 保存图片

cv.imwrite("output.jpg", img)

保存 PNG:

cv.imwrite("output.png", img)

控制 JPEG 质量:

cv.imwrite("output.jpg", img, [cv.IMWRITE_JPEG_QUALITY, 95])

5. 基础图像操作

5.1 裁剪 ROI

roi = img[y1:y2, x1:x2]
cv.imwrite("roi.jpg", roi)

5.2 修改像素

img[100, 200] = (0, 0, 255) # BGR,红色

5.3 拆分和合并通道

b, g, r = cv.split(img)
merged = cv.merge([b, g, r])

多数情况下直接用 NumPy 切片更快:

b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]

5.4 改变亮度和对比度

alpha = 1.2 # 对比度
beta = 20 # 亮度
out = cv.convertScaleAbs(img, alpha=alpha, beta=beta)

6. 颜色空间

6.1 BGR 转灰度

gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

6.2 BGR 转 HSV

HSV 常用于颜色分割:

hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)

提取红色区域示例:

import cv2 as cv
import numpy as np

img = cv.imread("input.jpg")
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)

lower1 = np.array([0, 80, 80])
upper1 = np.array([10, 255, 255])
lower2 = np.array([170, 80, 80])
upper2 = np.array([180, 255, 255])

mask1 = cv.inRange(hsv, lower1, upper1)
mask2 = cv.inRange(hsv, lower2, upper2)
mask = cv.bitwise_or(mask1, mask2)

result = cv.bitwise_and(img, img, mask=mask)
cv.imwrite("red_mask.png", mask)
cv.imwrite("red_result.jpg", result)

7. 绘图和标注

OpenCV 常用于在图像上画检测结果。

img = cv.imread("input.jpg")

cv.line(img, (20, 20), (300, 20), (255, 0, 0), 2)
cv.rectangle(img, (50, 50), (250, 180), (0, 255, 0), 2)
cv.circle(img, (150, 120), 40, (0, 0, 255), 1)
cv.putText(
img,
"OpenCV",
(50, 240),
cv.FONT_HERSHEY_SIMPLEX,
1.0,
(255, 255, 255),
2,
cv.LINE_AA,
)

cv.imwrite("annotated.jpg", img)

颜色仍然是 BGR。

8. 几何变换

8.1 Resize

small = cv.resize(img, None, fx=0.5, fy=0.5, interpolation=cv.INTER_AREA)

指定尺寸:

resized = cv.resize(img, (640, 360))

常用插值:

  • cv.INTER_AREA:缩小常用。
  • cv.INTER_LINEAR:默认通用。
  • cv.INTER_CUBIC:放大质量较好但更慢。

8.2 翻转

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

8.3 旋转

h, w = img.shape[:2]
center = (w // 2, h // 2)
M = cv.getRotationMatrix2D(center, 30, 1.0)
rotated = cv.warpAffine(img, M, (w, h))

8.4 透视变换

import numpy as np

src = np.float32([[100, 100], [500, 120], [80, 400], [520, 420]])
dst = np.float32([[0, 0], [400, 0], [0, 300], [400, 300]])

M = cv.getPerspectiveTransform(src, dst)
warped = cv.warpPerspective(img, M, (400, 300))

适合做文档矫正、平面投影校正等。

9. 滤波和降噪

9.1 均值滤波

blur = cv.blur(img, (5, 5))

9.2 高斯滤波

gaussian = cv.GaussianBlur(img, (5, 5), 0)

适合去除自然噪声,也常作为边缘检测前处理。

9.3 中值滤波

median = cv.medianBlur(img, 5)

适合椒盐噪声。

9.4 双边滤波

bilateral = cv.bilateralFilter(img, 9, 75, 75)

能尽量保边去噪,但比普通滤波慢。

10. 阈值和二值化

10.1 固定阈值

gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
_, binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)

10.2 Otsu 自动阈值

_, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)

10.3 自适应阈值

适合光照不均匀的图像:

binary = cv.adaptiveThreshold(
gray,
255,
cv.ADAPTIVE_THRESH_GAUSSIAN_C,
cv.THRESH_BINARY,
11,
2,
)

11. 形态学操作

形态学通常作用于二值图或 mask。

kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))

腐蚀:

eroded = cv.erode(binary, kernel, iterations=1)

膨胀:

dilated = cv.dilate(binary, kernel, iterations=1)

开运算:先腐蚀后膨胀,去小噪点。

opened = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel)

闭运算:先膨胀后腐蚀,填小孔。

closed = cv.morphologyEx(binary, cv.MORPH_CLOSE, kernel)

12. 边缘检测

Canny 是最常用的边缘检测算法之一。

gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray = cv.GaussianBlur(gray, (5, 5), 0)
edges = cv.Canny(gray, 50, 150)
cv.imwrite("edges.png", edges)

经验:

  • 先轻微高斯模糊,减少噪声。
  • 低阈值和高阈值需要按图像调。
  • 边缘结果常与轮廓、霍夫变换一起用。

13. 轮廓检测

轮廓适合分析二值区域的形状、面积、位置。

gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
_, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)

contours, hierarchy = cv.findContours(
binary,
cv.RETR_EXTERNAL,
cv.CHAIN_APPROX_SIMPLE,
)

canvas = img.copy()
for cnt in contours:
area = cv.contourArea(cnt)
if area < 100:
continue
x, y, w, h = cv.boundingRect(cnt)
cv.rectangle(canvas, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv.putText(canvas, f"{area:.0f}", (x, y 5),
cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)

cv.imwrite("contours.jpg", canvas)

常用函数:

  • cv.contourArea(cnt):面积。
  • cv.arcLength(cnt, True):周长。
  • cv.boundingRect(cnt):外接矩形。
  • cv.minAreaRect(cnt):最小旋转外接矩形。
  • cv.approxPolyDP(…):多边形近似。

14. 特征检测和匹配

特征点用于图像拼接、定位、匹配、跟踪等。

14.1 ORB 特征

ORB 免费、快速、常用于工程。

img1 = cv.imread("a.jpg", cv.IMREAD_GRAYSCALE)
img2 = cv.imread("b.jpg", cv.IMREAD_GRAYSCALE)

orb = cv.ORB_create(nfeatures=1000)
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)

bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda m: m.distance)

vis = cv.drawMatches(img1, kp1, img2, kp2, matches[:50], None)
cv.imwrite("matches.jpg", vis)

14.2 用 RANSAC 估计单应性

import numpy as np

good = matches[:80]
pts1 = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(1, 1, 2)
pts2 = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(1, 1, 2)

H, mask = cv.findHomography(pts1, pts2, cv.RANSAC, 5.0)

单应性适合平面目标、文档、海报、图像拼接的局部场景。

15. 视频和摄像头

15.1 打开摄像头

import cv2 as cv

cap = cv.VideoCapture(0)
if not cap.isOpened():
raise RuntimeError("摄像头打开失败")

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

gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150)

cv.imshow("frame", frame)
cv.imshow("edges", edges)

if cv.waitKey(1) & 0xFF == ord("q"):
break

cap.release()
cv.destroyAllWindows()

15.2 读取视频文件

cap = cv.VideoCapture("input.mp4")

while True:
ok, frame = cap.read()
if not ok:
break
# process frame

cap.release()

15.3 写出视频

cap = cv.VideoCapture("input.mp4")
fps = cap.get(cv.CAP_PROP_FPS)
w = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))

fourcc = cv.VideoWriter_fourcc(*"mp4v")
writer = cv.VideoWriter("output.mp4", fourcc, fps, (w, h))

while True:
ok, frame = cap.read()
if not ok:
break
writer.write(frame)

cap.release()
writer.release()

16. 人脸检测:Haar Cascade 入门例子

Haar Cascade 是传统方法,速度快但鲁棒性有限。现代项目通常会使用 DNN 或专门的人脸检测器。

import cv2 as cv

img = cv.imread("people.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

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

faces = face_cascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
)

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

cv.imwrite("faces.jpg", img)

17. 相机标定的基本思路

相机标定用于估计内参和畸变参数。常见流程:

  • 打印棋盘格。
  • 从不同角度拍摄多张棋盘格图片。
  • 检测棋盘角点。
  • 用 cv.calibrateCamera 求内参、畸变、外参。
  • 用 cv.undistort 去畸变。
  • 核心代码骨架:

    import cv2 as cv
    import numpy as np
    from pathlib import Path

    pattern_size = (9, 6)
    square_size = 1.0

    objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
    objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(1, 2)
    objp *= square_size

    objpoints = []
    imgpoints = []

    for path in Path("calib_images").glob("*.jpg"):
    img = cv.imread(str(path))
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    ok, corners = cv.findChessboardCorners(gray, pattern_size)
    if not ok:
    continue

    corners = cv.cornerSubPix(
    gray,
    corners,
    (11, 11),
    (1, 1),
    criteria=(cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001),
    )
    objpoints.append(objp)
    imgpoints.append(corners)

    ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv.calibrateCamera(
    objpoints,
    imgpoints,
    gray.shape[::1],
    None,
    None,
    )

    print(camera_matrix)
    print(dist_coeffs)

    18. DNN 模块简述

    OpenCV 的 cv.dnn 可以加载一些深度学习模型做推理,例如 ONNX。

    简化流程:

    net = cv.dnn.readNetFromONNX("model.onnx")
    blob = cv.dnn.blobFromImage(img, scalefactor=1/255.0, size=(640, 640), swapRB=True)
    net.setInput(blob)
    out = net.forward()

    注意:

    • swapRB=True 常用于把 OpenCV 的 BGR 转成模型需要的 RGB。
    • 输入尺寸、归一化、均值方差必须和模型训练时一致。
    • 后处理通常要自己写,比如 NMS、坐标反变换、类别映射。

    19. 一个完整小项目:提取图片中的文档区域

    目标:从图片中找到纸张/文档,做透视矫正。

    import cv2 as cv
    import numpy as np

    def order_points(pts):
    rect = np.zeros((4, 2), dtype=np.float32)
    s = pts.sum(axis=1)
    diff = np.diff(pts, axis=1).ravel()
    rect[0] = pts[np.argmin(s)] # top-left
    rect[2] = pts[np.argmax(s)] # bottom-right
    rect[1] = pts[np.argmin(diff)] # top-right
    rect[3] = pts[np.argmax(diff)] # bottom-left
    return rect

    img = cv.imread("document.jpg")
    orig = img.copy()
    ratio = img.shape[0] / 700.0
    img = cv.resize(img, (int(img.shape[1] / ratio), 700))

    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    gray = cv.GaussianBlur(gray, (5, 5), 0)
    edges = cv.Canny(gray, 50, 150)

    contours, _ = cv.findContours(edges, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours, key=cv.contourArea, reverse=True)

    doc = None
    for cnt in contours:
    peri = cv.arcLength(cnt, True)
    approx = cv.approxPolyDP(cnt, 0.02 * peri, True)
    if len(approx) == 4:
    doc = approx.reshape(4, 2)
    break

    if doc is None:
    raise RuntimeError("未找到文档四边形")

    doc = doc * ratio
    rect = order_points(doc)

    width_a = np.linalg.norm(rect[2] rect[3])
    width_b = np.linalg.norm(rect[1] rect[0])
    height_a = np.linalg.norm(rect[1] rect[2])
    height_b = np.linalg.norm(rect[0] rect[3])

    max_w = int(max(width_a, width_b))
    max_h = int(max(height_a, height_b))

    dst = np.float32([
    [0, 0],
    [max_w 1, 0],
    [max_w 1, max_h 1],
    [0, max_h 1],
    ])

    M = cv.getPerspectiveTransform(rect, dst)
    warped = cv.warpPerspective(orig, M, (max_w, max_h))
    cv.imwrite("document_warped.jpg", warped)

    20. 常见坑

    20.1 图片路径错误

    cv.imread 失败时不会直接抛异常,而是返回 None。

    img = cv.imread(path)
    if img is None:
    raise FileNotFoundError(path)

    20.2 BGR/RGB 混淆

    OpenCV 是 BGR,Matplotlib、PIL、很多深度学习模型是 RGB。颜色不对时先检查通道顺序。

    20.3 dtype 和数值范围

    uint8 图像范围是 0 到 255;深度学习或浮点计算常用 float32 和 0 到 1。混用时要明确转换。

    img_float = img.astype(np.float32) / 255.0
    img_uint8 = np.clip(img_float * 255, 0, 255).astype(np.uint8)

    20.4 GUI 环境问题

    服务器、Docker、远程终端里 cv.imshow 可能不可用。使用 headless 包时也没有 GUI 后端。此时用 cv.imwrite 保存结果,或用 Notebook/Matplotlib 显示。

    20.5 视频编码问题

    VideoWriter_fourcc 和输出容器要匹配。Windows、macOS、Linux 支持的编码器可能不同。遇到打不开的视频,尝试:

    cv.VideoWriter_fourcc(*"mp4v")
    cv.VideoWriter_fourcc(*"XVID")
    cv.VideoWriter_fourcc(*"MJPG")

    20.6 坐标和尺寸写反

    图像数组 shape 是 (h, w),而很多 OpenCV API 的 size 是 (w, h)。

    h, w = img.shape[:2]
    resized = cv.resize(img, (w // 2, h // 2))

    21. 学习路线

    建议按这个顺序练:

  • 图片读写、显示、保存。
  • NumPy 切片、ROI、通道操作。
  • 颜色空间转换和 mask。
  • 滤波、阈值、形态学。
  • 边缘、轮廓、形状测量。
  • 几何变换和透视校正。
  • 视频、摄像头、逐帧处理。
  • 特征匹配、图像拼接、相机标定。
  • DNN 推理、目标检测、部署优化。
  • 22. 常用模板

    22.1 批量处理文件夹图片

    import cv2 as cv
    from pathlib import Path

    in_dir = Path("input_images")
    out_dir = Path("output_images")
    out_dir.mkdir(exist_ok=True)

    for path in in_dir.glob("*.jpg"):
    img = cv.imread(str(path))
    if img is None:
    print("skip:", path)
    continue

    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    out_path = out_dir / f"{path.stem}_gray.png"
    cv.imwrite(str(out_path), gray)

    22.2 保持比例缩放到指定宽度

    def resize_width(img, width):
    h, w = img.shape[:2]
    scale = width / w
    return cv.resize(img, (width, int(h * scale)), interpolation=cv.INTER_AREA)

    22.3 画带文字的检测框

    def draw_box(img, box, label, color=(0, 255, 0)):
    x1, y1, x2, y2 = map(int, box)
    cv.rectangle(img, (x1, y1), (x2, y2), color, 2)
    cv.putText(img, label, (x1, max(0, y1 6)),
    cv.FONT_HERSHEY_SIMPLEX, 0.6, color, 2, cv.LINE_AA)

    23. 推荐资料和来源

    • OpenCV-Python 官方教程目录:https://docs.opencv.org/4.x/d6/d00/tutorial_py_root.html
    • OpenCV 官方 pip 安装教程:https://docs.opencv.org/4.x/db/dd1/tutorial_py_pip_install.html
    • OpenCV Windows 安装说明:https://docs.opencv.org/4.x/d5/de5/tutorial_py_setup_in_windows.html
    赞(0)
    未经允许不得转载:171主机测评 » OpenCV 学习
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址