OPENCV 基础入门

分类: 图像处理

OpenCV 简介

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它包含了数百种计算机视觉算法,广泛用于图像处理、视频分析、物体检测、人脸识别等任务。

简单来说:OpenCV就是让计算机"看懂"图像和视频的一把钥匙,就像给计算机安装了一双"眼睛"。


OpenCV 能做什么

  1. 图像读取与显示 - 读取图片、展示图片、保存图片
  2. 图像变换 - 调整大小、旋转、裁剪、翻转
  3. 颜色处理 - 颜色空间转换(如RGB转灰度、转HSV)
  4. 图像滤波 - 模糊、锐化、边缘检测
  5. 形状绘制 - 画线、画圆、画矩形、添加文字
  6. 目标检测 - 人脸检测、物体识别
  7. 视频处理 - 读取视频流、帧提取

环境安装

Windows/Mac/Linux 安装

pip install opencv-python

如果是苹果M系列芯片 Mac:

pip install opencv-python

验证安装:

import cv2
print(cv2.__version__)  # 输出 OpenCV 版本

基础概念

1. 图像是什么

在计算机中,图像本质上就是一个矩阵(Matrix)

假设有一张宽度300像素、高度200像素的彩色图片:

  • 这张图片在计算机中存储为一个 300 × 200 × 3 的三维数组
  • 300 是宽度(列数)
  • 200 是高度(行数)
  • 3 是通道数(RGB三个颜色通道)

每个像素点的值通常是 0-255 的整数:

  • 0 表示最暗(黑色)
  • 255 表示最亮(白色)

灰度图像 vs 彩色图像

  • 灰度图像:只有一个通道,值为0-255,代表亮度
  • 彩色图像:有三个通道(红、绿、蓝),每个通道0-255

2. OpenCV 中的图像坐标

OpenCV 使用的是 行-列 坐标系(注意:不是常见的x-y坐标系):

┌─────────────→ 列 (x)
│  (0,0)  (1,0)  (2,0)
│  (0,1)  (1,1)  (2,1)
│  (0,2)  (1,2)  (2,2)
↓
行 (y)

重要img[y, x] 表示访问第y行第x列的像素

例如:img[100, 200] 表示访问图像中第100行、第200列的像素值


核心操作

1. 读取图像

import cv2

# 读取图像(彩色模式)
img = cv2.imread('photo.jpg', cv2.IMREAD_COLOR)

# 读取图像(灰度模式)
img_gray = cv2.imread('photo.jpg', cv2.IMREAD_GRAYSCALE)

# 读取图像(不变模式)
img_unchanged = cv2.imread('photo.jpg', cv2.IMREAD_UNCHANGED)

参数说明

  • cv2.IMREAD_COLOR(或写1):读取彩色图像,忽略透明度
  • cv2.IMREAD_GRAYSCALE(或写0):读取灰度图像
  • cv2.IMREAD_UNCHANGED(或写-1):读取原始图像(含透明度通道)

注意:如果图像读取失败,imread 会返回 None,所以最好加个判断:

img = cv2.imread('photo.jpg')
if img is None:
    print("图像读取失败!")
else:
    print("图像读取成功,形状:", img.shape)

2. 显示图像

import cv2

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

# 显示图像(窗口名称,图像变量)
cv2.imshow('My Image', img)

# 等待按键(参数表示等待时间,单位毫秒。0表示无限等待)
cv2.waitKey(0)

# 关闭所有窗口
cv2.destroyAllWindows()

常用组合

