如何在不停止C中的父进程的情况下分叉子进程

时间:2016-07-28 17:09:21

标签: c opencv pipe fork gtk2

我的GTK + 2 / C应用程序使用Opencv库从网络摄像头源连续抓取帧,并且有一个UI按钮,它应该通过在点击时抓取帧来计算一些信息。

此按钮单击计算需要1-2秒,具体取决于系统资源。 (在Raspberry pi上,需要3-5秒)

以前所有功能(查看+计算)都在同一个主要流程中。但是在进行计算时,整个app(视频输入)用于冻结,直到完成计算。

我已经实现fork()来制作子进程并通过pipe()保持进程间通信,以使用计算数据更新UI。

然而现在滞后时间变得更大了,比如5-6秒。 下面显示了示例代码。

有没有什么特别的方法来避免这种主要过程的滞后?

#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
#include <diamond_calcs.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/videodev.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>

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


/* declaring the opencv capture variable */
CvCapture* capture, *fm_capture;
GdkPixbuf* pix;
IplImage* frame, *frame_sized;

gboolean expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data);
gboolean timeout(gpointer data);
gboolean autoon_clicked_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data[]);


// This function grabs a frame and recording some values. also this is the function called by the child process also.
gboolean frame_grabber(void)
{

    /* setting up the edge ditection */

    IplImage *frame_bgr;
    if(frame){
        //frame_bgr = cvQueryFrame( capture );
        frame_bgr = frame_sized;
    }
    else{
        frame_bgr = cvLoadImage("./data/media/novideo.png", 1);
    }
    /* Converting Color SPACE to greyscale */
    int y_pos, x_pos;
    int counter=0;
    typedef enum { false, true } bool;
    bool col_break = false;

    for(int y=0;y<frame_bgr->height;y++)
    {
        for(int x=0;x<frame_bgr->width;x++)
        {
            CvScalar color = cvGet2D(frame_bgr, y, x);
            if(color.val[0]<color_filter_thres){
                if (y_pos == y){
                    counter++;   
                }
                x_pos = x;
                y_pos = y;
                if (counter > 2){
                    //printf("top: %i, %i \n", x_pos, y_pos);
                    col_break = true;
                    break;
                }
            }
        }
        if (col_break){
            break;
        }
    }
    ruler_top_cal_preset = y_pos;    
    /* bottom ruler - scanning backwards*/  
    int y_pos_bot, x_pos_bot;
    int counter_bot=0;
    bool col_break2 = false;
    for(int y_b=frame_bgr->height-1;y_b>0;y_b--)
    {
        for(int x_b=frame_bgr->width-1;x_b>0;x_b--)
        {
            CvScalar color_bot = cvGet2D(frame_bgr, y_b, x_b);
            if(color_bot.val[0]<color_filter_thres){
                if (y_pos_bot == y_b){
                    counter_bot++;   
                }
                x_pos_bot = x_b;
                y_pos_bot = y_b;
                if (counter_bot > 2){
                    //printf("bottom: %i, %i \n", x_pos_bot, y_pos_bot);
                    col_break2 = true;
                    break;
                }
            }
        }
        if (col_break2){
            break;
        }
    }


    ruler_bottom_cal_preset = y_pos_bot;
    dfactor_preset = ruler_bottom_cal_preset - ruler_top_cal_preset;
}

/*
 * main
 *
 * Program begins here
 */

//####### Lot of processes going inside main, ignore it, just focus on the program flow
int main( int argc, char **argv )
{
    /* variables structs to load the gtk_ based widgets and windows */
    GtkBuilder *builder;
    GtkWidget  *window, *event_box, *drawing_area;
    GError     *error = NULL;
    GError     *error_settings = NULL;
    GtkButton *button;
    GtkLabel *label;

    /* Init GTK+ */
    gtk_init( &argc, &argv );

    /* Create new GtkBuilder object */
    builder = gtk_builder_new();
    /* Load UI from file. If error occurs, report it and quit application. */
    if( ! gtk_builder_add_from_file( builder, "file_glade.glade", &error ) )
    {
        g_warning( "%s", error->message );
        g_free( error );
        return( 1 );
    }

    /* Get main window pointer from UI */
    window = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );


    /* creating the drawing area */
    drawing_area = gtk_drawing_area_new();
    gtk_container_add (GTK_CONTAINER(event_box), drawing_area);
    gtk_widget_show (drawing_area);

    /* connects the draw (expose-event) with the handler */   
    g_signal_connect (drawing_area, "expose-event",
    G_CALLBACK (expose_event_callback), NULL);


    gtk_label_set(labels[0], "Min   :");

    //####### Here goes the calculation clicking
    gtk_signal_connect (GTK_OBJECT (gtk_builder_get_object( builder, "autoon_button" )), "clicked",
                        GTK_SIGNAL_FUNC (autoon_clicked_cb), labels);

    /* Show window. All other widgets are automatically shown by GtkBuilder */
    gtk_widget_show( window );

    /* load last saved values */
    file_load( spinners );

    //#######
    int fd; 
    if((fd = open("/dev/video0", O_RDONLY)) == -1){   
        printf("NO CAP\n");
        capture = NULL;
    }
    else{
        capture = cvCreateCameraCapture(0);
        cvSetCaptureProperty(capture, CV_CAP_PROP_BRIGHTNESS, brght);
        cvSetCaptureProperty(capture, CV_CAP_PROP_CONTRAST,contra);
        cvSetCaptureProperty(capture, CV_CAP_PROP_SATURATION, satu);
        cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 800);
        cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 600);

       // ## remove the forking from here and add that to the expose function 
        pid_t pID1 = vfork();
        if (pID1 == 0)                // child process
        {
          g_timeout_add (15, timeout, drawing_area);  
          //_exit(0);
        }
        else if ( pID1 < 0 ){
          printf("Failed to fork video overlay child process \n");
          exit(1);
        }
    }

    /* Destroy builder, since we don't need it anymore */
     g_object_unref( G_OBJECT( builder ) );
    //g_object_unref( G_OBJECT( settings_builder ) );
    //g_object_unref( G_OBJECT( about_builder ) );

    /* Start main loop */
    gtk_main();


    return( 0 );
}

