Toybrick

多线程双摄像头数据阻塞问题(C++小白求教)(已解决)

shopping

中级会员

积分
410
发表于 2019-10-18 10:28:08    查看: 5552|回复: 4 | [复制链接]    打印 | 显示全部楼层
本帖最后由 shopping 于 2019-10-25 17:01 编辑

因为公司以后要一个板子连接两个摄像头,而且对帧率有要求,所以考虑使用多线程。在看了这位老哥的( http://t.rock-chips.com/forum.php?mod=viewthread&tid=349 )demo 后,写了我的 摄像头 + 双显示屏 demo 版本( V4L2 + OpenCV USB摄像头),代码如下:
  1. using namespace cv;
  2. using namespace std;

  3. mutex mtxQueueInput_1,mtxQueueInput_2;  // 两个全局变量,1对应第一个摄像头。2对应第二个摄像头。
  4. queue<Mat> queueInput_1,queueInput_2;  // 同样,1显示第一个摄像头数据,2同理。

  5. int get_video_1()
  6. {
  7.     char video[] = "/dev/video10" ;
  8.     int cpuid = 0;  // 4个线程 4个函数 分别绑定到4个CPU上
  9.     cpu_set_t mask;
  10.     CPU_ZERO(&mask);
  11.     CPU_SET(cpuid, &mask);

  12.     if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)
  13.     {
  14.         printf("set thread affinity failed");
  15.         return(FALSE);
  16.     }
  17.     printf("Bind CameraCapture process to CPU %d\n", cpuid);

  18.     cv::Mat img( IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC(3),cv::Scalar(0,0,0));

  19.     struct v4l2_buffer dequeue;
  20.     dequeue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  21.     dequeue.memory = V4L2_MEMORY_MMAP ;

  22.     BUF *buffers = (struct buffer *)malloc(COUNT*(sizeof (*buffers)));

  23.     unsigned char *srcBuffer = (unsigned char*)malloc(sizeof(unsigned char) * BUFFER_SIZE_src);  // yuyv
  24.     unsigned char *dstBuffer = (unsigned char*)malloc(sizeof(unsigned char) * BUFFER_SIZE_det);  // RGB

  25.     /********************* function start *********************/
  26.     buffers = v4l2(video,buffers) ;
  27.     while(1)
  28.     {
  29.         srcBuffer = get_img(dequeue,buffers,srcBuffer);
  30.         dstBuffer = yuyv2rgb24(srcBuffer,dstBuffer);
  31.         img = unchar_to_Mat(dstBuffer,img);

  32.         mtxQueueInput_1.lock();
  33.         queueInput_1.push(img);
  34.         if (queueInput_1.size() >= 30)
  35.         {
  36.             mtxQueueInput_1.unlock();
  37.             printf("Img in queueInput is too much !");
  38.             sleep(1);
  39.             if (queueInput_1.size() >= 60)
  40.             {
  41.                 printf("queue is full !\n");
  42.                 return(FALSE);
  43.             }
  44.         }else{
  45.             mtxQueueInput_1.unlock();
  46.         }
  47.     }
  48.     free(buffers);
  49.     free(srcBuffer);
  50.     free(dstBuffer);

  51.     close_v4l2(buffers);
  52.     return(TRUE);
  53. }

  54. int get_video_2()  // get_video_1 和 get_video_2  除了 摄像头、需要绑定的CPU  不一样外,其余都一样。displayImage_1 和 displayImage_2 的情况类似。
复制代码
每次执行这个文件,都要开个好几次才会显示出画面,即使有画面也坚持不了 1 min 。每次卡住都是因为队列里数据太多,所以休眠卡住,但图像还是没能继续显示,使得程序一直卡住,然后崩溃退出。

