OpenCV 图像金字塔教程:在 Web 端用 cv.pyrDown() 与 cv.pyrUp() 做多尺度处理
2026/9/5 16:00:37 网站建设 项目流程

OpenCV 图像金字塔教程:在 Web 端用 cv.pyrDown() 与 cv.pyrUp() 做多尺度处理

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

本篇围绕 OpenCV 官方 JS 教程中的“Image Pyramids(图像金字塔)”一文展开:先讲清图像金字塔为何存在、高斯金字塔与拉普拉斯金字塔的构造原理,再给出浏览器环境中cv.pyrDown()(降采样)与cv.pyrUp()(升采样)的完整参数说明和可运行的 JavaScript 代码,并结合 OpenCV 仓库中modules/imgproc/src/pyramids.cpp的源码实现,剖析 5×5 高斯核、尺寸约束、支持的像素类型与后端加速路径,帮助你把多尺度图像处理的原理与实战一次打通。

为什么需要图像金字塔

在常规图像处理中,我们通常使用固定尺寸的一幅图像工作。但在某些场景下,需要对同一幅图像以不同分辨率处理:最典型的例子是目标检测——当在图像中搜索人脸等目标时,你无法预知目标会以多大的尺度出现。解决办法是构造一组同一图像、不同分辨率的图像集合,并在每一层上分别搜索。这个集合就是图像金字塔(Image Pyramids):若把最高分辨率的图像放在底部、最低分辨率的放在顶部堆叠起来,整体形状正像一座金字塔。

金字塔分为两种:

  1. 高斯金字塔(Gaussian Pyramid)——通过“先平滑、再抽取偶数行列”逐层缩小;
  2. 拉普拉斯金字塔(Laplacian Pyramid)——由相邻两层高斯金字塔的差值构成,形似边缘图。

高斯金字塔的构造

高斯金字塔中,上一层(低分辨率)由下一层(高分辨率)经过“删除相邻行/列”的方式生成;上一层的每个像素由下一层 5 个像素按高斯权重贡献而成。这样一幅 M×N 的图像会变成 M/2×N/2,面积缩小为原来的四分之一——这样一次尺寸减半称为一个Octave(倍频程)。向上每升一层分辨率继续减半,向下(扩展方向)则每层面积变为 4 倍。高斯金字塔正是通过cv.pyrDown()cv.pyrUp()两个函数获得。

拉普拉斯金字塔的构造

拉普拉斯金字塔从高斯金字塔推导而来,OpenCV 没有为其提供专门的独立函数。拉普拉斯金字塔的每一层图像近似一张“只有边缘”的图:大多数元素为零,因此常用于图像压缩。某一层拉普拉斯金字塔 = 该层的高斯金字塔图像 − 其上层高斯金字塔图像经扩展(pyrUp)后的版本。也就是说,你只需组合pyrDownpyrUp加一次逐像素减法即可手工构造。

降采样:cv.pyrDown()

函数签名与参数

在 JS 环境中,函数原型为:

cv.pyrDown(src, dst, dstsize = new cv.Size(0, 0), borderType = cv.BORDER_DEFAULT)

参数说明(与 C++ 接口cv::pyrDown完全对应):

参数说明
src输入图像
dst输出图像;具有指定大小,类型与src相同
dstsize输出图像大小;默认(0,0)表示自动计算为Size((src.cols+1)/2, (src.rows+1)/2)
borderType像素外推方式,见cv.BorderTypes不支持cv.BORDER_CONSTANT

dstsize并非可以任意指定。无论取何值,都必须满足约束:

  • |dstsize.width * 2 - src.cols| <= 2
  • |dstsize.height * 2 - src.rows| <= 2

即输出尺寸必须与“源尺寸的一半”相差不超过 1 个像素,否则底层实现会直接触发断言失败。

JS 代码示例

以下代码取自教程配套的在线示例页面 js_pyramids_pyrDown.html。该页面已准备好canvasInput(默认加载 lena.jpg,也可通过文件选择器换图)和canvasOutput两个<canvas>元素,点击 “Try it” 按钮即可执行,textarea 中的代码也可以自由修改实验:

