下载本文示例代码
在电视采访中,有时候一些采访对象不愿意抛头露面。这种情况下,被采访者可能会背对摄像镜头;但更通常的做法是,被采访者仍然面对镜头,而在电视节目播出时对采访对象的面部进行马赛克处理。这种马赛克处理,使观众无法看清被采访者的真实面目,从而满足被采访者不愿抛头露面的初衷。作为程序员,你想过如何来实现这种效果处理吗?本文介绍的就是一种实现视频局部区域马赛克处理的简单易行的编程方法。 一. 马赛克处理原理及其实现 我们首先来看一下同一帧视频图像在进行马赛克处理前后的对比效果,如图1。
图1 对人像面部进行马赛克处理前后的效果对比 经过马赛克处理后,你无法识别她的真实面目了吧?那么,怎么会出现马赛克效果的呢?大家知道,图像是由像素组成的;像素颗粒的大小决定了图像表现的精度(这就是为什么小尺寸的电视机比大尺寸的电视机看起来更清楚的原因)。如果我们把指定区域的像素进行放大,不就出现马赛克效果了吗?别急,这里有个关键问题:对于给定的一种显示设备,其像素颗粒大小是物理不可变的,怎么进行放大呢?! 有办法:使用相邻的几个像素同时表现为同一个像素值,不就等于将像素放大了吗?只是如果我们把指定区域中的每一个像素都进行这样的放大处理后,马赛克区域将超出用户最初指定的区域(如果用户指定区域的宽度为w,像素水平放大的比率为ratiox,则这种马赛克处理后的区域宽度将覆盖w x ratiox)。如何将马赛克处理后的区域仍然限定在用户指定的区域内呢?笔者的做法是,对指定区域内的像素进行一次亚采样。如图2,假设我们将对图像中的R1区域进行马赛克处理。
图2 需要进行马赛克处理的区域 假设R1区域的像素排列如图3:
图3 R1区域的像素示意图 再假设像素的水平放大比率(ratiox)为3,垂直放大比率(ratioy)也为3,则经过马赛克处理后各对应位置像素值分布如图4:
图4 R1区域经过马赛克处理后的像素示意图 我们看到在R1区域内,水平方向上每3个像素采样1次(P00、P03、P06、P09、P30、P33、P36、P39、P60、P63、P66、P69等都是采样点),垂直方向上每个采样像素行都重复3次(第2、3行复制第1行的内容,第5、6行复制第4行的内容,以此类推);每个采样点像素都被放大到一个3 x 3的宏块,也就是说,采样点像素被放大了9倍。 图像指定区域马赛克处理的C 实现
// 图像帧数据指针PBYTE pImage; // 获取图像数据// …// 指向图像第1行开头的指针PBYTE pImageTopLine = NULL; // 图像的跨度(以字节为单位)long imageStride = 0;// 如果图像数据是以从下往上的扫描顺序存储的,// 则图像的第1行应该在pImage数据的倒数第1行;// 如果图像数据是以从上往下的扫描顺序存储的,// 则图像的第1行就是pImage指的位置if (m_bIsBottomUp){ imageStride = -m_nImageStride; pImageTopLine = pImage m_nImageStride * (m_nImageHeight - 1); }else{ imageStride = m_nImageStride; pImageTopLine = pImage;}// ratioX是水平方向上像素的放大倍数// ratioY是垂直方向上像素的放大倍数// maskStride为进行马赛克处理的区域的宽度(以字节为单位)/* macroWidth和macorHeight有如下计算关系:RECT m_MaskRect; // 需要进行马赛克处理的矩形区域(由用户指定)int maskWidth = m_MaskRect.right - m_MaskRect.left 1;int maskHeight = m_MaskRect.bottom - m_MaskRect.top 1;macroWidth = maskWidth / m_nRatioX;macroHeight = maskHeight / m_nRatioY;*/int macroWidth, macroHeight, maskStride, ratioX, ratioY;// 马赛克处理过程中:// pMaskPixel 指向当前像素,// pMaskLine指向当前行,// pMaskNextLine下一行PBYTE pMaskTopLine, pMaskLine, pMaskNextLine, pMaskPixel;// pMaskTopLine指向需要进行马赛克处理的区域的第1行// 注:m_nPixelBytes为单个像素占用的字节数pMaskTopLine = pImageTopLine m_MaskRect.top * imageStride m_MaskRect.left * m_nPixelBytes;macroWidth = m_nMacroWidth;macroHeight = m_nMacroHeight;maskStride = m_nMaskStride;ratioX = m_nRatioX;ratioY = m_nRatioY;// 扫描指定区域的像素,进行马赛克处理…int cycle = 0;for (int i = 0; i < macroHeight; i ){ // 定位需要进行马赛克处理的当前行 pMaskLine = pMaskTopLine i * ratioY * imageStride; // 定位需要进行马赛克处理的当前像素 pMaskPixel = pMaskLine; for (int j = 0; j < macroWidth; j ) { // 水平方向上进行像素放大 for (cycle = 0; cycle < ratioX - 1; cycle ) { // 将当前像素值复制给右边的下一个像素 memcpy(pMaskPixel m_nPixelBytes, pMaskPixel, m_nPixelBytes); // 指向下一个像素 pMaskPixel = m_nPixelBytes; } // 指向下一个采样像素 pMaskPixel = m_nPixelBytes; } // 垂直方向上进行像素放大 for (cycle = 0; cycle < ratioY - 1; cycle ) { // 获得马赛克处理区域的下一行指针 pMaskNextLine = pMaskLine imageStride; // 将马赛克处理区域的当前行(已经完成马赛克处理)复制给下一行 memcpy(pMaskNextLine, pMaskLine, maskStride); // 修改当前行指针,指向下一行 pMaskLine = pMaskNextLine; }} 共2页。 1 2 :
在电视采访中,有时候一些采访对象不愿意抛头露面。这种情况下,被采访者可能会背对摄像镜头;但更通常的做法是,被采访者仍然面对镜头,而在电视节目播出时对采访对象的面部进行马赛克处理。这种马赛克处理,使观众无法看清被采访者的真实面目,从而满足被采访者不愿抛头露面的初衷。作为程序员,你想过如何来实现这种效果处理吗?本文介绍的就是一种实现视频局部区域马赛克处理的简单易行的编程方法。 一. 马赛克处理原理及其实现 我们首先来看一下同一帧视频图像在进行马赛克处理前后的对比效果,如图1。
图1 对人像面部进行马赛克处理前后的效果对比 经过马赛克处理后,你无法识别她的真实面目了吧?那么,怎么会出现马赛克效果的呢?大家知道,图像是由像素组成的;像素颗粒的大小决定了图像表现的精度(这就是为什么小尺寸的电视机比大尺寸的电视机看起来更清楚的原因)。如果我们把指定区域的像素进行放大,不就出现马赛克效果了吗?别急,这里有个关键问题:对于给定的一种显示设备,其像素颗粒大小是物理不可变的,怎么进行放大呢?! 有办法:使用相邻的几个像素同时表现为同一个像素值,不就等于将像素放大了吗?只是如果我们把指定区域中的每一个像素都进行这样的放大处理后,马赛克区域将超出用户最初指定的区域(如果用户指定区域的宽度为w,像素水平放大的比率为ratiox,则这种马赛克处理后的区域宽度将覆盖w x ratiox)。如何将马赛克处理后的区域仍然限定在用户指定的区域内呢?笔者的做法是,对指定区域内的像素进行一次亚采样。如图2,假设我们将对图像中的R1区域进行马赛克处理。
图2 需要进行马赛克处理的区域 假设R1区域的像素排列如图3:
图3 R1区域的像素示意图 再假设像素的水平放大比率(ratiox)为3,垂直放大比率(ratioy)也为3,则经过马赛克处理后各对应位置像素值分布如图4:
图4 R1区域经过马赛克处理后的像素示意图 我们看到在R1区域内,水平方向上每3个像素采样1次(P00、P03、P06、P09、P30、P33、P36、P39、P60、P63、P66、P69等都是采样点),垂直方向上每个采样像素行都重复3次(第2、3行复制第1行的内容,第5、6行复制第4行的内容,以此类推);每个采样点像素都被放大到一个3 x 3的宏块,也就是说,采样点像素被放大了9倍。 图像指定区域马赛克处理的C 实现
// 图像帧数据指针PBYTE pImage; // 获取图像数据// …// 指向图像第1行开头的指针PBYTE pImageTopLine = NULL; // 图像的跨度(以字节为单位)long imageStride = 0;// 如果图像数据是以从下往上的扫描顺序存储的,// 则图像的第1行应该在pImage数据的倒数第1行;// 如果图像数据是以从上往下的扫描顺序存储的,// 则图像的第1行就是pImage指的位置if (m_bIsBottomUp){ imageStride = -m_nImageStride; pImageTopLine = pImage m_nImageStride * (m_nImageHeight - 1); }else{ imageStride = m_nImageStride; pImageTopLine = pImage;}// ratioX是水平方向上像素的放大倍数// ratioY是垂直方向上像素的放大倍数// maskStride为进行马赛克处理的区域的宽度(以字节为单位)/* macroWidth和macorHeight有如下计算关系:RECT m_MaskRect; // 需要进行马赛克处理的矩形区域(由用户指定)int maskWidth = m_MaskRect.right - m_MaskRect.left 1;int maskHeight = m_MaskRect.bottom - m_MaskRect.top 1;macroWidth = maskWidth / m_nRatioX;macroHeight = maskHeight / m_nRatioY;*/int macroWidth, macroHeight, maskStride, ratioX, ratioY;// 马赛克处理过程中:// pMaskPixel 指向当前像素,// pMaskLine指向当前行,// pMaskNextLine下一行PBYTE pMaskTopLine, pMaskLine, pMaskNextLine, pMaskPixel;// pMaskTopLine指向需要进行马赛克处理的区域的第1行// 注:m_nPixelBytes为单个像素占用的字节数pMaskTopLine = pImageTopLine m_MaskRect.top * imageStride m_MaskRect.left * m_nPixelBytes;macroWidth = m_nMacroWidth;macroHeight = m_nMacroHeight;maskStride = m_nMaskStride;ratioX = m_nRatioX;ratioY = m_nRatioY;// 扫描指定区域的像素,进行马赛克处理…int cycle = 0;for (int i = 0; i < macroHeight; i ){ // 定位需要进行马赛克处理的当前行 pMaskLine = pMaskTopLine i * ratioY * imageStride; // 定位需要进行马赛克处理的当前像素 pMaskPixel = pMaskLine; for (int j = 0; j < macroWidth; j ) { // 水平方向上进行像素放大 for (cycle = 0; cycle < ratioX - 1; cycle ) { // 将当前像素值复制给右边的下一个像素 memcpy(pMaskPixel m_nPixelBytes, pMaskPixel, m_nPixelBytes); // 指向下一个像素 pMaskPixel = m_nPixelBytes; } // 指向下一个采样像素 pMaskPixel = m_nPixelBytes; } // 垂直方向上进行像素放大 for (cycle = 0; cycle < ratioY - 1; cycle ) { // 获得马赛克处理区域的下一行指针 pMaskNextLine = pMaskLine imageStride; // 将马赛克处理区域的当前行(已经完成马赛克处理)复制给下一行 memcpy(pMaskNextLine, pMaskLine, maskStride); // 修改当前行指针,指向下一行 pMaskLine = pMaskNextLine; }} 共2页。 1 2 :
下载本文示例代码
用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理用DirectShow实现视频马赛克处理
阅读(189) | 评论(0) | 转发(0) |