templatematch: Allow changing template property on the fly

Previously changing the template property resulted in an exception
thrown from cvMatchTemplate, because "dist_image" (the intermediate
match-certainty-distribution) was the wrong size (because the
template image size had changed).

Locking has also been added to allow changing the properties (e.g. the
pattern to match) while the pipeline is playing.

 * gst_element_post_message is moved outside of the lock, because it will
   call into arbitrary user code (otherwise, if that user code calls into
   gst_templatematch_set_property on this same thread it would deadlock).

 * gst_template_match_load_template: If we fail to load the new template
   we still unload the previous template, so this element becomes a no-op
   in the pipeline. The alternative would be to keep the previous template;
   I believe unloading the previous template is a better choice, because it
   is consistent with the state this element would be in if it fails to
   load the very first template at start-up.

Thanks to Will Manley for the bulk of this work; any errors are probably
mine.
This commit is contained in:
William Manley 2012-06-20 15:05:40 +01:00 committed by Thiago Santos
parent c77808055d
commit b608767288

View file

@ -113,7 +113,8 @@ static gboolean gst_template_match_handle_sink_event (GstPad * pad,
static GstFlowReturn gst_template_match_chain (GstPad * pad, GstObject * parent, static GstFlowReturn gst_template_match_chain (GstPad * pad, GstObject * parent,
GstBuffer * buf); GstBuffer * buf);
static void gst_template_match_load_template (GstTemplateMatch * filter); static void gst_template_match_load_template (GstTemplateMatch * filter,
gchar * template);
static void gst_template_match_match (IplImage * input, IplImage * template, static void gst_template_match_match (IplImage * input, IplImage * template,
IplImage * dist_image, double *best_res, CvPoint * best_pos, int method); IplImage * dist_image, double *best_res, CvPoint * best_pos, int method);
@ -190,6 +191,7 @@ gst_template_match_set_property (GObject * object, guint prop_id,
switch (prop_id) { switch (prop_id) {
case PROP_METHOD: case PROP_METHOD:
GST_OBJECT_LOCK (filter);
switch (g_value_get_int (value)) { switch (g_value_get_int (value)) {
case 0: case 0:
filter->method = CV_TM_SQDIFF; filter->method = CV_TM_SQDIFF;
@ -210,13 +212,15 @@ gst_template_match_set_property (GObject * object, guint prop_id,
filter->method = CV_TM_CCOEFF_NORMED; filter->method = CV_TM_CCOEFF_NORMED;
break; break;
} }
GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_TEMPLATE: case PROP_TEMPLATE:
filter->template = (char *) g_value_get_string (value); gst_template_match_load_template (filter, g_value_dup_string (value));
gst_template_match_load_template (filter);
break; break;
case PROP_DISPLAY: case PROP_DISPLAY:
GST_OBJECT_LOCK (filter);
filter->display = g_value_get_boolean (value); filter->display = g_value_get_boolean (value);
GST_OBJECT_UNLOCK (filter);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -290,6 +294,7 @@ gst_template_match_finalize (GObject * object)
GstTemplateMatch *filter; GstTemplateMatch *filter;
filter = GST_TEMPLATE_MATCH (object); filter = GST_TEMPLATE_MATCH (object);
g_free (filter->template);
if (filter->cvImage) { if (filter->cvImage) {
cvReleaseImageHeader (&filter->cvImage); cvReleaseImageHeader (&filter->cvImage);
} }
@ -313,6 +318,7 @@ gst_template_match_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
CvPoint best_pos; CvPoint best_pos;
double best_res; double best_res;
GstMapInfo info; GstMapInfo info;
GstMessage *m = NULL;
filter = GST_TEMPLATE_MATCH (parent); filter = GST_TEMPLATE_MATCH (parent);
@ -325,6 +331,7 @@ gst_template_match_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
gst_buffer_map (buf, &info, GST_MAP_READWRITE); gst_buffer_map (buf, &info, GST_MAP_READWRITE);
filter->cvImage->imageData = (char *) info.data; filter->cvImage->imageData = (char *) info.data;
GST_OBJECT_LOCK (filter);
if (filter->cvTemplateImage && !filter->cvDistImage) { if (filter->cvTemplateImage && !filter->cvDistImage) {
if (filter->cvTemplateImage->width > filter->cvImage->width) { if (filter->cvTemplateImage->width > filter->cvImage->width) {
GST_WARNING ("Template Image is wider than input image"); GST_WARNING ("Template Image is wider than input image");
@ -348,7 +355,6 @@ gst_template_match_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
} }
if (filter->cvTemplateImage && filter->cvImage && filter->cvDistImage) { if (filter->cvTemplateImage && filter->cvImage && filter->cvDistImage) {
GstStructure *s; GstStructure *s;
GstMessage *m;
gst_template_match_match (filter->cvImage, filter->cvTemplateImage, gst_template_match_match (filter->cvImage, filter->cvTemplateImage,
filter->cvDistImage, &best_res, &best_pos, filter->method); filter->cvDistImage, &best_res, &best_pos, filter->method);
@ -361,7 +367,6 @@ gst_template_match_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
"result", G_TYPE_DOUBLE, best_res, NULL); "result", G_TYPE_DOUBLE, best_res, NULL);
m = gst_message_new_element (GST_OBJECT (filter), s); m = gst_message_new_element (GST_OBJECT (filter), s);
gst_element_post_message (GST_ELEMENT (filter), m);
if (filter->display) { if (filter->display) {
CvPoint corner = best_pos; CvPoint corner = best_pos;
@ -375,7 +380,11 @@ gst_template_match_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
} }
} }
GST_OBJECT_UNLOCK (filter);
if (m) {
gst_element_post_message (GST_ELEMENT (filter), m);
}
return gst_pad_push (filter->srcpad, buf); return gst_pad_push (filter->srcpad, buf);
} }
@ -402,22 +411,35 @@ gst_template_match_match (IplImage * input, IplImage * template,
} }
/* We take ownership of template here */
static void static void
gst_template_match_load_template (GstTemplateMatch * filter) gst_template_match_load_template (GstTemplateMatch * filter, gchar * template)
{ {
if (filter->template) { gchar *oldTemplateFilename = NULL;
IplImage *oldTemplateImage = NULL, *newTemplateImage = NULL, *oldDistImage =
NULL;
if (filter->cvTemplateImage) { if (template) {
cvReleaseImage (&filter->cvTemplateImage); newTemplateImage = cvLoadImage (template, CV_LOAD_IMAGE_COLOR);
} if (!newTemplateImage) {
filter->cvTemplateImage = GST_WARNING ("Couldn't load template image: %s. error: %s",
cvLoadImage (filter->template, CV_LOAD_IMAGE_COLOR); template, g_strerror (errno));
if (!filter->cvTemplateImage) {
GST_WARNING ("Couldn't load template image: %s. error: %s",
filter->template, g_strerror (errno));
} }
} }
GST_OBJECT_LOCK (filter);
oldTemplateFilename = filter->template;
filter->template = template;
oldTemplateImage = filter->cvTemplateImage;
filter->cvTemplateImage = newTemplateImage;
oldDistImage = filter->cvDistImage;
/* This will be recreated in the chain function as required: */
filter->cvDistImage = NULL;
GST_OBJECT_UNLOCK (filter);
cvReleaseImage (&oldDistImage);
cvReleaseImage (&oldTemplateImage);
g_free (oldTemplateFilename);
} }