let src = cv.imread('canvasInput'); let dst = new cv.Mat(); // You can try more different parameters cv.pyrDown(src, dst, new cv.Size(0, 0), cv.BORDER_DEFAULT); cv.imshow('canvasOutput', dst); src.delete(); dst.delete();

注意两个内存管理细节:cv.imreadnew cv.Mat()在堆上分配,使用完毕后必须delete()释放,避免 WebAssembly 堆泄漏。

底层实现:5×5 高斯核与“抽取偶数行列”

结合 modules/imgproc/include/opencv2/imgproc.hpp 中pyrDown的接口文档与 modules/imgproc/src/pyramids.cpp 的实现可以看到:pyrDown执行的是高斯金字塔构造中的降采样步骤——先与一个 5×5 高斯核做卷积:

1/256 * [ 1 4 6 4 1 4 16 24 16 4 6 24 36 24 6 4 16 24 16 4 1 4 6 4 1 ]

然后丢弃偶数行和偶数列完成下采样。源码中pyrDown_模板函数(约 L880 起)用环形缓冲区先做水平方向卷积+隔列抽取,再做垂直方向卷积+隔行抽取,并按通道数 1/2/3/4 分别走标量或向量化(SSE/NEON)路径,外层还通过cv::parallel_for_按行范围多线程并行(cv::PyrDownInvoker)。

入口函数cv::pyrDown(L1266 起)还揭示了几个实用事实:

  • 首先CV_Assert(borderType != BORDER_CONSTANT),这就是“不支持 BORDER_CONSTANT”的原因——常量填充会破坏高斯卷积的对称性,接口文档因此明确排除该模式;
  • 默认dstsize为空时计算为Size((src.cols+1)/2, (src.rows+1)/2),即向上取整,所以 400×400 的输入会得到 200×200,而 401×400 会得到 201×200;
  • 支持的深度为CV_8U / CV_16S / CV_16U / CV_32F / CV_64F,其他类型会抛出StsUnsupportedFormat错误;
  • 若输出为 UMat 且设备支持,会优先走 OpenCL 后端ocl_pyrDownpyrDownkernel);同时预留了 HAL 扩展点(cv_hal_pyrdown)与 IPP 加速路径。

升采样:cv.pyrUp()

函数签名与参数

JS 环境中函数原型为:

cv.pyrUp(src, dst, dstsize = new cv.Size(0, 0), borderType = cv.BORDER_DEFAULT)
参数说明
src输入图像
dst输出图像;具有指定大小,类型与src相同
dstsize输出图像大小;默认(0,0)表示自动计算为Size(src.cols*2, src.rows*2)
borderType像素外推方式;仅支持cv.BORDER_DEFAULT

dstsize的约束条件是:

  • |dstsize.width - src.cols*2| <= (dstsize.width mod 2)
  • |dstsize.height - src.rows*2| <= (dstsize.height mod 2)

即输出尺寸可以比“源尺寸的两倍”多出 0 或 1 个像素(奇数尺寸场景)。

JS 代码示例

以下代码同样取自教程配套的在线示例页面 js_pyramids_pyrUp.html,页面结构与 pyrDown 示例一致(canvasInput/canvasOutput+ 可编辑代码区):

let src = cv.imread('canvasInput'); let dst = new cv.Mat(); // You can try more different parameters cv.pyrUp(src, dst, new cv.Size(0, 0), cv.BORDER_DEFAULT); cv.imshow('canvasOutput', dst); src.delete(); dst.delete();

可以看到升采样后的图像在视觉上更“软”:因为它是零填充+模糊的结果,并不包含新的高频细节,只适合做金字塔层级间的过渡,而非真正意义上的超分辨率放大。

底层实现:注入零行零列,再用同一核×4 卷积

