圖像處理之水彩畫特效生成算法

       在研究非真實感繪製相關算法時,水彩畫算法是第一個開始看的,只是卻拖到最後總結。

水彩畫仍是挺很差模擬的,裏面涉及的算法比較多,本文實現的水彩畫算法主要參考如下兩篇文章,《Interactive watercolor rendering with temporal coherence and abstraction》 、《Towards Photo Watercolorization with Artistic Verisimilitude》 。第一篇文章比較早。第二篇文章比較新。經過這兩篇文章。可以對水彩畫的模擬過程有一個大概瞭解。只是由於涉及的環節比較多。在實現過程當中,有些地方作了簡化,又新增了一些計算環節。總體上獲得效果和文章比,並不是嚴格一致。只是本文實現的水彩畫算法執行較慢。仍是比較耗時的。算法

       水彩畫算法主要涉及的計算環節有:(1)簡化細節;(2)邊緣抖動;(3)邊緣加深;(4)顏料分散。(5)紊流效果;(6)疊加紙張紋理等。上述環節中,簡化細節是很是耗時的,主要是採用了meanshift及各向異性的Kuwahara濾波緣由,也許有更好的替代方法。有待優化。本文很是多地方使用了噪聲,主要用到了高斯噪聲及柏林噪聲。主要用於模擬邊緣抖動、顏料分散及紊流效果。

邊緣抖動有點相似前文水面倒影算法中的置換濾鏡,主要模擬顏料的流動效果。post

邊緣加深主要模擬顏料中止流動後。在邊緣處的沉澱痕跡。顏料分散及紊流效果,主要是模擬水彩顏料滲透效果。只是本文實現的算法,顏料分散作的不是很是愜意。離真實水彩畫那種顏料擴散及滲透效果還有很是大差距。優化

       如下是部分演示樣例代碼:
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
相關文章
相關標籤/搜索