1 /*
2 * Copyright (c) 2015, Piotr Dobrowolski dobrypd[at]gmail[dot]com
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
24 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27 #include <cstdlib>
28 #include <cstdio>
29 #include <iostream>
30 #include <algorithm>
31 #include <opencv2/opencv.hpp>
32
33 using namespace std;
34 using namespace cv;
35
36 const char * windowOriginal = "Captured preview";
37 const int FOCUS_STEP = 1024;
38 const int MAX_FOCUS_STEP = 32767;
39 const int FOCUS_DIRECTION_INFTY = 1;
40 const int DEFAULT_BREAK_LIMIT = 5;
41 const int DEFAULT_OUTPUT_FPS = 20;
42 const double epsylon = 0.0005; // compression, noice, etc.
43
44 struct Args_t
45 {
46 const char * deviceName;
47 const char * output;
48 unsigned int fps;
49 unsigned int minimumFocusStep;
50 unsigned int breakLimit;
51 bool measure;
52 bool verbose;
53 } GlobalArgs;
54
55 struct FocusState
56 {
57 int step;
58 int direction;
59 int minFocusStep;
60 int lastDirectionChange;
61 int stepToLastMax;
62 double rate;
63 double rateMax;
64 };
65
operator <<(ostream & os,FocusState & state)66 static ostream & operator<<(ostream & os, FocusState & state)
67 {
68 return os << "RATE=" << state.rate << "\tSTEP="
69 << state.step * state.direction << "\tLast change="
70 << state.lastDirectionChange << "\tstepToLastMax="
71 << state.stepToLastMax;
72 }
73
createInitialState()74 static FocusState createInitialState()
75 {
76 FocusState state;
77 state.step = FOCUS_STEP;
78 state.direction = FOCUS_DIRECTION_INFTY;
79 state.minFocusStep = 0;
80 state.lastDirectionChange = 0;
81 state.stepToLastMax = 0;
82 state.rate = 0;
83 state.rateMax = 0;
84 return state;
85 }
86
focusDriveEnd(VideoCapture & cap,int direction)87 static void focusDriveEnd(VideoCapture & cap, int direction)
88 {
89 while (cap.set(CAP_PROP_ZOOM, (double) MAX_FOCUS_STEP * direction))
90 ;
91 }
92
93 /**
94 * Minimal focus step depends on lens
95 * and I don't want to make any assumptions about it.
96 */
findMinFocusStep(VideoCapture & cap,unsigned int startWith,int direction)97 static int findMinFocusStep(VideoCapture & cap, unsigned int startWith,
98 int direction)
99 {
100 int lStep, rStep;
101 lStep = 0;
102 rStep = startWith;
103
104 focusDriveEnd(cap, direction * FOCUS_DIRECTION_INFTY);
105 while (lStep < rStep)
106 {
107 int mStep = (lStep + rStep) / 2;
108 cap.set(CAP_PROP_ZOOM, direction * FOCUS_DIRECTION_INFTY * FOCUS_STEP);
109 if (cap.set(CAP_PROP_ZOOM, -direction * mStep))
110 {
111 rStep = mStep;
112 }
113 else
114 {
115 lStep = mStep + 1;
116 }
117 }
118 cap.set(CAP_PROP_ZOOM, direction * FOCUS_DIRECTION_INFTY * MAX_FOCUS_STEP);
119 if (GlobalArgs.verbose)
120 {
121 cout << "Found minimal focus step = " << lStep << endl;
122 }
123 return lStep;
124 }
125
126 /**
127 * Rate frame from 0/blury/ to 1/sharp/.
128 */
rateFrame(Mat & frame)129 static double rateFrame(Mat & frame)
130 {
131 unsigned long int sum = 0;
132 unsigned long int size = frame.cols * frame.rows;
133 Mat edges;
134 cvtColor(frame, edges, CV_BGR2GRAY);
135 GaussianBlur(edges, edges, Size(7, 7), 1.5, 1.5);
136 Canny(edges, edges, 0, 30, 3);
137
138 MatIterator_<uchar> it, end;
139 for (it = edges.begin<uchar>(), end = edges.end<uchar>(); it != end; ++it)
140 {
141 sum += *it != 0;
142 }
143
144 return (double) sum / (double) size;
145 }
146
correctFocus(bool lastSucceeded,FocusState & state,double rate)147 static int correctFocus(bool lastSucceeded, FocusState & state, double rate)
148 {
149 if (GlobalArgs.verbose)
150 {
151 cout << "RATE=" << rate << endl;
152 }
153 state.lastDirectionChange++;
154 double rateDelta = rate - state.rate;
155
156 if (rate >= state.rateMax + epsylon)
157 {
158 // Update Max
159 state.stepToLastMax = 0;
160 state.rateMax = rate;
161 // My local minimum is now on the other direction, that's why:
162 state.lastDirectionChange = 0;
163 }
164
165 if (!lastSucceeded)
166 {
167 // Focus at limit or other problem, change the direction.
168 state.direction *= -1;
169 state.lastDirectionChange = 0;
170 state.step /= 2;
171 }
172 else
173 {
174 if (rate < epsylon)
175 { // It's hard to say anything
176 state.step = FOCUS_STEP;
177 }
178 else if (rateDelta < -epsylon)
179 { // Wrong direction ?
180 state.direction *= -1;
181 state.step = static_cast<int>(static_cast<double>(state.step) * 0.75);
182 state.lastDirectionChange = 0;
183 }
184 else if ((rate + epsylon < state.rateMax)
185 && ((state.lastDirectionChange > 3)
186 || ((state.step < (state.minFocusStep * 1.5))
187 && state.stepToLastMax > state.step)))
188 { // I've done 3 steps (or I'm finishing) without improvement, go back to max.
189 state.direction = state.stepToLastMax >= 0 ? 1 : -1;
190 state.step = static_cast<int>(static_cast<double>(state.step) * 0.75);
191 int stepToMax = abs(state.stepToLastMax);
192 state.stepToLastMax = 0;
193 state.lastDirectionChange = 0; // Like reset.
194 state.rate = rate;
195 return stepToMax;
196 }
197 }
198 // Update state.
199 state.rate = rate;
200 state.stepToLastMax -= state.direction * state.step;
201 return state.step;
202 }
203
showHelp(const char * pName,bool welcomeMsg)204 static void showHelp(const char * pName, bool welcomeMsg)
205 {
206 cout << "This program demonstrates usage of gPhoto2 VideoCapture.\n\n"
207 "With OpenCV build without gPhoto2 library support it will "
208 "do nothing special, just capture.\n\n"
209 "Simple implementation of autofocus is based on edges detection.\n"
210 "It was tested (this example) only with Nikon DSLR (Nikon D90).\n"
211 "But shall work on all Nikon DSLRs, and with little effort with other devices.\n"
212 "Visit http://www.gphoto.org/proj/libgphoto2/support.php\n"
213 "to find supported devices (need Image Capture at least).\n"
214 "Before run, set your camera autofocus ON.\n\n";
215
216 if (!welcomeMsg)
217 {
218 cout << "usage " << pName << ": [OPTIONS] DEVICE_NAME\n\n"
219 "OPTIONS:\n"
220 "\t-h\t\treturns this help message,\n"
221 "\t-o FILENAME\tsave output video in file (MJPEG only),\n"
222 "\t-f FPS\t\tframes per second in output video,\n"
223 "\t-m\t\tmeasure exposition\n"
224 "\t\t\t(returns rates from closest focus to INTY\n"
225 "\t\t\tfor every minimum step),\n"
226 "\t-d INT\t\tset minimum focus step,\n"
227 "\t-v\t\tverbose mode.\n\n\n"
228 "DEVICE_NAME\t\tis your digital camera model substring.\n\n\n"
229 "On runtime you can use keys to control:\n";
230 }
231 else
232 {
233 cout << "Actions:\n";
234 }
235
236 cout << "\tk:\t- focus out,\n"
237 "\tj:\t- focus in,\n"
238 "\t,:\t- focus to the closest point,\n"
239 "\t.:\t- focus to infinity,\n"
240 "\tr:\t- reset autofocus state,\n"
241 "\tf:\t- switch autofocus on/off,\n"
242 "\tq:\t- quit.\n";
243 }
244
parseArguments(int argc,char ** argv)245 static bool parseArguments(int argc, char ** argv)
246 {
247 int index;
248 GlobalArgs.deviceName = "Nikon";
249 GlobalArgs.output = NULL;
250 GlobalArgs.fps = DEFAULT_OUTPUT_FPS;
251 GlobalArgs.minimumFocusStep = 0;
252 GlobalArgs.breakLimit = DEFAULT_BREAK_LIMIT;
253 GlobalArgs.measure = false;
254 GlobalArgs.verbose = false;
255
256 for (index = 1; index < argc; index++)
257 {
258 const char * arg = argv[index];
259 if (strcmp(arg, "-h") == 0)
260 {
261 return false;
262 }
263 else if (strcmp(arg, "-o") == 0)
264 {
265 GlobalArgs.output = argv[++index];
266 }
267 else if (strcmp(arg, "-f") == 0)
268 {
269 if (sscanf(argv[++index], "%u", &GlobalArgs.fps) != 1
270 || GlobalArgs.fps <= 0)
271 {
272 cerr << "Invalid fps argument." << endl;
273 return false;
274 }
275 }
276 else if (strcmp(arg, "-m") == 0)
277 {
278 GlobalArgs.measure = true;
279 }
280 else if (strcmp(arg, "-v") == 0)
281 {
282 GlobalArgs.verbose = true;
283 }
284 else if (strcmp(arg, "-d") == 0)
285 {
286 if (sscanf(argv[++index], "%u", &GlobalArgs.minimumFocusStep) != 1
287 || GlobalArgs.minimumFocusStep <= 0)
288 {
289 cerr << "Invalid minimum focus step argument." << endl;
290 return false;
291 }
292 }
293 else if (arg[0] != '-')
294 {
295 GlobalArgs.deviceName = arg;
296 }
297 else
298 {
299 cerr << "Unknown option " << arg << endl;
300 }
301 }
302 return true;
303 }
304
main(int argc,char ** argv)305 int main(int argc, char ** argv)
306 {
307 if (!parseArguments(argc, argv))
308 {
309 showHelp(argv[0], false);
310 return -1;
311 }
312 VideoCapture cap(GlobalArgs.deviceName);
313 if (!cap.isOpened())
314 {
315 cout << "Cannot find device " << GlobalArgs.deviceName << endl;
316 showHelp(argv[0], false);
317 return -1;
318 }
319
320 VideoWriter videoWriter;
321 Mat frame;
322 FocusState state = createInitialState();
323 bool focus = true;
324 bool lastSucceeded = true;
325 namedWindow(windowOriginal, 1);
326
327 // Get settings:
328 if (GlobalArgs.verbose)
329 {
330 if ((cap.get(CAP_PROP_GPHOTO2_WIDGET_ENUMERATE) == 0)
331 || (cap.get(CAP_PROP_GPHOTO2_WIDGET_ENUMERATE) == -1))
332 {
333 // Some VideoCapture implementations can return -1, 0.
334 cout << "This is not GPHOTO2 device." << endl;
335 return -2;
336 }
337 cout << "List of camera settings: " << endl
338 << (const char *) (intptr_t) cap.get(CAP_PROP_GPHOTO2_WIDGET_ENUMERATE)
339 << endl;
340 cap.set(CAP_PROP_GPHOTO2_COLLECT_MSGS, true);
341 }
342
343 cap.set(CAP_PROP_GPHOTO2_PREVIEW, true);
344 cap.set(CAP_PROP_VIEWFINDER, true);
345 cap >> frame; // To check PREVIEW output Size.
346 if (GlobalArgs.output != NULL)
347 {
348 Size S = Size((int) cap.get(CAP_PROP_FRAME_WIDTH), (int) cap.get(CAP_PROP_FRAME_HEIGHT));
349 int fourCC = CV_FOURCC('M', 'J', 'P', 'G');
350 videoWriter.open(GlobalArgs.output, fourCC, GlobalArgs.fps, S, true);
351 if (!videoWriter.isOpened())
352 {
353 cerr << "Cannot open output file " << GlobalArgs.output << endl;
354 showHelp(argv[0], false);
355 return -1;
356 }
357 }
358 showHelp(argv[0], true); // welcome msg
359
360 if (GlobalArgs.minimumFocusStep == 0)
361 {
362 state.minFocusStep = findMinFocusStep(cap, FOCUS_STEP / 16, -FOCUS_DIRECTION_INFTY);
363 }
364 else
365 {
366 state.minFocusStep = GlobalArgs.minimumFocusStep;
367 }
368 focusDriveEnd(cap, -FOCUS_DIRECTION_INFTY); // Start with closest
369
370 char key = 0;
371 while (key != 'q' && key != 27 /*ESC*/)
372 {
373 cap >> frame;
374 if (frame.empty())
375 {
376 break;
377 }
378 if (GlobalArgs.output != NULL)
379 {
380 videoWriter << frame;
381 }
382
383 if (focus && !GlobalArgs.measure)
384 {
385 int stepToCorrect = correctFocus(lastSucceeded, state, rateFrame(frame));
386 lastSucceeded = cap.set(CAP_PROP_ZOOM,
387 max(stepToCorrect, state.minFocusStep) * state.direction);
388 if ((!lastSucceeded) || (stepToCorrect < state.minFocusStep))
389 {
390 if (--GlobalArgs.breakLimit <= 0)
391 {
392 focus = false;
393 state.step = state.minFocusStep * 4;
394 cout << "In focus, you can press 'f' to improve with small step, "
395 "or 'r' to reset." << endl;
396 }
397 }
398 else
399 {
400 GlobalArgs.breakLimit = DEFAULT_BREAK_LIMIT;
401 }
402 }
403 else if (GlobalArgs.measure)
404 {
405 double rate = rateFrame(frame);
406 if (!cap.set(CAP_PROP_ZOOM, state.minFocusStep))
407 {
408 if (--GlobalArgs.breakLimit <= 0)
409 {
410 break;
411 }
412 }
413 else
414 {
415 cout << rate << endl;
416 }
417 }
418
419 if ((focus || GlobalArgs.measure) && GlobalArgs.verbose)
420 {
421 cout << "STATE\t" << state << endl;
422 cout << "Output from camera: " << endl
423 << (const char *) (intptr_t) cap.get(CAP_PROP_GPHOTO2_FLUSH_MSGS) << endl;
424 }
425
426 imshow(windowOriginal, frame);
427 switch (key = static_cast<char>(waitKey(30)))
428 {
429 case 'k': // focus out
430 cap.set(CAP_PROP_ZOOM, 100);
431 break;
432 case 'j': // focus in
433 cap.set(CAP_PROP_ZOOM, -100);
434 break;
435 case ',': // Drive to closest
436 focusDriveEnd(cap, -FOCUS_DIRECTION_INFTY);
437 break;
438 case '.': // Drive to infinity
439 focusDriveEnd(cap, FOCUS_DIRECTION_INFTY);
440 break;
441 case 'r': // reset focus state
442 focus = true;
443 state = createInitialState();
444 break;
445 case 'f': // focus switch on/off
446 focus ^= true;
447 break;
448 }
449 }
450
451 if (GlobalArgs.verbose)
452 {
453 cout << "Captured " << (int) cap.get(CAP_PROP_FRAME_COUNT) << " frames"
454 << endl << "in " << (int) (cap.get(CAP_PROP_POS_MSEC) / 1e2)
455 << " seconds," << endl << "at avg speed "
456 << (cap.get(CAP_PROP_FPS)) << " fps." << endl;
457 }
458
459 return 0;
460 }
461