从接口文档与pyrUp_模板函数(modules/imgproc/src/pyramids.cpp L1038 起)可以看出,pyrUp执行高斯金字塔构造的升采样步骤,但也可以用来构造拉普拉斯金字塔:先把源图像注入偶数的零行和零列(即两像素之间插入一个零像素,图像面积变为 4 倍),再与pyrDown相同的 5×5 高斯核相乘 4 后卷积。源码中可见src[x]*6 + src[x+cn]*2(src[x] + src[x+cn])*4这类系数组合,正是该核经 4 倍放大并简化后的水平/垂直分解卷积系数(PU_SZ=3 的行缓存只保留必要的 3 行邻域)。

入口函数cv::pyrUp(L1374 起)的事实要点:

  • CV_Assert(borderType == BORDER_DEFAULT)——升采样只接受默认边界模式(实现内部实际按BORDER_REFLECT_101语义处理,见 L1077 的borderInterpolate调用),传入其他模式会直接断言失败;
  • 默认输出尺寸为Size(src.cols*2, src.rows*2),比pyrDown的向上取整更直接——pyrDown 是 5×5 卷积所以奇数尺寸要进位,而 pyrUp 是 2 倍拉伸,奇数目标尺寸允许相差 1 像素;
  • 支持的深度同样是CV_8U / CV_16S / CV_16U / CV_32F / CV_64F,固定点类型用FixPtCast、浮点类型用FltCast完成卷积中间值的溢出安全转换;
  • 编译启用 IPP 时(HAVE_IPP且 IPP >= 8.1),对 8UC1/8UC3/32FC1/32FC3 的默认情形会优先调用ippiPyrUp_Gauss5x5_*硬件优化例程;UMat 场景同样有ocl_pyrUp的 OpenCL 路径(含pyrUp_cols2pyrUp_unrolled两个 kernel)。

相关接口与配套示例

  • buildPyramid:modules/imgproc/include/opencv2/imgproc.hpp 中定义了cv::buildPyramid(src, dst, maxlevel, borderType = BORDER_DEFAULT),它把dst[0] == src之后递归地逐层应用pyrDown,一次性构造出maxlevel + 1层的高斯金字塔向量,适合需要整组金字塔层级的批量场景。
  • C++ 交互示例:仓库提供了同一套函数的 C++ 演示程序 samples/cpp/tutorial_code/ImgProc/Pyramids/Pyramids.cpp,加载图像后按i键调用pyrUp(src, src, Size(src.cols*2, src.rows*2))放大、按o键调用pyrDown(src, src, Size(src.cols/2, src.rows/2))缩小,是“图像变焦”玩法的最小实现。注意其中对pyrDown传入了整数除法得到的src.cols/2, src.rows/2——只有满足上文尺寸约束(奇数尺寸向下取整)时才合法。
  • 性能测试modules/imgproc/perf/perf_pyramids.cpppyrDownpyrUp覆盖多种尺寸与像素类型组合的基准测试,其中DISABLED_pyrDown_ovx用例验证了pyrDown(src, dst, cv::Size(), BORDER_REPLICATE)这一“默认尺寸 + 非默认边界”的组合。

小结

函数作用默认输出尺寸边界模式核心算法
cv.pyrDown()模糊 + 降采样(金字塔上行一层)(src.cols+1)/2, (src.rows+1)/2任意,但BORDER_CONSTANT不支持1/256 归一 5×5 高斯卷积后抽取偶数行列
cv.pyrUp()升采样 + 模糊(金字塔下行一层)src.cols*2, src.rows*2BORDER_DEFAULT注入零行零列后以同核×4 卷积

掌握这两个函数后,你已经在 Web 端具备了完整的多尺度处理工具箱:用pyrDown循环可搭出高斯金字塔用于多尺度检测;用“本层高斯层 − pyrUp(上层)”的差值可手工搭出拉普拉斯金字塔用于图像融合与压缩类任务。参数细节以本仓库 modules/imgproc/include/opencv2/imgproc.hpp 的接口文档为准,行为差异可通过 doc/js_tutorials/js_assets/js_pyramids_pyrDown.html 与 doc/js_tutorials/js_assets/js_pyramids_pyrUp.html 两个在线示例边改边验。

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

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

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

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

立即咨询