OpenCV 保姆级入门:从图像本质到代码实现的核心操作全解

一、前置基础:图像在计算机里到底是什么?

在写代码之前,先搞懂最核心的底层概念,后面所有操作都基于这个:

  1. 灰度图:本质是一个二维数组,行数 = 图像高度,列数 = 图像宽度。每个元素是一个像素值,范围 0~255,0 代表纯黑,255 代表纯白,中间值是不同深浅的灰色。
  2. 彩色图:本质是三维数组,形状为(高度, 宽度, 通道数)。OpenCV 默认通道顺序是BGR(蓝 - 绿 - 红),和我们常说的 RGB 顺序相反,这是新手最容易踩的坑。
  3. 数据类型:像素默认是uint8(无符号 8 位整数),刚好对应 0~255 的取值范围,节省存储空间。

我们后面做的裁剪、打码、通道分离,本质全都是对这个 numpy 数组做增删改查。


二、环境安装

OpenCV 开发只需要两个核心库,直接 pip 安装即可,下载慢可以加清华源:

#numpy的安装 pip install numpy==2.4.6 -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com #opencv的安装 pip install opencv-python -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com pip install opencv-contrib-python -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
  • opencv-python:OpenCV 的 Python 封装,所有图像处理 API 都在这里
  • numpy:图像处理的底层依赖,所有像素运算、数组切片都靠它

三、Demo1:彩色图像读取与属性查看

核心知识点

  1. cv2.imread():图像读取函数

    • 默认模式cv2.IMREAD_COLOR(可简写为 1):彩色模式读取,忽略透明通道
    • 路径错误不会报错,只会返回 None,所以运行后没画面先检查路径
  2. cv2.imshow():创建窗口显示图像,第一个参数是窗口名,同名窗口会被覆盖
  3. cv2.waitKey():键盘绑定函数,单位毫秒
    • 参数为 0:无限等待,直到按下任意键才继续执行
    • 参数为正数:等待对应毫秒数,超时自动往下走
    • 返回值是按下按键的 ASCII 码,ESC 键对应值为 27
    • 必须和imshow配合使用,否则窗口会一闪而过、无响应
  4. 图像三大属性
    • shape:返回(高度, 宽度, 通道数),彩色图通道数为 3,灰度图只有前两个值
    • dtype:像素数据类型,默认uint8
    • size:总像素数 = 高 × 宽 × 通道数
import cv2 import numpy as np # 读取彩色图片,默认BGR模式;文件要和代码同目录,否则写完整路径 img = cv2.imread("test.jpg") # 创建窗口显示图片,第一个参数是窗口名称 cv2.imshow("彩色原图", img) # 等待键盘输入,0代表无限等待;接收按键的ASCII码 key_code = cv2.waitKey(0) print("按下的按键编码:", key_code) # 销毁所有窗口,释放内存,养成随手写的好习惯 cv2.destroyAllWindows() # 打印图像基础属性 print(f"图像形状(高度, 宽度, 通道数):{img.shape}") print(f"像素数据类型:{img.dtype}") print(f"图像总像素数量:{img.size}")

四、Demo2:灰度图读取、ROI 裁剪与图像保存

核心知识点

  1. 灰度图读取:imread第二个参数传cv2.IMREAD_GRAYSCALE(可简写为 0),直接读取为单通道灰度图,比 “读彩色再转灰度” 效率更高。
  2. ROI(感兴趣区域):图像处理中只需要处理的局部区域,通过 numpy 数组切片实现,格式为[行起始:行结束, 列起始:列结束],对应图像的「高度范围、宽度范围」。
  3. cv2.imwrite():图像保存函数,根据文件名后缀自动判断格式,支持 jpg、png 等常见格式。
  4. 原始字符串r"":路径前加 r,可以避免路径中反斜杠的转义问题,Windows 路径推荐加上。
