表面模糊

PS中有个表面模糊的功能,这个功能可以在保留边缘的情况下对图像平坦区域进行模糊滤波,这个功能可以实现很好的磨皮效果,它的算法如下:


这个算法很简单,设置半径r,得到一个边长为(2r+1)的正方形窗口,那么窗口中心像素的像素值即为x,当然,对于像素的RGB三个分量,是需要分别计算的,因此这个算法的时间消耗比较大。

类似的算法还有双边滤波等,不过这类滤波普遍都比较耗时,虽然双边滤波已经可以在PC上实时,但是,在手机端还存在较大问题。

下面给出一些对比效果图:


原图                                                                                                                       表面模糊(r=20,y=28)



双边滤波(r=20)

可以看出,表面模糊的效果要比双边滤波效果更好一些!

图像的表面模糊处理,其作用是在保留图像边缘的情况下,对图像的表面进行模糊处理。在对人物皮肤处理上,比高斯模糊更有效。因为高斯模糊在使人物皮肤光洁的同时,也将一些边缘特征如脸部的眉毛、嘴唇等给模糊了,不得不用蒙版小心的抹去这些地方的模糊部分。

在处理手法上,表面模糊也与其它卷积处理手段不同,如高斯模糊等在处理图像时都是采用统一的卷积矩阵进行,而表面模糊却是每一个像素点都有自己的卷积矩阵,而且还是3(4)套,用以对应于像素的R、G、B(A、R、G、B)分量。所以表面模糊在编程处理时,比其它卷积操作更复杂、更耗时,因为它要对每一个像素计算自己的卷积矩阵。表面模糊编程的难点也在计算卷积矩阵上,其它与一般图像卷积处理一样。

表面模糊处理有2个参数,即模糊半径和模糊阈值,前者确定模糊的范围,后者确定模糊的程度。模糊范围就是卷积矩阵大小,如模糊半径为1,则模糊矩阵直径为  1×2+1=3 ,矩阵元素个数为 3×3=9 ,矩阵的中间元素即是当前像素点。

矩阵元素值的计算公式为:

wij=1(|IijI0|)2.5T

其中, T  是阈值, wij  是模板矩阵的元素值,也可以称为权重,   Iij  是图像值,   I0  是模板矩阵中心的图像值。一般来说, wij  会做一个预处理:

wij=max(0,wij)

根据卷积运算,每一个像素通过表面模糊之后的值为:

Inew=wijIijwij

clc;
clear all;
close all;

addpath('E:\Visual Effects\PS Algorithm');

Img=imread('1.jpg');
Img=double(Img);
imshow(Img/255);

img_out=Img;

R=Img(:, :, 1);
G=Img(:, :, 2);
B=Img(:, :, 3);

radi=5;
thre=25;

img_out(:, :, 1)=Surface_Blur(R, radi, thre);
img_out(:, :, 2)=Surface_Blur(G, radi, thre);
img_out(:, :, 3)=Surface_Blur(B, radi, thre);

figure, imshow(img_out/255);


%% surface blur

function I_out=Surface_Blur(I_in, radi, thre)


[r, c]=size(I_in);

I_out=I_in;


for ii=1+radi : r-radi
    for jj=1+radi : c-radi

            patch=I_in (ii-radi:ii+radi, jj-radi:jj+radi);

            p0=I_in(ii, jj);

            mask_1= repmat(p0, 2*radi+1, 2*radi+1);

            mask_2=1-abs(patch-mask_1)/(2.5*thre);

            mask_3=max(mask_2, 0);

            I_out(ii, jj)=sum(sum(patch.*mask_3))/sum(mask_3(:));

    end    
end
 
 
  • 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
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 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
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

