Vuo 2.4.4
Loading...
Searching...
No Matches
VuoImageConvolve.c
Go to the documentation of this file.
1
10#include "VuoImageRenderer.h"
11#include "VuoImageConvolve.h"
12#include <OpenGL/CGLMacro.h>
13
15#ifdef VUO_COMPILER
17 "title" : "VuoImageConvolve",
18 "dependencies" : [
19 "VuoImageRenderer",
20 "VuoInteger",
21 "VuoReal",
22 ]
23 });
24#endif
26
30typedef struct
31{
32 VuoShader shader;
34
38void VuoImageConvolve_free(void *convolve)
39{
41 VuoRelease(bi->shader);
42 free(bi);
43}
44
50double VuoImageConvolve_laplacianOfGaussian(double x, double y, double radius)
51{
52 const double sigma = radius * .6 * sqrt(2);
53 double r2 = x*x + y*y;
54 // Clamp normalization term to minimum of 1, to keep the DC offset low for small sigma values.
55 return -10. / sqrt(2. * M_PI * fmax(1, sigma * sigma * sigma * sigma))
56 * (1. - r2/(2*sigma*sigma))
57 * exp(-r2/(2*sigma*sigma));
58}
59
64{
65 // Kernel width estimation from
66 // "On the discrete representation of the Laplacian of Gaussian" by Steve R. Gunn
67 // (Pattern Recognition 32 (1999), page 1463 - 1472).
68 // µₛ where epsilon < 10^-6.
69 const double mu_s = 5;
70
71 // Scale as in VuoImageBlur_calculateWeights().
72 const double sigma = radius * .6 * sqrt(2);
73
74 return 1 + 2 * ceil(mu_s * sigma);
75}
76
80VuoImage VuoImageConvolve_generateMatrix(VuoImageConvolveFunction f, unsigned int width, bool removeDCOffset, double param)
81{
82 // Width must be odd.
83 int pixelsWide = width | 1;
84// printf("pixelsWide = %d\n", pixelsWide);
85
86 float *pixelFloats = (float *)malloc(sizeof(float) * pixelsWide * pixelsWide);
87 double sum = 0;
88 for (int y = 0; y < pixelsWide; ++y)
89 for (int x = 0; x < pixelsWide; ++x)
90 {
91 float v = f(x - pixelsWide/2, y - pixelsWide/2, param);
92 pixelFloats[y * pixelsWide + x] = v;
93 sum += v;
94 }
95
96 if (removeDCOffset)
97 {
98 double offset = sum / (pixelsWide * pixelsWide);
99 for (int y = 0; y < pixelsWide; ++y)
100 for (int x = 0; x < pixelsWide; ++x)
101 pixelFloats[y * pixelsWide + x] -= offset;
102 }
103
104// double sum2 = 0;
105// for (int y = 0; y < pixelsWide; ++y)
106// {
107// for (int x = 0; x < pixelsWide; ++x)
108// {
109// sum2 += pixelFloats[y * pixelsWide + x];
110// printf("%9.5f ", pixelFloats[y * pixelsWide + x]);
111// }
112// printf("\n");
113// }
114// if (removeDCOffset)
115// printf("sum before removing DC offset = %g after = %g\n", sum, sum2);
116// else
117// printf("sum = %g\n", sum);
118
119 return VuoImage_makeFromBuffer(pixelFloats, GL_LUMINANCE, pixelsWide, pixelsWide, VuoImageColorDepth_32, ^(void *buffer){ free(pixelFloats); });
120}
121
126{
129
130 static const char *fragmentShader = VUOSHADER_GLSL_SOURCE(120,
131 \n#include "VuoGlslAlpha.glsl"
132 \n#include "VuoGlslBrightness.glsl"
133
134 varying vec2 fragmentTextureCoordinate;
135
136 uniform sampler2D image;
137 uniform vec2 viewportSize;
138 uniform sampler2D convolutionMatrix;
139 uniform int convolutionMatrixWidth;
140 uniform int channels;
141 uniform float intensity;
142 uniform float threshold;
143 uniform int range;
144
145 float weight(int x, int y)
146 {
147 return texture2D(convolutionMatrix, vec2(x + .5, y + .5) / float(convolutionMatrixWidth)).r;
148 }
149
150 void main(void)
151 {
152 vec2 uv = fragmentTextureCoordinate;
153
154 vec4 colorSum = vec4(0.);
155
156 for (int y = 0; y < convolutionMatrixWidth; y++)
157 for (int x = 0; x < convolutionMatrixWidth; x++)
158 {
159 float w = weight(x,y);
160 vec4 color = VuoGlsl_sample(image, uv + vec2(
161 (x - convolutionMatrixWidth/2)/viewportSize.x,
162 (y - convolutionMatrixWidth/2)/viewportSize.y
163 ));
164 color = VuoGlsl_gray(color, channels) * w;
165 colorSum += color;
166 }
167
168 float brightness = VuoGlsl_brightness(colorSum, 0);
169 if (abs(brightness) < threshold)
170 colorSum.rgb = vec3(0.);
171
172 colorSum.rgb *= intensity;
173
174 vec4 color = VuoGlsl_sample(image, uv);
175
176 if (range == 0) // VuoDiode_Unipolar
177 colorSum.rgb = clamp(colorSum.rgb / 2. + .5, 0, color.a);
178 else if (range == 1) // VuoDiode_Bipolar
179 colorSum.rgb = clamp(colorSum.rgb, 0, color.a);
180 else if (range == 2) // VuoDiode_Absolute
181 colorSum.rgb = clamp(abs(colorSum.rgb), 0, color.a);
182
183 gl_FragColor = vec4(colorSum.rgb, color.a);
184 }
185 );
186
187 bi->shader = VuoShader_make("General Convolution Shader");
188 VuoShader_addSource(bi->shader, VuoMesh_IndividualTriangles, NULL, NULL, fragmentShader);
189 VuoRetain(bi->shader);
190
191 return (VuoImageConvolve)bi;
192}
193
199VuoImage VuoImageConvolve_convolve(VuoImageConvolve convolve, VuoImage image, VuoImage convolutionMatrix, VuoThresholdType channels, double intensity, double threshold, VuoDiode range)
200{
201 if (!VuoImage_isPopulated(image))
202 return NULL;
203
204 if (!VuoImage_isPopulated(convolutionMatrix)
205 || convolutionMatrix->pixelsWide != convolutionMatrix->pixelsHigh
206 || !(convolutionMatrix->pixelsWide & 1)
207 || !(convolutionMatrix->pixelsHigh & 1))
208 return NULL;
209
211
212 VuoShader_setUniform_VuoImage (bi->shader, "image", image);
213 VuoShader_setUniform_VuoImage (bi->shader, "convolutionMatrix", convolutionMatrix);
214 VuoShader_setUniform_VuoInteger(bi->shader, "convolutionMatrixWidth", convolutionMatrix->pixelsWide);
215 VuoShader_setUniform_VuoInteger(bi->shader, "channels", channels);
216 VuoShader_setUniform_VuoReal (bi->shader, "intensity", intensity);
217 VuoShader_setUniform_VuoReal (bi->shader, "threshold", threshold);
218 VuoShader_setUniform_VuoInteger(bi->shader, "range", range);
219
220 VuoImage convolvedImage = VuoImageRenderer_render(bi->shader, image->pixelsWide, image->pixelsHigh, VuoImage_getColorDepth(image));
221 VuoImage_setWrapMode(convolvedImage, VuoImage_getWrapMode(image));
222 return convolvedImage;
223}