一个简单的马赛克例子

本文详细介绍了马赛克算法的工作原理,通过C++代码实现了不同尺寸晶体小方格的马赛克效果,包括2x2到32x32。随着晶体尺寸增大,马赛克效果逐渐明显,直至无法辨认图像细节。提供了代码示例及不同尺寸的马赛克效果对比,便于理解与实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看一张正常的图像,图像尺寸为640x558
在这里插入图片描述
人脸部分打赏马赛克,效果如下:

在这里插入图片描述
可以看出,马赛克为一个个的晶体小方格,每个小方格的各个像素颜色是一样的。

现在简单说明下,马赛克的原理。
我们列举一些数字,共5行8列,40个数字。
1 2 3 4 5 6 7 8
9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
25 26 27 28 29 30 31 32
33 34 35 36 37 38 39 40

我们不妨将这40个数字看成40个像素,马赛克就是将这些像素相邻的像素进行归一化,取这些相邻数字的平均数,然后用这个平均数替代原来的数。

比如相邻定义为2x2,即相邻的4个进行归一化,则1,2,9,10为一组;3,4,11,12为一组;17,18,25,26为一组,33,34为一组。
其中第一组1,2,9,10的和为22,取平均数5(22/4),然后将这四个数字都替换成5;经过这样的处理后,
原来的数字变成如下模式:
5 7 9 11
21 23 25 27
33 35 37 39

打马赛克就是选定区域,然后将这块区域划分若干个晶体小方格,每个晶体小方格里面的rgb三原色取平均值。

本文采取开源库cximage进行图片像素的处理。
代码不多,直接粘贴出来,其中iGridLength为晶体小方格的尺寸,iGridLength为2时,表示2x2,为4表示4x4。
注意:cximage读取jpg像素时,像素数据顺序都是从最后一行到第一行,从左到右。

// CxImageTest.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>

#include "cximage/ximage.h"

std::wstring string2wstring(const std::string& str)
{
	std::wstring result;
	//获取缓冲区大小,并申请空间,缓冲区大小按字符计算  
	int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
	TCHAR* buffer = new TCHAR[len + 1];
	//多字节编码转换成宽字节编码  
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);
	buffer[len] = '\0';             //添加字符串结尾  
	//删除缓冲区并返回值  
	result.append(buffer);
	delete[] buffer;
	return result;
}

int main()
{
	CxImage image(CXIMAGE_FORMAT_JPG);
	image.Load(L"E:\\learn\\c++\\cximage\\CxImageTest\\x64\\Release\\qiushuzhen_640x558.jpeg", CXIMAGE_FORMAT_JPG);

	int iJpgWidth = image.GetWidth();
	int iJpgHeight = image.GetHeight();

	long x1 = 0;
	long y1 = 0;
	image.GetOffset(&x1, &y1);

	const int iBeginX = 256;
	const int iBeginY = 96;

	const int iEndX = 424;
	const int iEndY = 280;

	int iRSum = 0;
	int iGSum = 0;
	int iBSum = 0;

	int iAverageR = 0;
	int iAverageG = 0;
	int iAverageB = 0;

	int iGridLength = 2;

	for (int x = iBeginX; x < iEndX; x = x + iGridLength)
	{
		for (int y = iBeginY; y < iEndY; y = y + iGridLength)
		{
			int iPartGridXNum = 0;
			int iPartGridYNum = 0;
			for (int iSpanX = 0; iSpanX < iGridLength; iSpanX++)
			{
				if (x + iSpanX < iEndX)
				{
					iPartGridXNum++;
				}
			}
			for (int iSpanY = 0; iSpanY < iGridLength; iSpanY++)
			{
				if (y + iSpanY < iEndY)
				{
					iPartGridYNum++;
				}
			}

			iRSum = 0;
			iGSum = 0;
			iBSum = 0;

			int iPartGridNum = iPartGridXNum * iPartGridYNum;
			for (int i = x; i < x + iPartGridXNum; i++)
			{
				for (int j = y; j < y + iPartGridYNum; j++)
				{
					RGBQUAD rgb = image.GetPixelColor(i, (iJpgHeight - 1) - j, false);
					iRSum += rgb.rgbRed;
					iGSum += rgb.rgbGreen;
					iBSum += rgb.rgbBlue;
				}
			}


			iAverageR = iRSum / iPartGridNum;
			iAverageG = iGSum / iPartGridNum;
			iAverageB = iBSum / iPartGridNum;
			COLORREF color = COLORREF(RGB(iAverageR, iAverageG, iAverageB));

			for (int i = x; i < x + iPartGridXNum; i++)
			{
				for (int j = y; j < y + iPartGridYNum; j++)
				{
					image.SetPixelColor(i, (iJpgHeight - 1) - j, color);

				}
			}
		}
	}

	std::string strSavePath = "E:\\learn\\c++\\cximage\\CxImageTest\\x64\\Release\\";
	char szPicName[100] = { 0 };
	sprintf(szPicName, "qiushuzhen_640x558_msk_%dx%d.jpeg", iGridLength, iGridLength);
	strSavePath = strSavePath + szPicName;

	std::wstring strwSavePath = string2wstring(strSavePath);

	image.Save(strwSavePath.c_str(), CXIMAGE_FORMAT_JPG);
	return 0;
}





本人尝试了2x2,4x4,8x8,16x16,32x32五种晶体小方格进行打马赛克,其中32x32就是本文前面部分的马赛克尺寸。
2x2的效果如下:
在这里插入图片描述
可以看出2x2情况下,完全看不出打了马赛克。

4x4的效果如下:
在这里插入图片描述
可以看出4x4已经有点马赛克效果了

8x8的效果如下:
在这里插入图片描述
马赛克效果很明显了

16x16的效果如下:
在这里插入图片描述
这时已经分辨不出是谁了。

本人就代码例子进行了百度网盘共享:
链接:https://pan.baidu.com/s/1nPO-XRcEVtD_mjuE9K2OKw
提取码:1234

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值