lygia
/v1.1.6
/filter
/kuwahara
)Kuwahara image abstraction, drawn from the work of Kyprianidis, et. al. in their publication "Anisotropic Kuwahara Filtering on the GPU" within the GPU Pro collection. This produces an oil-painting-like image, but it is extremely computationally expensive, so it can take seconds to render a frame on an iPad 2. This might be best used for still images.
Dependencies:
Use:
kuwahara(<SAMPLER_TYPE> texture, <vec2> st, <vec2> pixel)
#ifndef KUWAHARA_TYPE
#define KUWAHARA_TYPE vec4
#endif
#ifndef KUWAHARA_SAMPLER_FNC
#define KUWAHARA_SAMPLER_FNC(TEX, UV) SAMPLER_FNC(TEX, UV)
#endif
#ifndef FNC_KUWAHARA
#define FNC_KUWAHARA
#ifdef TARGET_MOBILE
KUWAHARA_TYPE kuwahara(in SAMPLER_TYPE tex, in vec2 st, in vec2 pixel, in int radius) {
#ifndef KUWAHARA_RADIUS
#define KUWAHARA_RADIUS radius
#endif
float n = float((KUWAHARA_RADIUS + 1) * (KUWAHARA_RADIUS + 1));
int i; int j;
KUWAHARA_TYPE m0 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE m1 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE m2 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE m3 = KUWAHARA_TYPE(0.0);
KUWAHARA_TYPE s0 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE s1 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE s2 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE s3 = KUWAHARA_TYPE(0.0);
KUWAHARA_TYPE rta = KUWAHARA_TYPE(0.0);
KUWAHARA_TYPE c = KUWAHARA_TYPE(0.0);
for (j = -KUWAHARA_RADIUS; j <= 0; ++j) {
for (i = -KUWAHARA_RADIUS; i <= 0; ++i) {
c = KUWAHARA_SAMPLER_FNC(tex, st + vec2(i,j) * pixel);
m0 += c;
s0 += c * c;
}
}
for (j = -KUWAHARA_RADIUS; j <= 0; ++j) {
for (i = 0; i <= KUWAHARA_RADIUS; ++i) {
c = KUWAHARA_SAMPLER_FNC(tex, st + vec2(i,j) * pixel);
m1 += c;
s1 += c * c;
}
}
for (j = 0; j <= KUWAHARA_RADIUS; ++j) {
for (i = 0; i <= KUWAHARA_RADIUS; ++i) {
c = KUWAHARA_SAMPLER_FNC(tex, st + vec2(i,j) * pixel);
m2 += c;
s2 += c * c;
}
}
for (j = 0; j <= KUWAHARA_RADIUS; ++j) {
for (i = -KUWAHARA_RADIUS; i <= 0; ++i) {
c = KUWAHARA_SAMPLER_FNC(tex, st + vec2(i,j) * pixel);
m3 += c;
s3 += c * c;
}
}
float min_sigma2 = 1e+2;
m0 /= n;
s0 = abs(s0 / n - m0 * m0);
float sigma2 = s0.r + s0.g + s0.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m0;
}
m1 /= n;
s1 = abs(s1 / n - m1 * m1);
sigma2 = s1.r + s1.g + s1.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m1;
}
m2 /= n;
s2 = abs(s2 / n - m2 * m2);
sigma2 = s2.r + s2.g + s2.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m2;
}
m3 /= n;
s3 = abs(s3 / n - m3 * m3);
sigma2 = s3.r + s3.g + s3.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m3;
}
return rta;
}
#else
KUWAHARA_TYPE kuwahara(in SAMPLER_TYPE tex, in vec2 st, in vec2 pixel, in int radius) {
#ifndef KUWAHARA_RADIUS
#if defined(PLATFORM_WEBGL)
#define KUWAHARA_RADIUS 20
float n = float((radius + 1) * (radius + 1));
#else
#define KUWAHARA_RADIUS radius
float n = float((KUWAHARA_RADIUS + 1) * (KUWAHARA_RADIUS + 1));
#endif
#else
float n = float((KUWAHARA_RADIUS + 1) * (KUWAHARA_RADIUS + 1));
#endif
int i; int j;
KUWAHARA_TYPE m0 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE m1 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE m2 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE m3 = KUWAHARA_TYPE(0.0);
KUWAHARA_TYPE s0 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE s1 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE s2 = KUWAHARA_TYPE(0.0); KUWAHARA_TYPE s3 = KUWAHARA_TYPE(0.0);
KUWAHARA_TYPE rta = KUWAHARA_TYPE(0.0);
KUWAHARA_TYPE c = KUWAHARA_TYPE(0.0);
for (j = -KUWAHARA_RADIUS; j <= 0; ++j) {
for (i = -KUWAHARA_RADIUS; i <= 0; ++i) {
c = KUWAHARA_SAMPLER_FNC(tex, st + vec2(i,j) * pixel);
m0 += c;
s0 += c * c;
}
}
for (j = -KUWAHARA_RADIUS; j <= 0; ++j) {
for (i = 0; i <= KUWAHARA_RADIUS; ++i) {
#if defined(PLATFORM_WEBGL)
if (i > radius)
break;
#endif
c = KUWAHARA_SAMPLER_FNC(tex, st + vec2(i,j) * pixel);
m1 += c;
s1 += c * c;
}
}
for (j = 0; j <= KUWAHARA_RADIUS; ++j) {
#if defined(PLATFORM_WEBGL)
if (j > radius)
break;
#endif
for (i = 0; i <= KUWAHARA_RADIUS; ++i) {
#if defined(PLATFORM_WEBGL)
if (i > radius)
break;
#endif
c = KUWAHARA_SAMPLER_FNC(tex, st + vec2(i,j) * pixel);
m2 += c;
s2 += c * c;
}
}
for (j = 0; j <= KUWAHARA_RADIUS; ++j) {
for (i = -KUWAHARA_RADIUS; i <= 0; ++i) {
c = KUWAHARA_SAMPLER_FNC(tex, st + vec2(i,j) * pixel);
m3 += c;
s3 += c * c;
}
}
float min_sigma2 = 1e+2;
m0 /= n;
s0 = abs(s0 / n - m0 * m0);
float sigma2 = s0.r + s0.g + s0.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m0;
}
m1 /= n;
s1 = abs(s1 / n - m1 * m1);
sigma2 = s1.r + s1.g + s1.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m1;
}
m2 /= n;
s2 = abs(s2 / n - m2 * m2);
sigma2 = s2.r + s2.g + s2.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m2;
}
m3 /= n;
s3 = abs(s3 / n - m3 * m3);
sigma2 = s3.r + s3.g + s3.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m3;
}
return rta;
}
#endif
#endif
Dependencies:
Use:
kuwahara(<SAMPLER_TYPE> texture, <float2> st, <float2> pixel)
#ifndef KUWAHARA_TYPE
#define KUWAHARA_TYPE float4
#endif
#ifndef KUWAHARA_SAMPLER_FNC
#define KUWAHARA_SAMPLER_FNC(TEX, UV) SAMPLER_FNC(TEX, UV)
#endif
#ifndef KUWAHARA_RADIUS_MAX
#define KUWAHARA_RADIUS_MAX 15
#endif
#ifndef KUWAHARA_RADIUS
#define KUWAHARA_RADIUS radius
#endif
#ifndef FNC_KUWAHARA
#define FNC_KUWAHARA
KUWAHARA_TYPE kuwahara(in SAMPLER_TYPE tex, in float2 st, in float2 pixel, in int radius) {
float n = float((KUWAHARA_RADIUS + 1) * (KUWAHARA_RADIUS + 1));
int i; int j;
KUWAHARA_TYPE m0 = float4(0.0, 0.0, 0.0, 0.0); KUWAHARA_TYPE m1 = float4(0.0, 0.0, 0.0, 0.0); KUWAHARA_TYPE m2 = float4(0.0, 0.0, 0.0, 0.0); KUWAHARA_TYPE m3 = float4(0.0, 0.0, 0.0, 0.0);
KUWAHARA_TYPE s0 = float4(0.0, 0.0, 0.0, 0.0); KUWAHARA_TYPE s1 = float4(0.0, 0.0, 0.0, 0.0); KUWAHARA_TYPE s2 = float4(0.0, 0.0, 0.0, 0.0); KUWAHARA_TYPE s3 = float4(0.0, 0.0, 0.0, 0.0);
KUWAHARA_TYPE rta = float4(0.0, 0.0, 0.0, 0.0);
KUWAHARA_TYPE c = float4(0.0, 0.0, 0.0, 0.0);
for (j = -KUWAHARA_RADIUS; j <= 0; ++j) {
for (i = -KUWAHARA_RADIUS; i <= 0; ++i) {
c = KUWAHARA_SAMPLER_FNC(tex, st + float2(i,j) * pixel);
m0 += c;
s0 += c * c;
}
}
for (j = -KUWAHARA_RADIUS; j <= 0; ++j) {
for (i = 0; i <= KUWAHARA_RADIUS_MAX; ++i) {
if (i > KUWAHARA_RADIUS)
break;
c = KUWAHARA_SAMPLER_FNC(tex, st + float2(i,j) * pixel);
m1 += c;
s1 += c * c;
}
}
for (j = 0; j <= KUWAHARA_RADIUS_MAX; ++j) {
if (j > KUWAHARA_RADIUS)
break;
for (i = 0; i <= KUWAHARA_RADIUS_MAX; ++i) {
if (i > KUWAHARA_RADIUS)
break;
c = KUWAHARA_SAMPLER_FNC(tex, st + float2(i,j) * pixel);
m2 += c;
s2 += c * c;
}
}
for (j = 0; j <= KUWAHARA_RADIUS; ++j) {
if (j > KUWAHARA_RADIUS)
break;
for (i = -KUWAHARA_RADIUS; i <= 0; ++i) {
c = KUWAHARA_SAMPLER_FNC(tex, st + float2(i,j) * pixel);
m3 += c;
s3 += c * c;
}
}
float min_sigma2 = 1e+2;
m0 /= n;
s0 = abs(s0 / n - m0 * m0);
float sigma2 = s0.r + s0.g + s0.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m0;
}
m1 /= n;
s1 = abs(s1 / n - m1 * m1);
sigma2 = s1.r + s1.g + s1.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m1;
}
m2 /= n;
s2 = abs(s2 / n - m2 * m2);
sigma2 = s2.r + s2.g + s2.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m2;
}
m3 /= n;
s3 = abs(s3 / n - m3 * m3);
sigma2 = s3.r + s3.g + s3.b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
rta = m3;
}
return rta;
}
#endif
LYGIA is dual-licensed under the Prosperity License and the Patron License for sponsors and contributors.
Sponsors and contributors are automatically added to the Patron License and they can ignore the any non-commercial rule of the Prosperity Licensed software (please take a look to the exception).
It's also possible to get a permanent comercial license hook to a single and specific version of LYGIA.
Sign up for the news letter bellow, joing the LYGIA's channel on Discord or follow the Github repository