blob: 0c57d85e952d7e827f6588af79308f903a14bdd5 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001#include "USBCamera.h"
2
3#include "Utility.h"
4
5#include <regex>
6#include <chrono>
7#include <thread>
8#include <memory>
9#include <iostream>
10#include <iomanip>
11
12// This macro expands the given imaq function to ensure that it is called and
13// properly checked for an error, calling the wpi_setImaqErrorWithContext
14// macro
15// To call it, just give the name of the function and the arguments
16#define SAFE_IMAQ_CALL(funName, ...) \
17 { \
18 unsigned int error = funName(__VA_ARGS__); \
19 if (error != IMAQdxErrorSuccess) \
20 wpi_setImaqErrorWithContext(error, #funName); \
21 }
22
23/**
24 * Helper function to determine the size of a jpeg. The general structure of
25 * how to parse a jpeg for length can be found in this stackoverflow article:
26 * http://stackoverflow.com/a/1602428. Be sure to also read the comments for
27 * the SOS flag explanation.
28 */
29unsigned int USBCamera::GetJpegSize(void* buffer, unsigned int buffSize) {
30 uint8_t* data = (uint8_t*)buffer;
31 if (!wpi_assert(data[0] == 0xff && data[1] == 0xd8)) return 0;
32 unsigned int pos = 2;
33 while (pos < buffSize) {
34 // All control markers start with 0xff, so if this isn't present,
35 // the JPEG is not valid
36 if (!wpi_assert(data[pos] == 0xff)) return 0;
37 unsigned char t = data[pos + 1];
38 // These are RST markers. We just skip them and move onto the next marker
39 if (t == 0x01 || (t >= 0xd0 && t <= 0xd7)) {
40 pos += 2;
41 } else if (t == 0xd9) {
42 // End of Image, add 2 for this and 0-indexed
43 return pos + 2;
44 } else if (!wpi_assert(t != 0xd8)) {
45 // Another start of image, invalid image
46 return 0;
47 } else if (t == 0xda) {
48 // SOS marker. The next two bytes are a 16-bit big-endian int that is
49 // the length of the SOS header, skip that
50 unsigned int len = (((unsigned int)(data[pos + 2] & 0xff)) << 8 |
51 ((unsigned int)data[pos + 3] & 0xff));
52 pos += len + 2;
53 // The next marker is the first marker that is 0xff followed by a non-RST
54 // element. 0xff followed by 0x00 is an escaped 0xff. 0xd0-0xd7 are RST
55 // markers
56 while (data[pos] != 0xff || data[pos + 1] == 0x00 ||
57 (data[pos + 1] >= 0xd0 && data[pos + 1] <= 0xd7)) {
58 pos += 1;
59 if (pos >= buffSize) return 0;
60 }
61 } else {
62 // This is one of several possible markers. The next two bytes are a
63 // 16-bit
64 // big-endian int with the length of the marker header, skip that then
65 // continue searching
66 unsigned int len = (((unsigned int)(data[pos + 2] & 0xff)) << 8 |
67 ((unsigned int)data[pos + 3] & 0xff));
68 pos += len + 2;
69 }
70 }
71
72 return 0;
73}
74
75USBCamera::USBCamera(std::string name, bool useJpeg)
76 : m_name(name),
77 m_useJpeg(useJpeg) {}
78
79void USBCamera::OpenCamera() {
80 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
81 for (unsigned int i = 0; i < 3; i++) {
82 uInt32 id = 0;
83 // Can't use SAFE_IMAQ_CALL here because we only error on the third time
84 IMAQdxError error = IMAQdxOpenCamera(
85 m_name.c_str(), IMAQdxCameraControlModeController, &id);
86 if (error != IMAQdxErrorSuccess) {
87 // Only error on the 3rd try
88 if (i >= 2) wpi_setImaqErrorWithContext(error, "IMAQdxOpenCamera");
89 // Sleep for a few seconds to ensure the error has been dealt with
90 std::this_thread::sleep_for(std::chrono::milliseconds(2000));
91 } else {
92 m_id = id;
93 m_open = true;
94 return;
95 }
96 }
97}
98
99void USBCamera::CloseCamera() {
100 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
101 if (!m_open) return;
102 SAFE_IMAQ_CALL(IMAQdxCloseCamera, m_id);
103 m_id = 0;
104 m_open = false;
105}
106
107void USBCamera::StartCapture() {
108 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
109 if (!m_open || m_active) return;
110 SAFE_IMAQ_CALL(IMAQdxConfigureGrab, m_id);
111 SAFE_IMAQ_CALL(IMAQdxStartAcquisition, m_id);
112 m_active = true;
113}
114
115void USBCamera::StopCapture() {
116 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
117 if (!m_open || !m_active) return;
118 SAFE_IMAQ_CALL(IMAQdxStopAcquisition, m_id);
119 SAFE_IMAQ_CALL(IMAQdxUnconfigureAcquisition, m_id);
120 m_active = false;
121}
122
123void USBCamera::UpdateSettings() {
124 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
125 bool wasActive = m_active;
126
127 if (wasActive) StopCapture();
128 if (m_open) CloseCamera();
129 OpenCamera();
130
131 uInt32 count = 0;
132 uInt32 currentMode = 0;
133 SAFE_IMAQ_CALL(IMAQdxEnumerateVideoModes, m_id, nullptr, &count, &currentMode);
134 auto modes = std::make_unique<IMAQdxVideoMode[]>(count);
135 SAFE_IMAQ_CALL(IMAQdxEnumerateVideoModes, m_id, modes.get(), &count, &currentMode);
136
137 // Groups are:
138 // 0 - width
139 // 1 - height
140 // 2 - format
141 // 3 - fps
142 std::regex reMode("([0-9]+)\\s*x\\s*([0-9]+)\\s+(.*?)\\s+([0-9.]+)\\s*fps");
143 IMAQdxVideoMode* foundMode = nullptr;
144 IMAQdxVideoMode* currentModePtr = &modes[currentMode];
145 double foundFps = 1000.0;
146
147 // Loop through the modes, and find the match with the lowest fps
148 for (unsigned int i = 0; i < count; i++) {
149 std::cmatch m;
150 if (!std::regex_match(modes[i].Name, m, reMode)) continue;
151 unsigned int width = (unsigned int)std::stoul(m[1].str());
152 unsigned int height = (unsigned int)std::stoul(m[2].str());
153 if (width != m_width) continue;
154 if (height != m_height) continue;
155 double fps = atof(m[4].str().c_str());
156 if (fps < m_fps) continue;
157 if (fps > foundFps) continue;
158 bool isJpeg =
159 m[3].str().compare("jpeg") == 0 || m[3].str().compare("JPEG") == 0;
160 if ((m_useJpeg && !isJpeg) || (!m_useJpeg && isJpeg)) continue;
161 foundMode = &modes[i];
162 foundFps = fps;
163 }
164 if (foundMode != nullptr) {
165 if (foundMode->Value != currentModePtr->Value) {
166 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, IMAQdxAttributeVideoMode,
167 IMAQdxValueTypeU32, foundMode->Value);
168 }
169 }
170
171 if (m_whiteBalance.compare(AUTO) == 0) {
172 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_WB_MODE,
173 IMAQdxValueTypeString, AUTO);
174 } else {
175 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_WB_MODE,
176 IMAQdxValueTypeString, MANUAL);
177 if (m_whiteBalanceValuePresent)
178 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_WB_VALUE,
179 IMAQdxValueTypeU32, m_whiteBalanceValue);
180 }
181
182 if (m_exposure.compare(AUTO) == 0) {
183 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_EX_MODE,
184 IMAQdxValueTypeString,
185 std::string("AutoAperaturePriority").c_str());
186 } else {
187 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_EX_MODE,
188 IMAQdxValueTypeString, MANUAL);
189 if (m_exposureValuePresent) {
190 double minv = 0.0;
191 double maxv = 0.0;
192 SAFE_IMAQ_CALL(IMAQdxGetAttributeMinimum, m_id, ATTR_EX_VALUE,
193 IMAQdxValueTypeF64, &minv);
194 SAFE_IMAQ_CALL(IMAQdxGetAttributeMaximum, m_id, ATTR_EX_VALUE,
195 IMAQdxValueTypeF64, &maxv);
196 double val = minv + ((maxv - minv) * ((double)m_exposureValue / 100.0));
197 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_EX_VALUE,
198 IMAQdxValueTypeF64, val);
199 }
200 }
201
202 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_BR_MODE, IMAQdxValueTypeString,
203 MANUAL);
204 double minv = 0.0;
205 double maxv = 0.0;
206 SAFE_IMAQ_CALL(IMAQdxGetAttributeMinimum, m_id, ATTR_BR_VALUE,
207 IMAQdxValueTypeF64, &minv);
208 SAFE_IMAQ_CALL(IMAQdxGetAttributeMaximum, m_id, ATTR_BR_VALUE,
209 IMAQdxValueTypeF64, &maxv);
210 double val = minv + ((maxv - minv) * ((double)m_brightness / 100.0));
211 SAFE_IMAQ_CALL(IMAQdxSetAttribute, m_id, ATTR_BR_VALUE, IMAQdxValueTypeF64,
212 val);
213
214 if (wasActive) StartCapture();
215}
216
217void USBCamera::SetFPS(double fps) {
218 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
219 if (m_fps != fps) {
220 m_needSettingsUpdate = true;
221 m_fps = fps;
222 }
223}
224
225void USBCamera::SetSize(unsigned int width, unsigned int height) {
226 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
227 if (m_width != width || m_height != height) {
228 m_needSettingsUpdate = true;
229 m_width = width;
230 m_height = height;
231 }
232}
233
234void USBCamera::SetBrightness(unsigned int brightness) {
235 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
236 if (m_brightness != brightness) {
237 m_needSettingsUpdate = true;
238 m_brightness = brightness;
239 }
240}
241
242unsigned int USBCamera::GetBrightness() {
243 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
244 return m_brightness;
245}
246
247void USBCamera::SetWhiteBalanceAuto() {
248 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
249 m_whiteBalance = AUTO;
250 m_whiteBalanceValue = 0;
251 m_whiteBalanceValuePresent = false;
252 m_needSettingsUpdate = true;
253}
254
255void USBCamera::SetWhiteBalanceHoldCurrent() {
256 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
257 m_whiteBalance = MANUAL;
258 m_whiteBalanceValue = 0;
259 m_whiteBalanceValuePresent = false;
260 m_needSettingsUpdate = true;
261}
262
263void USBCamera::SetWhiteBalanceManual(unsigned int whiteBalance) {
264 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
265 m_whiteBalance = MANUAL;
266 m_whiteBalanceValue = whiteBalance;
267 m_whiteBalanceValuePresent = true;
268 m_needSettingsUpdate = true;
269}
270
271void USBCamera::SetExposureAuto() {
272 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
273 m_exposure = AUTO;
274 m_exposureValue = 0;
275 m_exposureValuePresent = false;
276 m_needSettingsUpdate = true;
277}
278
279void USBCamera::SetExposureHoldCurrent() {
280 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
281 m_exposure = MANUAL;
282 m_exposureValue = 0;
283 m_exposureValuePresent = false;
284 m_needSettingsUpdate = true;
285}
286
287void USBCamera::SetExposureManual(unsigned int level) {
288 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
289 m_exposure = MANUAL;
290 if (level > 100)
291 m_exposureValue = 100;
292 else
293 m_exposureValue = level;
294 m_exposureValuePresent = true;
295 m_needSettingsUpdate = true;
296}
297
298void USBCamera::GetImage(Image* image) {
299 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
300 if (m_needSettingsUpdate || m_useJpeg) {
301 m_needSettingsUpdate = false;
302 m_useJpeg = false;
303 UpdateSettings();
304 }
305 // BufNum is not actually used for anything at our level, since we are
306 // waiting until the next image is ready anyway
307 uInt32 bufNum;
308 SAFE_IMAQ_CALL(IMAQdxGrab, m_id, image, 1, &bufNum);
309}
310
311unsigned int USBCamera::GetImageData(void* buffer, unsigned int bufferSize) {
312 std::lock_guard<priority_recursive_mutex> lock(m_mutex);
313 if (m_needSettingsUpdate || !m_useJpeg) {
314 m_needSettingsUpdate = false;
315 m_useJpeg = true;
316 UpdateSettings();
317 }
318 // BufNum is not actually used for anything at our level
319 uInt32 bufNum;
320 SAFE_IMAQ_CALL(IMAQdxGetImageData, m_id, buffer, bufferSize,
321 IMAQdxBufferNumberModeLast, 0, &bufNum);
322 return GetJpegSize(buffer, bufferSize);
323}