图像处理之水彩画特效生成算法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
图像处理之⽔彩画特效⽣成算法
在研究⾮真实感绘制相关算法时。
⽔彩画算法是第⼀个開始看的,只是却拖到最后总结。
⽔彩画还是挺不好模拟的,⾥⾯涉及的算法⽐較多。
本⽂实现的⽔彩画算法主要參考以下两篇⽂章,《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:。