package com.marshalchen.common.uimodule.imageprocessing.filter.effect; import com.marshalchen.common.uimodule.imageprocessing.filter.BasicFilter; /** * 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. * radius: In integer specifying the number of pixels out from the center pixel to test when applying the filter. A higher value creates a more abstracted image, but at the cost of much greater processing time. * This may not work on some devices. Use {@link KuwaharaRadius3Filter} if it does not. * @author Chris Batt */ public class KuwaharaFilter extends BasicFilter { protected static final String UNIFORM_RADIUS = "u_Radius"; private int radius; public KuwaharaFilter(int radius) { this.radius = radius; } @Override protected String getFragmentShader() { return "precision highp float;\n" +"uniform sampler2D "+UNIFORM_TEXTURE0+";\n" +"varying vec2 "+VARYING_TEXCOORD+";\n" +"const int "+UNIFORM_RADIUS+" = "+radius+";\n" +"const vec2 src_size = vec2 (1.0 / 768.0, 1.0 / 1024.0);\n" +"void main(){\n" +" vec2 uv = "+VARYING_TEXCOORD+";\n" +" float n = float(("+UNIFORM_RADIUS+" + 1) * ("+UNIFORM_RADIUS+" + 1));\n" +" int i; int j;\n" +" vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n" +" vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n" +" vec3 c;\n" +" for (j = -"+UNIFORM_RADIUS+"; j <= 0; ++j) {\n" +" for (i = -"+UNIFORM_RADIUS+"; i <= 0; ++i) {\n" +" c = texture2D("+UNIFORM_TEXTURE0+", uv + vec2(i,j) * src_size).rgb;\n" +" m0 += c;\n" +" s0 += c * c;\n" +" }\n" +" }\n" +" for (j = -"+UNIFORM_RADIUS+"; j <= 0; ++j) {\n" +" for (i = 0; i <= "+UNIFORM_RADIUS+"; ++i) {\n" +" c = texture2D("+UNIFORM_TEXTURE0+", uv + vec2(i,j) * src_size).rgb;\n" +" m1 += c;\n" +" s1 += c * c;\n" +" }\n" +" }\n" +" for (j = 0; j <= "+UNIFORM_RADIUS+"; ++j) {\n" +" for (i = 0; i <= "+UNIFORM_RADIUS+"; ++i) {\n" +" c = texture2D("+UNIFORM_TEXTURE0+", uv + vec2(i,j) * src_size).rgb;\n" +" m2 += c;\n" +" s2 += c * c;\n" +" }\n" +" }\n" +" for (j = 0; j <= "+UNIFORM_RADIUS+"; ++j) {\n" +" for (i = -"+UNIFORM_RADIUS+"; i <= 0; ++i) {\n" +" c = texture2D("+UNIFORM_TEXTURE0+", uv + vec2(i,j) * src_size).rgb;\n" +" m3 += c;\n" +" s3 += c * c;\n" +" }\n" +" }\n" +" float min_sigma2 = 1e+2;\n" +" m0 /= n;\n" +" s0 = abs(s0 / n - m0 * m0);\n" +" float sigma2 = s0.r + s0.g + s0.b;\n" +" if (sigma2 < min_sigma2) {\n" +" min_sigma2 = sigma2;\n" +" gl_FragColor = vec4(m0, 1.0);\n" +" }\n" +" m1 /= n;\n" +" s1 = abs(s1 / n - m1 * m1);\n" +" sigma2 = s1.r + s1.g + s1.b;\n" +" if (sigma2 < min_sigma2) {\n" +" min_sigma2 = sigma2;\n" +" gl_FragColor = vec4(m1, 1.0);\n" +" }\n" +" m2 /= n;\n" +" s2 = abs(s2 / n - m2 * m2);\n" +" sigma2 = s2.r + s2.g + s2.b;\n" +" if (sigma2 < min_sigma2) {\n" +" min_sigma2 = sigma2;\n" +" gl_FragColor = vec4(m2, 1.0);\n" +" }\n" +" m3 /= n;\n" +" s3 = abs(s3 / n - m3 * m3);\n" +" sigma2 = s3.r + s3.g + s3.b;\n" +" if (sigma2 < min_sigma2) {\n" +" min_sigma2 = sigma2;\n" +" gl_FragColor = vec4(m3, 1.0);\n" +" }\n" +"}\n"; } }