处于试验,我把这个程序拆分编译成两个可执行文件,即两个进程,每个进程调用一个摄像头做显示,每个进程里有两个线程,分别绑定不同CPU上,然后发现图像一点也不卡,非常流畅。不知道各位看了后有没有头绪,麻烦告知一下,谢谢。
怎么代码没了,再贴上:
  1. int get_video_2()
  2. {
  3.     char video[] = "/dev/video12" ;
  4.     int cpuid = 1;
  5.     cpu_set_t mask;
  6.     CPU_ZERO(&mask);
  7.     CPU_SET(cpuid, &mask);

  8.     if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)
  9.     {
  10.         printf("set thread affinity failed");
  11.         return(FALSE);
  12.     }
  13.     printf("Bind CameraCapture process to CPU %d\n", cpuid);

  14.     cv::Mat img( IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC(3),cv::Scalar(0,0,0));

  15.     struct v4l2_buffer dequeue;
  16.     dequeue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  17.     dequeue.memory = V4L2_MEMORY_MMAP ;

  18.     BUF *buffers = (struct buffer *)malloc(COUNT*(sizeof (*buffers)));

  19.     unsigned char *srcBuffer = (unsigned char*)malloc(sizeof(unsigned char) * BUFFER_SIZE_src);  // yuyv
  20.     unsigned char *dstBuffer = (unsigned char*)malloc(sizeof(unsigned char) * BUFFER_SIZE_det);  // RGB

  21.     /********************* function start *********************/
  22.     buffers = v4l2(video,buffers) ;
  23.     while(1)
  24.     {
  25.         srcBuffer = get_img(dequeue,buffers,srcBuffer);
  26.         dstBuffer = yuyv2rgb24(srcBuffer,dstBuffer);
  27.         img = unchar_to_Mat(dstBuffer,img);

  28.         mtxQueueInput_2.lock();
  29.         queueInput_2.push(img);
  30.         if (queueInput_2.size() >= 30)
  31.         {
  32.             mtxQueueInput_2.unlock();
  33.             printf("Img in queueInput is too much !");
  34.             sleep(1);
  35.             if (queueInput_2.size() >= 60)
  36.             {
  37.                 printf("queue is full !\n");
  38.                 return(FALSE);
  39.             }
  40.         }else{
  41.             mtxQueueInput_2.unlock();
  42.         }
  43.     }
  44.     free(buffers);
  45.     free(srcBuffer);
  46.     free(dstBuffer);

  47.     close_v4l2(buffers);
  48.     return(TRUE);
  49. }

  50. int displayImage_1()
  51. {
  52.     int cpuid = 2;
  53.     Mat img;
  54.     cpu_set_t mask;
  55.     CPU_ZERO(&mask);
  56.     CPU_SET(cpuid, &mask);
  57.     if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0){
  58.         cerr << "set thread affinity failed" << endl;
  59.     }
  60.     printf("Bind Display process to CPU %d\n", cpuid);
  61.     while (1)
  62.     {
  63.         mtxQueueInput_1.lock();
  64.         if (queueInput_1.empty())
  65.         {
  66.             mtxQueueInput_1.unlock();
  67.             usleep(10);
  68.         }
  69.         else
  70.         {
  71.             img = queueInput_1.front();
  72.             cv::imshow("image_10", img);  // display image
  73.             queueInput_1.pop();
  74.             mtxQueueInput_1.unlock();
  75.         }
  76.         if (waitKey(25) == 'Q') {
  77.            break;
  78.         }
  79.     }
  80.     return(TRUE);
  81. }

  82. int displayImage_2()
  83. {
  84.     int cpuid = 3;
  85.     Mat img;
  86.     cpu_set_t mask;
  87.     CPU_ZERO(&mask);
  88.     CPU_SET(cpuid, &mask);
  89.     if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0){
  90.         cerr << "set thread affinity failed" << endl;
  91.     }
  92.     printf("Bind Display process to CPU %d\n", cpuid);
  93.     while (1)
  94.     {
  95.         mtxQueueInput_2.lock();
  96.         if (queueInput_2.empty())
  97.         {
  98.             mtxQueueInput_2.unlock();
  99.             usleep(10);
  100.         }
  101.         else
  102.         {
  103.             img = queueInput_2.front();
  104.             cv::imshow("image_12", img);  // display image
  105.             queueInput_2.pop();
  106.             mtxQueueInput_2.unlock();
  107.         }
  108.         if (waitKey(25) == 'Q') {
  109.            break;
  110.         }
  111.     }
  112.     return(TRUE);
  113. }

  114. int main()
  115. {
  116.     array<thread, 4> threads;
  117.     threads = {thread(get_video_1),
  118.                thread(displayImage_1),
  119.                thread(get_video_2),
  120.                thread(displayImage_2)};
  121.     for (int i = 0; i < 4; i++){
  122.         threads[i].join();
  123.     }
  124.     return(TRUE);
  125. }
复制代码


回复

使用道具 举报

shopping

中级会员

积分
410
 楼主| 发表于 2019-10-18 10:42:13 | 显示全部楼层
补充一下,其实 get_video_1 和 get_video_2   、 displayImage_1 和 displayImage_2  其实可以是一个函数,但是因为函数里的队列和互斥锁不一样,涉及到将 队列 和 互斥锁 作为参数传进函数里。可是互斥锁不可拷贝也不可移动,让我无法实现这个想法,才不得已拆分成四个函数。还是自己太菜。
回复

使用道具 举报

shopping

中级会员

积分
410
 楼主| 发表于 2019-10-18 17:26:42 | 显示全部楼层
后来试验了一下,取消displayImage_1 和 displayImage_2函数,直接把 imshow 放get_video_1 和 get_video_2里面显示图片,结果还是会卡。本来怀疑是我 V4L2 + OpenCV 代码写的有问题,用opencv又写了一遍,双线程调用两个摄像头,分别绑定在2个CPU上,然后分别显示。让我无语的是,就连Opencv也会卡住。郁闷了,自闭了。
回复

使用道具 举报

shopping

中级会员

积分
410
 楼主| 发表于 2019-10-21 10:32:03 | 显示全部楼层
通过观察,每次卡住都是图片播放卡住,导致最后程序崩溃,所以怀疑是 opencv 的显示导致。做了个试验,注释掉 imshow 函数,改为一个累加变量 int num ,num每500次打印看一下,用来判断程序是否在运行。结果是符合我怀疑的,图片不用 imshow 之后,程序确实正常运行。这么说问题出在 图片显示 上?
回复

使用道具 举报

shopping

中级会员

积分
410
 楼主| 发表于 2019-10-22 09:15:03 | 显示全部楼层
后来又试验了一个版本:调用两个摄像头写在一个函数里,绑定到一个CPU上,作为一个线程运行;再把显示两个摄像头图像的代码写进一个函数里,再绑定到一个CPU上,作为一个线程运行。 与一开始相比,少了两个线程,少了两个函数,目的还是没变,双摄 + 双显 。最后运行时发现竟然能长时间运行,不会因卡住而崩溃,不过偶尔会有小停顿,据观察是数据队列存放太多,来不及显示而导致的卡顿,在这可以确定是显示问题。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

产品中心 购买渠道 开源社区 Wiki教程 资料下载 关于Toybrick


快速回复 返回顶部 返回列表