cv2.imshow('Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

这段代码会显示图像,等待用户按键,然后关闭窗口。


3. 保存图像

# 保存图像
cv2.imwrite('output.png', img)
# 第一个参数是保存的文件名,第二个是图像变量

4. 获取图像信息

import cv2

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

# 获取图像形状
print(img.shape)
# 输出例如:(480, 640, 3) 表示 高度480,宽度640,3通道

# 获取图像高度
print(img.shape[0])

# 获取图像宽度
print(img.shape[1]

# 获取通道数
print(img.shape[2])

# 获取图像数据类型
print(img.dtype)
# 输出通常是 uint8(无符号8位整数,范围0-255)

# 获取图像像素总数
print(img.size)
# 输出:480 × 640 × 3 = 921600

图像基础变换

1. 调整图像大小

import cv2

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

# 方法一:直接指定目标尺寸
# 参数:(图像, 宽度, 高度)
resized = cv2.resize(img, (300, 200))  # 注意:先宽后高

# 方法二:按比例缩放
# 参数:(图像, None, fx, fy)
# fx, fy 是缩放因子
resized2 = cv2.resize(img, None, fx=0.5, fy=0.5)  # 缩小到一半

# 放大到2倍
resized3 = cv2.resize(img, None, fx=2, fy=2)

注意:OpenCV 中 resize 的参数顺序是 (宽度, 高度),而不是(高, 宽)


2. 裁剪图像

图像本质上是矩阵,裁剪就是从矩阵中提取一部分。

import cv2

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

# 裁剪图像:img[行_start:行_end, 列_start:列_end]
# 注意:是左闭右开区间 [start, end)
cropped = img[100:300, 200:400]  # 裁剪 y:100-300, x:200:400 的区域

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

示例图解

原图:
┌────────────────────────────┐
│                            │
│      裁剪区域              │
│   ┌──────────┐             │
│   │  这是    │             │
│   │  裁剪    │             │
│   │  结果    │             │
│   └──────────┘             │
│                            │
└────────────────────────────┘

3. 翻转图像

import cv2

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

# flipCode 参数:
# 1  →  水平翻转(左右颠倒)
# 0  →  垂直翻转(上下颠倒)
# -1 →  同时水平垂直翻转(旋转180度)

# 水平翻转
flipped_h = cv2.flip(img, 1)

# 垂直翻转
flipped_v = cv2.flip(img, 0)

# 水平+垂直翻转
flipped_hv = cv2.flip(img, -1)

4. 旋转图像

import cv2
import numpy as np

img = cv2.imread('photo.jpg')
height, width = img.shape[:2]

# 方法一:使用 getRotationMatrix2D
# 参数:(旋转中心, 旋转角度, 缩放因子)
# 旋转中心可以是 (width/2, height/2) 即图像中心
# 角度:正数表示逆时针旋转

# 旋转90度
M = cv2.getRotationMatrix2D((width/2, height/2), 90, 1)
rotated = cv2.warpAffine(img, M, (width, height))

# 方法二:使用 transpose(只能旋转90度的倍数)
# 顺时针旋转90度
rotated90 = cv2.transpose(img)

# 逆时针旋转90度
rotated90_cw = cv2.transpose(cv2.flip(img, 1))

颜色空间

1. 常见的颜色空间

RGB:红-绿-蓝,加法混色模式,屏幕显示常用

BGR:OpenCV默认使用BGR模式(注意:不是RGB!)

灰度(Gray):只有亮度信息,黑白照片

HSV

  • H(Hue):色相,表示颜色种类(0-180)
  • S(Saturation):饱和度,表示颜色纯度(0-255)
  • V(Value):明度,表示颜色明亮程度(0-255)

YUV:常用于视频处理


2. 颜色空间转换

import cv2

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

# BGR 转 灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# BGR 转 RGB(用于 matplotlib 显示)
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# BGR 转 HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 灰度 转 BGR
bgr = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)

重要:OpenCV 默认使用 BGR 格式,而 matplotlib 使用 RGB 格式。如果用 OpenCV 读取图像后用 matplotlib 显示,颜色会偏红/蓝。解决方法:

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('photo.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换后再显示
plt.imshow(img_rgb)
plt.show()

3. 通道分离与合并

import cv2

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

# 分离通道
b, g, r = cv2.split(img)

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

# 也可以只保留某个通道
only_red = img[:, :, 2]  # 保留红色通道

绘图功能

OpenCV 可以直接在图像上绘制各种形状。

1. 画线

import cv2
import numpy as np

# 创建一张空白图像(黑色背景)
img = np.zeros((512, 512, 3), np.uint8)

# 画线:cv2.line(图像, 起点, 终点, 颜色, 线宽)
# 颜色是 BGR 格式
cv2.line(img, (0, 0), (511, 511), (0, 255, 0), 5)

# (0, 0) 是左上角,(511, 511) 是右下角
# (0, 255, 0) 是绿色,线宽5像素

2. 画矩形

import cv2
import numpy as np

img = np.zeros((512, 512, 3), np.uint8)

# 画矩形:cv2.rectangle(图像, 左上角, 右下角, 颜色, 线宽)
# 线宽为 -1 表示填充整个矩形
cv2.rectangle(img, (384, 0), (510, 128), (0, 255, 0), 3)

# 填充矩形
cv2.rectangle(img, (100, 100), (200, 200), (255, 0, 0), -1)

3. 画圆

import cv2
import numpy as np

img = np.zeros((512, 512, 3), np.uint8)

# 画圆:cv2.circle(图像, 圆心, 半径, 颜色, 线宽)
cv2.circle(img, (447, 63), 63, (0, 0, 255), -1)
# 圆心(447, 63),半径63,红色,填充

4. 添加文字

import cv2
import numpy as np

img = np.zeros((512, 512, 3), np.uint8)

# 添加文字:cv2.putText(图像, 文字, 位置, 字体, 大小, 颜色, 粗细, 线条类型)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, 'OpenCV', (10, 250), font, 4, (255, 255, 255), 2, cv2.LINE_AA)

常用字体

  • cv2.FONT_HERSHEY_SIMPLEX - 正常大小无衬线字体
  • cv2.FONT_HERSHEY_DUPLEX - 正常大小无衬线字体(复杂)
  • cv2.FONT_HERSHEY_COMPLEX - 正常大小衬线字体
  • cv2.FONT_ITALIC - 斜体(需要与其他字体组合)

5. 综合示例:绘制人脸标记

import cv2
import numpy as np

# 创建空白图像
img = np.ones((500, 500, 3), np.uint8) * 255  # 白色背景

# 画脸部轮廓(椭圆)
cv2.ellipse(img, (250, 250), (100, 120), 0, 0, 360, (255, 0, 0), 2)

# 画左眼(圆)
cv2.circle(img, (210, 220), 20, (0, 255, 0), 2)

# 画右眼(圆)
cv2.circle(img, (290, 220), 20, (0, 255, 0), 2)

# 画鼻子(线)
cv2.line(img, (250, 250), (240, 300), (0, 0, 255), 2)
cv2.line(img, (250, 250), (260, 300), (0, 0, 255), 2)

# 画嘴巴(椭圆的一部分)
cv2.ellipse(img, (250, 320), (60, 20), 0, 0, 180, (0, 0, 255), 2)

cv2.imshow('Face', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

图像滤波

图像滤波就是对图像进行"美化"或"提取特征"的过程,可以理解为给图像"化妆"或"美颜"。

1. 模糊(平滑)

模糊可以去除图像噪声,让图像看起来更柔和。

import cv2

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

# 均值模糊
# 参数:图像, 内核大小 (必须是奇数)
blur = cv2.blur(img, (5, 5))

# 高斯模糊(更自然的模糊)
# 参数:图像, 内核大小, 标准差
gaussian = cv2.GaussianBlur(img, (5, 5), 0)

# 中值模糊(对椒盐噪声效果好)
median = cv2.medianBlur(img, 5)

# 双边滤波(保留边缘的模糊)
bilateral = cv2.bilateralFilter(img, 9, 75, 75)

2. 锐化

锐化让图像看起来更清晰,边缘更明显。

import cv2
import numpy as np

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

# 定义锐化核
kernel = np.array([[-1, -1, -1],
                   [-1,  9, -1],
                   [-1, -1, -1]])

# 应用锐化
sharpened = cv2.filter2D(img, -1, kernel)

# 另一种锐化方法:锐化 + 模糊的混合
kernel2 = np.array([[1, 1, 1],
                    [1, -7, 1],
                    [1, 1, 1]])
sharpened2 = cv2.filter2D(img, -1, kernel2)

3. 边缘检测

边缘检测用于找出图像中物体的轮廓。

import cv2

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

# Canny 边缘检测
# 参数:图像, 阈值1, 阈值2
edges = cv2.Canny(gray, 50, 150)

# 阈值1过低:检测到更多边缘(包含噪声)
# 阈值2过高:只检测到最明显的边缘

原理:Canny 边缘检测会检测梯度变化大的地方,这些地方通常就是物体的边缘。


4. 形态学操作

形态学操作主要用于处理图像的形状和结构。

import cv2
import numpy as np

img = cv2.imread('shape.png', 0)

# 二值化处理
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

# 腐蚀(缩小白色区域)
# 参数:图像, 内核, 迭代次数
kernel = np.ones((5, 5), np.uint8)
eroded = cv2.erode(binary, kernel, iterations=1)

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

# 开运算(先腐蚀后膨胀,去除小物体)
opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)

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

应用场景

  • 去除噪点:开运算
  • 填充孔洞:闭运算
  • 提取轮廓:先边缘检测

图像运算

1. 图像加法

import cv2
import numpy as np

img1 = cv2.imread('image1.jpg')
img2 = cv2.imread('image2.jpg')

# 方法一:直接相加(会截断到255)
added = cv2.add(img1, img2)

# 方法二:带权重的相加(混合两幅图)
# 参数:图像1, 权重1, 图像2, 权重2, 偏移量
blended = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)

2. 图像掩码(ROI)

使用掩码可以选择性地处理图像的特定区域。

import cv2
import numpy as np

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

# 创建掩码(全黑)
mask = np.zeros(img.shape[:2], dtype=np.uint8)

# 在掩码上创建白色区域(ROI)
mask[100:300, 200:400] = 255

# 使用掩码提取感兴趣区域
roi = cv2.bitwise_and(img, img, mask=mask)

视频处理

1. 读取视频

import cv2

# 读取视频文件
cap = cv2.VideoCapture('video.mp4')

# 或者读取摄像头(0 表示第一个摄像头)
cap = cv2.VideoCapture(0)

while cap.isOpened():
    # 读取一帧
    ret, frame = cap.read()

    if not ret:
        break  # 视频结束

    # 处理这一帧
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 显示
    cv2.imshow('Video', gray)

    # 按 q 键退出
    if cv2.waitKey(25) & 0xFF == ord('q'):
        break

# 释放资源
cap.release()
cv2.destroyAllWindows()

2. 保存视频

import cv2

cap = cv2.VideoCapture(0)

# 获取视频属性
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(cap.get(cv2.CAP_PROP_FPS))

# 创建 VideoWriter 对象
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('output.mp4', fourcc, fps, (width, height))

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

    # 写入帧
    out.write(frame)

    cv2.imshow('Recording', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
out.release()
cv2.destroyAllWindows()

实战案例

案例1:人脸检测

import cv2

# 加载预训练的人脸检测模型
# OpenCV 提供了 Haar Cascade 分类器
face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)

# 读取图片
img = cv2.imread('photo.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 检测人脸
# 参数:灰度图像, 缩放因子, 最小邻居数
faces = face_cascade.detectMultiScale(gray, 1.1, 4)

# 在每张人脸上画矩形
for (x, y, w, h) in faces:
    cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)

cv2.imshow('Face Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

案例2:车牌分割(简单示例)

import cv2
import numpy as np

# 读取车牌图像
img = cv2.imread('car_plate.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

# 形态学操作
kernel = np.ones((3, 3), np.uint8)
dilated = cv2.dilate(binary, kernel, iterations=2)

# 找轮廓
contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 筛选可能是字符的轮廓
for contour in contours:
    x, y, w, h = cv2.boundingRect(contour)
    # 根据宽高比筛选
    if.2 < w/h < 1.0 and w > 10 and h > 10:
        # 切割字符
        char_img = img[y:y+h, x:x+w]
        cv2.imshow('Character', char_img)
        cv2.waitKey(0)

cv2.destroyAllWindows()

案例3:实时美颜效果(简单版)

import cv2

cap = cv2.VideoCapture(0)

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

    # 高斯模糊模拟美颜
    # 参数:图像, 核大小, 标准差
    blurred = cv2.GaussianBlur(frame, (15, 15), 0)

    # 混合原图和模糊图(保留细节)
    # 权重:原图0.6,模糊图0.4
    beauty = cv2.addWeighted(frame, 0.6, blurred, 0.4, 0)

    cv2.imshow('Beauty', beauty)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

常用函数速查表

功能 函数
读取图像 cv2.imread()
显示图像 cv2.imshow()
保存图像 cv2.imwrite()
等待按键 cv2.waitKey()
调整大小 cv2.resize()
裁剪 img[y1:y2, x1:x2]
翻转 cv2.flip()
旋转 cv2.warpAffine()
颜色转换 cv2.cvtColor()
灰度化 cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
画线 cv2.line()
画矩形 cv2.rectangle()
画圆 cv2.circle()
画椭圆 cv2.ellipse()
写文字 cv2.putText()
均值模糊 cv2.blur()
高斯模糊 cv2.GaussianBlur()
中值模糊 cv2.medianBlur()
边缘检测 cv2.Canny()
二值化 cv2.threshold()
腐蚀 cv2.erode()
膨胀 cv2.dilate()
开运算 cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
闭运算 cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
人脸检测 face_cascade.detectMultiScale()
读取视频 cv2.VideoCapture()
写入视频 cv2.VideoWriter()

学习建议

  1. 多动手:光学不练假把式,一定要自己敲代码运行
  2. 看效果:每学一个函数,就用图像试试效果
  3. 理解原理:了解每个操作背后的数学原理
  4. 项目驱动:设定一个小目标,比如识别身份证号码
  5. 查阅文档:OpenCV 官方文档很详细,不会就查

参考资源

  • OpenCV 官方文档:https://docs.opencv.org/
  • OpenCV Python 教程:https://docs.opencv.org/master/d6/d00/tutorial_py_root.html
  • GitHub 示例代码:https://github.com/opencv/opencv