QT使用Opencv进行KCF目标跟踪时无法关闭上次跟踪开始第二次跟踪(init)

在这种特定条件下有没有其他的方法能在MainWindow主体下重启在其他Class线程下的KCF跟踪器的init()函数与update()函数呢?

前提

项目采用QT使用FFmpeg线程拉取网络摄像头的RTSP流转入Opencv线程进行实时(100ms延迟内)的KCF算法跟踪,对于目标跟踪后的图像显示到主线程的ui界面上来。

部分代码
void MyThread::GotMatimg(cv::Mat img)  //目标跟踪显示画面
{
    targetstatus = "3";
    if(successTrack && !Tack) //鼠标框选判别
    {
        targetstatus = "2";
        tracker->init(img,SignPoint);
        Tack = true;
    }
    else if(Tack)
    {
        tracker->update(img,SignPoint);
        rectangle(img,SignPoint, Scalar(255, 0, 0), 2, 3);
        trackx = SignPoint.x + (SignPoint.width /2);
        tracky = SignPoint.y + (SignPoint.height /2);
        if(!tracker->update(img,SignPoint))
        {
            targetstatus = "丢失";
        }
        else
        {
            targetstatus = "跟踪";
        }
    }
    emit SendMatimg(img); //发送图片数据
}

void MainWindow::GainMatimg(cv::Mat img)
{
    showImage(img,QImage::Format_RGB888,ui->label); //显示视频
}

void MainWindow::on_btnsign_clicked()
{
    if(ui->btnsign->text() == "识别")
    {
        threeD->Track_Sign = true;//切换为opencv鼠标框选功能
        Tack = false;        
        targetstatus = "4";
        
        NET_DVR_StopRealPlay(IRealPlayHandle);
        struPlayInfo.hPlayWnd = NULL;//需要 SDK 解码时句柄设为有效值,仅取流不解码时可设为空
        IRealPlayHandle = NET_DVR_RealPlay_V40(userID,&struPlayInfo,NULL,NULL);
        
        connect(m_trad, SIGNAL(SendMatimg(cv::Mat)), this, SLOT(GainMatimg(cv::Mat)),Qt::AutoConnection); //连接发送跟踪处理后的图片信号与接收图片显示槽
        connect(m_ffmpeg, SIGNAL(SigGetOneFrame(cv::Mat)), m_trad, SLOT(GotMatimg(cv::Mat)),Qt::AutoConnection);//连接发送原始图片信号与获取图片进行跟踪处理槽
        
        ui->btnsign->setText("定位");
    }
    else if(ui->btnsign->text() == "定位")
    {
        threeD->Track_Sign = false;//关闭opencv鼠标框选功能
        successTrack = false; //关闭跟踪处理
        
        struPlayInfo.hPlayWnd = hWnd;//需要 SDK 解码时句柄设为有效值,仅取流不解码时可设为空
        IRealPlayHandle = NET_DVR_RealPlay_V40(userID,&struPlayInfo,NULL,NULL);
        
        disconnect(m_trad, SIGNAL(SendMatimg(cv::Mat)), this, SLOT(GainMatimg(cv::Mat)));//断开连接
        disconnect(m_ffmpeg, SIGNAL(SigGetOneFrame(cv::Mat)), m_trad, SLOT(GotMatimg(cv::Mat)));
        ui->btnsign->setText("识别");
    }
}
项目必备(无法改变与替换)
  1. 必须单独使用FFmpeg作为子线程拉取流来进行处理才能达到实时的要求,使用Opencv自带拉流(VideoCapture)无法做到实时要求

  2. 鼠标框选子线程来实现要求的目标框选功能,并该子线程需要具备多功能,所以该功能无法改变

  3. 在OpenCV子线程将获得到的FFmpeg框选的图像进行KCF初始化(init())后实时更新跟踪图像(update()),KCF跟踪算法对于项目的契合度更高

疑问难题

尝试过在父线程使用KCF算法中的release()、reset()函数进行关闭跟踪,但这两种函数都无法关闭跟踪;无法关闭跟踪就无法进行下一步的操作,请帮我解答一下如何C++下在父线程重新开启在子线程的KCF算法。

使用 OpenCV 进行 KCF 目标跟踪时如果想要在主线程中重新开始跟踪,可以在主线程中重新调用 KCF 跟踪器的 init() 函数。要确保跟踪器在主线程之外的线程中的 update() 函数是在安全的方式下被调用的。

如果你使用的是 QThread 来运行 OpenCV 线程,可以使用 QMetaObject::invokeMethod() 函数来从主线程安全地调用 update() 函数。例如:

QMetaObject::invokeMethod(opencvThread, "update", Qt::QueuedConnection);

这样就可以在主线程中安全地调用 update() 函数了。

如果你使用的是其他线程库,可以使用相应的同步机制来保证 update() 函数在安全的方式下被调用。

