马赛克效果的本质是区域像素均值替换:将图像划分为整数网格块(如10×10),每块内所有像素统一替换为该块bgr均值;需手动遍历roi计算填充,避免resize插值导致的颜色偏移与边缘发虚。

马赛克效果的本质是区域像素均值替换
马赛克不是模糊,也不是降采样后放大,而是把图像划分成若干块(比如 10×10 像素),每一块内所有像素统一替换成该块的平均颜色。OpenCV 的 cv::resize 配合上采样能快速实现,但要注意:直接用 cv::resize 下采样再上采样会引入插值误差,导致颜色偏移;更稳妥的做法是手动遍历区块、计算均值、批量赋值。
常见错误是把“缩放后放大”当成马赛克——那样得到的是双线性插值结果,边缘发虚,且无法控制块大小粒度。真正的马赛克必须对齐整数网格,且每个块内部像素值完全一致。
用 OpenCV 手动实现块均值马赛克(C++)
核心逻辑:遍历每个马赛克单元左上角坐标,提取 ROI,算 BGR 均值,再用该均值填充整个 ROI。注意 ROI 必须是矩形且不越界。
-
block_size应为正整数,建议 ≥ 4,太小看不出效果,太大丢失结构 - 遍历时用
for (int i = 0; i ,不要用浮点除法四舍五入,否则块边界错位 - ROI 提取前必须检查边界:
cv::Rect roi(x, y, w, h)中w = std::min(block_size, cols - x),h = std::min(block_size, rows - y) - 均值计算用
cv::mean(roi_mat),返回cv::Scalar,注意 BGR 顺序(不是 RGB) - 填充用
roi_mat.setTo(mean_val),比循环赋值快一个数量级
cv::Mat mosaic(const cv::Mat& src, int block_size) {
cv::Mat dst = src.clone();
int rows = src.rows, cols = src.cols;
for (int y = 0; y <h3>性能关键:避免重复内存拷贝和 Mat 构造</h3><p>上面函数中 <code>src(roi)</code> 是只读视图,开销小;但 <code>dst(roi)</code> 是可写视图,也无额外分配。真正拖慢速度的是频繁调用 <code>cv::mean</code>——它内部做通道求和与除法,对大图很敏感。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><p>优化方向:</p>
- 若
block_size固定且较小(如 8 或 16),可预计算整张图的积分图(cv::integral),用 O(1) 得到任意 ROI 的像素和,再除以面积得均值 - 避免在循环内创建临时
cv::Mat对象;cv::mean返回cv::Scalar,直接传给setTo即可 - Release 模式编译 +
-O2或-O3后,上述朴素实现处理 1920×1080 图像在现代 CPU 上通常
彩色图 vs 灰度图:通道处理要一致
如果输入是灰度图(src.channels() == 1),cv::mean 返回单值 Scalar(val),setTo 会自动广播;但若强制转成三通道灰度再处理,反而增加冗余计算。最简做法是:保持原通道数,不做转换。
容易踩的坑:
- 误用
cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY)再马赛克——这丢掉了原始色彩信息,且灰度化本身有加权计算,后续均值已非原始 BGR 平均 - 对 HSV 图做马赛克时,直接对 H/S/V 分量取均值会导致色相跳跃(比如红色 0° 和黄色 30° 均值是 15°,但 0° 和 180° 均值应是 90°?实际需按圆周距离处理)——除非明确需求,否则坚持用 BGR 空间
- Alpha 通道(若有)需单独处理:检测
src.depth() == CV_8UC4,然后对前 3 通道均值填充,第 4 通道另算均值或直接复制
实际应用中,块大小选择比算法本身更重要:10–20 像素适合人脸局部遮挡,50+ 像素适合快速示意性模糊;而亚像素级或非整数块尺寸,本质上已不属于传统马赛克范畴。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