import cv2 import numpy as np # ========== 1. 灰度图读取与保存 ========== # 第二个参数设为灰度模式,直接读取单通道灰度图 gray_img = cv2.imread(r'test.png', cv2.IMREAD_GRAYSCALE) cv2.imshow("灰度图", gray_img) cv2.waitKey(0) cv2.destroyAllWindows() # 将灰度图保存到本地,后缀名决定保存格式 cv2.imwrite('test_gray.png', gray_img) # ========== 2. 图片切片裁剪(ROI提取) ========== # 读取彩色原图 origin_img = cv2.imread(r'test.png') # 数组切片:[行范围, 列范围] = [高度方向, 宽度方向] # 截取行30到230、列100到300的矩形区域 cut_img = origin_img[30:230, 100:300] # 同时显示原图和裁剪后的区域 cv2.imshow("原图", origin_img) cv2.imshow("裁剪区域", cut_img) cv2.waitKey(0) cv2.destroyAllWindows()

五、Demo3:视频文件读取与播放

核心知识点

  1. 视频本质:由一帧一帧的静态图像组成,按固定帧率连续播放形成动态画面。处理视频本质就是循环处理每一帧图像。
  2. cv2.VideoCapture():视频捕获对象
    • 传文件路径:读取本地视频
    • 传数字 0:调用电脑默认摄像头
  3. isOpened():判断视频 / 摄像头是否成功打开,是必加的异常判断,避免程序崩溃。
  4. read()方法:逐帧读取视频,返回两个值
    • ret:布尔值,读取成功返回 True,视频结束或读取失败返回 False
    • frame:当前帧的图像数组,和普通图片格式完全一致,可以直接用所有图片处理 API
  5. 播放速度控制:通过cv2.waitKey()的参数控制,数值越大每帧间隔越长,播放越慢。通常 25~30ms 对应正常播放速度。
import cv2 # 创建视频捕获对象,传入本地视频路径 video = cv2.VideoCapture('test.mp4') # 判断视频是否成功打开,失败直接退出 if not video.isOpened(): print("视频读取失败,请检查文件路径!") exit() # 循环逐帧读取视频 while True: # 读取一帧画面,ret为是否读取成功,frame为当前帧图像 ret, frame = video.read() # 视频读取完毕(ret为False),跳出循环 if not ret: break # 取消注释可将视频转为灰度播放 # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 显示当前帧 cv2.imshow("视频播放", frame) # 每20ms刷新一帧,按下ESC键(ASCII码27)退出播放 if cv2.waitKey(20) == 27: break # 释放视频资源,销毁窗口 video.release() cv2.destroyAllWindows()

六、Demo4:图像缩放 resize

核心知识点

  1. 两种常用缩放方式
    • 固定尺寸缩放:直接指定目标(宽度, 高度),简单粗暴,但比例不对会导致图像变形
    • 比例缩放:通过fx(宽度缩放比例)、fy(高度缩放比例)控制,dsize传 None,等比例缩放不会变形
  2. 高频踩坑:resize的尺寸参数顺序是(宽度, 高度),而img.shape返回的是(高度, 宽度),顺序完全相反,新手 90% 会在这里踩坑。
  3. 插值算法:resize 默认使用双线性插值,缩放时自动填充 / 减少像素,保证画面平滑,普通场景不用手动修改
import cv2 import numpy as np img = cv2.imread('test.jpg') # 方式1:固定尺寸缩放,参数顺序是(宽度, 高度) resize_fixed = cv2.resize(img, (300, 300)) # 方式2:按比例缩放,宽高都缩小为原来的0.5倍,不会变形 resize_scale = cv2.resize(img, None, fx=0.5, fy=0.5) # 同时显示对比 cv2.imshow("原图", img) cv2.imshow("等比例缩放后", resize_scale) cv2.waitKey(0) cv2.destroyAllWindows()

七、Demo5:BGR 通道分离与合并

核心知识点

  1. 颜色通道原理:彩色图像由蓝 (B)、绿 (G)、红 (R) 三个独立的单通道叠加而成。每个通道本身都是灰度图,代表对应颜色的亮度分布。
  2. 通道索引:OpenCV 中第 0 维是 B 通道,第 1 维是 G 通道,第 2 维是 R 通道。
  3. cv2.split():自动分离三个通道,返回顺序为b, g, r
  4. cv2.merge():将多个单通道合并为彩色图像,传入顺序必须是 B、G、R,否则颜色会完全错乱。
  5. 应用场景:单独调整某一颜色分量、颜色识别、图像调色等,是所有颜色处理的基础操作。
