Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1290202
  • 博文数量: 79
  • 博客积分: 1959
  • 博客等级: 上尉
  • 技术积分: 2719
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-19 12:07
个人简介

樽中酒不空

文章分类

全部博文(79)

文章存档

2024年(3)

2020年(4)

2019年(1)

2017年(2)

2016年(2)

2015年(7)

2014年(11)

2013年(13)

2012年(18)

2011年(2)

2010年(16)

分类: C/C++

2013-02-26 12:23:19

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

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

效果:     



采用的是美图秀秀软件下面的一张图,如果有版权问题,请联系,及时删掉。不过说实话,美图秀秀还有很大可优化空间。尤其是处理图片的时候,能不能先对缩略图处理一下?直接对大图动手,什么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 <= 4; i++)
    d_m [i] = d_p [i];
#endif


  n_m[0] = 0.0;
  for (i = 1; i <= 4; 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 <= 4; 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 <= 4; 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函数读选中区域。


源码:


联系 sxcong@163.com



阅读(10054) | 评论(6) | 转发(0) |
给主人留下些什么吧!~~

sxcong2015-08-25 15:59:23

lindatian550:很感谢博主的回复,我现在就是想抽取gimp的部分功能来实现,可是每次在源码里面看得特别晕乎,文件之间的依赖关系太复杂,理不清啊,看来还是我的代码能力不强,博主可否指点指点如何剖析源码???不甚感激。。。

没有什么好方法,一是先把软件用熟悉了,每个操作执行什么功能。二是图像知识要先了解好,各种图片格式,各种特效的原理。之后就是多看代码了。

回复 | 举报

lindatian5502015-08-17 14:41:58

sxcong:好久不做VC了。
当时是先看GIMP的菜单,根据菜单找到源码的位置,比如我这个功能是softglow,搜索很容易就找到了。然后看这个菜单响应后怎么执行的代码。我也没有编译DEBUG,就是从代码看大致分析中出部分是什么功能,然后复制出来,再修改成标准的C++代码,不依赖于OS的。
从GIMP里只提取算法,图片的读写和显示有很多现成的库。

很感谢博主的回复,我现在就是想抽取gimp的部分功能来实现,可是每次在源码里面看得特别晕乎,文件之间的依赖关系太复杂,理不清啊,看来还是我的代码能力不强,博主可否指点指点如何剖析源码???不甚感激。。。

回复 | 举报

sxcong2015-08-14 22:13:11

lindatian550:博主,你好,很开心看到你的博文,我最近也在研究gimp,前段时间花了很多精力去研究gimp的编译工作,现在也要深入到源码内部,可是毫无头绪。看到博主对gimp的不同功能的源码解析,很厉害。不知道博主可否将移植到VC下的程序分享给我,以便我更加清晰地学习gimp的源码,请指点,不甚感激,谢谢啦。。

好久不做VC了。
当时是先看GIMP的菜单,根据菜单找到源码的位置,比如我这个功能是softglow,搜索很容易就找到了。然后看这个菜单响应后怎么执行的代码。我也没有编译DEBUG,就是从代码看大致分析中出部分是什么功能,然后复制出来,再修改成标准的C++代码,不依赖于OS的。
从GIMP里只提取算法,图片的读写和显示有很多现成的库。

回复 | 举报

lindatian5502015-08-13 15:23:09

博主,你好,很开心看到你的博文,我最近也在研究gimp,前段时间花了很多精力去研究gimp的编译工作,现在也要深入到源码内部,可是毫无头绪。看到博主对gimp的不同功能的源码解析,很厉害。不知道博主可否将移植到VC下的程序分享给我,以便我更加清晰地学习gimp的源码,请指点,不甚感激,谢谢啦。。

sxcong2014-02-27 08:58:38

wangyanysu:博主好,看了博主关于gimp的研究,感觉很有成果。我也想着手学习写gimp的算法,我是windows vs2010环境,请问我该如何着手,请指点,谢谢

gimp在windows下编译稍麻烦一点
不过,本身代码很大,编译成功也没意义。
我个人是先使用软件,熟悉各部分功能,然后,想看哪个部分功能是怎么实现的,再专门找相关代码来看。
可以自己建一个工程,再参考gimp相关代码改写一下。
如果之前在图像方面经验不多的话,可以先看CxImage的源码,这个是VC实现的。

回复 | 举报