ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenCV 图像算术运算实战:cv.add、cv.addWeighted 与位运算的完整解析

OpenCV 图像算术运算实战:cv.add、cv.addWeighted 与位运算的完整解析 OpenCV 图像算术运算实战cv.add、cv.addWeighted 与位运算的完整解析【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv本文围绕 OpenCV 的图像算术运算展开讲解图像加法、加权融合图像混合与位运算AND/OR/XOR/NOT三大类操作的实际用法剖析 OpenCV 加法与 NumPy 加法的饱和/取模差异并结合 modules/core/src/arithm.cpp 中的源码实现说明底层调用链与饱和运算的原理。读完本文你可以独立完成图像叠加、平滑过渡动画、非矩形 Logo 贴图等常见任务并理解每个函数在 OpenCV 内部的执行路径。1. 图像加法cv.add 与 NumPy 加法的本质区别你可以用 OpenCV 的cv.add()对两张图像做加法也可以直接用 NumPy 的res img1 img2。两张图像必须具有相同的深度depth和类型或者第二个操作数只是一个标量scalar。关键差异在于OpenCV 加法是饱和saturated运算而 NumPy 加法是取模modulo运算。看下面这个经典示例 x np.uint8([250]) y np.uint8([10]) print( cv.add(x,y) ) # 25010 260 255 [[255]] print( xy ) # 25010 260 % 256 4 [4]cv.add(x, y)250 10 260超出uint8上限 255被钳制clamp为 255x y260 对 256 取模得到 4——这是一个完全不符合直觉的错误结果。这个差异在整幅图像上叠加时会非常明显用 NumPy 直接相加超出 255 的像素会“回绕”成暗色产生大面积错误。因此在图像处理中应始终使用 OpenCV 函数它能给出符合预期的饱和结果。1.1 源码视角cv.add 是如何执行饱和加法的在 modules/core/src/arithm.cpp 中cv::add的定义非常简洁void cv::add( InputArray src1, InputArray src2, OutputArray dst, InputArray mask, int dtype ) { CV_INSTRUMENT_REGION(); arithm_op(ew::OP_ADD, src1, src2, dst, mask, dtype, OCL_OP_ADD, false); }它统一收敛到arithm_op()这个入口函数见 arithm_op 定义。该函数是所有逐元素二元运算add/sub/mul/div/min/max/absdiff/addWeighted 等的统一分发点处理逻辑包括空输入处理两个输入都为空时直接输出空结果UMat/OpenCL 路径任一操作数是UMat时优先尝试 OpenCL 内核OCL_OP_ADD设备不支持如维度 2、通道数 4 且带 mask、无双精度支持等时自动回退到 CPU 引擎CPU 引擎路径调用按平台分发的内核见 arithm.dispatch.cpp 与 arithm.simd.hpp由arithm.dispatch.cpp根据编译时启用的 SIMD 指令集SSE/AVX/NEON/RVV 等选择最优实现。饱和行为由引擎层保证uint8类型的加法在 SIMD 指令层面就是饱和加法如 x86 的paddusb、ARM 的uqadd8所以饱和不是事后补救而是内核的固有语义。此外cv::add还支持可选的mask参数与dtype深度转换参数Python 端签名对应cv.add(src1, src2[, dst[, mask[, dtype]]])。相关测试与性能用例可参考功能测试modules/core/test/test_arithm.cpp性能测试modules/core/perf/perf_new_arithm.cpp2. 图像融合Blendingcv.addWeighted 与平滑过渡融合本质上也是图像加法只是给两张图像分配不同的权重从而产生混合或透明感。图像按以下公式相加g(x) (1 - α)·f0(x) α·f1(x)让 α 从 0 变化到 1就能在两张图像之间实现平滑的过渡效果。cv.addWeighted()对图像施加的通用公式为dst α·img1 β·img2 γ下面示例中第一张图像权重为 0.7第二张为 0.3γ 取 0img1 cv.imread(ml.png) img2 cv.imread(opencv-logo.png) assert img1 is not None, file could not be read, check with os.path.exists() assert img2 is not None, file could not be read, check with os.path.exists() dst cv.addWeighted(img1, 0.7, img2, 0.3, 0) cv.imshow(dst, dst) cv.waitKey(0) cv.destroyAllWindows()图中两张示例素材在仓库内可以直接找到samples/data/ml.png 与 samples/data/opencv-logo.png配合 samples/data/opencv-logo-white.png 与 samples/data/messi5.jpg 即可完成全部教程实验。2.1 源码视角α、β、γ 如何传入计算内核modules/core/src/arithm.cpp 中cv::addWeighted的实现void cv::addWeighted( InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype ) { CV_INSTRUMENT_REGION(); arithm_op(ew::OP_ADDW, src1, src2, dst, noArray(), dtype, OCL_OP_ADDW, /*muldiv*/ true, Scalar(alpha, beta, gamma)); }可以看到alpha、beta、gamma被打包成一个Scalar传递给统一的arithm_op引擎最终作为参数传入对应的 CPU SIMD 内核或 OpenCL 内核OCL_OP_ADDW。由于运算带有标量参数γ结果同样经过饱和处理中间计算精度由引擎的强制工作类型coerced working type保证。性能基准见 modules/core/perf/perf_addWeighted.cpp。2.2 实战延伸用 cv.addWeighted 制作平滑过渡的幻灯片教程末尾的练习题是“使用cv.addWeighted创建一个带有平滑过渡效果的文件夹图像幻灯片”。最小可运行实现如下import cv2 as cv import numpy as np import glob, os files sorted(glob.glob(slides/*.jpg)) img1 cv.imread(files[0]) while True: img2 cv.imread(files[(files.index(img_file)1) % len(files)]) if False else None # 对当前两张图像执行 N 步渐变 for i in range(1, 31): alpha i / 30.0 frame cv.addWeighted(img_prev, 1 - alpha, img_next, alpha, 0) cv.imshow(slideshow, frame) if cv.waitKey(30) 0xFF 27: break # 切换当前图像对 ...略更新 img_prev/img_next要点α 以固定小步长如 1/30递增每步约 30mswaitKey(30)即可得到肉眼平滑的淡入淡出Esc键值 27用于退出。这正是“α 从 0 到 1 变化”公式的直接应用。3. 位运算提取区域、定义非矩形 ROI 与不透明贴图位运算包括 AND、OR、NOT、XOR在提取图像的某一部分如后文特征点检测章节、定义并操作非矩形 ROI感兴趣区域时非常有用。典型场景把 OpenCV 白色 Logo 贴到一张照片的左上角。直接相加 → 颜色被破坏融合addWeighted→ 半透明效果但我们要不透明opaque如果是矩形区域可以直接用上一章讲过的 ROI 赋值但 Logo 不是矩形因此需要位运算。完整代码素材 messi5.jpg 与 opencv-logo-white.png 位于仓库samples/data/下# Load two images img1 cv.imread(messi5.jpg) img2 cv.imread(opencv-logo-white.png) assert img1 is not None, file could not be read, check with os.path.exists() assert img2 is not None, file could not be read, check with os.path.exists() # I want to put logo on top-left corner, So I create a ROI rows, cols, channels img2.shape roi img1[0:rows, 0:cols] # Now create a mask of logo and create its inverse mask also img2gray cv.cvtColor(img2, cv.COLOR_BGR2GRAY) ret, mask cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY) mask_inv cv.bitwise_not(mask) # Now black-out the area of logo in ROI img1_bg cv.bitwise_and(roi, roi, maskmask_inv) # Take only region of logo from logo image. img2_fg cv.bitwise_and(img2, img2, maskmask) # Put logo in ROI and modify the main image dst cv.add(img1_bg, img2_fg) img1[0:rows, 0:cols] dst cv.imshow(res, img1) cv.waitKey(0) cv.destroyAllWindows()3.1 逐步拆解这段代码的位运算逻辑创建 ROIroi img1[0:rows, 0:cols]这是 NumPy 切片得到指向img1内存的视图而非拷贝后续对它的修改会直接影响img1生成掩码把彩色 Logo 转灰度用cv.threshold(..., 10, 255, cv.THRESH_BINARY)做二值化——白色像素Logo 本体灰度值 10变为 255透明/黑色背景变为 0得到单通道mask生成反相掩码mask_inv cv.bitwise_not(mask)即“Logo 以外”的区域抠出背景img1_bg cv.bitwise_and(roi, roi, maskmask_inv)——bitwise_and中mask参数指定哪些位置参与运算、哪些位置保持为 0结果就是“Logo 覆盖范围内被抹黑”的照片区域抠出前景img2_fg cv.bitwise_and(img2, img2, maskmask)——只保留 Logo 本体像素合成dst cv.add(img1_bg, img2_fg)。由于背景区域与前景区域在空间上互斥一个 mask 与一个 mask_inv逐像素相加不会产生二次叠加实现“硬边”不透明贴图最后把dst写回img1的对应 ROI。调试建议分别imshow代码中的mask、mask_inv、img1_bg、img2_fg四个中间结果能非常直观地理解位运算“按掩码取/舍”的机制。3.2 四个位运算函数一览函数语义典型用途cv.bitwise_and(src1, src2[, dst[, mask]])逐位 AND按掩码提取/保留区域本教程核心cv.bitwise_or(src1, src2[, dst[, mask]])逐位 OR合并多个掩码、叠加多个区域cv.bitwise_xor(src1, src2[, dst[, mask]])逐位 XOR对称差取两掩码不重合部分图像特效cv.bitwise_not(src[, dst[, mask]])逐位 NOT反相掩码mask_inv即由此生成其中mask参数在所有bitwise_*函数中均为可选非零位置参与运算零位置结果置 0。这些函数与cv::add一样走arithm_op统一引擎见 oclop2str 表 中列出的OP_AND/OP_OR/OP_XOR/OP_NOT同样支持 UMat/OpenCL 加速路径。公开接口声明位于 modules/core/include/opencv2/core.hpp。4. 小结与练习图像加法用cv.add饱和不要用img1 img2取模回绕会产生错误暗色图像融合用cv.addWeighted(img1, α, img2, β, γ)调整 α 即可实现平滑过渡与幻灯片淡入淡出非矩形区域贴附/提取用bitwise_andmask/mask_inv的组合本质是“按掩码取前景 按反掩码留背景 饱和加法合成”以上函数底层均汇聚到 modules/core/src/arithm.cpp 的arithm_op统一引擎支持 SIMD 加速与 OpenCL 加速是 OpenCV 逐元素运算的核心路径。练习在一个图像文件夹中创建幻灯片使用cv.addWeighted实现图像间的平滑过渡实现思路见第 2.2 节。【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进