【问题标题】:OpenCV camera capture from within a thread从线程内捕获 OpenCV 相机
【发布时间】:2014-12-26 19:56:37
【问题描述】:

这是我试图开始工作的代码的一小部分。这也是我第一次使用 C++。我习惯了高级语言,比如 Java 或 C#。

主版本旨在作为共享对象或 DLL 运行。这个想法是外部程序(在 C# 中)将启动主循环。来自相机的帧将在一个线程中捕获。信息将在该线程内部处理并复制到数组(“dataArray”)。此复制过程将在锁定类互斥锁时完成。然后,另一个在外部调用的函数会将保存的数组(“dataArray”)复制到第二个数组(“outArray”)并返回指向第二个数组的指针。外部程序将使用指针从第二个Array中复制数据,直到再次调用该函数时才会修改该数据。

但要让所有这些工作,我需要不断地捕捉帧。我意识到我需要一些东西来让我的main 函数继续运行,所以我在那里保持了一个无限循环。在“真实”版本中,keepRunning 变量将被运行库的外部程序更改。

我最近在 StackOverflow 上接受了关于不创建全局变量的讲座,因此我将我的类的一个实例保留在静态成员中。这在 Java 中是相当标准的。我不知道这在 C++ 中是否是不好的做法。我也对 C++ 线程如何在创建后立即启动感到惊讶,而没有明确的“启动”指令。这就是为什么我把我唯一的线程放在一个向量中。这似乎是大多数人推荐的。

我知道如果keepRunning 从未真正被更改,线程将永远不会被加入,但我稍后会很高兴。我在 Mac 上运行它,但我最终需要它在 Windows、Mac 和 Linux 上运行。

这是我的标题:

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>

using namespace cv;
using namespace std;

class MyCap {
public:
  MyCap();
  VideoCapture cap;
  static MyCap * instance;
  void run();
  static void RunThreads(MyCap * cap);
  bool keepRunning = true; // Will be changed by the external program.
  vector<thread> capThreads;
private:
  Mat frame;
};

这是我的代码:

#include "theheader.h"

MyCap * MyCap::instance = NULL;

int main(int argc, char** argv) {
  MyCap::instance = new MyCap();
  MyCap::instance->capThreads.push_back(thread(MyCap::RunThreads, MyCap::instance));
  // Outside loop.
  while(MyCap::instance->keepRunning) {
  }
  for (int i = 0; i < MyCap::instance->capThreads.size(); i++) {
    MyCap::instance->capThreads[i].join();
  }
}

MyCap::MyCap() {
  namedWindow("flow", 1);
  cap.open(0);
}

void MyCap::RunThreads(MyCap * cap) {
  cap->run();
}

void MyCap::run() {
  // Inside loop.
  while(keepRunning) {
    cap >> frame;
    imshow("flow", frame);
    if (waitKey(30) >= 0) {
      break;
    }
  }
}

使用此代码,我得到一个黑屏。如果我从run 方法中运行cap.open(0),我什至不明白。我显然做错了什么。但真正让我困惑的是:为什么调用相同的代码会有所不同?如果我在main 内部运行现在在run 中的内容,它将起作用。如果我将cap.open(0) 的调用从构造函数更改为run,这会改变方法的作用。 waitKey 条件也停止在线程内工作。我错过了什么大事?

版本 2

根据@darien-pardibas 的建议,我做了第二个版本:

标题:

#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>

using namespace cv;
using namespace std;

class MyCap {
public:
  MyCap();
  void run();
  bool keepRunning = true; // Will be changed by the external program.
  static void RunThreads(MyCap * cap);
  static vector<thread> capThreads;
  static MyCap * getInstance();
private:
  static MyCap * instance;
};

主文件:

#include "theprogram.h" // I'll admit that, even for a placeholder, it was a bad name.

MyCap * MyCap::instance = NULL;

vector<thread> MyCap::capThreads;

MyCap::MyCap() {
  cout << "Instantiate" << endl;
}

MyCap * MyCap::getInstance() {
  if (MyCap::instance == NULL) {
    MyCap::instance = new MyCap;
  }
  return MyCap::instance;
}

void MyCap::RunThreads(MyCap * cap) {
  cap->run();
}

void MyCap::run() {
  cout << "Run" << endl;
  namedWindow("flow", 1);
  cout << "Window created." << endl;
  VideoCapture cap(0); // HANGS HERE!
  cout << "Camera open." << endl; // This never gets printed.
  // Inside loop.
  Mat frame;
  while(keepRunning) {
    cap >> frame;
    imshow("flow", frame);
    if (waitKey(30) >= 0) {
      break;
    }
  }
}

int main(int argc, char** argv) {
  MyCap::capThreads.push_back(thread(&MyCap::RunThreads, MyCap::getInstance()));
  for (int i = 0; i < MyCap::capThreads.size(); i++) {
    MyCap::capThreads[i].join();
  }
}

打印出来:

Instantiate
Run
Window created.

然后挂在那里。

但是如果我将代码从run 移动到main 并将keepRunning 更改为true,那么它会按预期工作。我想我遗漏了其他东西,我猜这与 C++ 的工作方式有关。

【问题讨论】:

  • 您的主线程中有一个无限循环:while(MyCap::instance-&gt;keepRunning) { } 。你不需要那个。也尝试用thread(&amp;MyCap::RunThreads, MyCap::instance));替换thread(MyCap::RunThreads, MyCap::instance));

