a922f46562
* Remove Camera Capture Popup * clang
570 lines
No EOL
15 KiB
C++
570 lines
No EOL
15 KiB
C++
#include "webcam.h"
|
|
#include "tenv.h"
|
|
|
|
#ifdef WIN32
|
|
#include <Windows.h>
|
|
#include <mfobjects.h>
|
|
#include <mfapi.h>
|
|
#include <mfidl.h>
|
|
#pragma comment(lib, "Mfplat.lib")
|
|
#pragma comment(lib, "Mf.lib")
|
|
#pragma comment(lib, "Mfreadwrite.lib")
|
|
#pragma comment(lib, "mfuuid.lib")
|
|
#pragma comment(lib, "shlwapi.lib")
|
|
#endif
|
|
|
|
#include <QCamera>
|
|
#include <QCameraInfo>
|
|
#include <QApplication>
|
|
|
|
TEnv::IntVar StopMotionUseDirectShow("StopMotionUseDirectShow", 1);
|
|
TEnv::IntVar StopMotionUseMjpg("StopMotionUseMjpg", 1);
|
|
//-----------------------------------------------------------------------------
|
|
|
|
Webcam::Webcam() {
|
|
m_useDirectShow = StopMotionUseDirectShow;
|
|
m_useMjpg = StopMotionUseMjpg;
|
|
m_lut = cv::Mat(1, 256, CV_8U);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
Webcam::~Webcam() {}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
QList<QCameraInfo> Webcam::getWebcams() {
|
|
m_webcams.clear();
|
|
m_webcams = QCameraInfo::availableCameras();
|
|
return m_webcams;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setWebcam(QCamera* camera) { m_webcam = camera; }
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
bool Webcam::initWebcam(int index) {
|
|
#ifdef WIN32
|
|
if (!m_useDirectShow) {
|
|
// the webcam order obtained from Qt isn't always the same order as
|
|
// the one obtained from OpenCV without DirectShow
|
|
translateIndex(index);
|
|
m_cvWebcam.open(m_webcamIndex);
|
|
} else {
|
|
m_webcamIndex = index;
|
|
m_cvWebcam.open(m_webcamIndex, cv::CAP_DSHOW);
|
|
}
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
return false;
|
|
}
|
|
#else
|
|
m_webcamIndex = index;
|
|
m_cvWebcam.open(index);
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
return false;
|
|
}
|
|
#endif
|
|
// mjpg is used by many webcams
|
|
// opencv runs very slow on some webcams without it.
|
|
if (m_useMjpg) {
|
|
m_cvWebcam.set(cv::CAP_PROP_FOURCC,
|
|
cv::VideoWriter::fourcc('m', 'j', 'p', 'g'));
|
|
m_cvWebcam.set(cv::CAP_PROP_FOURCC,
|
|
cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
|
|
}
|
|
m_cvWebcam.set(3, m_webcamWidth);
|
|
m_cvWebcam.set(4, m_webcamHeight);
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::releaseWebcam() { m_cvWebcam.release(); }
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
int Webcam::getIndexOfResolution() {
|
|
return m_webcamResolutions.indexOf(QSize(m_webcamWidth, m_webcamHeight));
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
bool Webcam::getWebcamImage(TRaster32P& tempImage) {
|
|
bool error = false;
|
|
cv::Mat imgOriginal;
|
|
cv::Mat imgCorrected;
|
|
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
// mjpg is used by many webcams
|
|
// opencv runs very slow on some webcams without it.
|
|
if (m_useMjpg) {
|
|
m_cvWebcam.set(cv::CAP_PROP_FOURCC,
|
|
cv::VideoWriter::fourcc('m', 'j', 'p', 'g'));
|
|
m_cvWebcam.set(cv::CAP_PROP_FOURCC,
|
|
cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
|
|
}
|
|
m_cvWebcam.set(3, m_webcamWidth);
|
|
m_cvWebcam.set(4, m_webcamHeight);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
error = true;
|
|
}
|
|
}
|
|
|
|
bool blnFrameReadSuccessfully =
|
|
m_cvWebcam.read(imgOriginal); // get next frame
|
|
|
|
if (!blnFrameReadSuccessfully ||
|
|
imgOriginal.empty()) { // if frame not read successfully
|
|
std::cout << "error: frame not read from webcam\n";
|
|
error = true; // print error message to std out
|
|
}
|
|
|
|
if (!error) {
|
|
cv::cvtColor(imgOriginal, imgCorrected, cv::COLOR_BGR2BGRA);
|
|
cv::flip(imgCorrected, imgCorrected, 0);
|
|
emit updateHistogram(imgCorrected);
|
|
|
|
bool convertBack = false;
|
|
if (m_colorMode != 0) {
|
|
cv::cvtColor(imgCorrected, imgCorrected, cv::COLOR_RGB2GRAY);
|
|
convertBack = true;
|
|
}
|
|
|
|
// color and grayscale mode
|
|
if (m_colorMode != 2)
|
|
adjustLevel(imgCorrected);
|
|
else
|
|
binarize(imgCorrected);
|
|
|
|
if (convertBack) {
|
|
cv::cvtColor(imgCorrected, imgCorrected, cv::COLOR_GRAY2BGRA);
|
|
}
|
|
|
|
int width = m_cvWebcam.get(3);
|
|
int height = m_cvWebcam.get(4);
|
|
int size = imgCorrected.total() * imgCorrected.elemSize();
|
|
|
|
tempImage = TRaster32P(width, height);
|
|
// m_liveViewImage = TRaster32P(width, height);
|
|
tempImage->lock();
|
|
uchar* imgBuf = imgCorrected.data;
|
|
uchar* rawData = tempImage->getRawData();
|
|
memcpy(rawData, imgBuf, size);
|
|
tempImage->unlock();
|
|
}
|
|
if (error) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setUseDirectShow(int state) {
|
|
m_useDirectShow = state;
|
|
StopMotionUseDirectShow = state;
|
|
emit(useDirectShowSignal(state));
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setUseMjpg(bool on) {
|
|
m_useMjpg = on;
|
|
StopMotionUseMjpg = int(on);
|
|
emit(useMjpgSignal(on));
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::clearWebcam() {
|
|
m_webcamDescription = QString();
|
|
m_webcamDeviceName = QString();
|
|
m_webcamIndex = -1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::clearWebcamResolutions() { m_webcamResolutions.clear(); }
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::refreshWebcamResolutions() {
|
|
clearWebcamResolutions();
|
|
m_webcamResolutions = getWebcam()->supportedViewfinderResolutions();
|
|
if (m_webcamResolutions.size() == 0) {
|
|
m_webcamResolutions.push_back(QSize(640, 360));
|
|
m_webcamResolutions.push_back(QSize(640, 480));
|
|
m_webcamResolutions.push_back(QSize(800, 448));
|
|
m_webcamResolutions.push_back(QSize(800, 600));
|
|
m_webcamResolutions.push_back(QSize(848, 480));
|
|
m_webcamResolutions.push_back(QSize(864, 480));
|
|
m_webcamResolutions.push_back(QSize(960, 540));
|
|
m_webcamResolutions.push_back(QSize(960, 720));
|
|
m_webcamResolutions.push_back(QSize(1024, 576));
|
|
m_webcamResolutions.push_back(QSize(1280, 720));
|
|
m_webcamResolutions.push_back(QSize(1600, 896));
|
|
m_webcamResolutions.push_back(QSize(1600, 900));
|
|
m_webcamResolutions.push_back(QSize(1920, 1080));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
bool Webcam::getWebcamAutofocusStatus() {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (m_cvWebcam.isOpened()) {
|
|
double value = m_cvWebcam.get(cv::CAP_PROP_AUTOFOCUS);
|
|
if (value > 0.0) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
void Webcam::setWebcamAutofocusStatus(bool on) {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return;
|
|
}
|
|
}
|
|
if (m_cvWebcam.isOpened()) {
|
|
double value = on ? 1.0 : 0.0;
|
|
m_cvWebcam.set(cv::CAP_PROP_AUTOFOCUS, value);
|
|
value = m_cvWebcam.get(cv::CAP_PROP_AUTOFOCUS);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
int Webcam::getWebcamFocusValue() {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (m_cvWebcam.isOpened()) {
|
|
double value = m_cvWebcam.get(cv::CAP_PROP_FOCUS);
|
|
return static_cast<int>(value);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setWebcamFocusValue(int value) {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return;
|
|
}
|
|
}
|
|
m_cvWebcam.set(cv::CAP_PROP_FOCUS, value);
|
|
value = m_cvWebcam.get(cv::CAP_PROP_FOCUS);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
int Webcam::getWebcamExposureValue() {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (m_cvWebcam.isOpened()) {
|
|
double value = m_cvWebcam.get(cv::CAP_PROP_EXPOSURE);
|
|
return static_cast<int>(value);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setWebcamExposureValue(int value) {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return;
|
|
}
|
|
}
|
|
m_cvWebcam.set(cv::CAP_PROP_AUTO_EXPOSURE, 0.25);
|
|
m_cvWebcam.set(cv::CAP_PROP_EXPOSURE, value);
|
|
value = m_cvWebcam.get(cv::CAP_PROP_EXPOSURE);
|
|
getWebcamExposureValue();
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
int Webcam::getWebcamBrightnessValue() {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (m_cvWebcam.isOpened()) {
|
|
double value = m_cvWebcam.get(cv::CAP_PROP_BRIGHTNESS);
|
|
return static_cast<int>(value);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setWebcamBrightnessValue(int value) {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return;
|
|
}
|
|
}
|
|
m_cvWebcam.set(cv::CAP_PROP_BRIGHTNESS, value);
|
|
value = m_cvWebcam.get(cv::CAP_PROP_BRIGHTNESS);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
int Webcam::getWebcamContrastValue() {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (m_cvWebcam.isOpened()) {
|
|
double value = m_cvWebcam.get(cv::CAP_PROP_CONTRAST);
|
|
return static_cast<int>(value);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setWebcamContrastValue(int value) {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return;
|
|
}
|
|
}
|
|
m_cvWebcam.set(cv::CAP_PROP_CONTRAST, value);
|
|
value = m_cvWebcam.get(cv::CAP_PROP_CONTRAST);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
int Webcam::getWebcamGainValue() {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (m_cvWebcam.isOpened()) {
|
|
double value = m_cvWebcam.get(cv::CAP_PROP_GAIN);
|
|
return static_cast<int>(value);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setWebcamGainValue(int value) {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return;
|
|
}
|
|
}
|
|
m_cvWebcam.set(cv::CAP_PROP_GAIN, value);
|
|
value = m_cvWebcam.get(cv::CAP_PROP_GAIN);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
int Webcam::getWebcamSaturationValue() {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return 0;
|
|
}
|
|
}
|
|
if (m_cvWebcam.isOpened()) {
|
|
double value = m_cvWebcam.get(cv::CAP_PROP_SATURATION);
|
|
return static_cast<int>(value);
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::setWebcamSaturationValue(int value) {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return;
|
|
}
|
|
}
|
|
m_cvWebcam.set(cv::CAP_PROP_SATURATION, value);
|
|
value = m_cvWebcam.get(cv::CAP_PROP_SATURATION);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::openSettingsWindow() {
|
|
if (m_cvWebcam.isOpened() == false) {
|
|
initWebcam(m_webcamIndex);
|
|
|
|
if (!m_cvWebcam.isOpened()) {
|
|
return;
|
|
}
|
|
}
|
|
m_cvWebcam.set(cv::CAP_PROP_SETTINGS, 1.0);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::adjustLevel(cv::Mat& image) {
|
|
if (m_black == 0 && m_white == 255 && m_gamma == 1.0) return;
|
|
|
|
cv::LUT(image, m_lut, image);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::binarize(cv::Mat& image) {
|
|
cv::threshold(image, image, m_threshold, 255, cv::THRESH_BINARY);
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
void Webcam::computeLut() {
|
|
const float maxChannelValueF = 255.0f;
|
|
|
|
float value;
|
|
|
|
uchar* p = m_lut.data;
|
|
for (int i = 0; i < 256; i++) {
|
|
if (i <= m_black)
|
|
value = 0.0f;
|
|
else if (i >= m_white)
|
|
value = 1.0f;
|
|
else {
|
|
value = (float)(i - m_black) / (float)(m_white - m_black);
|
|
value = std::pow(value, 1.0f / m_gamma);
|
|
}
|
|
|
|
p[i] = (uchar)std::floor(value * maxChannelValueF);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
bool Webcam::translateIndex(int index) {
|
|
// We are using Qt to get the camera info and supported resolutions, but
|
|
// we are using OpenCV to actually get the images.
|
|
// The camera index from OpenCV and from Qt don't always agree,
|
|
// So this checks the name against the correct index.
|
|
m_webcamIndex = index;
|
|
|
|
#ifdef WIN32
|
|
|
|
// Thanks to:
|
|
// https://elcharolin.wordpress.com/2017/08/28/webcam-capture-with-the-media-foundation-sdk/
|
|
// for the webcam enumeration here
|
|
|
|
#define CLEAN_ATTRIBUTES() \
|
|
if (attributes) { \
|
|
attributes->Release(); \
|
|
attributes = NULL; \
|
|
} \
|
|
for (DWORD i = 0; i < count; i++) { \
|
|
if (&devices[i]) { \
|
|
devices[i]->Release(); \
|
|
devices[i] = NULL; \
|
|
} \
|
|
} \
|
|
CoTaskMemFree(devices); \
|
|
return hr;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// this is important!!
|
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
|
UINT32 count = 0;
|
|
IMFAttributes* attributes = NULL;
|
|
IMFActivate** devices = NULL;
|
|
|
|
if (FAILED(hr)) {
|
|
CLEAN_ATTRIBUTES()
|
|
}
|
|
// Create an attribute store to specify enumeration parameters.
|
|
hr = MFCreateAttributes(&attributes, 1);
|
|
|
|
if (FAILED(hr)) {
|
|
CLEAN_ATTRIBUTES()
|
|
}
|
|
|
|
// The attribute to be requested is devices that can capture video
|
|
hr = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
|
|
if (FAILED(hr)) {
|
|
CLEAN_ATTRIBUTES()
|
|
}
|
|
// Enummerate the video capture devices
|
|
hr = MFEnumDeviceSources(attributes, &devices, &count);
|
|
|
|
if (FAILED(hr)) {
|
|
CLEAN_ATTRIBUTES()
|
|
}
|
|
// if there are any available devices
|
|
if (count > 0) {
|
|
WCHAR* nameString = NULL;
|
|
// Get the human-friendly name of the device
|
|
UINT32 cchName;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
hr = devices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
|
|
&nameString, &cchName);
|
|
std::string desc = m_webcamDescription.toStdString();
|
|
if (nameString == m_webcamDescription.toStdWString()) {
|
|
m_webcamIndex = i;
|
|
break;
|
|
}
|
|
// devices[0]->ShutdownObject();
|
|
}
|
|
|
|
CoTaskMemFree(nameString);
|
|
}
|
|
// clean
|
|
CLEAN_ATTRIBUTES()
|
|
#else
|
|
return true;
|
|
#endif
|
|
} |