Vuo  2.0.0
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
26 
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 int range;
143 
144  float weight(int x, int y)
145  {
146  return texture2D(convolutionMatrix, vec2(x + .5, y + .5) / float(convolutionMatrixWidth)).r;
147  }
148 
149  void main(void)
150  {
151  vec2 uv = fragmentTextureCoordinate;
152 
153  vec4 colorSum = vec4(0.);
154 
155  for (int y = 0; y < convolutionMatrixWidth; y++)
156  for (int x = 0; x < convolutionMatrixWidth; x++)
157  {
158  float w = weight(x,y);
159  vec4 color = VuoGlsl_sample(image, uv + vec2(
160  (x - convolutionMatrixWidth/2)/viewportSize.x,
161  (y - convolutionMatrixWidth/2)/viewportSize.y
162  ));
163  color = VuoGlsl_gray(color, channels) * w;
164  colorSum += color;
165  }
166 
167  colorSum.rgb *= intensity;
168 
169  vec4 color = VuoGlsl_sample(image, uv);
170 
171  if (range == 0) // VuoDiode_Unipolar
172  colorSum.rgb = clamp(colorSum.rgb / 2. + .5, 0, color.a);
173  else if (range == 1) // VuoDiode_Bipolar
174  colorSum.rgb = clamp(colorSum.rgb, 0, color.a);
175  else if (range == 2) // VuoDiode_Absolute
176  colorSum.rgb = clamp(abs(colorSum.rgb), 0, color.a);
177 
178  gl_FragColor = vec4(colorSum.rgb, color.a);
179  }
180  );
181 
182  bi->shader = VuoShader_make("General Convolution Shader");
183  VuoShader_addSource(bi->shader, VuoMesh_IndividualTriangles, NULL, NULL, fragmentShader);
184  VuoRetain(bi->shader);
185 
186  return (VuoImageConvolve)bi;
187 }
188 
194 VuoImage VuoImageConvolve_convolve(VuoImageConvolve convolve, VuoImage image, VuoImage convolutionMatrix, VuoThresholdType channels, double intensity, VuoDiode range)
195 {
196  if (!VuoImage_isPopulated(image))
197  return NULL;
198 
199  if (!VuoImage_isPopulated(convolutionMatrix)
200  || convolutionMatrix->pixelsWide != convolutionMatrix->pixelsHigh
201  || !(convolutionMatrix->pixelsWide & 1)
202  || !(convolutionMatrix->pixelsHigh & 1))
203  return NULL;
204 
206 
207  VuoShader_setUniform_VuoImage (bi->shader, "image", image);
208  VuoShader_setUniform_VuoImage (bi->shader, "convolutionMatrix", convolutionMatrix);
209  VuoShader_setUniform_VuoInteger(bi->shader, "convolutionMatrixWidth", convolutionMatrix->pixelsWide);
210  VuoShader_setUniform_VuoInteger(bi->shader, "channels", channels);
211  VuoShader_setUniform_VuoReal (bi->shader, "intensity", intensity);
212  VuoShader_setUniform_VuoInteger(bi->shader, "range", range);
213 
214  VuoImage convolvedImage = VuoImageRenderer_render(bi->shader, image->pixelsWide, image->pixelsHigh, VuoImage_getColorDepth(image));
215  VuoImage_setWrapMode(convolvedImage, VuoImage_getWrapMode(image));
216  return convolvedImage;
217 }