OpenCV Python 图像基本操作详解:像素访问、图像属性、ROI、通道拆合与 copyMakeBorder 边界填充
2026/9/7 15:23:33 网站建设 项目流程

OpenCV Python 图像基本操作详解:像素访问、图像属性、ROI、通道拆合与 copyMakeBorder 边界填充

【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv

本文以 OpenCV 官方 Python 教程“Basic Operations on Images”为主体,系统讲解图像像素的读写、图像属性(shape/size/dtype)的读取、感兴趣区域(ROI)的选取、BGR 通道的拆分与合并,以及cv.copyMakeBorder()的五种边界类型。读完后你将能够熟练使用 NumPy 索引与 OpenCV API 完成日常图像处理的基本操作,并理解这些操作在 OpenCV C++ 源码中的真实实现依据。

教程目标与前提

该教程(py_basic_ops.markdown)要求读者学会四件事:

  • 访问并修改像素值(Access pixel values and modify them)
  • 访问图像属性(Access image properties)
  • 设置感兴趣区域 ROI(Region of Interest)
  • 拆分与合并图像通道(Split and merge images)

教程明确指出:本节几乎所有操作本质上都是 NumPy 数组操作,而非 OpenCV 专属功能。“Almost all the operations in this section are mainly related to Numpy rather than OpenCV. A good knowledge of Numpy is required to write better optimized code with OpenCV.”——扎实的 NumPy 功底是写出高效 OpenCV Python 代码的前提。示例均以 Python 交互终端(单行命令)形式给出。

访问与修改像素值

先加载一张彩色图像:

>>> import numpy as np >>> import cv2 as cv >>> img = cv.imread('messi5.jpg') >>> assert img is not None, "file could not be read, check with os.path.exists()"

cv.imread()读取成功后,图像就是一个 NumPy 数组。可以用行列坐标直接访问像素值:对 BGR 彩色图像,返回一个包含蓝、绿、红三个分量的一维数组;对灰度图像,则只返回对应的灰度强度值:

>>> px = img[100,100] >>> print( px ) [157 166 200] # accessing only blue pixel >>> blue = img[100,100,0] >>> print( blue ) 157

注意 OpenCV 与 NumPy 的索引顺序是img[行, 列, 通道],即[y, x, c],这与某些库的[x, y, c]习惯不同。img[100,100,0]取的是第 100 行、第 100 列像素的蓝色分量(BGR 顺序中第 0 通道)。

以完全相同的方式可以修改像素值:

>>> img[100,100] = [255,255,255] >>> print( img[100,100] ) [255 255 255]

性能警告(原文强调):NumPy 是面向快速数组运算优化的库,逐像素地访问和修改每个像素会非常慢,教程明确不推荐("very slow and it is discouraged")。批量操作应使用切片、广播等向量化手段,见下文 ROI 与通道处理部分。

访问图像属性

图像属性包括行数、列数、通道数、数据类型、总像素数等。

shapeimg.shape返回一个元组(行数, 列数, 通道数),通道数仅在彩色图像时出现:

>>> print( img.shape ) (342, 548, 3)

注:如果图像是灰度图,返回的元组只有行数和列数两项(如(342, 548))。因此shape的维度数是判断图像是灰度还是彩色的简便方法。

size:总像素数通过img.size获得(行 × 列 × 通道):

>>> print( img.size ) 562248

dtype:图像数据类型通过img.dtype获得:

>>> print( img.dtype ) uint8

注:img.dtype在调试中非常重要——OpenCV-Python 代码中出现的大量错误源于数据类型无效(例如把 float 型矩阵传给了只接受uint8的函数)。

图像 ROI(感兴趣区域)

实际应用中经常需要只处理图像的某个区域。典型场景是人脸/眼睛检测:先对整幅图做人脸检测,得到人脸后,只选取人脸区域在其中搜索眼睛,而不用搜索整幅图。这既提高了准确率(眼睛总是在脸上),也提升了性能(搜索范围更小)。

ROI 同样通过 NumPy 索引获得。下面的示例选取图中的“足球”区域,并把它复制到图像中的另一个位置(先切片取出,再赋值过去):

>>> ball = img[280:340, 330:390] >>> img[273:333, 100:160] = ball

第一行用切片img[280:340, 330:390]取出 60×60 的球区域;第二行将其赋值到img[273:333, 100:160]指定的目标区域,完成一次“区域复制”。

从底层看,这种切片赋值依赖 NumPy 视图(view)语义:ball与原图共享内存,对它的修改会反映到img上——这既是方便之处,也意味着赋值方向(img[...] = ball)与预期一致时才能安全使用。

拆分与合并图像通道

有时需要单独处理 B、G、R 中的某一个通道(例如只操作红色),此时需要将 BGR 图像拆分成单通道;反之也常需要把独立通道合并回 BGR 图像。

方式一:使用 OpenCV 的 split / merge

>>> b,g,r = cv.split(img) >>> img = cv.merge((b,g,r))

方式二:直接使用 NumPy 索引取单通道

>>> b = img[:,:,0]

性能提示:如果你的目标只是修改某一个通道,根本不需要先拆分。例如要把所有红色像素置零,直接用 NumPy 索引更快:

>>> img[:,:,2] = 0

警告(原文强调):cv.split()是耗时操作,仅在确实需要独立通道对象(例如对单个通道做滤波、统计)时才使用;否则优先用 NumPy 索引。

为图像创建边界:cv.copyMakeBorder()

若要给图像加一圈“相框”式的边界,可以使用cv.copyMakeBorder()。但它在工程中有更广泛的应用:卷积操作的边界扩展、零填充(zero padding)等——从源码结构看,它本身就是 OpenCV 内部许多算法的基础设施,例如模板匹配在补齐模板块时调用它(templmatch.cpp 第 201 行附近),CLAHE 用BORDER_REFLECT_101扩展图像到 tile 边界(clahe.cpp 第 381 行),warpAffineBORDER_WRAP处理角点(imgwarp.cpp 第 3130 行),轮廓检测与泛洪填充则用BORDER_CONSTANT | BORDER_ISOLATED隔离 ROI 边界(contours_new.cpp 第 703 行、floodfill.cpp 第 510 行)。

函数参数

copyMakeBorder()的完整签名(见 core.hpp):

void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, const Scalar& value = Scalar() );

参数说明:

  • src— 输入图像
  • top, bottom, left, right— 上、下、左、右四个方向各扩展的像素数
  • borderType— 边界类型标志
  • value— 当 borderType 为cv.BORDER_CONSTANT时的边界颜色

输出图像dst的尺寸为Size(src.cols+left+right, src.rows+top+bottom),与 src 类型相同。

边界类型 BorderTypes

教程中列出的五种边界类型,与 OpenCV 源码中的枚举定义一一对应,枚举及各类型的 ASCII 示意图在 base.hpp:

enum BorderTypes { BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i` BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh` BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb` BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg` BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba` ... BORDER_DEFAULT = BORDER_REFLECT_101 };

各类型含义(|为原图边界):

  • cv.BORDER_CONSTANT— 用指定常量色填充边界(颜色由value参数给出),形如iiiiii|abcdefgh|iiiiiii
  • cv.BORDER_REPLICATE— 沿边缘无限复制最后一个像素,形如aaaaaa|abcdefgh|hhhhhhh
  • cv.BORDER_REFLECT— 边界元素的镜面反射,形如fedcba|abcdefgh|hgfedcb
  • cv.BORDER_REFLECT_101(即cv.BORDER_DEFAULT,默认值)— 与 REFLECT 类似但略有不同,形如gfedcb|abcdefgh|gfedcba
  • cv.BORDER_WRAP— 环绕式边界,形如cdefgh|abcdefgh|abcdefg

另外源码枚举中还定义了BORDER_TRANSPARENT(视越界像素为透明,形如uvwxyz|abcdefgh|ijklmno)和BORDER_ISOLATED(标志位 16,插值/外推被限制在 ROI 边界内——即上文的 note:当 src 本身是更大图像的一个 ROI 时,copyMakeBorder默认会尝试利用 ROI 外部的真实像素来构造边界,加上BORDER_ISOLATED可禁用该行为)。

完整示例代码

下面示例对同一图像分别生成五种类型的边界并用 matplotlib 展示(边界宽度均为 10 像素):

import cv2 as cv import numpy as np from matplotlib import pyplot as plt BLUE = [255,0,0] img1 = cv.imread('opencv-logo.png') assert img1 is not None, "file could not be read, check with os.path.exists()" replicate = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REPLICATE) reflect = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT) reflect101 = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT_101) wrap = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_WRAP) constant= cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_CONSTANT,value=BLUE) plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL') plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE') plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT') plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101') plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP') plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT') plt.show()

注意两点:

  1. 示例中value=BLUE[255,0,0]——因为 OpenCV 读取的图像是 BGR 顺序,[255,0,0]表示的是蓝色(虽然变量名沿用教程习惯)。
  2. 结果图由 matplotlib 显示,matplotlib 使用 RGB 顺序,因此红色与蓝色通道会互换,直接imshowBGR 图像时颜色可能“看起来反了”。

小结与工程建议

结合本教程与源码可以归纳出以下实践准则:

  • 优先 NumPy 索引,慎用 cv.split/cv.merge:单通道读取img[:,:,0]、单通道写入img[:,:,2]=0都更快;cv.split()开销大,仅在需要独立通道对象时使用。
  • 避免 Python 级逐像素循环:像素读写用切片/广播完成,性能差异显著。
  • shape 三元组判断彩色/灰度(rows, cols)为灰度,(rows, cols, 3)为彩色。
  • dtype 是首要调试对象:OpenCV-Python 大量异常源于数据类型不匹配,报错时先检查img.dtype
  • copyMakeBorder 是通用垫底工具:边界宽度四参数 + 五种(及以上)borderType,既可用作视觉“相框”,也是卷积、模板匹配、CLAHE 等算法内部零填充/边界外推的标准手段;需要隔离 ROI 外部像素时记得BORDER_ISOLATED

延伸阅读:同系列教程中的图像算术运算 py_image_arithmetics.markdown 与优化 py_optimization.markdown,以及borderInterpolatecopyMakeBorder文档中的@sa关联函数,用于单像素边界外推)在 core.hpp 中的声明。

【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询