import cv2 img = cv2.imread('test.jpg') # ========== 通道分离 ========== # 方法1:数组切片手动提取通道 b_channel = img[:, :, 0] # 所有行、所有列、第0个通道(蓝色) g_channel = img[:, :, 1] # 绿色通道 r_channel = img[:, :, 2] # 红色通道 # 方法2:split函数一次性分离,更简洁,返回顺序b, g, r b, g, r = cv2.split(img) # 单通道显示出来都是灰度效果,代表对应颜色的亮度 cv2.imshow("蓝色通道", b) cv2.imshow("绿色通道", g) cv2.imshow("红色通道", r) cv2.waitKey(0) cv2.destroyAllWindows() # ========== 通道合并 ========== # 按BGR顺序合并三个单通道,还原彩色图 merge_img = cv2.merge((b, g, r)) cv2.imshow("通道合并结果", merge_img) cv2.waitKey(0) cv2.destroyAllWindows()

八、Demo6:像素级修改 —— 马赛克打码与区域粘贴

核心知识点

  1. 像素级操作原理:图像是 numpy 数组,直接对数组切片赋值,就能批量修改对应区域的所有像素。
  2. 随机马赛克原理:用np.random.randint生成 0~255 范围内的随机整数数组,替换掉原图指定区域的像素,破坏原有图像信息,达到打码效果。
  3. 区域粘贴原理:把源图像的切片区域,直接赋值给目标图像的切片区域。硬性前提:源区域和目标区域的尺寸必须完全一致,否则会报形状不匹配错误。
  4. 注意:np.random.randint(0,256, size)是左闭右开区间,上限写 256 才能取到 255 这个像素最大值。
import cv2 import numpy as np # ========== 1. 随机噪声马赛克打码 ========== img = cv2.imread('test.jpg') # 给行100-200、列200-300的矩形区域填充随机像素 # 生成形状为(100,100,3)的随机数组,值范围0~255 img[100:200, 200:300] = np.random.randint(0, 256, (100, 100, 3)) cv2.imshow("马赛克打码效果", img) cv2.waitKey(0) cv2.destroyAllWindows() # ========== 2. 图片区域复制粘贴 ========== img1 = cv2.imread('test.jpg') img2 = cv2.imread('test2.jpg') # 将img1的局部区域,复制粘贴到img2的指定位置 # 两边区域尺寸必须完全一致:都是300行×300列 img2[200:500, 300:600] = img1[150:450, 100:400] cv2.imshow("原图1", img1) cv2.imshow("粘贴后的图2", img2) cv2.waitKey(0) cv2.destroyAllWindows()

九、综合实战作业:所有知识点串联

作业要求

  1. 读取彩色原图,统一缩放为 500×500 尺寸
  2. 截取左上角 ROI 区域:行范围 0:250,列范围 100:250
  3. 将截取的 ROI 精准粘贴到右下角:行 250:500,列 250:400
  4. 在图像右上角区域做随机噪声马赛克打码
  5. 将最终处理后的彩色图转成灰度图并保存到本地
  6. 同时显示处理后的彩色图和灰度图

思路拆解

用到的知识点依次是:图像读取 → resize 缩放 → 数组切片裁剪 → 区域粘贴赋值 → 随机像素马赛克 → 色彩空间转换 → 图像保存 → 多窗口显示。

import cv2 import numpy as np # 1. 读取原图,统一缩放到500×500(注意参数是宽,高) img = cv2.imread('bird.png') img = cv2.resize(img, (500, 500)) # 2. 截取左上角ROI区域,粘贴到右下角对应位置 # 截取:行0-250,列100-250,尺寸为250行×150列 roi = img[0:250, 100:250] # 粘贴:行250-500,列250-400,和截取区域尺寸完全一致 img[250:500, 250:400] = roi # 3. 右上角区域马赛克打码:行50-150,列350-450,100×100区域 img[50:150, 350:450] = np.random.randint(0, 256, (100, 100, 3)) # 4. 彩色图转灰度图并保存到本地 gray_result = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) cv2.imwrite('bird_gray_result.png', gray_result) # 5. 同时显示处理后的彩色图和灰度图 cv2.imshow("处理后彩色图", img) cv2.imshow("最终灰度图", gray_result) cv2.waitKey(0) cv2.destroyAllWindows()

十、入门核心知识点汇总