樽中酒不空
分类: 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
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实现的。