1 #include "webcam.h"
2 #include "tenv.h"
3
4 #ifdef WIN32
5 #include <Windows.h>
6 #include <mfobjects.h>
7 #include <mfapi.h>
8 #include <mfidl.h>
9 #pragma comment(lib, "Mfplat.lib")
10 #pragma comment(lib, "Mf.lib")
11 #pragma comment(lib, "Mfreadwrite.lib")
12 #pragma comment(lib, "mfuuid.lib")
13 #pragma comment(lib, "shlwapi.lib")
14 #endif
15
16 #include <QCamera>
17 #include <QCameraInfo>
18 #include <QApplication>
19
20 TEnv::IntVar StopMotionUseDirectShow("StopMotionUseDirectShow", 1);
21 TEnv::IntVar StopMotionUseMjpg("StopMotionUseMjpg", 1);
22 //-----------------------------------------------------------------------------
23
Webcam()24 Webcam::Webcam() {
25 m_useDirectShow = StopMotionUseDirectShow;
26 m_useMjpg = StopMotionUseMjpg;
27 }
28
29 //-----------------------------------------------------------------
30
~Webcam()31 Webcam::~Webcam() {}
32
33 //-----------------------------------------------------------------
34
getWebcams()35 QList<QCameraInfo> Webcam::getWebcams() {
36 m_webcams.clear();
37 m_webcams = QCameraInfo::availableCameras();
38 return m_webcams;
39 }
40
41 //-----------------------------------------------------------------
42
setWebcam(QCamera * camera)43 void Webcam::setWebcam(QCamera* camera) { m_webcam = camera; }
44
45 //-----------------------------------------------------------------
46
initWebcam(int index)47 bool Webcam::initWebcam(int index) {
48 #ifdef WIN32
49 if (!m_useDirectShow) {
50 // the webcam order obtained from Qt isn't always the same order as
51 // the one obtained from OpenCV without DirectShow
52 translateIndex(index);
53 m_cvWebcam.open(m_webcamIndex);
54 } else {
55 m_webcamIndex = index;
56 m_cvWebcam.open(m_webcamIndex, cv::CAP_DSHOW);
57 }
58 if (m_cvWebcam.isOpened() == false) {
59 return false;
60 }
61 #else
62 m_webcamIndex = index;
63 m_cvWebcam.open(index);
64 if (m_cvWebcam.isOpened() == false) {
65 return false;
66 }
67 #endif
68 // mjpg is used by many webcams
69 // opencv runs very slow on some webcams without it.
70 if (m_useMjpg) {
71 m_cvWebcam.set(cv::CAP_PROP_FOURCC,
72 cv::VideoWriter::fourcc('m', 'j', 'p', 'g'));
73 m_cvWebcam.set(cv::CAP_PROP_FOURCC,
74 cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
75 }
76 m_cvWebcam.set(3, m_webcamWidth);
77 m_cvWebcam.set(4, m_webcamHeight);
78
79 return true;
80 }
81
82 //-----------------------------------------------------------------
83
releaseWebcam()84 void Webcam::releaseWebcam() { m_cvWebcam.release(); }
85
86 //-----------------------------------------------------------------
87
getIndexOfResolution()88 int Webcam::getIndexOfResolution() {
89 return m_webcamResolutions.indexOf(QSize(m_webcamWidth, m_webcamHeight));
90 }
91
92 //-----------------------------------------------------------------
93
getWebcamImage(TRaster32P & tempImage)94 bool Webcam::getWebcamImage(TRaster32P& tempImage) {
95 bool error = false;
96 cv::Mat imgOriginal;
97 cv::Mat imgCorrected;
98
99 if (m_cvWebcam.isOpened() == false) {
100 initWebcam(m_webcamIndex);
101
102 // mjpg is used by many webcams
103 // opencv runs very slow on some webcams without it.
104 if (m_useMjpg) {
105 m_cvWebcam.set(cv::CAP_PROP_FOURCC,
106 cv::VideoWriter::fourcc('m', 'j', 'p', 'g'));
107 m_cvWebcam.set(cv::CAP_PROP_FOURCC,
108 cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
109 }
110 m_cvWebcam.set(3, m_webcamWidth);
111 m_cvWebcam.set(4, m_webcamHeight);
112
113 if (!m_cvWebcam.isOpened()) {
114 error = true;
115 }
116 }
117
118 bool blnFrameReadSuccessfully =
119 m_cvWebcam.read(imgOriginal); // get next frame
120
121 if (!blnFrameReadSuccessfully ||
122 imgOriginal.empty()) { // if frame not read successfully
123 std::cout << "error: frame not read from webcam\n";
124 error = true; // print error message to std out
125 }
126
127 if (!error) {
128 cv::cvtColor(imgOriginal, imgCorrected, cv::COLOR_BGR2BGRA);
129 cv::flip(imgCorrected, imgCorrected, 0);
130 int width = m_cvWebcam.get(3);
131 int height = m_cvWebcam.get(4);
132 int size = imgCorrected.total() * imgCorrected.elemSize();
133
134 tempImage = TRaster32P(width, height);
135 // m_liveViewImage = TRaster32P(width, height);
136 tempImage->lock();
137 uchar* imgBuf = imgCorrected.data;
138 uchar* rawData = tempImage->getRawData();
139 memcpy(rawData, imgBuf, size);
140 tempImage->unlock();
141 }
142 if (error) {
143 return false;
144 } else {
145 return true;
146 }
147 }
148
149 //-----------------------------------------------------------------
150
setUseDirectShow(int state)151 void Webcam::setUseDirectShow(int state) {
152 m_useDirectShow = state;
153 StopMotionUseDirectShow = state;
154 emit(useDirectShowSignal(state));
155 }
156
157 //-----------------------------------------------------------------
158
setUseMjpg(bool on)159 void Webcam::setUseMjpg(bool on) {
160 m_useMjpg = on;
161 StopMotionUseMjpg = int(on);
162 emit(useMjpgSignal(on));
163 }
164
165 //-----------------------------------------------------------------
166
clearWebcam()167 void Webcam::clearWebcam() {
168 m_webcamDescription = QString();
169 m_webcamDeviceName = QString();
170 m_webcamIndex = -1;
171 }
172
173 //-----------------------------------------------------------------
174
clearWebcamResolutions()175 void Webcam::clearWebcamResolutions() { m_webcamResolutions.clear(); }
176
177 //-----------------------------------------------------------------
178
refreshWebcamResolutions()179 void Webcam::refreshWebcamResolutions() {
180 clearWebcamResolutions();
181 m_webcamResolutions = getWebcam()->supportedViewfinderResolutions();
182 }
183
184 //-----------------------------------------------------------------
185
getWebcamAutofocusStatus()186 bool Webcam::getWebcamAutofocusStatus() {
187 if (m_cvWebcam.isOpened() == false) {
188 initWebcam(m_webcamIndex);
189
190 if (!m_cvWebcam.isOpened()) {
191 return false;
192 }
193 }
194 if (m_cvWebcam.isOpened()) {
195 double value = m_cvWebcam.get(cv::CAP_PROP_AUTOFOCUS);
196 if (value > 0.0) {
197 return true;
198 } else {
199 return false;
200 }
201 }
202 return false;
203 }
204
205 //-----------------------------------------------------------------
setWebcamAutofocusStatus(bool on)206 void Webcam::setWebcamAutofocusStatus(bool on) {
207 if (m_cvWebcam.isOpened() == false) {
208 initWebcam(m_webcamIndex);
209
210 if (!m_cvWebcam.isOpened()) {
211 return;
212 }
213 }
214 if (m_cvWebcam.isOpened()) {
215 double value = on ? 1.0 : 0.0;
216 m_cvWebcam.set(cv::CAP_PROP_AUTOFOCUS, value);
217 value = m_cvWebcam.get(cv::CAP_PROP_AUTOFOCUS);
218 }
219 }
220
221 //-----------------------------------------------------------------
getWebcamFocusValue()222 int Webcam::getWebcamFocusValue() {
223 if (m_cvWebcam.isOpened() == false) {
224 initWebcam(m_webcamIndex);
225
226 if (!m_cvWebcam.isOpened()) {
227 return 0;
228 }
229 }
230 if (m_cvWebcam.isOpened()) {
231 double value = m_cvWebcam.get(cv::CAP_PROP_FOCUS);
232 return static_cast<int>(value);
233 }
234 return 0.0;
235 }
236
237 //-----------------------------------------------------------------
238
setWebcamFocusValue(int value)239 void Webcam::setWebcamFocusValue(int value) {
240 if (m_cvWebcam.isOpened() == false) {
241 initWebcam(m_webcamIndex);
242
243 if (!m_cvWebcam.isOpened()) {
244 return;
245 }
246 }
247 m_cvWebcam.set(cv::CAP_PROP_FOCUS, value);
248 value = m_cvWebcam.get(cv::CAP_PROP_FOCUS);
249 }
250
251 //-----------------------------------------------------------------
getWebcamExposureValue()252 int Webcam::getWebcamExposureValue() {
253 if (m_cvWebcam.isOpened() == false) {
254 initWebcam(m_webcamIndex);
255
256 if (!m_cvWebcam.isOpened()) {
257 return 0;
258 }
259 }
260 if (m_cvWebcam.isOpened()) {
261 double value = m_cvWebcam.get(cv::CAP_PROP_EXPOSURE);
262 return static_cast<int>(value);
263 }
264 return 0.0;
265 }
266
267 //-----------------------------------------------------------------
268
setWebcamExposureValue(int value)269 void Webcam::setWebcamExposureValue(int value) {
270 if (m_cvWebcam.isOpened() == false) {
271 initWebcam(m_webcamIndex);
272
273 if (!m_cvWebcam.isOpened()) {
274 return;
275 }
276 }
277 m_cvWebcam.set(cv::CAP_PROP_AUTO_EXPOSURE, 0.25);
278 m_cvWebcam.set(cv::CAP_PROP_EXPOSURE, value);
279 value = m_cvWebcam.get(cv::CAP_PROP_EXPOSURE);
280 getWebcamExposureValue();
281 }
282
283 //-----------------------------------------------------------------
getWebcamBrightnessValue()284 int Webcam::getWebcamBrightnessValue() {
285 if (m_cvWebcam.isOpened() == false) {
286 initWebcam(m_webcamIndex);
287
288 if (!m_cvWebcam.isOpened()) {
289 return 0;
290 }
291 }
292 if (m_cvWebcam.isOpened()) {
293 double value = m_cvWebcam.get(cv::CAP_PROP_BRIGHTNESS);
294 return static_cast<int>(value);
295 }
296 return 0.0;
297 }
298
299 //-----------------------------------------------------------------
300
setWebcamBrightnessValue(int value)301 void Webcam::setWebcamBrightnessValue(int value) {
302 if (m_cvWebcam.isOpened() == false) {
303 initWebcam(m_webcamIndex);
304
305 if (!m_cvWebcam.isOpened()) {
306 return;
307 }
308 }
309 m_cvWebcam.set(cv::CAP_PROP_BRIGHTNESS, value);
310 value = m_cvWebcam.get(cv::CAP_PROP_BRIGHTNESS);
311 }
312
313 //-----------------------------------------------------------------
getWebcamContrastValue()314 int Webcam::getWebcamContrastValue() {
315 if (m_cvWebcam.isOpened() == false) {
316 initWebcam(m_webcamIndex);
317
318 if (!m_cvWebcam.isOpened()) {
319 return 0;
320 }
321 }
322 if (m_cvWebcam.isOpened()) {
323 double value = m_cvWebcam.get(cv::CAP_PROP_CONTRAST);
324 return static_cast<int>(value);
325 }
326 return 0.0;
327 }
328
329 //-----------------------------------------------------------------
330
setWebcamContrastValue(int value)331 void Webcam::setWebcamContrastValue(int value) {
332 if (m_cvWebcam.isOpened() == false) {
333 initWebcam(m_webcamIndex);
334
335 if (!m_cvWebcam.isOpened()) {
336 return;
337 }
338 }
339 m_cvWebcam.set(cv::CAP_PROP_CONTRAST, value);
340 value = m_cvWebcam.get(cv::CAP_PROP_CONTRAST);
341 }
342
343 //-----------------------------------------------------------------
getWebcamGainValue()344 int Webcam::getWebcamGainValue() {
345 if (m_cvWebcam.isOpened() == false) {
346 initWebcam(m_webcamIndex);
347
348 if (!m_cvWebcam.isOpened()) {
349 return 0;
350 }
351 }
352 if (m_cvWebcam.isOpened()) {
353 double value = m_cvWebcam.get(cv::CAP_PROP_GAIN);
354 return static_cast<int>(value);
355 }
356 return 0.0;
357 }
358
359 //-----------------------------------------------------------------
360
setWebcamGainValue(int value)361 void Webcam::setWebcamGainValue(int value) {
362 if (m_cvWebcam.isOpened() == false) {
363 initWebcam(m_webcamIndex);
364
365 if (!m_cvWebcam.isOpened()) {
366 return;
367 }
368 }
369 m_cvWebcam.set(cv::CAP_PROP_GAIN, value);
370 value = m_cvWebcam.get(cv::CAP_PROP_GAIN);
371 }
372
373 //-----------------------------------------------------------------
getWebcamSaturationValue()374 int Webcam::getWebcamSaturationValue() {
375 if (m_cvWebcam.isOpened() == false) {
376 initWebcam(m_webcamIndex);
377
378 if (!m_cvWebcam.isOpened()) {
379 return 0;
380 }
381 }
382 if (m_cvWebcam.isOpened()) {
383 double value = m_cvWebcam.get(cv::CAP_PROP_SATURATION);
384 return static_cast<int>(value);
385 }
386 return 0.0;
387 }
388
389 //-----------------------------------------------------------------
390
setWebcamSaturationValue(int value)391 void Webcam::setWebcamSaturationValue(int value) {
392 if (m_cvWebcam.isOpened() == false) {
393 initWebcam(m_webcamIndex);
394
395 if (!m_cvWebcam.isOpened()) {
396 return;
397 }
398 }
399 m_cvWebcam.set(cv::CAP_PROP_SATURATION, value);
400 value = m_cvWebcam.get(cv::CAP_PROP_SATURATION);
401 }
402
403 //-----------------------------------------------------------------
404
openSettingsWindow()405 void Webcam::openSettingsWindow() {
406 if (m_cvWebcam.isOpened() == false) {
407 initWebcam(m_webcamIndex);
408
409 if (!m_cvWebcam.isOpened()) {
410 return;
411 }
412 }
413 m_cvWebcam.set(cv::CAP_PROP_SETTINGS, 1.0);
414 }
415
416 //-----------------------------------------------------------------
417
translateIndex(int index)418 bool Webcam::translateIndex(int index) {
419 // We are using Qt to get the camera info and supported resolutions, but
420 // we are using OpenCV to actually get the images.
421 // The camera index from OpenCV and from Qt don't always agree,
422 // So this checks the name against the correct index.
423 m_webcamIndex = index;
424
425 #ifdef WIN32
426
427 // Thanks to:
428 // https://elcharolin.wordpress.com/2017/08/28/webcam-capture-with-the-media-foundation-sdk/
429 // for the webcam enumeration here
430
431 #define CLEAN_ATTRIBUTES() \
432 if (attributes) { \
433 attributes->Release(); \
434 attributes = NULL; \
435 } \
436 for (DWORD i = 0; i < count; i++) { \
437 if (&devices[i]) { \
438 devices[i]->Release(); \
439 devices[i] = NULL; \
440 } \
441 } \
442 CoTaskMemFree(devices); \
443 return hr;
444
445 HRESULT hr = S_OK;
446
447 // this is important!!
448 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
449
450 UINT32 count = 0;
451 IMFAttributes* attributes = NULL;
452 IMFActivate** devices = NULL;
453
454 if (FAILED(hr)) {
455 CLEAN_ATTRIBUTES()
456 }
457 // Create an attribute store to specify enumeration parameters.
458 hr = MFCreateAttributes(&attributes, 1);
459
460 if (FAILED(hr)) {
461 CLEAN_ATTRIBUTES()
462 }
463
464 // The attribute to be requested is devices that can capture video
465 hr = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
466 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
467 if (FAILED(hr)) {
468 CLEAN_ATTRIBUTES()
469 }
470 // Enummerate the video capture devices
471 hr = MFEnumDeviceSources(attributes, &devices, &count);
472
473 if (FAILED(hr)) {
474 CLEAN_ATTRIBUTES()
475 }
476 // if there are any available devices
477 if (count > 0) {
478 WCHAR* nameString = NULL;
479 // Get the human-friendly name of the device
480 UINT32 cchName;
481
482 for (int i = 0; i < count; i++) {
483 hr = devices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
484 &nameString, &cchName);
485 std::string desc = m_webcamDescription.toStdString();
486 if (nameString == m_webcamDescription.toStdWString()) {
487 m_webcamIndex = i;
488 break;
489 }
490 // devices[0]->ShutdownObject();
491 }
492
493 CoTaskMemFree(nameString);
494 }
495 // clean
496 CLEAN_ATTRIBUTES()
497 #else
498 return true;
499 #endif
500 }