图像的表面模糊处理是Photoshop CS2以后才有的新功能,其作用是在保留图像边缘的情况下,对图像的表面进行模糊处理。在对人物皮肤处理上,比高斯模糊更有效。因为高斯模糊在使人物皮肤光洁的同时,也将一些边缘特征如脸部的眉毛、嘴唇等给模糊了,不得不用蒙版小心的抹去这些地方的模糊部分。

    在处理手法上,表面模糊也与其它卷积处理手段不同,如高斯模糊等在处理图像时都是采用统一的卷积矩阵进行,而表面模糊却是每一个像素点都有自己的卷积矩阵,而且还是3(4)套,用以对应于像素的R、G、B(A、R、G、B)分量。所以表面模糊在编程处理时,比其它卷积操作更复杂、更耗时,因为它要对每一个像素计算自己的卷积矩阵。表面模糊编程的难点也在计算卷积矩阵上,其它与一般图像卷积处理一样。

    表面模糊处理有2个参数,即模糊半径和模糊阈值,前者确定模糊的范围,后者确定模糊的程度。模糊范围就是卷积矩阵大小,如模糊半径为1,则模糊矩阵直径为1*2+1等于3,矩阵元素个数为3*3等于9,矩阵的中间元素即是当前像素点。

    矩阵元素值的计算公式为:

    mij = 1 - (|pij - p0|) / 2.5T

    其中,mij为矩阵的各元素值,pij为矩阵元素对应的像素分量值,p0为矩阵中心元素对应的像素分量值,T为阈值,|pij - p0|为矩阵元素对应像素分量值与中心元素对应像素分量值的绝对差值。如果mij < 0,则mij = 0。

    对ARGB格式图像数据来说,因有4个分量,故需要4套卷积矩阵。

    矩阵元素确定后,就可按照一般图像卷积操作进行处理了,即分别累计矩阵元素值和与之对应的像素分量值乘积,用累计像素分量值除以累计元素值,即可得到当前像素分量模糊处理后的值。

    下面是表面模糊处理代码:

