[440/906] sobel: implement sobel filter using separable kernels

Reimplement sobel in a multipass fully separated convolution:
- calculate x gradient map convolving first horizontally with blurring
  kernel and then vertically with differentiating kernel
- calculate y gradient map convolving first vertically with blurring
  kernel and then horizonally with differentiating kernel
- calculate length of the gradient vector
Particular care was needed with normalization of the blurring kernel and
with grey level offset of the differentiating one to prevent overflow of
rgb values from the [0.0,1.0] range in intermediate passes.
Now works on i915.
This commit is contained in:
Filippo Argiolas 2010-04-27 09:08:22 +02:00 committed by Matthew Waters
parent 002f069962
commit 49df4dca3c
4 changed files with 229 additions and 63 deletions

View file

@ -308,8 +308,63 @@ const gchar *sobel_fragment_source =
" gl_FragColor = vec4(vec3(g), 1.0);" " gl_FragColor = vec4(vec3(g), 1.0);"
"}"; "}";
const gchar *sobel_gradient_length_fragment_source =
"#extension GL_ARB_texture_rectangle : enable\n"
"uniform sampler2DRect gx;"
"uniform sampler2DRect gy;"
"void main () {"
" vec4 dx = texture2DRect (gx, gl_TexCoord[0].st);"
" vec4 dy = texture2DRect (gy, gl_TexCoord[0].st);"
" dx = (dx - 0.5);"
" dy = (dy - 0.5);"
" gl_FragColor = vec4(sqrt(dx*dx + dy*dy));"
"}";
/* horizontal convolution */ /* horizontal convolution 3x3 */
const gchar *hconv3_fragment_source =
"#extension GL_ARB_texture_rectangle : enable\n"
"uniform sampler2DRect tex;"
"uniform float kernel[3];"
"uniform float offset;"
"void main () {"
" vec2 texturecoord[3];"
" float s = gl_TexCoord[0].s;"
" float t = gl_TexCoord[0].t;"
" texturecoord[0] = vec2(s-1.0, t);"
" texturecoord[1] = vec2(s, t);"
" texturecoord[2] = vec2(s+1.0, t);"
" int i;"
" vec4 sum = vec4 (0.0);"
" for (i = 0; i < 3; i++) { "
" vec4 neighbor = texture2DRect(tex, texturecoord[i]); "
" sum += neighbor * kernel[i];"
" }"
" gl_FragColor = sum + offset;"
"}";
/* vertical convolution 3x3 */
const gchar *vconv3_fragment_source =
"#extension GL_ARB_texture_rectangle : enable\n"
"uniform sampler2DRect tex;"
"uniform float kernel[3];"
"uniform float offset;"
"void main () {"
" vec2 texturecoord[3];"
" float s = gl_TexCoord[0].s;"
" float t = gl_TexCoord[0].t;"
" texturecoord[0] = vec2(s, t-1.0);"
" texturecoord[1] = vec2(s, t);"
" texturecoord[2] = vec2(s, t+1.0);"
" int i;"
" vec4 sum = vec4 (0.0);"
" for (i = 0; i < 3; i++) { "
" vec4 neighbor = texture2DRect(tex, texturecoord[i]);"
" sum += neighbor * kernel[i]; "
" }"
" gl_FragColor = sum + offset;"
"}";
/* horizontal convolution 9x9 */
const gchar *hconv9_fragment_source = const gchar *hconv9_fragment_source =
"#extension GL_ARB_texture_rectangle : enable\n" "#extension GL_ARB_texture_rectangle : enable\n"
"uniform sampler2DRect tex;" "uniform sampler2DRect tex;"
@ -336,7 +391,7 @@ const gchar *hconv9_fragment_source =
" gl_FragColor = sum;" " gl_FragColor = sum;"
"}"; "}";
/* vertical convolution */ /* vertical convolution 9x9 */
const gchar *vconv9_fragment_source = const gchar *vconv9_fragment_source =
"#extension GL_ARB_texture_rectangle : enable\n" "#extension GL_ARB_texture_rectangle : enable\n"
"uniform sampler2DRect tex;" "uniform sampler2DRect tex;"

View file