1.将新对象的Rect2d直接传递给update()函数;
2.再次使用tracker的init()函数。

如果想要在 MainWindow 主线程中重启在 MyThread 线程中运行的 KCF 跟踪器的 init() 函数和 update() 函数,可以在 MainWindow 中添加一个信号,连接到 MyThread 中的一个槽函数,并在这个槽函数中执行重启操作。

具体来说,可以在 MainWindow 中定义一个信号:

signals:
  void resetTracker();

然后在 MyThread 中添加一个槽函数来接收这个信号:

public slots:
  void onResetTracker();

在 onResetTracker() 函数中,可以执行重启 KCF 跟踪器的操作:

void MyThread::onResetTracker()
{
  tracker->init(currentFrame, SignPoint);
}

最后,在 MainWindow 中,可以连接 resetTracker() 信号与 MyThread 中的 onResetTracker() 槽函数:

connect(this, SIGNAL(resetTracker()), m_trad, SLOT(onResetTracker()));

然后在需要重启 KCF 跟踪器时,可以调用 emit resetTracker() 来发送信号,从而在 MyThread 中重启 KCF 跟踪器。
仅供参考,望采纳,谢谢。

目标跟踪的深度学习方法与opencv下的KCF方法
借鉴下
https://blog.csdn.net/LOVE1055259415/article/details/81006596

我看到一篇类似的文章希望对你有所帮助,目标跟踪的深度学习方法与opencv下的KCF方法
借鉴下:《目标跟踪的深度学习方法与opencv下的KCF方法》

在您的项目中,您使用QT作为前端界面开发框架,并使用OpenCV作为图像处理库,并使用KCF算法进行目标跟踪。

若您想要在主线程中重启在其他线程中运行的KCF跟踪器,您可以使用Qt信号与槽机制来进行通信。在主线程中添加一个按钮或其他控件,在点击这个按钮时,主线程发送一个信号到跟踪线程,告诉它重启KCF跟踪器。在跟踪线程中,您需要定义一个槽函数来接收这个信号并执行相应的操作,例如重新调用KCF跟踪器的init()函数。

还有一种可能是,在跟踪线程中,对KCF跟踪器进行初始化之前需要设置标志位, 在主线程中,在需要重启跟踪器时将这个标志位设置为真,并在跟踪线程的update函数中判断这个标志位,如果为真就调用kcf跟踪器的init()函数,再将标志位设置为假。这样做的好处是可以在运行期间随时重启跟踪器,而不会影响到其他的操作。

需要注意,需要确保线程之间的安全性,避免在跟踪线程和主线程之间产生竞争条件。

你可以通过在主线程中发送信号,在其他类的线程中接收信号并调用 init() 和 update() 函数来重启跟踪器。

首先,你需要在其他类的线程中定义一个槽函数来接收信号。然后,你可以在主窗口的某个按钮的点击事件中发射信号,触发其他类的线程中的槽函数。其他类的线程中的槽函数就可以调用跟踪器的 init() 和 update() 函数来重启跟踪器了。

这里是一个简单的示例代码:

主窗口(MainWindow)中的代码:

// 发射信号
void MainWindow::on_btnsign_clicked()
{
    emit RestartTracker();
}

其他类(OtherClass)中的代码:

// 接收信号并调用跟踪器的init()和update()函数
void OtherClass::onRestartTracker()
{
    tracker->init(img,SignPoint);
    tracker->update(img,SignPoint);
}

// 连接信号和槽
OtherClass::OtherClass(QObject *parent) : QObject(parent)
{
    connect(mainWindow, SIGNAL(RestartTracker()), this, SLOT(onRestartTracker()));
}

在这种情况下,你需要在主窗口的构造函数中将其他类的对象传入,以便主窗口能够发射信号并触发其他类的槽函数。你也可以在其他类的构造函数中将主窗口的对象传入,以便其他类能够连接信号和槽。

这里是完整的示例代码:

主窗口(MainWindow)中的代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "otherclass.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    OtherClass *otherClass = new OtherClass(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 发射信号
void MainWindow::on_btnsign_clicked()
{
    emit RestartTracker();
}

其他类(OtherClass)中的代码:

#include "otherclass.h"
#include "mainwindow.h"

OtherClass::OtherClass(QObject *parent) : QObject(parent)
{
    // 获取主窗口的对象
    mainWindow = qobject_cast<MainWindow*>(parent);

    // 连接信号和槽
    connect(mainWindow, SIGNAL(RestartTracker()), this, SLOT(onRestartTracker()));
}

OtherClass::~OtherClass()
{

}

// 接收信号并调用跟踪器的init()和update()函数
void OtherClass::onRestartTracker()
{
    tracker->init(img,SignPoint);
    tracker->update(img,SignPoint);
}