v3.0.t4
I'm going to test a move away from opencv's videoio module. Videoio simply refuses to open any video file even with FFMPEG builtin. I tested old v2.2 code and even that failed on a fresh install of ubuntu sever so this tells me an update on opencv's side broke something. This issue is not new and frankly I'm tired of chasing it. I'm giving QT's QMediaPlayer a try to see how it works out. Will still need opencv for the absdiff and threshold functions, otherwise I would have dropped the API altogeather. Now that the app has QT::Multimedia, QT6 is now the minimum version it will support. CMakeList.txt and the setup script updated accordingly.
This commit is contained in:
parent
40ef014e0f
commit
496bac7d7e
|
@ -13,8 +13,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
include_directories(${OpenCV_INCLUDE_DIRS})
|
include_directories(${OpenCV_INCLUDE_DIRS})
|
||||||
|
|
||||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
find_package(QT NAMES Qt6 COMPONENTS Core REQUIRED)
|
||||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)
|
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Multimedia REQUIRED)
|
||||||
find_package(OpenCV REQUIRED)
|
find_package(OpenCV REQUIRED)
|
||||||
|
|
||||||
add_executable(mow
|
add_executable(mow
|
||||||
|
@ -31,4 +31,4 @@ add_executable(mow
|
||||||
src/camera.cpp
|
src/camera.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(mow Qt${QT_VERSION_MAJOR}::Core ${OpenCV_LIBS})
|
target_link_libraries(mow Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Multimedia ${OpenCV_LIBS})
|
||||||
|
|
24
setup.sh
24
setup.sh
|
@ -1,24 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
apt update -y
|
apt update -y
|
||||||
apt install -y pkg-config
|
apt install -y pkg-config cmake make g++ wget unzip git
|
||||||
apt install -y cmake
|
apt install -y ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libgstreamer1.0-dev x264 libx264-dev libilmbase-dev libopencv-dev qt6-base-dev qtchooser qmake6 qt6-base-dev-tools qt6-multimedia-dev apache2
|
||||||
apt install -y make
|
|
||||||
apt install -y g++
|
|
||||||
apt install -y wget
|
|
||||||
apt install -y unzip
|
|
||||||
apt install -y git
|
|
||||||
apt install -y ffmpeg
|
|
||||||
apt install -y libavcodec-dev
|
|
||||||
apt install -y libavformat-dev
|
|
||||||
apt install -y libavutil-dev
|
|
||||||
apt install -y libswscale-dev
|
|
||||||
apt install -y libgstreamer1.0-dev
|
|
||||||
apt install -y x264
|
|
||||||
apt install -y libx264-dev
|
|
||||||
apt install -y libilmbase-dev
|
|
||||||
apt install -y libopencv-dev
|
|
||||||
apt install -y qtbase5-dev
|
|
||||||
apt install -y qtchooser
|
|
||||||
apt install -y qt5-qmake
|
|
||||||
apt install -y qtbase5-dev-tools
|
|
||||||
apt install -y apache2
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ Camera::Camera(QObject *parent) : QObject(parent)
|
||||||
shared.camName.clear();
|
shared.camName.clear();
|
||||||
|
|
||||||
shared.retCode = 0;
|
shared.retCode = 0;
|
||||||
|
shared.maxScore = 0;
|
||||||
shared.pixThresh = 50;
|
shared.pixThresh = 50;
|
||||||
shared.imgThresh = 800;
|
shared.imgThresh = 800;
|
||||||
shared.maxEvents = 40;
|
shared.maxEvents = 40;
|
||||||
|
@ -240,7 +241,8 @@ bool EventLoop::exec()
|
||||||
if (wrOutVod(event))
|
if (wrOutVod(event))
|
||||||
{
|
{
|
||||||
genHTMLvod(event.evName);
|
genHTMLvod(event.evName);
|
||||||
imwrite(QString("events/" + event.evName + ".jpg").toUtf8().data(), event.thumbnail);
|
|
||||||
|
event.thumbnail.save(QString("events/" + event.evName + ".jpg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
shared->recList.removeFirst();
|
shared->recList.removeFirst();
|
||||||
|
@ -314,16 +316,22 @@ bool EventLoop::wrOutVod(const evt_t &event)
|
||||||
|
|
||||||
DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
|
DetectLoop::DetectLoop(shared_t *sharedRes, QThread *thr, QObject *parent) : Loop(sharedRes, thr, parent)
|
||||||
{
|
{
|
||||||
heartBeat = 0;
|
heartBeat = 0;
|
||||||
evId = 0;
|
evId = 0;
|
||||||
pcId = 0;
|
pcId = 0;
|
||||||
|
player = new QMediaPlayer(this);
|
||||||
|
frameAnalyzer = new MoDetect(shared, this);
|
||||||
|
|
||||||
|
player->setVideoSink(frameAnalyzer);
|
||||||
|
|
||||||
|
connect(player, &QMediaPlayer::errorOccurred, this, &DetectLoop::playError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetectLoop::init()
|
void DetectLoop::init()
|
||||||
{
|
{
|
||||||
thread()->sleep(1);
|
thread()->sleep(1);
|
||||||
|
|
||||||
resetTimers(); Loop::init();
|
resetTimers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetectLoop::resetTimers()
|
void DetectLoop::resetTimers()
|
||||||
|
@ -357,7 +365,8 @@ void DetectLoop::timerEvent(QTimerEvent *event)
|
||||||
|
|
||||||
shared->curEvent.srcPaths.clear();
|
shared->curEvent.srcPaths.clear();
|
||||||
shared->curEvent.evName.clear();
|
shared->curEvent.evName.clear();
|
||||||
shared->curEvent.thumbnail.release();
|
|
||||||
|
shared->curEvent.thumbnail = QImage();
|
||||||
|
|
||||||
shared->maxScore = 0;
|
shared->maxScore = 0;
|
||||||
}
|
}
|
||||||
|
@ -380,6 +389,31 @@ void DetectLoop::timerEvent(QTimerEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DetectLoop::resetDetect()
|
||||||
|
{
|
||||||
|
if (player->playbackState() != QMediaPlayer::PlayingState)
|
||||||
|
{
|
||||||
|
frameAnalyzer->stop();
|
||||||
|
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectLoop::playError(QMediaPlayer::Error error, const QString &errorString)
|
||||||
|
{
|
||||||
|
Q_UNUSED(error)
|
||||||
|
|
||||||
|
detLog("err: media player error - " + errorString, shared);
|
||||||
|
resetDetect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectLoop::playStateChanged(QMediaPlayer::PlaybackState newState)
|
||||||
|
{
|
||||||
|
Q_UNUSED(newState)
|
||||||
|
|
||||||
|
resetDetect();
|
||||||
|
}
|
||||||
|
|
||||||
bool DetectLoop::exec()
|
bool DetectLoop::exec()
|
||||||
{
|
{
|
||||||
QFile fileIn("stream.m3u8");
|
QFile fileIn("stream.m3u8");
|
||||||
|
@ -429,12 +463,9 @@ bool DetectLoop::exec()
|
||||||
{
|
{
|
||||||
prevTs = tsPath;
|
prevTs = tsPath;
|
||||||
|
|
||||||
if (moDetect(tsPath, thread(), shared))
|
player->setSource(QUrl::fromLocalFile(tsPath));
|
||||||
{
|
frameAnalyzer->play(tsPath);
|
||||||
shared->curEvent.srcPaths.append(tsPath);
|
player->play();
|
||||||
|
|
||||||
shared->skipCmd = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Loop::exec();
|
return Loop::exec();
|
||||||
|
|
11
src/camera.h
11
src/camera.h
|
@ -114,16 +114,21 @@ class DetectLoop : public Loop
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
int pcId;
|
int pcId;
|
||||||
int evId;
|
int evId;
|
||||||
QString prevTs;
|
QString prevTs;
|
||||||
|
QMediaPlayer *player;
|
||||||
|
MoDetect *frameAnalyzer;
|
||||||
|
|
||||||
void resetTimers();
|
void resetTimers();
|
||||||
|
void resetDetect();
|
||||||
void timerEvent(QTimerEvent *event);
|
void timerEvent(QTimerEvent *event);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
void playError(QMediaPlayer::Error error, const QString &errorString);
|
||||||
|
void playStateChanged(QMediaPlayer::PlaybackState newState);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QMediaPlayer>
|
||||||
|
#include <QVideoSink>
|
||||||
|
#include <QVideoFrame>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
#include <opencv4/opencv2/opencv.hpp>
|
#include <opencv4/opencv2/opencv.hpp>
|
||||||
#include <opencv4/opencv2/videoio.hpp>
|
#include <opencv4/opencv2/videoio.hpp>
|
||||||
|
@ -31,7 +35,7 @@
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define APP_VER "3.0.t3"
|
#define APP_VER "3.0.t4"
|
||||||
#define APP_NAME "Motion Watch"
|
#define APP_NAME "Motion Watch"
|
||||||
#define APP_BIN "mow"
|
#define APP_BIN "mow"
|
||||||
#define REC_LOG_NAME "rec_log_lines.html"
|
#define REC_LOG_NAME "rec_log_lines.html"
|
||||||
|
@ -42,7 +46,7 @@ struct evt_t
|
||||||
{
|
{
|
||||||
QString evName;
|
QString evName;
|
||||||
QStringList srcPaths;
|
QStringList srcPaths;
|
||||||
Mat thumbnail;
|
QImage thumbnail;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct shared_t
|
struct shared_t
|
||||||
|
|
|
@ -12,93 +12,70 @@
|
||||||
|
|
||||||
#include "mo_detect.h"
|
#include "mo_detect.h"
|
||||||
|
|
||||||
bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share)
|
MoDetect::MoDetect(shared_t *share, QObject *parent) : QVideoSink(parent)
|
||||||
{
|
{
|
||||||
Mat prevGray;
|
shared = share;
|
||||||
Mat nextGray;
|
|
||||||
|
|
||||||
cvtColor(prev, prevGray, COLOR_BGR2GRAY);
|
connect(this, &MoDetect::videoFrameChanged, this, &MoDetect::newFrame);
|
||||||
cvtColor(next, nextGray, COLOR_BGR2GRAY);
|
|
||||||
|
|
||||||
Mat diff;
|
|
||||||
|
|
||||||
absdiff(prevGray, nextGray, diff);
|
|
||||||
threshold(diff, diff, share->pixThresh, 255, THRESH_BINARY);
|
|
||||||
|
|
||||||
score = countNonZero(diff);
|
|
||||||
|
|
||||||
detLog("diff_score: " + QString::number(score) + " thresh: " + QString::number(share->imgThresh), share);
|
|
||||||
|
|
||||||
return score >= share->imgThresh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool moDetect(const QString &buffFile, QThread *thr, shared_t *share)
|
void MoDetect::stop()
|
||||||
{
|
{
|
||||||
auto score = 0;
|
baseImg = QImage(); gap = 0; shared->maxScore = 0;
|
||||||
auto mod = false;
|
}
|
||||||
|
|
||||||
detLog("stream_clip: " + buffFile, share);
|
void MoDetect::play(const QString &path)
|
||||||
|
{
|
||||||
|
filePath = path;
|
||||||
|
}
|
||||||
|
|
||||||
VideoCapture capture;
|
void MoDetect::newFrame()
|
||||||
|
{
|
||||||
if (!capture.open(buffFile.toUtf8().data(), CAP_FFMPEG))
|
if (!baseImg.isNull())
|
||||||
{
|
{
|
||||||
thr->usleep(500);
|
baseImg = videoFrame().toImage().convertToFormat(QImage::Format_Grayscale8);
|
||||||
|
|
||||||
capture.open(buffFile.toUtf8().data(), CAP_FFMPEG);
|
|
||||||
}
|
}
|
||||||
|
else if (shared->frameGap > gap)
|
||||||
if (capture.isOpened())
|
|
||||||
{
|
{
|
||||||
Mat prev;
|
gap++;
|
||||||
Mat next;
|
|
||||||
|
|
||||||
int fps = capture.get(cv::CAP_PROP_FPS);
|
|
||||||
|
|
||||||
for (auto gap = 0, frm = fps; capture.grab(); ++gap, ++frm)
|
|
||||||
{
|
|
||||||
if (frm == fps) thr->sleep(1); frm = 1;
|
|
||||||
|
|
||||||
if (prev.empty())
|
|
||||||
{
|
|
||||||
capture.retrieve(prev);
|
|
||||||
}
|
|
||||||
else if (gap == (share->frameGap - 1))
|
|
||||||
{
|
|
||||||
capture.retrieve(next);
|
|
||||||
|
|
||||||
if (!next.empty())
|
|
||||||
{
|
|
||||||
if (imgDiff(prev, next, score, share))
|
|
||||||
{
|
|
||||||
mod = true;
|
|
||||||
|
|
||||||
if (share->maxScore <= score)
|
|
||||||
{
|
|
||||||
share->maxScore = score;
|
|
||||||
|
|
||||||
resize(next, share->curEvent.thumbnail, Size(720, 480), INTER_LINEAR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = next.clone();
|
|
||||||
gap = 0;
|
|
||||||
|
|
||||||
next.release();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
capture.grab();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
detLog("err: failed to open: " + buffFile + " after 500 msecs. giving up.", share);
|
auto nextImg = videoFrame().toImage();
|
||||||
|
auto nextImgGray = nextImg.convertToFormat(QImage::Format_Grayscale8);
|
||||||
|
auto score = 0;
|
||||||
|
|
||||||
|
if (diff(baseImg, nextImgGray, &score))
|
||||||
|
{
|
||||||
|
shared->skipCmd = true;
|
||||||
|
|
||||||
|
if (!shared->curEvent.srcPaths.contains(filePath))
|
||||||
|
{
|
||||||
|
shared->curEvent.srcPaths.append(filePath);
|
||||||
|
|
||||||
|
if (shared->maxScore <= score)
|
||||||
|
{
|
||||||
|
shared->maxScore = score;
|
||||||
|
|
||||||
|
shared->curEvent.thumbnail = nextImg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
capture.release();
|
|
||||||
|
bool MoDetect::diff(QImage &base, QImage &next, int *score)
|
||||||
return mod;
|
{
|
||||||
|
Mat baseMat = Mat(base.height(), base.width(), CV_8U, base.bits(), base.bytesPerLine());
|
||||||
|
Mat nextMat = Mat(next.height(), next.width(), CV_8U, next.bits(), next.bytesPerLine());
|
||||||
|
Mat diffMat;
|
||||||
|
|
||||||
|
absdiff(baseMat, nextMat, diffMat);
|
||||||
|
threshold(diffMat, diffMat, shared->pixThresh, 255, THRESH_BINARY);
|
||||||
|
|
||||||
|
*score = countNonZero(diffMat);
|
||||||
|
|
||||||
|
detLog("diff_score: " + QString::number(*score) + " thresh: " + QString::number(shared->imgThresh), shared);
|
||||||
|
|
||||||
|
return *score >= shared->imgThresh;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,29 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
bool imgDiff(const Mat &prev, const Mat &next, int &score, shared_t *share);
|
class MoDetect : public QVideoSink
|
||||||
bool moDetect(const QString &buffFile, QThread *thr, shared_t *share);
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QImage baseImg;
|
||||||
|
QString filePath;
|
||||||
|
shared_t *shared;
|
||||||
|
int gap;
|
||||||
|
|
||||||
|
bool diff(QImage &base, QImage &next, int *score);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void newFrame();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void play(const QString &path);
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
explicit MoDetect(shared_t *share, QObject *parent = nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
#endif // MO_DETECT_H
|
#endif // MO_DETECT_H
|
||||||
|
|
Loading…
Reference in New Issue
Block a user