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 }