Vuo  2.3.2
VuoImageConvolve.c
Go to the documentation of this file.
1 
10 #include "node.h"
11 #include "VuoImageRenderer.h"
12 #include "VuoImageConvolve.h"
13 #include <OpenGL/CGLMacro.h>
14 
16 #ifdef VUO_COMPILER
18  "title" : "VuoImageConvolve",
19  "dependencies" : [
20  "VuoImageRenderer",
21  "VuoInteger",
22  "VuoReal",
23  ]
24  });
25 #endif
27 
31 typedef struct
32 {
33  VuoShader shader;
35 
39 void VuoImageConvolve_free(void *convolve)
40 {
42  VuoRelease(bi->shader);
43 }
44 
50 double 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 
63 unsigned int VuoImageConvolve_laplacianOfGaussianWidth(double radius)
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 
80 VuoImage 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 
199 VuoImage 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 }