我的GTK+2/C应用程序使用Opencv库从网络摄像机馈送中连续抓取帧,并且有一个UI按钮,它应该通过点击时抓取帧来计算一些信息。
此按钮点击计算需要1-2秒,具体取决于系统资源。(在Raspberry pi上,需要3-5秒)
以前,所有功能(查看+计算)都在同一个主流程中。然而,在进行计算时,整个应用程序(视频馈送)习惯于冻结,直到计算完成。
我已经实现了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 CAPn");
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 itn");
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: %dn", 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;
}
**注意,我已经从实际的整个代码中粘贴了这段代码,而且这不会运行。粘贴在这里只是为了给我一些关于我正在努力实现的目标的想法。
看起来您在父进程中阻止了read
和wait
调用。换句话说,尽管您生成了一个子进程来执行计算,但在子进程完成之前,您仍然会阻止父进程。没有实现并行性。
我建议在进程中有一个工作线程来进行计算,并在结果可用时通知主线程。
在父进程/线程中,应该使管道的读取端不阻塞,并将其注册到事件循环中。一旦读取端准备好进行读取,请在非阻塞模式下读取所有数据。
为SIGCHLD
设置一个信号处理程序,并在其中调用wait
以释放子进程资源。