[cpp]  view plain  copy
 print ?
  1. //---------------------------------------------------------------------------  
  2.   
  3. // ARGB图像数据表面模糊处理  
  4. // 参数: 图像数据, 模糊半径(1 - 100), 阈值(2 - 255)  
  5. VOID SurfaceBlur(BitmapData *data, UINT radius, UINT threshold)  
  6. {  
  7.     if (radius < 1 || radius > 100 || threshold < 2 || threshold > 255)  
  8.         return;  
  9.   
  10.     // 设置模糊矩阵元素表  
  11.     FLOAT *matrixItems = new FLOAT[255*2+1];  
  12.     FLOAT *items = &matrixItems[255];  
  13.     FLOAT fv = threshold * 2.5;  
  14.     INT i;  
  15.     for (i = 1; i < 256; i ++)  
  16.     {  
  17.         items[-i] = items[i] = 1 - i / fv;  
  18.         if (items[i] < 0) break;  
  19.     }  
  20.     for (; i < 256; i ++)  
  21.         items[-i] = items[i] = 0;  
  22.     *items = 1;  
  23.   
  24.     // 获取边框像素扩展图像数据到src  
  25.     BitmapData src;  
  26.     GetExpendData(data, radius, &src);  
  27.     // 获取数据处理参数  
  28.     PARGBQuad pd, ps;  
  29.     UINT width, height;  
  30.     INT dstOffset, srcOffset;  
  31.     GetDataCopyParams(data, &src, width, height, pd, ps, dstOffset, srcOffset);  
  32.   
  33.     INT size = (radius << 1) + 1;  
  34.     INT pOffset = ((src.Stride >> 2) + 1) * radius;  
  35.     INT iOffset = (src.Stride >> 2) - size;  
  36.     FLOAT pixelA, pixelR, pixelG, pixelB;  
  37.     FLOAT nuclearA, nuclearR, nuclearG, nuclearB;  
  38.     FLOAT ivA, ivR, ivG, ivB;  
  39.     for (UINT y = 0; y < height; y ++, ps += srcOffset, pd += dstOffset)  
  40.     {  
  41.         for (UINT x = 0; x < width; x ++, ps ++, pd ++)  
  42.         {  
  43.             pixelA = pixelR = pixelG = pixelB = 0;  
  44.             nuclearA = nuclearR = nuclearG = nuclearB = 0;  
  45.             PARGBQuad p = ps, p0 = p + pOffset;  
  46.             for (INT i = 0;  i < size; i ++, p += iOffset)  
  47.             {  
  48.                 for (INT j = 0; j < size; j ++, p ++)  
  49.                 {  
  50.                     // 以Pij - p0为下标从模糊矩阵元素表中获取像素各分量的元素值  
  51.                     ivB = items[p->Blue - p0->Blue];  
  52.                     ivG = items[p->Green - p0->Green];  
  53.                     ivR = items[p->Red - p0->Red];  
  54.                     ivA = items[p->Alpha - p0->Alpha];  
  55.                     // 累计像素各分量元素值  
  56.                     nuclearB += ivB;  
  57.                     nuclearG += ivG;  
  58.                     nuclearR += ivR;  
  59.                     nuclearA += ivA;  
  60.                     // 累计像素各分量元素值与分量值的乘积  
  61.                     pixelB += (ivB * p->Blue);  
  62.                     pixelG += (ivG * p->Green);  
  63.                     pixelR += (ivR * p->Red);  
  64.                     pixelA += (ivA * p->Alpha);  
  65.                 }  
  66.             }  
  67.             // 计算像素表面模糊后的新分量值  
  68.             if (nuclearB > 0)  
  69.                 pd->Blue = (BYTE)(pixelB / nuclearB + 0.5);  
  70.             if (nuclearG > 0)  
  71.                 pd->Green = (BYTE)(pixelG / nuclearG + 0.5);  
  72.             if (nuclearR > 0)  
  73.                 pd->Red = (BYTE)(pixelR / nuclearR + 0.5);  
  74.             if (nuclearA > 0)  
  75.                 pd->Alpha = (BYTE)(pixelA / nuclearA + 0.5);  
  76.         }  
  77.     }  
  78.   
  79.     FreeBitmapData(&src);  
  80.     delete[] matrixItems;  
  81.     // 如果图像数据含Alpha,将PARGB像素格式还原为ARGB像素格式  
  82.     if (HasAlphaFlag(data))  
  83.         ArgbConvertPArgb(data);  
  84. }  
  85. //---------------------------------------------------------------------------  

    表面模糊处理函数SurfaceBlur采用浮点数进行矩阵元素的计算和像素的模糊处理,为减少循环过程中的模糊矩阵元素的计算量,函数预先定义并计算了255*2+1大小的模糊矩阵元素表matrixItems,元素指针items指向matrixItems[255]元素位置,该位置为pij - p0 = 0时的元素值,如果pij - p0 > 0,其对应元素值在items右边,反之,如果pij - p0 < 0则对应元素值在items左边,如此便免去了|pij - p0|中的绝对值处理。

    为了加快运行速度,对于不含Alpha信息的图像可不处理alpha分量。也可将处理函数改为定点数运算,但计算精度要差一些。

    下面是一个利用SurfaceBlur函数对GDI+位图进行表面模糊的例子(使用BCB2010):

[cpp]  view plain  copy
 print ?
  1. //---------------------------------------------------------------------------  
  2.   
  3. void __fastcall TForm1::Button1Click(TObject *Sender)  
  4. {  
  5.     Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\Source1.jpg");  
  6.     Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);  
  7.     g->DrawImage(bmp, 0, 0);  
  8.   
  9.     BitmapData data;  
  10.     LockBitmap(bmp, &data);  
  11.     SurfaceBlur(&data, 3, 10);  
  12.     UnlockBitmap(bmp, &data);  
  13.     g->DrawImage(bmp, data.Width + 8, 0);  
  14.   
  15.     delete g;  
  16.     delete bmp;  
  17. }  
  18. //---------------------------------------------------------------------------  

    例子运行效果截图:


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值