/* This function runs every x seconds and making the video feed */

/*
 * Function:  expose_event_callback
 * --------------------
 * When the widget is exposed of force to expose ths function handler will run to generate the video feed
 *  
 *  returns: none
 */
gboolean
expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{

    /* Capture a single frame from the video stream */
    if (capture){
        frame = cvQueryFrame( capture );
        frame_sized = frame; 
        pix = gdk_pixbuf_new_from_data((guchar*) frame->imageData,
               GDK_COLORSPACE_RGB, FALSE, frame->depth, frame->width,
               frame->height, (frame->widthStep), NULL, NULL);
    }
    else{
        printf("You've done it\n");
        pix = gdk_pixbuf_new_from_file("./data/media/novideo.png", NULL);
    }
    /* putting the generated pix buff to the correct layout (GTK widget) */    
    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 */

    return TRUE;
}

/*
 * Function:  timeout
 * --------------------
 * This function runs inbetween given time period. It calls the required functions to generate video and drawing
 *  
 *  returns: none
 */
gboolean timeout(gpointer data)
{
    /* DEBUG print */
    //printf("Time out Hello: %d\n", rand());

    /* Redraws the Widget. Widget will call for the callback function again */
    gtk_widget_queue_draw (data);

    /* Starting the drawing function */
    //ofdra(data,NULL);

    return TRUE;
}

gboolean autoon_clicked_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data[])
{   
  char display1_entry_text[100];
  int x = 1;

    int pfds[2];
    pipe(pfds);

    pid_t pID = fork();
    if (pID == 0)                // child process
    {
      double ytops[10];
      double ybots[10];    
      double min_diam;
      double max_diam;
      double avg_diam;

      clock_t t1, t2,end_t; // clocking for debugging purposes

      t2 = clock();
      for(int i=0;i<15;i++)
      {
        //measure_clicked_callback(widget, NULL, data);
        frame_grabber();
        ytops[i] = ruler_top_cal_preset;
        ybots[i] = ruler_bottom_cal_preset;
        t1 = clock();
        float x = (float)(t1-t2);
        float y = x/CLOCKS_PER_SEC;
        float z = y*1000;
        t2 = t1;
      }

      /* sorting arrays for ascending order */
      if(ybots && ytops){
        array_sorter(ybots);
        array_sorter(ytops);

        min_diam = (ybots[0]-ytops[9])/scale_factor;
        max_diam = (ybots[9]-ytops[0])/scale_factor;
        avg_diam = (min_diam+max_diam)/2;
      }
      sprintf(display1_entry_text0,"Min   :    %.3g",min_diam);

      write(pfds[1], display1_entry_text0, 100);
      _exit(0);
    }
    else if(pID <0){
      printf("Failed to fork \n");
      exit(1);
    }
    else{
      //parent process
      char buf[100];
      read(fpds[0], buf, 100);
      //updates the gtk interface with read value
      gtk_lable_set(data[0], buf);
      wait(NULL);
    }
    return TRUE;
}

**注意我已经从实际的整个代码中粘贴了这段代码,这也不会运行。只在这里粘贴,以便了解我正在努力实现的目标。

1 个答案:

答案 0 :(得分:1)

您的父进程中似乎阻止了readwait次呼叫。换句话说,尽管您生成了一个子进程来执行计算,但您仍然会阻止父进程,直到子进程完成。没有实现并行性。

我建议在进程中有一个工作线程进行计算,并在结果可用时通知主线程。

在父进程/线程中,您应该使管道的读取结束非阻塞,并将其注册到事件循环。一旦读取结束准备好读取,就以非阻塞模式读取所有数据。

SIGCHLD设置信号处理程序并在其中调用wait以释放子进程资源。