zoukankan      html  css  js  c++  java
  • 图像处理之水彩画特效生成算法

           在研究非真实感绘制相关算法时。水彩画算法是第一个開始看的,只是却拖到最后总结。水彩画还是挺不好模拟的,里面涉及的算法比較多。本文实现的水彩画算法主要參考以下两篇文章,《Interactive watercolor rendering with temporal coherence and abstraction》 、《Towards Photo Watercolorization with Artistic Verisimilitude》 ,第一篇文章比較早。第二篇文章比較新,通过这两篇文章,能够对水彩画的模拟过程有一个大概了解。只是因为涉及的环节比較多,在实现过程中,有些地方做了简化,又新增了一些计算环节,整体上得到效果和文章比,并非严格一致,只是本文实现的水彩画算法执行较慢,还是比較耗时的。
           水彩画算法主要涉及的计算环节有:(1)简化细节;(2)边缘抖动;(3)边缘加深。(4)颜料分散;(5)紊流效果;(6)叠加纸张纹理等。

    上述环节中,简化细节是非常耗时的,主要是採用了meanshift及各向异性的Kuwahara滤波原因,也许有更好的替代方法。有待优化。本文非常多地方使用了噪声,主要用到了高斯噪声及柏林噪声。主要用于模拟边缘抖动、颜料分散及紊流效果。

    边缘抖动有点类似前文水面倒影算法中的置换滤镜,主要模拟颜料的流动效果。

    边缘加深主要模拟颜料停止流动后,在边缘处的沉淀痕迹。颜料分散及紊流效果。主要是模拟水彩颜料渗透效果。只是本文实现的算法。颜料分散做的不是非常惬意,离真实水彩画那种颜料扩散及渗透效果还有非常大差距。

           以下是部分演示样例代码:
    void* WaterColorThread(void *arg)
    {
    	WaterColorInfo *watercolor_info = (WaterColorInfo *)arg;
    	BMPINFO *pSrcBitmap = watercolor_info->pSrcBitmap;
    	BMPINFO *pPaperBitmap = watercolor_info->pPaperBitmap;
    	float *noise_perlin = watercolor_info->noise_perlin;
    	float *mean = watercolor_info->mean;
    	float *stdev = watercolor_info->stdev;
    	int color_index = watercolor_info->color_index;
    	int thread_id = watercolor_info->thread_id;
    	int block_count = watercolor_info->block_count;
    
    	// 数据转换
    	int width = pSrcBitmap->lWidth;
    	int height=  pSrcBitmap->lHeight;
    	int size = width*height;
    	float *rdata = (float *)malloc(size * sizeof(float));
    	float *gdata = (float *)malloc(size * sizeof(float));
    	float *bdata = (float *)malloc(size * sizeof(float));
    	ConvertToFloat(pSrcBitmap, rdata, gdata, bdata);
    
    	// 简化细节
    	Abstraction(rdata, gdata, bdata, mean, stdev, width, height, color_index, thread_id);
    
    	// 边缘抖动
    	EdgeWobbling(rdata, gdata, bdata, noise_perlin, width, height, block_count);
    
    	// 边缘加深
    	EdgeDarkening(rdata, gdata, bdata, 1.5f, width, height);
    
    	// 颜料分散
    	PigmentDispersion(rdata, gdata, bdata, 0.5f, width, height);
    
    	// 紊流效果
    	TurbulenceFlow(rdata, gdata, bdata, noise_perlin, 2.0f, width, height);
    
    	// 纸张纹理
    	PaperTexture(rdata, gdata, bdata, pPaperBitmap, 0.225f, width, height);
    
    	// 数据转换
    	ConvertToUchar(rdata, gdata, bdata, pSrcBitmap);
    
    	// 效果微调
    	ImageAdjust(pSrcBitmap);
    
    	free(rdata);
    	free(gdata);
    	free(bdata);
    	rdata = NULL;
    	gdata = NULL;
    	bdata = NULL;
    
    	return NULL;
    }
           边缘抖动演示样例代码:
    void EdgeWobbling(float *rdata, float *gdata, float *bdata, float *noise_perlin, int width, int height, int block_count)
    {
    	int size = width*height;
    	float strengthx = width / 3.5f;
    	float strengthy = height*block_count / 3.5f;
    	float strength = MAX(strengthx, strengthy);
    
    	float *rcopy = (float *)malloc(size * sizeof(float));
    	float *gcopy = (float *)malloc(size * sizeof(float));
    	float *bcopy = (float *)malloc(size * sizeof(float));
    	memcpy(rcopy, rdata, size * sizeof(float));
    	memcpy(gcopy, gdata, size * sizeof(float));
    	memcpy(bcopy, bdata, size * sizeof(float));
    
    	int index = 0, new_index = 0;
    	int p_offsetx = 0, p_offsety = 0, border_w = width - 1, border_h = height - 1;
    	float *pNoiseData = noise_perlin;
    	for (int i = 0; i < height - 1; i++)
    	{
    		for (int j = 0; j < width - 1; j++)
    		{
    			index = i*width + j;
    			pNoiseData = noise_perlin + index;
    			float new_posx = CLAMP0255_XY(j + (*(pNoiseData + 1) - *pNoiseData) * strength, border_w);
    			float new_posy = CLAMP0255_XY(i + (*(pNoiseData + width) - *pNoiseData) * strength, border_h);
    			int n_posx = (int)new_posx;
    			int n_posy = (int)new_posy;
    			float dx = new_posx - n_posx;
    			float dy = new_posy - n_posy;
    
    			p_offsetx = (n_posx != border_w);
    			p_offsety = (n_posy != border_h);
    
    			float r0 = 0.0f, g0 = 0.0f, b0 = 0.0f, r1 = 0.0f, g1 = 0.0f, b1 = 0.0f;
    			new_index = n_posy*width + n_posx;
    			r0 = *(rcopy + new_index);
    			g0 = *(gcopy + new_index);
    			b0 = *(bcopy + new_index);
    			r1 = *(rcopy + new_index + p_offsetx);
    			g1 = *(gcopy + new_index + p_offsetx);
    			b1 = *(bcopy + new_index + p_offsetx);
    
    			float r2 = 0.0f, g2 = 0.0f, b2 = 0.0f, r3 = 0.0f, g3 = 0.0f, b3 = 0.0f;
    			new_index = (n_posy + p_offsety)*width + n_posx;
    			r2 = *(rcopy + new_index);
    			g2 = *(gcopy + new_index);
    			b2 = *(bcopy + new_index);
    			r3 = *(rcopy + new_index + p_offsetx);
    			g3 = *(gcopy + new_index + p_offsetx);
    			b3 = *(bcopy + new_index + p_offsetx);
    
    			float r_val = 0.0f, g_val = 0.0f, b_val = 0.0f, fx1 = 0.0f, fx2 = 0.0f;
    			fx1 = r0 + (r1 - r0) * dx;
    			fx2 = r2 + (r3 - r2) * dx;
    			r_val = fx1 + (fx2 - fx1) * dy;
    
    			fx1 = g0 + (g1 - g0) * dx;
    			fx2 = g2 + (g3 - g2) * dx;
    			g_val = fx1 + (fx2 - fx1) * dy;
    
    			fx1 = b0 + (b1 - b0) * dx;
    			fx2 = b2 + (b3 - b2) * dx;
    			b_val = fx1 + (fx2 - fx1) * dy;
    
    			*(rdata + index) = *(rcopy + index)*0.4f + r_val*0.6f;
    			*(gdata + index) = *(gcopy + index)*0.4f + g_val*0.6f;
    			*(bdata + index) = *(bcopy + index)*0.4f + b_val*0.6f;
    		}
    	}
    
    	free(rcopy);
    	rcopy = NULL;
    	free(gcopy);
    	gcopy = NULL;
    	free(bcopy);
    	bcopy = NULL;
    }
           边缘加深演示样例代码:
    void EdgeDarkening(float *rdata, float *gdata, float *bdata, float strength, int width, int height)
    {
    	int step = width;
    	int size = width*height;
    
    	float *rcopy = (float *)malloc(size * sizeof(float));
    	float *gcopy = (float *)malloc(size * sizeof(float));
    	float *bcopy = (float *)malloc(size * sizeof(float));
    	memcpy(rcopy, rdata, size * sizeof(float));
    	memcpy(gcopy, gdata, size * sizeof(float));
    	memcpy(bcopy, bdata, size * sizeof(float));
    
    	int index = 0;
    	float *pSrcRData = NULL, *pSrcGData = NULL, *pSrcBData = NULL;
    	float *pNewRData = NULL, *pNewGData = NULL, *pNewBData = NULL;
    	float *pLeftData = NULL, *pRightData = NULL, *pUpData = NULL, *pDownData = NULL;
    	for (int i = 1; i < height - 1; i++)
    	{
    		for (int j = 1; j < width - 1; j++)
    		{
    			index = i*width + j;
    			
    			// red
    			pNewRData = rcopy + index;
    			pLeftData = pNewRData - 1;
    			pRightData = pNewRData + 1;
    			pUpData = pNewRData - step;
    			pDownData = pNewRData + step;
     			float gradient_r = fabs(*pRightData - *pLeftData) + fabs(*pDownData - *pUpData);
    
    			// green
    			pNewGData = gcopy + index;
    			pLeftData = pNewGData - 1;
    			pRightData = pNewGData + 1;
    			pUpData = pNewGData - step;
    			pDownData = pNewGData + step;
     			float gradient_g = fabs(*pRightData - *pLeftData) + fabs(*pDownData - *pUpData);
    
    			// blue
    			pNewBData = bcopy + index;
    			pLeftData = pNewBData - 1;
    			pRightData = pNewBData + 1;
    			pUpData = pNewBData - step;
    			pDownData = pNewBData + step;
    			float gradient_b = fabs(*pRightData - *pLeftData) + fabs(*pDownData - *pUpData);
    
    			// result
    			float gradient = (gradient_r + gradient_g + gradient_b)/* / 1.0f*/;
    			pSrcRData = rdata + index;
    			pSrcGData = gdata + index;
    			pSrcBData = bdata + index;
    			*pSrcRData = (float)CLAMP0255_XY(BousseauColorModel(*pSrcRData, gradient, strength), 1.0f);
    			*pSrcGData = (float)CLAMP0255_XY(BousseauColorModel(*pSrcGData, gradient, strength), 1.0f);
    			*pSrcBData = (float)CLAMP0255_XY(BousseauColorModel(*pSrcBData, gradient, strength), 1.0f);
    		}
    	}
    
    	free(rcopy);
    	rcopy = NULL;
    	free(gcopy);
    	gcopy = NULL;
    	free(bcopy);
    	bcopy = NULL;
    }
           颜料分散演示样例代码:
    void PigmentDispersion(float *rdata, float *gdata, float *bdata, float strength, int width, int height)
    {
    	int size = width*height;
    	float *noise_gaussian = (float *)malloc(size * sizeof(float));
    	GaussianNoise(noise_gaussian, width, height);
    
    	float *pSrcRData = rdata, *pSrcGData = gdata, *pSrcBData = bdata;
    	for (int i = 0; i < size; i++, pSrcRData++, pSrcGData++, pSrcBData++)
    	{
    		*pSrcRData = (float)CLAMP0255_XY(BousseauColorModel(*pSrcRData, noise_gaussian[i], strength), 1.0f);
    		*pSrcGData = (float)CLAMP0255_XY(BousseauColorModel(*pSrcGData, noise_gaussian[i], strength), 1.0f);
    		*pSrcBData = (float)CLAMP0255_XY(BousseauColorModel(*pSrcBData, noise_gaussian[i], strength), 1.0f);
    	}
    
    	free(noise_gaussian);
    	noise_gaussian = NULL;
    }
           假设颜料分散模拟的好。感觉还是有水彩画那么点意思的,只是如今也懒得优化了。以下是一些效果图:
             
         
         
         
         
                     
                     
        
        
           也欢迎下载演示样例demo:http://download.csdn.net/detail/u013085897/9747177



  • 相关阅读:
    cross-domain
    【转】React、Vue访问NotFound
    Flutter环境配置
    antd遇见的坑
    npm源设置
    React中的生命周期函数
    【备忘】javascript原型、Function、eval、闭包、json处理、类、arguments不定
    ADB获取手机信息
    selenium操作
    操作execl
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7345287.html
Copyright © 2011-2022 走看看