@ -33,6 +33,9 @@ extern const gchar *bulge_fragment_source;
extern const gchar *square_fragment_source; extern const gchar *square_fragment_source;
extern const gchar *luma_threshold_fragment_source; extern const gchar *luma_threshold_fragment_source;
extern const gchar *sobel_fragment_source; extern const gchar *sobel_fragment_source;
extern const gchar *sobel_gradient_length_fragment_source;
extern const gchar *hconv3_fragment_source;
extern const gchar *vconv3_fragment_source;
extern const gchar *hconv9_fragment_source; extern const gchar *hconv9_fragment_source;
extern const gchar *vconv9_fragment_source; extern const gchar *vconv9_fragment_source;
extern const gchar *sum_fragment_source; extern const gchar *sum_fragment_source;

View file

@ -65,32 +65,56 @@ static void gst_gl_filtersobel_draw_texture (GstGLFilterSobel * filtersobel,
static void gst_gl_filtersobel_init_shader (GstGLFilter * filter); static void gst_gl_filtersobel_init_shader (GstGLFilter * filter);
static gboolean gst_gl_filtersobel_filter (GstGLFilter * filter, static gboolean gst_gl_filtersobel_filter (GstGLFilter * filter,
GstGLBuffer * inbuf, GstGLBuffer * outbuf); GstGLBuffer * inbuf, GstGLBuffer * outbuf);
static void gst_gl_filtersobel_callback (gint width, gint height, guint texture, static void gst_gl_filtersobel_step_one (gint width, gint height, guint texture,
gpointer stuff); gpointer stuff);
static void gst_gl_filtersobel_step_two (gint width, gint height, guint texture,
gpointer stuff);
static void gst_gl_filtersobel_step_three (gint width, gint height,
guint texture, gpointer stuff);
static void gst_gl_filtersobel_step_four (gint width, gint height,
guint texture, gpointer stuff);
static void gst_gl_filtersobel_step_five (gint width, gint height,
guint texture, gpointer stuff);
static gfloat grad_kern[3] = {
1.0, 0.0, -1.0,
};
static gfloat blur_kern[3] = {
1.0 / 4.0, 2.0 / 4.0, 1.0 / 4.0,
};
static void static void
gst_gl_filtersobel_init_resources (GstGLFilter * filter) gst_gl_filtersobel_init_resources (GstGLFilter * filter)
{ {
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter);
int i;
glGenTextures (1, &filtersobel->midtexture); for (i = 0; i < 5; i++) {
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, filtersobel->midtexture); glGenTextures (1, &filtersobel->midtexture[i]);
glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, glBindTexture (GL_TEXTURE_RECTANGLE_ARB, filtersobel->midtexture[i]);
filter->width, filter->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); filter->width, filter->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_LINEAR);
GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_LINEAR);
GL_CLAMP_TO_EDGE); glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
}
} }
static void static void
gst_gl_filtersobel_reset_resources (GstGLFilter * filter) gst_gl_filtersobel_reset_resources (GstGLFilter * filter)
{ {
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter);
int i;
glDeleteTextures (1, &filtersobel->midtexture); for (i = 0; i < 5; i++) {
glDeleteTextures (1, &filtersobel->midtexture[i]);
}
} }
static void static void
@ -132,9 +156,13 @@ static void
gst_gl_filtersobel_init (GstGLFilterSobel * filtersobel, gst_gl_filtersobel_init (GstGLFilterSobel * filtersobel,
GstGLFilterSobelClass * klass) GstGLFilterSobelClass * klass)
{ {
filtersobel->shader0 = NULL; int i;
filtersobel->midtexture = 0; filtersobel->hconv = NULL;
filtersobel->vconv = NULL;
filtersobel->invert = FALSE; filtersobel->invert = FALSE;
for (i = 0; i < 5; i++) {
filtersobel->midtexture[i] = 0;
}
} }
static void static void
@ -143,7 +171,9 @@ gst_gl_filter_filtersobel_reset (GstGLFilter * filter)
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter);
//blocking call, wait the opengl thread has destroyed the shader //blocking call, wait the opengl thread has destroyed the shader
gst_gl_display_del_shader (filter->display, filtersobel->shader0); gst_gl_display_del_shader (filter->display, filtersobel->hconv);
gst_gl_display_del_shader (filter->display, filtersobel->vconv);
gst_gl_display_del_shader (filter->display, filtersobel->len);
} }
static void static void
@ -184,8 +214,12 @@ gst_gl_filtersobel_init_shader (GstGLFilter * filter)
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter);
//blocking call, wait the opengl thread has compiled the shader //blocking call, wait the opengl thread has compiled the shader
gst_gl_display_gen_shader (filter->display, 0, sobel_fragment_source, gst_gl_display_gen_shader (filter->display, 0, hconv3_fragment_source,
&filtersobel->shader0); &filtersobel->hconv);
gst_gl_display_gen_shader (filter->display, 0, vconv3_fragment_source,
&filtersobel->vconv);
gst_gl_display_gen_shader (filter->display, 0,
sobel_gradient_length_fragment_source, &filtersobel->len);
} }
static void static void
@ -211,71 +245,143 @@ gst_gl_filtersobel_draw_texture (GstGLFilterSobel * filtersobel, GLuint tex)
glEnd (); glEnd ();
} }
/*static void
change_view (GstGLDisplay *display, gpointer data)
{
// GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (data);
const double mirrormatrix[16] = {
-1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
glMatrixMode (GL_MODELVIEW);
glLoadMatrixd (mirrormatrix);
}*/
static gboolean static gboolean
gst_gl_filtersobel_filter (GstGLFilter * filter, GstGLBuffer * inbuf, gst_gl_filtersobel_filter (GstGLFilter * filter, GstGLBuffer * inbuf,
GstGLBuffer * outbuf) GstGLBuffer * outbuf)
{ {
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter); GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (filter);
// gst_gl_display_thread_add (filter->display, change_view, filtersobel); gst_gl_filter_render_to_target (filter, inbuf->texture,
filtersobel->midtexture[0], gst_gl_filtersobel_step_one, filtersobel);
gst_gl_filter_render_to_target (filter, inbuf->texture, outbuf->texture, gst_gl_filter_render_to_target (filter, filtersobel->midtexture[0],
gst_gl_filtersobel_callback, filtersobel); filtersobel->midtexture[1], gst_gl_filtersobel_step_two, filtersobel);
gst_gl_filter_render_to_target (filter, inbuf->texture,
filtersobel->midtexture[2], gst_gl_filtersobel_step_three, filtersobel);
gst_gl_filter_render_to_target (filter, filtersobel->midtexture[2],
filtersobel->midtexture[3], gst_gl_filtersobel_step_four, filtersobel);
gst_gl_filter_render_to_target (filter, filtersobel->midtexture[3],
outbuf->texture, gst_gl_filtersobel_step_five, filtersobel);
return TRUE; return TRUE;
} }
static void static void
gst_gl_filtersobel_callback (gint width, gint height, guint texture, gst_gl_filtersobel_step_one (gint width, gint height, guint texture,
gpointer stuff) gpointer stuff)
{ {
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (stuff); GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (stuff);
gfloat hkern[9] = {
1.0, 0.0, -1.0,
2.0, 0.0, -2.0,
1.0, 0.0, -1.0
};
gfloat vkern[9] = {
1.0, 2.0, 1.0,
0.0, 0.0, 0.0,
-1.0, -2.0, -1.0
};
glMatrixMode (GL_PROJECTION); glMatrixMode (GL_PROJECTION);
glLoadIdentity (); glLoadIdentity ();
gst_gl_shader_use (filtersobel->shader0); gst_gl_shader_use (filtersobel->hconv);
glActiveTexture (GL_TEXTURE1); glActiveTexture (GL_TEXTURE1);
glEnable (GL_TEXTURE_RECTANGLE_ARB); glEnable (GL_TEXTURE_RECTANGLE_ARB);
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture); glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture);
glDisable (GL_TEXTURE_RECTANGLE_ARB); glDisable (GL_TEXTURE_RECTANGLE_ARB);
gst_gl_shader_set_uniform_1i (filtersobel->shader0, "tex", 1); gst_gl_shader_set_uniform_1i (filtersobel->hconv, "tex", 1);
gst_gl_shader_set_uniform_1fv (filtersobel->hconv, "kernel", 3, blur_kern);
gst_gl_shader_set_uniform_1fv (filtersobel->shader0, "hkern", 9, hkern); gst_gl_shader_set_uniform_1f (filtersobel->hconv, "offset", 0.0);
gst_gl_shader_set_uniform_1fv (filtersobel->shader0, "vkern", 9, vkern);
gst_gl_filtersobel_draw_texture (filtersobel, texture);
gst_gl_shader_set_uniform_1i (filtersobel->shader0, "invert", }
filtersobel->invert);
static void
gst_gl_filtersobel_step_two (gint width, gint height, guint texture,
gpointer stuff)
{
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (stuff);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gst_gl_shader_use (filtersobel->vconv);
glActiveTexture (GL_TEXTURE1);
glEnable (GL_TEXTURE_RECTANGLE_ARB);
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture);
glDisable (GL_TEXTURE_RECTANGLE_ARB);
gst_gl_shader_set_uniform_1i (filtersobel->vconv, "tex", 1);
gst_gl_shader_set_uniform_1fv (filtersobel->vconv, "kernel", 3, grad_kern);
gst_gl_shader_set_uniform_1f (filtersobel->vconv, "offset", 0.5);
gst_gl_filtersobel_draw_texture (filtersobel, texture);
}
static void
gst_gl_filtersobel_step_three (gint width, gint height, guint texture,
gpointer stuff)
{
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (stuff);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gst_gl_shader_use (filtersobel->vconv);
glActiveTexture (GL_TEXTURE1);
glEnable (GL_TEXTURE_RECTANGLE_ARB);
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture);
glDisable (GL_TEXTURE_RECTANGLE_ARB);
gst_gl_shader_set_uniform_1i (filtersobel->vconv, "tex", 1);
gst_gl_shader_set_uniform_1fv (filtersobel->vconv, "kernel", 3, blur_kern);
gst_gl_shader_set_uniform_1f (filtersobel->vconv, "offset", 0.0);
gst_gl_filtersobel_draw_texture (filtersobel, texture);
}
static void
gst_gl_filtersobel_step_four (gint width, gint height, guint texture,
gpointer stuff)
{
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (stuff);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gst_gl_shader_use (filtersobel->hconv);
glActiveTexture (GL_TEXTURE1);
glEnable (GL_TEXTURE_RECTANGLE_ARB);
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, texture);
glDisable (GL_TEXTURE_RECTANGLE_ARB);
gst_gl_shader_set_uniform_1i (filtersobel->hconv, "tex", 1);
gst_gl_shader_set_uniform_1fv (filtersobel->hconv, "kernel", 3, grad_kern);
gst_gl_shader_set_uniform_1f (filtersobel->vconv, "offset", 0.5);
gst_gl_filtersobel_draw_texture (filtersobel, texture);
}
static void
gst_gl_filtersobel_step_five (gint width, gint height, guint texture,
gpointer stuff)
{
GstGLFilterSobel *filtersobel = GST_GL_FILTERSOBEL (stuff);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gst_gl_shader_use (filtersobel->len);
glActiveTexture (GL_TEXTURE1);
glEnable (GL_TEXTURE_RECTANGLE_ARB);
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, filtersobel->midtexture[1]);
glDisable (GL_TEXTURE_RECTANGLE_ARB);
gst_gl_shader_set_uniform_1i (filtersobel->len, "gx", 1);
glActiveTexture (GL_TEXTURE2);
glEnable (GL_TEXTURE_RECTANGLE_ARB);
glBindTexture (GL_TEXTURE_RECTANGLE_ARB, filtersobel->midtexture[3]);
glDisable (GL_TEXTURE_RECTANGLE_ARB);
gst_gl_shader_set_uniform_1i (filtersobel->len, "gy", 2);
gst_gl_filtersobel_draw_texture (filtersobel, texture); gst_gl_filtersobel_draw_texture (filtersobel, texture);
} }

View file

@ -36,11 +36,13 @@ typedef struct _GstGLFilterSobelClass GstGLFilterSobelClass;
struct _GstGLFilterSobel struct _GstGLFilterSobel
{ {
GstGLFilter filter; GstGLFilter filter;
GstGLShader *shader0; GstGLShader *hconv;
GstGLShader *vconv;
GstGLShader *len;
GLuint midtexture[5];
gboolean invert; gboolean invert;
GLuint midtexture;
}; };
struct _GstGLFilterSobelClass struct _GstGLFilterSobelClass