基于L0范数平滑的图像漫画特效生成算法

       图像漫画特效算法,主要思路可以分两部分,第一,简化图像,即去除图像细节,仅保留图像主要信息,第二,生成合适的边缘线条,最后将两者合成即可,合成其实就是将两者简单一乘即可。在简化图像步骤,除了meanshift算法外,大家肯定会想到很多边缘保持的图像平滑算法,比如经典的双边滤波、导向滤波等等。更多内容可以查阅这里,该文作者提供了多种边缘保持平滑算法(主要包括双边滤波,域变换,WLS、导向滤波、L0范数平滑等 )效果对比以及matlab代码,算法很多,但是本人认为L0范数平滑算法比较适合做漫画特效,平滑的结果图看着更有层次感。关于L0范数平滑算法,大家可以参考原文文献以及这篇文章 ,我这里不在赘述。L0范数平滑算法原理及公式推导比较好理解,不过实现起来比较复杂,需要用到傅里叶变换以及迭代求解方程。
                                                   
       图像经过平滑简化后,边缘检测效果也会更好。杂乱无章的零碎边缘线条将大大减少,检测到的图像边缘会更连贯,更集中。有了简化图和边缘图,那么基本就可以生成漫画特效了。传统方法为直接将两者合成,即将简化图像结果与边缘检测结果直接合成。但是,由于检测的边缘往往很生硬,缺乏平滑的笔触效果,这样直接合成的漫画特效看起来不是很理想。本算法的主要创新点就是在提取的边缘基础上,增加了线积分卷积(LIC算法)滤波,经过该算法处理后,提取的边缘将会非常圆润、平滑,视觉上更接近漫画手绘效果。
       下面贴出算法整体调用逻辑代码,基本可以了解本文提出的漫画特效算法思路。
void* ImageCartoonStylizationThread(void *arg)
{
	CartoonStylizationInfo *cartoonstylization_info = (CartoonStylizationInfo *)arg;
	BMPINFO *pSrcBitmap = cartoonstylization_info->pSrcBitmap;
	int block_count = cartoonstylization_info->block_count;

	int width = pSrcBitmap->lWidth;
	int height=  pSrcBitmap->lHeight;
	int size = width*height;
	int mem_size = size * sizeof(double);
	double *rdata = (double *)malloc(mem_size);
	double *gdata = (double *)malloc(mem_size);
	double *bdata = (double *)malloc(mem_size);

	// 数据转换
	ConvertToDouble(pSrcBitmap, rdata, gdata, bdata);

	// 简化细节
	double *rgb_data[3] = { rdata, gdata, bdata };
	L0ImageSmoothing(rgb_data, width, height, block_count, 0.01, 2.0);

	// 抽取边缘
	rdata = rgb_data[0];
	gdata = rgb_data[1];
	bdata = rgb_data[2];
	double *gray_data = (double *)malloc(mem_size);
	double *hor_edgedata = (double *)malloc(mem_size);
	double *ver_edgedata = (double *)malloc(mem_size);
	for (int i = 0; i < size; i++)
	{
		gray_data[i] = (rdata[i]*0.299f + gdata[i]*0.587f + bdata[i]*0.114f);
	}
	EdgeDetectionFilter(gray_data, width, height, hor_edgedata, ver_edgedata);

	for (int i = 0; i < size; i++)
	{
		double edge_val = (fabs(hor_edgedata[i]) + fabs(ver_edgedata[i]))*1.2;
		edge_val = edge_val < 1.0 ? 1.0 - edge_val : 0.0;
		gray_data[i] = edge_val;
	}

	double *edge_data = (double *)malloc(mem_size);
	ExtractEdge(gray_data, edge_data, width, height, 15.7, 0.017, 75.5f);

	// 计算矢量场
	double *vec_x = (double *)malloc(mem_size);
	double *vec_y = (double *)malloc(mem_size);
	CalcVectorField(gray_data, width, height, vec_x, vec_y, 1.0);

	// 线积分卷积
	LICFilter(edge_data, gray_data, vec_x, vec_y, width, height);

	free(vec_x);
	free(vec_y);
	vec_x = NULL;
	vec_y = NULL;
	free(edge_data);
	edge_data = NULL;

	// 结果合成
	for (int i = 0; i < size; i++)
	{
		rdata[i] *= gray_data[i];
		gdata[i] *= gray_data[i];
		bdata[i] *= gray_data[i];
	}

	// 数据转换
	ConvertToUchar(rdata, gdata, bdata, pSrcBitmap);

	// 效果微调
	ImageAdjust(pSrcBitmap);

	free(gray_data);
	free(hor_edgedata);
	free(ver_edgedata);
	gray_data = NULL;
	hor_edgedata = NULL;
	ver_edgedata = NULL;

	free(rdata);
	free(gdata);
	free(bdata);
	rdata = NULL;
	gdata = NULL;
	bdata = NULL;

	return NULL;
}
       当然,本算法也是有局限性的,就像滤镜一样,有的适合人像,有的适合风景。本文算法对于背景与前景简单一些的图效果比较好,如果图像背景或者前景比较杂乱,生成的结果可能不太理想。下面是一些效果图:
                 
                 
                     
                     
                                 
                                 
                                      
                                      
                                                            
                                                            
                 
                 
                 
                 
       
       欢迎下载示例demo:http://download.youkuaiyun.com/detail/u013085897/9747177,另外本算法已经集成进我的安卓应用《铅笔画》, 大家有兴趣可以到360、安卓、安智等商店下载使用。






评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值