标签: c++ multithreading opencv


【解决方案1】:

好的,不用考虑解决我在您的代码中看到的所有设计模式问题,我可以确认下面的代码有效。我认为主要问题是您需要在要捕获图像的同一线程中创建namedWindow,并删除您在main 方法中的while 循环。

// "theheader.h"

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>

class MyCap {
public:
    void run();
    static void RunThreads(MyCap * cap);
    bool keepRunning = true; // Will be changed by the external program.
    std::vector<std::thread> capThreads;
private:
    cv::Mat frame;
    cv::VideoCapture cap;
    MyCap() { }
    static MyCap * s_instance;
public:
    static MyCap *instance();
};

// "theheader.cpp"

#include "theheader.h"
#pragma comment(lib, "opencv_core248d")
#pragma comment(lib, "opencv_highgui248d")

using namespace std;
using namespace cv;

MyCap * MyCap::s_instance = NULL;
MyCap* MyCap::instance() {
    if (s_instance == NULL)
        s_instance = new MyCap();
    return s_instance;
}

void MyCap::RunThreads(MyCap * cap) {
    cap->run();
}

void MyCap::run() {
    namedWindow("flow", 1);
    cap.open(0);

    // Inside loop.
    while (keepRunning) {
        cap >> frame;
        imshow("flow", frame);
        if (waitKey(30) >= 0) {
            break;
        }
    }
}

int main(int argc, char** argv) {
    MyCap::instance()->capThreads.push_back(thread(&MyCap::RunThreads, MyCap::instance()));

    for (int i = 0; i < MyCap::instance()->capThreads.size(); i++) {
        MyCap::instance()->capThreads[i].join();
    }
}

【讨论】:

  • 我试过了,或者类似的东西。我将我的新版本放在问题的主体中。
  • 第一件事。你能确认从主线程运行该循环有效吗?也就是说,直接从 main() 方法调用你的 run 方法,而不在你的应用程序中产生任何线程?
  • 我发现这篇文章 stackoverflow.com/questions/23624698/…> 似乎说我无法从线程中运行 VideoCapture。但是当我从 C# 调用其他函数(不在这篇文章中)时,我需要保持主循环运行。我认为我需要在 C++ 中的一个单独线程中运行,但我试图将所有内容保存在 C++ 中的一个线程中,并在 C# 中的一个线程中保持对 main 的等效调用。希望这会奏效。但是,无论如何,我似乎无法在主线程之外运行 VideoCapture。
  • 我刚刚完全按照您在第 2 版中发布的代码尝试了您的代码,它就像一个魅力。我尝试将 VideoCapture 对象的创建移动到主线程和衍生线程,但没有任何区别。只是好奇,您使用的是什么版本的 OpenCV?
  • 2.4.10。这是最新的稳定版本。 3.0我没试过。
猜你喜欢
  • 1970-01-01
  • 2018-02-13
  • 2011-07-20
  • 1970-01-01
  • 2015-02-14
  • 1970-01-01
  • 1970-01-01
  • 2017-04-14
  • 1970-01-01
相关资源
最近更新 更多