Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/samples/cpp/autofocus.cpp
16337 views
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/core.hpp>
32
#include <opencv2/imgproc.hpp>
33
#include <opencv2/highgui.hpp>
34
35
using namespace std;
36
using namespace cv;
37
38
const char * windowOriginal = "Captured preview";
39
const int FOCUS_STEP = 1024;
40
const int MAX_FOCUS_STEP = 32767;
41
const int FOCUS_DIRECTION_INFTY = 1;
42
const int DEFAULT_BREAK_LIMIT = 5;
43
const int DEFAULT_OUTPUT_FPS = 20;
44
const double epsylon = 0.0005; // compression, noice, etc.
45
46
struct Args_t
47
{
48
string deviceName;
49
string output;
50
int fps;
51
int minimumFocusStep;
52
int breakLimit;
53
bool measure;
54
bool verbose;
55
} GlobalArgs;
56
57
struct FocusState
58
{
59
int step;
60
int direction;
61
int minFocusStep;
62
int lastDirectionChange;
63
int stepToLastMax;
64
double rate;
65
double rateMax;
66
};
67
68
static ostream & operator<<(ostream & os, FocusState & state)
69
{
70
return os << "RATE=" << state.rate << "\tSTEP="
71
<< state.step * state.direction << "\tLast change="
72
<< state.lastDirectionChange << "\tstepToLastMax="
73
<< state.stepToLastMax;
74
}
75
76
static FocusState createInitialState()
77
{
78
FocusState state;
79
state.step = FOCUS_STEP;
80
state.direction = FOCUS_DIRECTION_INFTY;
81
state.minFocusStep = 0;
82
state.lastDirectionChange = 0;
83
state.stepToLastMax = 0;
84
state.rate = 0;
85
state.rateMax = 0;
86
return state;
87
}
88
89
static void focusDriveEnd(VideoCapture & cap, int direction)
90
{
91
while (cap.set(CAP_PROP_ZOOM, (double) MAX_FOCUS_STEP * direction))
92
;
93
}
94
95
/**
96
* Minimal focus step depends on lens
97
* and I don't want to make any assumptions about it.
98
*/
99
static int findMinFocusStep(VideoCapture & cap, unsigned int startWith,
100
int direction)
101
{
102
int lStep, rStep;
103
lStep = 0;
104
rStep = startWith;
105
106
focusDriveEnd(cap, direction * FOCUS_DIRECTION_INFTY);
107
while (lStep < rStep)
108
{
109
int mStep = (lStep + rStep) / 2;
110
cap.set(CAP_PROP_ZOOM, direction * FOCUS_DIRECTION_INFTY * FOCUS_STEP);
111
if (cap.set(CAP_PROP_ZOOM, -direction * mStep))
112
{
113
rStep = mStep;
114
}
115
else
116
{
117
lStep = mStep + 1;
118
}
119
}
120
cap.set(CAP_PROP_ZOOM, direction * FOCUS_DIRECTION_INFTY * MAX_FOCUS_STEP);
121
if (GlobalArgs.verbose)
122
{
123
cout << "Found minimal focus step = " << lStep << endl;
124
}
125
return lStep;
126
}
127
128
/**
129
* Rate frame from 0/blury/ to 1/sharp/.
130
*/
131
static double rateFrame(Mat & frame)
132
{
133
unsigned long int sum = 0;
134
unsigned long int size = frame.cols * frame.rows;
135
Mat edges;
136
cvtColor(frame, edges, COLOR_BGR2GRAY);
137
GaussianBlur(edges, edges, Size(7, 7), 1.5, 1.5);
138
Canny(edges, edges, 0, 30, 3);
139
140
MatIterator_<uchar> it, end;
141
for (it = edges.begin<uchar>(), end = edges.end<uchar>(); it != end; ++it)
142
{
143
sum += *it != 0;
144
}
145
146
return (double) sum / (double) size;
147
}
148
149
static int correctFocus(bool lastSucceeded, FocusState & state, double rate)
150
{
151
if (GlobalArgs.verbose)
152
{
153
cout << "RATE=" << rate << endl;
154
}
155
state.lastDirectionChange++;
156
double rateDelta = rate - state.rate;
157
158
if (rate >= state.rateMax + epsylon)
159
{
160
// Update Max
161
state.stepToLastMax = 0;
162
state.rateMax = rate;
163
// My local minimum is now on the other direction, that's why:
164
state.lastDirectionChange = 0;
165
}
166
167
if (!lastSucceeded)
168
{
169
// Focus at limit or other problem, change the direction.
170
state.direction *= -1;
171
state.lastDirectionChange = 0;
172
state.step /= 2;
173
}
174
else
175
{
176
if (rate < epsylon)
177
{ // It's hard to say anything
178
state.step = FOCUS_STEP;
179
}
180
else if (rateDelta < -epsylon)
181
{ // Wrong direction ?
182
state.direction *= -1;
183
state.step = static_cast<int>(static_cast<double>(state.step) * 0.75);
184
state.lastDirectionChange = 0;
185
}
186
else if ((rate + epsylon < state.rateMax)
187
&& ((state.lastDirectionChange > 3)
188
|| ((state.step < (state.minFocusStep * 1.5))
189
&& state.stepToLastMax > state.step)))
190
{ // I've done 3 steps (or I'm finishing) without improvement, go back to max.
191
state.direction = state.stepToLastMax >= 0 ? 1 : -1;
192
state.step = static_cast<int>(static_cast<double>(state.step) * 0.75);
193
int stepToMax = abs(state.stepToLastMax);
194
state.stepToLastMax = 0;
195
state.lastDirectionChange = 0; // Like reset.
196
state.rate = rate;
197
return stepToMax;
198
}
199
}
200
// Update state.
201
state.rate = rate;
202
state.stepToLastMax -= state.direction * state.step;
203
return state.step;
204
}
205
206
static void showHelp(const char * pName, bool welcomeMsg)
207
{
208
cout << "This program demonstrates usage of gPhoto2 VideoCapture.\n\n"
209
"With OpenCV build without gPhoto2 library support it will "
210
"do nothing special, just capture.\n\n"
211
"Simple implementation of autofocus is based on edges detection.\n"
212
"It was tested (this example) only with Nikon DSLR (Nikon D90).\n"
213
"But shall work on all Nikon DSLRs, and with little effort with other devices.\n"
214
"Visit http://www.gphoto.org/proj/libgphoto2/support.php\n"
215
"to find supported devices (need Image Capture at least).\n"
216
"Before run, set your camera autofocus ON.\n\n";
217
218
if (!welcomeMsg)
219
{
220
cout << "usage " << pName << ": [OPTIONS] DEVICE_NAME\n\n"
221
"OPTIONS:\n"
222
"\t-h\t\treturns this help message,\n"
223
"\t-o=<FILENAME>\tsave output video in file (MJPEG only),\n"
224
"\t-f=FPS\t\tframes per second in output video,\n"
225
"\t-m\t\tmeasure exposition\n"
226
"\t\t\t(returns rates from closest focus to INTY\n"
227
"\t\t\tfor every minimum step),\n"
228
"\t-d=<INT>\t\tset minimum focus step,\n"
229
"\t-v\t\tverbose mode.\n\n\n"
230
"DEVICE_NAME\t\tis your digital camera model substring.\n\n\n"
231
"On runtime you can use keys to control:\n";
232
}
233
else
234
{
235
cout << "Actions:\n";
236
}
237
238
cout << "\tk:\t- focus out,\n"
239
"\tj:\t- focus in,\n"
240
"\t,:\t- focus to the closest point,\n"
241
"\t.:\t- focus to infinity,\n"
242
"\tr:\t- reset autofocus state,\n"
243
"\tf:\t- switch autofocus on/off,\n"
244
"\tq:\t- quit.\n";
245
}
246
247
static bool parseArguments(int argc, char ** argv)
248
{
249
cv::CommandLineParser parser(argc, argv, "{h help ||}{o||}{f||}{m||}{d|0|}{v||}{@device|Nikon|}");
250
if (parser.has("help"))
251
return false;
252
GlobalArgs.breakLimit = DEFAULT_BREAK_LIMIT;
253
if (parser.has("o"))
254
GlobalArgs.output = parser.get<string>("o");
255
else
256
GlobalArgs.output = "";
257
if (parser.has("f"))
258
GlobalArgs.fps = parser.get<int>("f");
259
else
260
GlobalArgs.fps = DEFAULT_OUTPUT_FPS;
261
GlobalArgs.measure = parser.has("m");
262
GlobalArgs.verbose = parser.has("v");
263
GlobalArgs.minimumFocusStep = parser.get<int>("d");
264
GlobalArgs.deviceName = parser.get<string>("@device");
265
if (!parser.check())
266
{
267
parser.printErrors();
268
return false;
269
}
270
if (GlobalArgs.fps < 0)
271
{
272
cerr << "Invalid fps argument." << endl;
273
return false;
274
}
275
if (GlobalArgs.minimumFocusStep < 0)
276
{
277
cerr << "Invalid minimum focus step argument." << endl;
278
return false;
279
}
280
return true;
281
}
282
283
int main(int argc, char ** argv)
284
{
285
if (!parseArguments(argc, argv))
286
{
287
showHelp(argv[0], false);
288
return -1;
289
}
290
VideoCapture cap(GlobalArgs.deviceName);
291
if (!cap.isOpened())
292
{
293
cout << "Cannot find device " << GlobalArgs.deviceName << endl;
294
showHelp(argv[0], false);
295
return -1;
296
}
297
298
VideoWriter videoWriter;
299
Mat frame;
300
FocusState state = createInitialState();
301
bool focus = true;
302
bool lastSucceeded = true;
303
namedWindow(windowOriginal, 1);
304
305
// Get settings:
306
if (GlobalArgs.verbose)
307
{
308
if ((cap.get(CAP_PROP_GPHOTO2_WIDGET_ENUMERATE) == 0)
309
|| (cap.get(CAP_PROP_GPHOTO2_WIDGET_ENUMERATE) == -1))
310
{
311
// Some VideoCapture implementations can return -1, 0.
312
cout << "This is not GPHOTO2 device." << endl;
313
return -2;
314
}
315
cout << "List of camera settings: " << endl
316
<< (const char *) (intptr_t) cap.get(CAP_PROP_GPHOTO2_WIDGET_ENUMERATE)
317
<< endl;
318
cap.set(CAP_PROP_GPHOTO2_COLLECT_MSGS, true);
319
}
320
321
cap.set(CAP_PROP_GPHOTO2_PREVIEW, true);
322
cap.set(CAP_PROP_VIEWFINDER, true);
323
cap >> frame; // To check PREVIEW output Size.
324
if (!GlobalArgs.output.empty())
325
{
326
Size S = Size((int) cap.get(CAP_PROP_FRAME_WIDTH), (int) cap.get(CAP_PROP_FRAME_HEIGHT));
327
int fourCC = VideoWriter::fourcc('M', 'J', 'P', 'G');
328
videoWriter.open(GlobalArgs.output, fourCC, GlobalArgs.fps, S, true);
329
if (!videoWriter.isOpened())
330
{
331
cerr << "Cannot open output file " << GlobalArgs.output << endl;
332
showHelp(argv[0], false);
333
return -1;
334
}
335
}
336
showHelp(argv[0], true); // welcome msg
337
338
if (GlobalArgs.minimumFocusStep == 0)
339
{
340
state.minFocusStep = findMinFocusStep(cap, FOCUS_STEP / 16, -FOCUS_DIRECTION_INFTY);
341
}
342
else
343
{
344
state.minFocusStep = GlobalArgs.minimumFocusStep;
345
}
346
focusDriveEnd(cap, -FOCUS_DIRECTION_INFTY); // Start with closest
347
348
char key = 0;
349
while (key != 'q' && key != 27 /*ESC*/)
350
{
351
cap >> frame;
352
if (frame.empty())
353
{
354
break;
355
}
356
if (!GlobalArgs.output.empty())
357
{
358
videoWriter << frame;
359
}
360
361
if (focus && !GlobalArgs.measure)
362
{
363
int stepToCorrect = correctFocus(lastSucceeded, state, rateFrame(frame));
364
lastSucceeded = cap.set(CAP_PROP_ZOOM,
365
max(stepToCorrect, state.minFocusStep) * state.direction);
366
if ((!lastSucceeded) || (stepToCorrect < state.minFocusStep))
367
{
368
if (--GlobalArgs.breakLimit <= 0)
369
{
370
focus = false;
371
state.step = state.minFocusStep * 4;
372
cout << "In focus, you can press 'f' to improve with small step, "
373
"or 'r' to reset." << endl;
374
}
375
}
376
else
377
{
378
GlobalArgs.breakLimit = DEFAULT_BREAK_LIMIT;
379
}
380
}
381
else if (GlobalArgs.measure)
382
{
383
double rate = rateFrame(frame);
384
if (!cap.set(CAP_PROP_ZOOM, state.minFocusStep))
385
{
386
if (--GlobalArgs.breakLimit <= 0)
387
{
388
break;
389
}
390
}
391
else
392
{
393
cout << rate << endl;
394
}
395
}
396
397
if ((focus || GlobalArgs.measure) && GlobalArgs.verbose)
398
{
399
cout << "STATE\t" << state << endl;
400
cout << "Output from camera: " << endl
401
<< (const char *) (intptr_t) cap.get(CAP_PROP_GPHOTO2_FLUSH_MSGS) << endl;
402
}
403
404
imshow(windowOriginal, frame);
405
switch (key = static_cast<char>(waitKey(30)))
406
{
407
case 'k': // focus out
408
cap.set(CAP_PROP_ZOOM, 100);
409
break;
410
case 'j': // focus in
411
cap.set(CAP_PROP_ZOOM, -100);
412
break;
413
case ',': // Drive to closest
414
focusDriveEnd(cap, -FOCUS_DIRECTION_INFTY);
415
break;
416
case '.': // Drive to infinity
417
focusDriveEnd(cap, FOCUS_DIRECTION_INFTY);
418
break;
419
case 'r': // reset focus state
420
focus = true;
421
state = createInitialState();
422
break;
423
case 'f': // focus switch on/off
424
focus ^= true;
425
break;
426
}
427
}
428
429
if (GlobalArgs.verbose)
430
{
431
cout << "Captured " << (int) cap.get(CAP_PROP_FRAME_COUNT) << " frames"
432
<< endl << "in " << (int) (cap.get(CAP_PROP_POS_MSEC) / 1e2)
433
<< " seconds," << endl << "at avg speed "
434
<< (cap.get(CAP_PROP_FPS)) << " fps." << endl;
435
}
436
437
return 0;
438
}
439
440