前言:柔光效果可以把一张照片美白化,影楼应该用的比较多。

打算把柔光效果用在自己软件中,看GIMP的代码,开始感觉牵扯的比较多,另外找了几个,比如tinyimage,imagestone等。效果也不错,不过看到处理图像大量使用了gdi+,stl,感觉不太合适。再回去认真看GIMP代码,最终成功移植到vc和iOS下。

效果:GIMP的柔光(softglow)源码分析-LMLPHP     GIMP的柔光(softglow)源码分析-LMLPHP



采用的是美图秀秀软件下面的一张图,如果有版权问题,请联系,及时删掉。不过说实话,美图秀秀还有很大可优化空间。尤其是处理图片的时候,能不能先对缩略图处理一下?直接对大图动手,什么CPU也受不了。

处理的参数是 SoftglowVals svals = {10, 0.5, 0.6};

有兴趣的朋友可以用GIMP自己处理到满意的程度,记住参数,再用程序来实现。这里只是演示功能,不考虑美观效果,有可能处理后比处理前还难看。


一 gimp softglow(柔光)分析


代码在 gimp-2.8.0\plug-ins\common\softglow.c里面。
1 选中显示图像的一部分
2 对选中部分进行亮度,锐度计算,保存在 width X height数组内。(图像的原始数据区是width X height X bytes per pixel)
3 高斯模糊计算


//  Calculate the standard deviations  
  radius  = fabs (svals.glow_radius) + 1.0;
  std_dev = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));


  //  derive the constants for calculating the gaussian from the std dev  
  find_constants(n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev);
根据半径算出高斯模板的矩阵值。find_constants里面计算的是5x5矩阵。
4 再根据原始图像数据,计算出目标图像数据。计算公式:
255 - INT_MULT((255 - src_ptr[col * bytes + b]), (255 - blur_ptr[col]), tmp);


5 把目标数据保存成图,就得到了柔光的结果。


总结:
算法比较简单,需要注意的是,原程序里读原始图像数据,使用的是gimp提供的api,比如图像数据是drawable,需要改写成从DC里取值,或直接从文件里读。


二 改造
毕竟gimp不是通常用c/c++库,如果用在自己项目里,是需要进行一些处理的。
改造从几个方面进行:
1 读写图像:推荐用FreeImage
2 显示图像:使用到drawable和rgn的地方要改。
3 算法:基本不用动,只是要加一些注释,源码过于简洁,比如
double       n_p[5], n_m[5];
double       d_p[5], d_m[5];
double       bd_p[5], bd_m[5];


static void
find_constants (gdouble n_p[],
                gdouble n_m[],
                gdouble d_p[],
                gdouble d_m[],
                gdouble bd_p[],
                gdouble bd_m[],
                gdouble std_dev)
{
  gint    i;
  gdouble constants [8];
  gdouble div;


  /*  The constants used in the implemenation of a casual sequence
   *  using a 4th order approximation of the gaussian operator
   */


  div = sqrt(2 * G_PI) * std_dev;


  constants [0] = -1.783  / std_dev;
  constants [1] = -1.723  / std_dev;
  constants [2] =  0.6318 / std_dev;
  constants [3] =  1.997  / std_dev;
  constants [4] =  1.6803 / div;
  constants [5] =  3.735  / div;
  constants [6] = -0.6803 / div;
  constants [7] = -0.2598 / div;


  n_p [0] = constants[4] + constants[6];
  n_p [1] = exp (constants[1]) *
    (constants[7] * sin (constants[3]) -
     (constants[6] + 2 * constants[4]) * cos (constants[3])) +
       exp (constants[0]) *
         (constants[5] * sin (constants[2]) -
          (2 * constants[6] + constants[4]) * cos (constants[2]));
  n_p [2] = 2 * exp (constants[0] + constants[1]) *
    ((constants[4] + constants[6]) * cos (constants[3]) * cos (constants[2]) -
     constants[5] * cos (constants[3]) * sin (constants[2]) -
     constants[7] * cos (constants[2]) * sin (constants[3])) +
       constants[6] * exp (2 * constants[0]) +
         constants[4] * exp (2 * constants[1]);
  n_p [3] = exp (constants[1] + 2 * constants[0]) *
    (constants[7] * sin (constants[3]) - constants[6] * cos (constants[3])) +
      exp (constants[0] + 2 * constants[1]) *
        (constants[5] * sin (constants[2]) - constants[4] * cos (constants[2]));
  n_p [4] = 0.0;


  d_p [0] = 0.0;
  d_p [1] = -2 * exp (constants[1]) * cos (constants[3]) -
    2 * exp (constants[0]) * cos (constants[2]);
  d_p [2] = 4 * cos (constants[3]) * cos (constants[2]) * exp (constants[0] + constants[1]) +
    exp (2 * constants[1]) + exp (2 * constants[0]);
  d_p [3] = -2 * cos (constants[2]) * exp (constants[0] + 2 * constants[1]) -
    2 * cos (constants[3]) * exp (constants[1] + 2 * constants[0]);
  d_p [4] = exp (2 * constants[0] + 2 * constants[1]);


#ifndef ORIGINAL_READABLE_CODE
  memcpy(d_m, d_p, 5 * sizeof(gdouble));
#else
  for (i = 0; i    d_m [i] = d_p [i];
#endif


  n_m[0] = 0.0;
  for (i = 1; i    n_m [i] = n_p[i] - d_p[i] * n_p[0];


  {
    gdouble sum_n_p, sum_n_m, sum_d;
    gdouble a, b;


    sum_n_p = 0.0;
    sum_n_m = 0.0;
    sum_d   = 0.0;


    for (i = 0; i      {
        sum_n_p += n_p[i];
        sum_n_m += n_m[i];
        sum_d += d_p[i];
      }


#ifndef ORIGINAL_READABLE_CODE
    sum_d++;
    a = sum_n_p / sum_d;
    b = sum_n_m / sum_d;
#else
    a = sum_n_p / (1 + sum_d);
    b = sum_n_m / (1 + sum_d);
#endif


    for (i = 0; i      {
        bd_p[i] = d_p[i] * a;
        bd_m[i] = d_m[i] * b;
      }
  }
}



总结:刚看gimp代码时,的确一头雾水。这时需要九方皋相马的方式,透过现象看本质。

比如:

guchar *src_ptr  = src_rgn.data;
guchar *dest_ptr = dest_rgn.data;

如果修改成标准c的写法就是:

uint8_t* src_ptr = image_src.accessPixels();

uint8_t*dest_ptr = new uint8_t[image_src.getImageSize()];

//以上假设是对整图处理,如果对部分处理的话,自己定义个矩形,选中图像数据,比如可以使用FreeImage的crop函数读选中区域。


源码:
https://github.com/sxcong/fytPhoto

联系 [email protected]



09-07 23:22