copencvgtkcairowebcam-capture

How to get webcam feed to GTK window?


I need to get webcam video feed using C, GTK2/3, Cairo. However can't find any references about it.

Below shows how did I try using opencv. It is unsuccessful.I need to know some other method using gstreamer, cairo.

I'm expecting simple code to get video to GTK window. Really appreciate if someone can provide example code.

    /*
gcc -o weby3 att3.c `pkg-config --libs --cflags gtk+-2.0 opencv`
*/

#include "highgui.h"
#include <gtk/gtk.h>
#include <opencv/cv.h>
#include <opencv/cxcore.h>

GdkPixbuf* pix;
IplImage* frame;
CvCapture* capture;

gboolean
expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
  while(1) {
         frame = cvQueryFrame( capture );
          if(!frame) break;  

         pix = gdk_pixbuf_new_from_data((guchar*) frame->imageData,
         GDK_COLORSPACE_RGB, FALSE, frame->depth, frame->width,
         frame->height, (frame->widthStep), NULL, NULL);


         gdk_draw_pixbuf(widget->window,
   widget->style->fg_gc[GTK_WIDGET_STATE (widget)], pix, 0, 0, 0, 0,
   -1, -1, GDK_RGB_DITHER_NONE, 0, 0); /* Other possible values are  GDK_RGB_DITHER_MAX,  GDK_RGB_DITHER_NORMAL */

         char c = cvWaitKey(33);
         if( c == 27 ) break;
   }

   cvReleaseCapture( &capture );   
   return TRUE;
}

int main( int argc, char** argv ) {

   /* GtkWidget is the storage type for widgets */
   GtkWidget *window;
   GtkWidget *drawing_area;

   gtk_init (&argc, &argv);

   CvCapture* capture = cvCreateCameraCapture(0); 
   /* create a new window */
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_window_set_title (GTK_WINDOW (window), "Hello WebCam and OpenCV!");
   g_signal_connect (G_OBJECT (window), "destroy",
    G_CALLBACK (gtk_main_quit), NULL);

   /* Sets the border width of the window. */
   gtk_container_set_border_width (GTK_CONTAINER (window), 10);

   /* Now add a child widget to the aspect frame */
   drawing_area = gtk_drawing_area_new();


   /* window since we are forcing a aspect ratio */
   gtk_widget_set_size_request(drawing_area, 600, 400);
   gtk_container_add(GTK_CONTAINER (window), drawing_area);
   gtk_widget_show(drawing_area);

   g_signal_connect (G_OBJECT (drawing_area), "expose_event",
      G_CALLBACK (expose_event_callback), NULL);

   /* and the window */
   gtk_widget_show (window);

   /* All GTK applications must have a gtk_main(). Control ends here
    * and waits for an event to occur (like a key press or
    * mouse event). */
   gtk_main ();

   return 0;
}

Solution

  • The main problem I see here is that you're not properly integrating your opencv code into the GTK event model.

    GTK basically works with a message pump. Messages are sent in a queue, and GTK reads and unqueue them to react to those messages. In GTK 2, the expose-event event is emitted when a widget or portion of it needs to be drawn (in GTK 3, the draw event is used). You're suposed to connect a callback to that event to catch the event. Then in your callback, you draw a single frame, and then give the control back to GTK. It will call your callback again when it needs the next frame.

    The problem here is that you never give the hand back to GTK, as your opencv code is enclosed in an infinite while(1) loop. You should instead just draw a single frame, and return.

    The key event logic should also be handled using GTK, and not in your expose-event callback. Just call gtk_main_quit once you have seen the keystroke you're waiting for.

    EDIT:

    Once you can display one frame, you have to display them all. Call gtk_widget_queue_draw to tell GTK that the widget needs to be redrawn.

    Once this is done, you'll see that you need some way of calling gtk_widget_queue_draw periodically. This is easilly done with GLib functions like g_timeout_add or g_idle_add. These functions tell the main event loop (what I called "message pump" above") to call a callback after a determined amount of time (for g_timeout_add) or just when there is no other events to process (for g_idle_add). Call gtk_widget_queue_draw in that callback and you're done.

    Be aware that g_timeout_add doesn't allow to have a perfect timing, so the time between each frame may be too different from one frame to another. You may avoid this by augmenting the priority passed to g_timeout_add. You may also use a GTimer to compute in your timeout callback the time elapsed between the last frame and the current time, and deduce time remaining until your next frame. Then call g_timeout_add with that more accurate time. Don't forget to return FALSE instead of TRUE at the end of the timeout callback if you use the GTimer trick, otherwise you'll have n timeout callbacks running at frame n.