From: Damien Fouilleul Date: Wed, 6 Sep 2006 22:07:22 +0000 (+0000) Subject: - OpenCV video filter module by Dugal Harris X-Git-Tag: 0.9.0-test0~10401 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=989cefffec6e0942a9264c820fa9d178a76493a6;p=vlc - OpenCV video filter module by Dugal Harris --- diff --git a/AUTHORS b/AUTHORS index cf5da3f0ed..e8fa76e697 100644 --- a/AUTHORS +++ b/AUTHORS @@ -200,6 +200,11 @@ D: XVideo video output D: Gnome and Gtk+ interface enhancements S: United Kingdom +N: Dugal Harris +E: dugalh (at) protoclea .dot co dot. za +D: OpenCV video filter +S: South Africa + N: Derk-Jan Hartman E: hartman@videolan.org C: hartman diff --git a/THANKS b/THANKS index 08ab977753..99454216ec 100644 --- a/THANKS +++ b/THANKS @@ -77,6 +77,7 @@ Jean-Alexis Montignies - coreaudio multiple streams fix Jean-Baptiste Kempf - Contrib system upgrade Jean-Baptiste Le Stang - Equalizer-GUI-fixes (OSX) Jean-Philippe Grimaldi - bug fixes +Dugal Harris - ActiveX bug fixes Jean-Pierre Kuypers - French translation Jeffrey Baker - port of the ALSA plugin to the ALSA 1.0 API Jeroen Massar - IPv6 hostname resolution fix diff --git a/configure.ac b/configure.ac index 1efc3eb48f..05c2ca881c 100644 --- a/configure.ac +++ b/configure.ac @@ -1143,6 +1143,7 @@ VLC_ADD_PLUGINS([access_http access_mms access_ftp ipv4]) VLC_ADD_PLUGINS([packetizer_mpegvideo packetizer_h264]) VLC_ADD_PLUGINS([packetizer_mpeg4video packetizer_mpeg4audio]) + if test "${SYS}" != "mingwce"; then dnl VLC_ADD_PLUGINS([externrun]) VLC_ADD_PLUGINS([access_fake access_filter_timeshift access_filter_record]) @@ -1873,6 +1874,45 @@ then fi fi + +dnl +dnl OpenCV wrapper and example filters +dnl +AC_ARG_ENABLE(opencv, + [ --enable-opencv OpenCV (computer vision) filter (default disabled)]) +if test "${enable_opencv}" = "yes" -a "${CXX}" != ""; +then + AC_ARG_WITH(opencv-tree, + [ --with-opencv-tree=PATH opencv tree for linking]) + if test -n "${with_opencv_tree}" + then + if test "${SYS}" = "mingw32" -o "${SYS}" = "cygwin" + then + AC_MSG_CHECKING(for opencv in ${with_opencv_tree}) + if test -f ${with_opencv_tree}/cv/include/cv.h -a -f ${with_opencv_tree}/cxcore/include/cxcore.h \ + -a -f ${with_opencv_tree}/cvaux/include/cvaux.h -a -f ${with_opencv_tree}/otherlibs/highgui/highgui.h + then + AC_MSG_RESULT(yes) + VLC_ADD_PLUGINS([opencv_wrapper]) + VLC_ADD_LDFLAGS([opencv_wrapper],[-L${with_opencv_tree}/lib -lcv -lcxcore -lcvaux -lhighgui]) + VLC_ADD_CFLAGS([opencv_wrapper],[-I${with_opencv_tree}/cv/include -I${with_opencv_tree}/cxcore/include -I${with_opencv_tree}/cvaux/include -I${with_opencv_tree}/otherlibs/highgui]) + AC_LANG_PUSH(C++) + VLC_ADD_PLUGINS([opencv_example]) + VLC_ADD_LDFLAGS([opencv_example],[-L${with_opencv_tree}/lib -lcv -lcxcore -lcvaux -lhighgui]) + VLC_ADD_CXXFLAGS([opencv_example],[-I${with_opencv_tree}/cv/include -I${with_opencv_tree}/cxcore/include -I${with_opencv_tree}/cvaux/include -I${with_opencv_tree}/otherlibs/highgui]) + AC_LANG_POP(C++) + else + dnl No opencv could be found, sorry + AC_MSG_RESULT(no) + AC_MSG_ERROR([cannot find opencv in ${with_opencv_tree}]) + fi + else + AC_MSG_WARN([--enable-opencv currently only works on windows]) + fi + fi +fi + + dnl dnl libsmbclient plugin dnl diff --git a/modules/video_filter/Modules.am b/modules/video_filter/Modules.am index b12bca2df5..3cd0b54958 100644 --- a/modules/video_filter/Modules.am +++ b/modules/video_filter/Modules.am @@ -21,4 +21,6 @@ SOURCES_wave = wave.c SOURCES_ripple = ripple.c SOURCES_psychedelic = psychedelic.c SOURCES_gradient = gradient.c +SOURCES_opencv_wrapper = opencv_wrapper.c +SOURCES_opencv_example = opencv_example.cpp noinst_HEADERS = filter_common.h diff --git a/modules/video_filter/filter_event_info.h b/modules/video_filter/filter_event_info.h new file mode 100644 index 0000000000..7815bad265 --- /dev/null +++ b/modules/video_filter/filter_event_info.h @@ -0,0 +1,59 @@ +/***************************************************************************** + * opencv_event_info.h: + ***************************************************************************** + * Copyright (C) 2004-2005 the VideoLAN team + * $Id: ??? + * + * Authors: Dugal Harris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef FILTER_EVENT_INFO_H_ +#define FILTER_EVENT_INFO_H_ + +#define VIDEO_FILTER_EVENT_VARIABLE N_("video-filter-event") + +typedef struct video_filter_region_info_t +{ + int i_x; /* x-coordinate of the left-most rectangle corner[s] */ + int i_y; /* y-coordinate of the top-most or bottom-most + rectangle corner[s] */ + int i_width; /* width of the rectangle */ + int i_height; /* height of the rectangle */ + + int i_id; + int i_type; + + char *p_description; + float *pf_param; + int i_param_size; +} video_filter_region_info_t; + +typedef struct video_filter_event_info_t +{ + video_filter_region_info_t *p_region; + int i_region_size; +} video_filter_event_info_t; + +/*class CTest +{ +public: + CTest(){i=0;}; + void Method(){i=0;}; + int i; +};*/ + +#endif /*FILTER_EVENT_INFO_H_*/ diff --git a/modules/video_filter/opencv_example.cpp b/modules/video_filter/opencv_example.cpp new file mode 100644 index 0000000000..ab73a2c176 --- /dev/null +++ b/modules/video_filter/opencv_example.cpp @@ -0,0 +1,235 @@ +/***************************************************************************** + * opencv_example.cpp : Example OpenCV internal video filter + * (performs face identification). Mostly taken from the facedetect.c + * OpenCV sample. + ***************************************************************************** + * Copyright (C) 2006 the VideoLAN team + * + * Authors: Dugal Harris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include +#include +#include + + +#include +#include +#include "vlc_filter.h" +#include "filter_common.h" +#include "vlc_image.h" +#include "filter_event_info.h" + +/***************************************************************************** + * filter_sys_t : filter descriptor + *****************************************************************************/ +struct filter_sys_t +{ + CvMemStorage* p_storage; + CvHaarClassifierCascade* p_cascade; + video_filter_event_info_t event_info; + int i_id; +}; + +/**************************************************************************** + * Local prototypes + ****************************************************************************/ +static int OpenFilter ( vlc_object_t * ); +static void CloseFilter( vlc_object_t * ); + +static picture_t *Filter( filter_t *, picture_t * ); + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +vlc_module_begin(); + set_description( _("OpenCV face detection example filter") ); + set_shortname( N_( "OpenCV example" )); + set_capability( "opencv example", 1 ); + add_shortcut( "opencv_example" ); + + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_VFILTER2 ); + set_callbacks( OpenFilter, CloseFilter ); + + add_string( "opencv-haarcascade-file", "c:\\haarcascade_frontalface_alt.xml", NULL, + N_("Haar cascade filename"), + N_("Name of XML file containing Haar cascade description"), VLC_FALSE); +vlc_module_end(); + +/***************************************************************************** + * OpenFilter: probe the filter and return score + *****************************************************************************/ +static int OpenFilter( vlc_object_t *p_this ) +{ + filter_t *p_filter = (filter_t*)p_this; + filter_sys_t *p_sys; + + /* Allocate the memory needed to store the decoder's structure */ + if( ( p_filter->p_sys = p_sys = + (filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL ) + { + msg_Err( p_filter, "out of memory" ); + return VLC_EGENERIC; + } + + //init the video_filter_event_info_t struct + p_sys->event_info.i_region_size = 0; + p_sys->event_info.p_region = NULL; + p_sys->i_id = 0; + + p_filter->pf_video_filter = Filter; + + //create the VIDEO_FILTER_EVENT_VARIABLE + vlc_value_t val; + if (var_Create( p_filter->p_libvlc, VIDEO_FILTER_EVENT_VARIABLE, VLC_VAR_ADDRESS | VLC_VAR_DOINHERIT ) != VLC_SUCCESS) + msg_Err( p_filter, "Could not create %s\n", VIDEO_FILTER_EVENT_VARIABLE); + + val.p_address = &(p_sys->event_info); + if (var_Set( p_filter->p_libvlc, VIDEO_FILTER_EVENT_VARIABLE, val )!=VLC_SUCCESS) + msg_Err( p_filter, "Could not set %s\n", VIDEO_FILTER_EVENT_VARIABLE); + + //OpenCV init specific to this example + char* filename = config_GetPsz( p_filter, "opencv-haarcascade-file" ); + p_filter->p_sys->p_cascade = (CvHaarClassifierCascade*)cvLoad( filename, 0, 0, 0 ); + p_filter->p_sys->p_storage = cvCreateMemStorage(0); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * CloseFilter: clean up the filter + *****************************************************************************/ +static void CloseFilter( vlc_object_t *p_this ) +{ + filter_t *p_filter = (filter_t*)p_this; + filter_sys_t *p_sys = p_filter->p_sys; + + if( p_filter->p_sys->p_cascade ) + cvReleaseHaarClassifierCascade( &p_filter->p_sys->p_cascade ); + + if( p_filter->p_sys->p_storage ) + cvReleaseMemStorage( &p_filter->p_sys->p_storage ); + + if (NULL != p_filter->p_sys->event_info.p_region) + free(p_filter->p_sys->event_info.p_region); + + free( p_sys ); + + var_Destroy( p_filter->p_libvlc, VIDEO_FILTER_EVENT_VARIABLE); +} + +/**************************************************************************** + * Filter: Check for faces and raises an event when one is found. + **************************************************************************** + * p_pic: A picture_t with its p_data_orig member set to an array of + * IplImages (one image for each picture_t plane). + ****************************************************************************/ +static picture_t *Filter( filter_t *p_filter, picture_t *p_pic ) +{ + IplImage** p_img = NULL; + int i_planes = 0; + CvPoint pt1, pt2; + int i, scale = 1; + + if ((!p_pic) ) + { + msg_Err( p_filter, "no image array" ); + return NULL; + } + if (!(p_pic->p_data_orig)) + { + msg_Err( p_filter, "no image array" ); + return NULL; + } + //(hack) cast the picture_t to array of IplImage* + p_img = (IplImage**) p_pic->p_data_orig; + i_planes = p_pic->i_planes; + + //check the image array for validity + if ((!p_img[0])) //1st plane is 'I' i.e. greyscale + { + msg_Err( p_filter, "no image" ); + return NULL; + } + if ((p_pic->format.i_chroma != VLC_FOURCC('I','4','2','0'))) + { + msg_Err( p_filter, "wrong chroma - use I420" ); + return NULL; + } + if (i_planes<1) + { + msg_Err( p_filter, "no image planes" ); + return NULL; + } + + //perform face detection + cvClearMemStorage(p_filter->p_sys->p_storage); + CvSeq* faces = NULL; + if( p_filter->p_sys->p_cascade ) + { + //we should make some of these params config variables + faces = cvHaarDetectObjects( p_img[0], p_filter->p_sys->p_cascade, + p_filter->p_sys->p_storage, 1.15, 5, CV_HAAR_DO_CANNY_PRUNING, + cvSize(20, 20) ); + //create the video_filter_region_info_t struct + CvRect* r; + if (faces && (faces->total > 0)) + { + //msg_Dbg( p_filter, "Found %d face(s)\n", faces->total ); + if (NULL != p_filter->p_sys->event_info.p_region) + { + free(p_filter->p_sys->event_info.p_region); + p_filter->p_sys->event_info.p_region = NULL; + } + if( NULL == ( p_filter->p_sys->event_info.p_region = + (video_filter_region_info_t *)malloc(faces->total*sizeof(video_filter_region_info_t)))) + { + msg_Err( p_filter, "out of memory" ); + return NULL; + } + memset(p_filter->p_sys->event_info.p_region, 0, faces->total*sizeof(video_filter_region_info_t)); + p_filter->p_sys->event_info.i_region_size = faces->total; + } + + //populate the video_filter_region_info_t struct + for( i = 0; i < (faces ? faces->total : 0); i++ ) + { + r = (CvRect*)cvGetSeqElem( faces, i ); + pt1.x = r->x*scale; + pt2.x = (r->x+r->width)*scale; + pt1.y = r->y*scale; + pt2.y = (r->y+r->height)*scale; + cvRectangle( p_img[0], pt1, pt2, CV_RGB(0,0,0), 3, 8, 0 ); + + *(CvRect*)(&(p_filter->p_sys->event_info.p_region[i])) = *r; + p_filter->p_sys->event_info.p_region[i].i_id = p_filter->p_sys->i_id++; + p_filter->p_sys->event_info.p_region[i].p_description = "Face Detected"; + } + + if (faces && (faces->total > 0)) //raise the video filter event + var_Change( p_filter->p_libvlc, VIDEO_FILTER_EVENT_VARIABLE, VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL ); + } + else + msg_Err( p_filter, "No cascade - is opencv-haarcascade-file valid?" ); + + return p_pic; +} + diff --git a/modules/video_filter/opencv_wrapper.c b/modules/video_filter/opencv_wrapper.c new file mode 100644 index 0000000000..d4f71d476d --- /dev/null +++ b/modules/video_filter/opencv_wrapper.c @@ -0,0 +1,619 @@ +/***************************************************************************** + * opencv_wrapper.c : OpenCV wrapper video filter + ***************************************************************************** + * Copyright (C) 2006 the VideoLAN team + * + * Authors: Dugal Harris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include /* malloc(), free() */ +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include "vlc_filter.h" +#include "filter_common.h" +#include "charset.h" +#include "vlc_image.h" +#include "vlc_input.h" +#include "vlc_playlist.h" + + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int Create ( vlc_object_t * ); +static void Destroy ( vlc_object_t * ); + +static int Init ( vout_thread_t * ); +static void End ( vout_thread_t * ); +static void Render ( vout_thread_t *, picture_t * ); + +static int SendEvents( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static void ReleaseImages( vout_thread_t *p_vout ); +static void VlcPictureToIplImage( vout_thread_t *p_vout, picture_t *p_in ); + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ + +static char *chroma_list[] = { "input", "I420", "RGB32"}; +static char *chroma_list_text[] = { N_("Use input chroma unaltered"), + N_("I420 - first plane is greyscale"), N_("RGB32")}; + +static char *output_list[] = { "none", "input", "processed"}; +static char *output_list_text[] = { N_("Don't display any video"), + N_("Display the input video"), N_("Display the processed video")}; + +static char *verbosity_list[] = { "error", "warning", "debug"}; +static char *verbosity_list_text[] = { N_("Show only errors"), + N_("Show errors and warnings"), N_("Show everything including debug messages")}; + +vlc_module_begin(); + set_description( _("OpenCV video filter wrapper") ); + set_shortname( N_("OpenCV" )); + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_VFILTER ); + set_capability( "video filter", 0 ); + add_shortcut( "opencv_wrapper" ); + set_callbacks( Create, Destroy ); + add_float_with_range( "opencv-scale", 1.0, 0.1, 2.0, NULL, + N_("Scale factor (0.1-2.0)"), + N_("Ammount by which to scale the picture before sending it to the internal OpenCV filter"), + VLC_FALSE ); + add_string( "opencv-chroma", "input", NULL, + N_("OpenCV filter chroma"), + N_("Chroma to convert picture to before sending it to the internal OpenCV filter"), VLC_FALSE); + change_string_list( chroma_list, chroma_list_text, 0); + add_string( "opencv-output", "input", NULL, + N_("Wrapper filter output"), + N_("Determines what (if any) video is displayed by the wrapper filter"), VLC_FALSE); + change_string_list( output_list, output_list_text, 0); + add_string( "opencv-verbosity", "error", NULL, + N_("Wrapper filter verbosity"), + N_("Determines wrapper filter verbosity level"), VLC_FALSE); + change_string_list( verbosity_list, verbosity_list_text, 0); + add_string( "opencv-filter-name", "none", NULL, + N_("OpenCV internal filter name"), + N_("Name of internal OpenCV plugin filter to use"), VLC_FALSE); +vlc_module_end(); + + +/***************************************************************************** + * wrapper_output_t: what video is output + *****************************************************************************/ +enum wrapper_output_t +{ + NONE, //not working yet + VINPUT, + PROCESSED +}; + +/***************************************************************************** + * internal_chroma_t: what chroma is sent to the internal opencv filter + *****************************************************************************/ +enum internal_chroma_t +{ + CINPUT, + GREY, + RGB +}; + +/***************************************************************************** + * verbosity_t: + *****************************************************************************/ +enum verbosity_t +{ + VERB_ERROR, + VERB_WARN, + VERB_DEBUG +}; + +/***************************************************************************** + * vout_sys_t: opencv_wrapper video output method descriptor + ***************************************************************************** + * This structure is part of the video output thread descriptor. + * It describes the opencv_wrapper specific properties of an output thread. + *****************************************************************************/ +struct vout_sys_t +{ + vout_thread_t *p_vout; + + image_handler_t *p_image; + + int i_cv_image_size; + + picture_t *p_proc_image; + picture_t *p_to_be_freed; + + float f_scale; + + int i_wrapper_output; + int i_internal_chroma; + int i_verbosity; + + IplImage *p_cv_image[VOUT_MAX_PLANES]; + + filter_t *p_opencv; + char* psz_inner_name; + + picture_t hacked_pic; +}; + +/***************************************************************************** + * Control: control facility for the vout (forwards to child vout) + *****************************************************************************/ +static int Control( vout_thread_t *p_vout, int i_query, va_list args ) +{ + return vout_vaControl( p_vout->p_sys->p_vout, i_query, args ); +} + +/***************************************************************************** + * Create: allocates opencv_wrapper video thread output method + ***************************************************************************** + * This function allocates and initializes a opencv_wrapper vout method. + *****************************************************************************/ +static int Create( vlc_object_t *p_this ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + char *psz_chroma, *psz_output, *psz_verbosity; + int i = 0; + + /* Allocate structure */ + p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); + if( p_vout->p_sys == NULL ) + { + msg_Err( p_vout, "out of memory" ); + return VLC_ENOMEM; + } + + /* Init structure */ + p_vout->p_sys->p_image = image_HandlerCreate( p_vout ); + for (i = 0; i < VOUT_MAX_PLANES; i++) + p_vout->p_sys->p_cv_image[i] = NULL; + p_vout->p_sys->p_proc_image = NULL; + p_vout->p_sys->p_to_be_freed = NULL; + p_vout->p_sys->i_cv_image_size = 0; + + p_vout->pf_init = Init; + p_vout->pf_end = End; + p_vout->pf_manage = NULL; + p_vout->pf_render = Render; + p_vout->pf_display = NULL; + p_vout->pf_control = Control; + + /* Retrieve and apply config */ + if( !(psz_chroma = config_GetPsz( p_vout, "opencv-chroma" )) ) + { + msg_Err( p_vout, "configuration variable %s empty, using 'grey'", + "opencv-chroma" ); + p_vout->p_sys->i_internal_chroma = GREY; + } + else + { + if( !strcmp( psz_chroma, "input" ) ) + p_vout->p_sys->i_internal_chroma = CINPUT; + else if( !strcmp( psz_chroma, "I420" ) ) + p_vout->p_sys->i_internal_chroma = GREY; + else if( !strcmp( psz_chroma, "RGB32" ) ) + p_vout->p_sys->i_internal_chroma = RGB; + else + { + msg_Err( p_vout, "no valid opencv-chroma provided, using 'grey'" ); + p_vout->p_sys->i_internal_chroma = GREY; + } + } + free( psz_chroma); + + if( !(psz_output = config_GetPsz( p_vout, "opencv-output" )) ) + { + msg_Err( p_vout, "configuration variable %s empty, using 'input'", + "opencv-output" ); + p_vout->p_sys->i_wrapper_output = VINPUT; + } + else + { + if( !strcmp( psz_output, "none" ) ) + p_vout->p_sys->i_wrapper_output = NONE; + else if( !strcmp( psz_output, "input" ) ) + p_vout->p_sys->i_wrapper_output = VINPUT; + else if( !strcmp( psz_output, "processed" ) ) + p_vout->p_sys->i_wrapper_output = PROCESSED; + else + { + msg_Err( p_vout, "no valid opencv-output provided, using 'input'" ); + p_vout->p_sys->i_wrapper_output = VINPUT; + } + } + free( psz_output); + + if( !(psz_verbosity = config_GetPsz( p_vout, "opencv-verbosity" )) ) + { + msg_Err( p_vout, "configuration variable %s empty, using 'input'", + "opencv-verbosity" ); + p_vout->p_sys->i_verbosity = VERB_ERROR; + } + else + { + if( !strcmp( psz_verbosity, "error" ) ) + p_vout->p_sys->i_verbosity = VERB_ERROR; + else if( !strcmp( psz_verbosity, "warning" ) ) + p_vout->p_sys->i_verbosity = VERB_WARN; + else if( !strcmp( psz_verbosity, "debug" ) ) + p_vout->p_sys->i_verbosity = VERB_DEBUG; + else + { + msg_Err( p_vout, "no valid opencv-verbosity provided, using 'error'" ); + p_vout->p_sys->i_verbosity = VERB_ERROR; + } + } + free( psz_verbosity); + + p_vout->p_sys->psz_inner_name = config_GetPsz( p_vout, "opencv-filter-name" ); + + p_vout->p_sys->f_scale = + config_GetFloat( p_vout, "opencv-scale" ); + + if (p_vout->p_sys->i_verbosity > VERB_WARN) + msg_Info(p_vout, "Configuration: opencv-scale: %f, opencv-chroma: %d, " + "opencv-output: %d, opencv-verbosity %d, opencv-filter %s", + p_vout->p_sys->f_scale, + p_vout->p_sys->i_internal_chroma, + p_vout->p_sys->i_wrapper_output, + p_vout->p_sys->i_verbosity, + p_vout->p_sys->psz_inner_name); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Init: initialize opencv_wrapper video thread output method + *****************************************************************************/ +static int Init( vout_thread_t *p_vout ) +{ + int i_index; + picture_t *p_pic; + video_format_t fmt = {0}; + vout_sys_t *p_sys = p_vout->p_sys; + I_OUTPUTPICTURES = 0; + + /* Initialize the output video format */ + p_vout->output.i_chroma = p_vout->render.i_chroma; + p_vout->output.i_width = p_vout->render.i_width; + p_vout->output.i_height = p_vout->render.i_height; + p_vout->output.i_aspect = p_vout->render.i_aspect; + p_vout->fmt_out = p_vout->fmt_in; //set to input video format + + fmt = p_vout->fmt_out; + if (p_sys->i_wrapper_output == PROCESSED) //set to processed video format + { + fmt.i_width = fmt.i_width * p_sys->f_scale; + fmt.i_height = fmt.i_height * p_sys->f_scale; + fmt.i_visible_width = fmt.i_visible_width * p_sys->f_scale; + fmt.i_visible_height = fmt.i_visible_height * p_sys->f_scale; + fmt.i_x_offset = fmt.i_x_offset * p_sys->f_scale; + fmt.i_y_offset = fmt.i_y_offset * p_sys->f_scale; + + if (p_sys->i_internal_chroma == GREY) + fmt.i_chroma = VLC_FOURCC('I','4','2','0'); + else if (p_sys->i_internal_chroma == RGB) + fmt.i_chroma = VLC_FOURCC('R','V','3','2'); + } + + /* Load the internal opencv filter */ + /* We don't need to set up video formats for this filter as it not actually using a picture_t */ + p_sys->p_opencv = vlc_object_create( p_vout, sizeof(filter_t) ); + vlc_object_attach( p_sys->p_opencv, p_vout ); + + if (p_vout->p_sys->psz_inner_name) + p_sys->p_opencv->p_module = + module_Need( p_sys->p_opencv, p_sys->psz_inner_name, 0, 0 ); + + if( !p_sys->p_opencv->p_module ) + { + msg_Err( p_vout, "can't open internal opencv filter: %s", p_vout->p_sys->psz_inner_name ); + p_vout->p_sys->psz_inner_name = NULL; + vlc_object_detach( p_sys->p_opencv ); + vlc_object_destroy( p_sys->p_opencv ); + p_sys->p_opencv = NULL; + } + + /* Try to open the real video output */ + if (p_sys->i_verbosity > VERB_WARN) + msg_Dbg( p_vout, "spawning the real video output" ); + + p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt ); + + /* Everything failed */ + if( p_vout->p_sys->p_vout == NULL ) + { + msg_Err( p_vout, "can't open vout, aborting" ); + return VLC_EGENERIC; + } + + ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES ); + + ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents ); + + ADD_PARENT_CALLBACKS( SendEventsToChild ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * End: terminate opencv_wrapper video thread output method + *****************************************************************************/ +static void End( vout_thread_t *p_vout ) +{ + int i_index; + + /* Free the fake output buffers we allocated */ + for( i_index = I_OUTPUTPICTURES ; i_index ; ) + { + i_index--; + free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig ); + } + + if ( p_vout->p_sys->p_opencv ) + { + //release the internal opencv filter + if( p_vout->p_sys->p_opencv->p_module ) + module_Unneed( p_vout->p_sys->p_opencv, p_vout->p_sys->p_opencv->p_module ); + vlc_object_detach( p_vout->p_sys->p_opencv ); + vlc_object_destroy( p_vout->p_sys->p_opencv ); + p_vout->p_sys->p_opencv = NULL; + } + +} + +/***************************************************************************** + * Destroy: destroy opencv_wrapper video thread output method + ***************************************************************************** + * Terminate an output method created by opencv_wrapperCreateOutputMethod + *****************************************************************************/ +static void Destroy( vlc_object_t *p_this ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + + if( p_vout->p_sys->p_vout ) + { + DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents ); + vlc_object_detach( p_vout->p_sys->p_vout ); + vout_Destroy( p_vout->p_sys->p_vout ); + } + + DEL_PARENT_CALLBACKS( SendEventsToChild ); + + ReleaseImages(p_vout); + + if( p_vout->p_sys->p_image ) + image_HandlerDelete( p_vout->p_sys->p_image ); + + free( p_vout->p_sys ); +} + +/***************************************************************************** + * ReleaseImages: Release OpenCV images in vout_sys_t. + *****************************************************************************/ +static void ReleaseImages(vout_thread_t *p_vout) +{ + int i = 0; + if (p_vout->p_sys->p_cv_image) + { + for (i = 0; i < VOUT_MAX_PLANES; i++) + { + if (p_vout->p_sys->p_cv_image[i]) + cvReleaseImageHeader(&(p_vout->p_sys->p_cv_image[i])); + p_vout->p_sys->p_cv_image[i] = NULL; + } + } + p_vout->p_sys->i_cv_image_size = 0; + + /* Release temp picture_t if it exists */ + if (p_vout->p_sys->p_to_be_freed) + { + p_vout->p_sys->p_to_be_freed->pf_release( p_vout->p_sys->p_to_be_freed ); + p_vout->p_sys->p_to_be_freed = NULL; + } + if (p_vout->p_sys->i_verbosity > VERB_WARN) + msg_Dbg( p_vout, "images released" ); +} + +/***************************************************************************** + * VlcPictureToIplImage: Convert picture_t to IplImage + ***************************************************************************** + * Converts given picture_t into IplImage(s) according to module config. + * IplImage(s) are stored in vout_sys_t. + *****************************************************************************/ +static void VlcPictureToIplImage( vout_thread_t *p_vout, picture_t *p_in ) +{ + int planes = p_in->i_planes; //num input video planes + // input video size + CvSize sz = cvSize(abs(p_in->format.i_width), abs(p_in->format.i_height)); + video_format_t fmt_out = {0}; + clock_t start, finish; //performance measures + double duration; + int i = 0; + vout_sys_t* p_sys = p_vout->p_sys; + + start = clock(); + + //do scale / color conversion according to p_sys config + if ((p_sys->f_scale != 1) || (p_sys->i_internal_chroma != CINPUT)) + { + fmt_out = p_in->format; + + //calc the scaled video size + fmt_out.i_width = p_in->format.i_width * p_sys->f_scale; + fmt_out.i_height = p_in->format.i_height * p_sys->f_scale; + + if (p_sys->i_internal_chroma == RGB) + { + //rgb2 gives 3 separate planes, this gives 1 interleaved plane + //rv24 gives is about 20% faster but gives r&b the wrong way round + //and I cant think of an easy way to fix this + fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); + } + else if (p_sys->i_internal_chroma == GREY) + { + //take the I (gray) plane (video seems to commonly be in this fmt so usually the + //conversion does nothing) + fmt_out.i_chroma = VLC_FOURCC('I','4','2','0'); + } + + //convert from the input image + p_sys->p_proc_image = image_Convert( p_sys->p_image, p_in, + &(p_in->format), &fmt_out ); + + if (!p_sys->p_proc_image) + { + msg_Err(p_vout, "can't convert (unsupported formats?), aborting..."); + return; + } + + p_sys->p_to_be_freed = p_sys->p_proc_image; //remember this so we can free it later + + } + else //((p_sys->f_scale != 1) || (p_sys->i_internal_chroma != CINPUT)) + { + //use the input image without conversion + p_sys->p_proc_image = p_in; + } + + //Convert to the IplImage array that is to be processed. + //If there are multiple planes in p_sys->p_proc_image, then 1 IplImage + //is created for each plane. + planes = p_sys->p_proc_image->i_planes; + p_sys->i_cv_image_size = planes; + for ( i = 0; i < planes; i++ ) + { + sz = cvSize(abs(p_sys->p_proc_image->p[i].i_visible_pitch / + p_sys->p_proc_image->p[i].i_pixel_pitch), + abs(p_sys->p_proc_image->p[i].i_visible_lines)); + + p_sys->p_cv_image[i] = cvCreateImageHeader(sz, IPL_DEPTH_8U, + p_sys->p_proc_image->p[i].i_pixel_pitch); + + cvSetData( p_sys->p_cv_image[i], + (char*)(p_sys->p_proc_image->p[i].p_pixels), p_sys->p_proc_image->p[i].i_pitch ); + } + + //Hack the above opencv image array into a picture_t so that it can be sent to + //another video filter + p_sys->hacked_pic.p_data_orig = p_sys->p_cv_image; + p_sys->hacked_pic.i_planes = planes; + p_sys->hacked_pic.format.i_chroma = fmt_out.i_chroma; + + //calculate duration of conversion + finish = clock(); + duration = (double)(finish - start) / CLOCKS_PER_SEC; + if (p_sys->i_verbosity > VERB_WARN) + msg_Dbg( p_vout, "VlcPictureToIplImageRgb took %2.4f seconds\n", duration ); +} + +/***************************************************************************** + * Render: displays previously rendered output + ***************************************************************************** + * This function send the currently rendered image to the internal opencv + * filter for processing. + *****************************************************************************/ +static void Render( vout_thread_t *p_vout, picture_t *p_pic ) +{ + picture_t *p_outpic = NULL; + clock_t start, finish; + double duration; + + while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) ) + == NULL ) + { + if( p_vout->b_die || p_vout->b_error ) + { return; } + msleep( VOUT_OUTMEM_SLEEP ); + } + + vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic ); + + start = clock(); + + if (p_vout->p_sys->i_wrapper_output == VINPUT) //output = input video + { + //This copy is a bit unfortunate but image_Convert can't write into an existing image so it is better to copy the + //(say) 16bit YUV image here than a 32bit RGB image somehwere else. + //It is also not that expensive in time. + vout_CopyPicture( p_vout, p_outpic, p_pic ); + VlcPictureToIplImage( p_vout, p_pic); + //pass the image to the internal opencv filter for processing + if ((p_vout->p_sys->p_opencv) && (p_vout->p_sys->p_opencv->p_module)) + p_vout->p_sys->p_opencv->pf_video_filter( p_vout->p_sys->p_opencv, &(p_vout->p_sys->hacked_pic)); + } + else //output = processed video (NONE option not working yet) + { + VlcPictureToIplImage( p_vout, p_pic); + //pass the image to the internal opencv filter for processing + if ((p_vout->p_sys->p_opencv) && (p_vout->p_sys->p_opencv->p_module)) + p_vout->p_sys->p_opencv->pf_video_filter( p_vout->p_sys->p_opencv, &(p_vout->p_sys->hacked_pic)); + //copy the processed image into the output image + if ((p_vout->p_sys->p_proc_image) && (p_vout->p_sys->p_proc_image->p_data)) + vout_CopyPicture( p_vout, p_outpic, p_vout->p_sys->p_proc_image ); + } + + //calculate duration + finish = clock(); + duration = (double)(finish - start) / CLOCKS_PER_SEC; + if (p_vout->p_sys->i_verbosity > VERB_WARN) + msg_Dbg( p_vout, "Render took %2.4f seconds\n", duration ); + + ReleaseImages(p_vout); + vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date ); + + vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic ); + vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic ); +} + +/***************************************************************************** + * SendEvents: forward mouse and keyboard events to the parent p_vout + *****************************************************************************/ +static int SendEvents( vlc_object_t *p_this, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + var_Set( (vlc_object_t *)p_data, psz_var, newval ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * SendEventsToChild: forward events to the child/children vout + *****************************************************************************/ +static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + var_Set( p_vout->p_sys->p_vout, psz_var, newval ); + return VLC_SUCCESS; +}