1 ////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
8 //
9 //
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
21 //
22 // * Redistribution's in binary form must reproduce the above copyright notice,
23 // this list of conditions and the following disclaimer in the documentation
24 // and/or other materials provided with the distribution.
25 //
26 // * The name of Intel Corporation may not be used to endorse or promote products
27 // derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //
41
42 //
43 // The code has been contributed by Arkadiusz Raj on 2016 Oct
44 //
45
46 #include "precomp.hpp"
47 #include "cap_interface.hpp"
48
49 #ifdef HAVE_ARAVIS_API
50
51 #include <arv.h>
52
53 //
54 // This file provides wrapper for using Aravis SDK library to access GigE Vision cameras.
55 // Aravis library (version 0.4 or 0.6) shall be installed else this code will not be included in build.
56 //
57 // To include this module invoke cmake with -DWITH_ARAVIS=ON
58 //
59 // Please obvserve, that jumbo frames are required when high fps & 16bit data is selected.
60 // (camera, switches/routers and the computer this software is running on)
61 //
62 // Basic usage: VideoCapture cap(<camera id>, CAP_ARAVIS);
63 //
64 // Supported properties:
65 // read/write
66 // CAP_PROP_AUTO_EXPOSURE(0|1)
67 // CAP_PROP_EXPOSURE(t), t in seconds
68 // CAP_PROP_BRIGHTNESS (ev), exposure compensation in EV for auto exposure algorithm
69 // CAP_PROP_GAIN(g), g >=0 or -1 for automatic control if CAP_PROP_AUTO_EXPOSURE is true
70 // CAP_PROP_FPS(f)
71 // CAP_PROP_FOURCC(type)
72 // CAP_PROP_BUFFERSIZE(n)
73 // read only:
74 // CAP_PROP_POS_MSEC
75 // CAP_PROP_FRAME_WIDTH
76 // CAP_PROP_FRAME_HEIGHT
77 //
78 // Supported types of data:
79 // video/x-raw, fourcc:'GREY' -> 8bit, 1 channel
80 // video/x-raw, fourcc:'Y800' -> 8bit, 1 channel
81 // video/x-raw, fourcc:'Y12 ' -> 12bit, 1 channel
82 // video/x-raw, fourcc:'Y16 ' -> 16bit, 1 channel
83 // video/x-raw, fourcc:'GRBG' -> 8bit, 1 channel
84 //
85
86 #define MODE_GREY CV_FOURCC_MACRO('G','R','E','Y')
87 #define MODE_Y800 CV_FOURCC_MACRO('Y','8','0','0')
88 #define MODE_Y12 CV_FOURCC_MACRO('Y','1','2',' ')
89 #define MODE_Y16 CV_FOURCC_MACRO('Y','1','6',' ')
90 #define MODE_GRBG CV_FOURCC_MACRO('G','R','B','G')
91
92 #define CLIP(a,b,c) (cv::max(cv::min((a),(c)),(b)))
93
94 /********************* Capturing video from camera via Aravis *********************/
95
96 class CvCaptureCAM_Aravis : public CvCapture
97 {
98 public:
99 CvCaptureCAM_Aravis();
~CvCaptureCAM_Aravis()100 virtual ~CvCaptureCAM_Aravis()
101 {
102 close();
103 }
104
105 virtual bool open(int);
106 virtual void close();
107 virtual double getProperty(int) const CV_OVERRIDE;
108 virtual bool setProperty(int, double) CV_OVERRIDE;
109 virtual bool grabFrame() CV_OVERRIDE;
110 virtual IplImage* retrieveFrame(int) CV_OVERRIDE;
getCaptureDomain()111 virtual int getCaptureDomain() CV_OVERRIDE
112 {
113 return cv::CAP_ARAVIS;
114 }
115
116 protected:
117 bool create(int);
118 bool init_buffers();
119
120 void stopCapture();
121 bool startCapture();
122
123 bool getDeviceNameById(int id, std::string &device);
124
125 void autoExposureControl(IplImage*);
126
127 ArvCamera *camera; // Camera to control.
128 ArvStream *stream; // Object for video stream reception.
129 void *framebuffer; //
130
131 unsigned int payload; // Width x height x Pixel width.
132
133 int widthMin; // Camera sensor minimum width.
134 int widthMax; // Camera sensor maximum width.
135 int heightMin; // Camera sensor minimum height.
136 int heightMax; // Camera sensor maximum height.
137 bool fpsAvailable;
138 double fpsMin; // Camera minimum fps.
139 double fpsMax; // Camera maximum fps.
140 bool gainAvailable;
141 double gainMin; // Camera minimum gain.
142 double gainMax; // Camera maximum gain.
143 bool exposureAvailable;
144 double exposureMin; // Camera's minimum exposure time.
145 double exposureMax; // Camera's maximum exposure time.
146
147 bool controlExposure; // Flag if automatic exposure shall be done by this SW
148 double exposureCompensation;
149 bool autoGain;
150 double targetGrey; // Target grey value (mid grey))
151 bool softwareTriggered; // Flag if the camera is software triggered
152 bool allowAutoTrigger; // Flag that user allowed to trigger software triggered cameras automatically
153
154 gint64 *pixelFormats;
155 guint pixelFormatsCnt;
156
157
158 int num_buffers; // number of payload transmission buffers
159
160 ArvPixelFormat pixelFormat; // pixel format
161
162 int xoffset; // current frame region x offset
163 int yoffset; // current frame region y offset
164 int width; // current frame width of frame
165 int height; // current frame height of image
166
167 double fps; // current value of fps
168 double exposure; // current value of exposure time
169 double gain; // current value of gain
170 double midGrey; // current value of mid grey (brightness)
171
172 unsigned frameID; // current frame id
173 unsigned prevFrameID;
174
175 IplImage *frame; // local frame copy
176 };
177
178
CvCaptureCAM_Aravis()179 CvCaptureCAM_Aravis::CvCaptureCAM_Aravis()
180 {
181 camera = NULL;
182 stream = NULL;
183 framebuffer = NULL;
184
185 payload = 0;
186
187 widthMin = widthMax = heightMin = heightMax = 0;
188 xoffset = yoffset = width = height = 0;
189 fpsMin = fpsMax = gainMin = gainMax = exposureMin = exposureMax = 0;
190 controlExposure = false;
191 exposureCompensation = 0;
192 targetGrey = 0;
193 frameID = prevFrameID = 0;
194 allowAutoTrigger = false;
195
196 num_buffers = 10;
197 frame = NULL;
198 }
199
close()200 void CvCaptureCAM_Aravis::close()
201 {
202 if(camera) {
203 stopCapture();
204
205 g_object_unref(camera);
206 camera = NULL;
207 }
208 }
209
getDeviceNameById(int id,std::string & device)210 bool CvCaptureCAM_Aravis::getDeviceNameById(int id, std::string &device)
211 {
212 arv_update_device_list();
213
214 if((id >= 0) && (id < (int)arv_get_n_devices())) {
215 device = arv_get_device_id(id);
216 return true;
217 }
218
219 return false;
220 }
221
create(int index)222 bool CvCaptureCAM_Aravis::create( int index )
223 {
224 std::string deviceName;
225 if(!getDeviceNameById(index, deviceName))
226 return false;
227
228 return NULL != (camera = arv_camera_new(deviceName.c_str()));
229 }
230
init_buffers()231 bool CvCaptureCAM_Aravis::init_buffers()
232 {
233 if(stream) {
234 g_object_unref(stream);
235 stream = NULL;
236 }
237 if( (stream = arv_camera_create_stream(camera, NULL, NULL)) ) {
238 if( arv_camera_is_gv_device(camera) ) {
239 g_object_set(stream,
240 "socket-buffer", ARV_GV_STREAM_SOCKET_BUFFER_AUTO,
241 "socket-buffer-size", 0, NULL);
242 g_object_set(stream,
243 "packet-resend", ARV_GV_STREAM_PACKET_RESEND_NEVER, NULL);
244 g_object_set(stream,
245 "packet-timeout", (unsigned) 40000,
246 "frame-retention", (unsigned) 200000, NULL);
247 }
248 payload = arv_camera_get_payload (camera);
249
250 for (int i = 0; i < num_buffers; i++)
251 arv_stream_push_buffer(stream, arv_buffer_new(payload, NULL));
252
253 return true;
254 }
255
256 return false;
257 }
258
open(int index)259 bool CvCaptureCAM_Aravis::open( int index )
260 {
261 if(create(index)) {
262 // fetch properties bounds
263 pixelFormats = arv_camera_get_available_pixel_formats(camera, &pixelFormatsCnt);
264
265 arv_camera_get_width_bounds(camera, &widthMin, &widthMax);
266 arv_camera_get_height_bounds(camera, &heightMin, &heightMax);
267 arv_camera_set_region(camera, 0, 0, widthMax, heightMax);
268
269 if( (fpsAvailable = arv_camera_is_frame_rate_available(camera)) )
270 arv_camera_get_frame_rate_bounds(camera, &fpsMin, &fpsMax);
271 if( (gainAvailable = arv_camera_is_gain_available(camera)) )
272 arv_camera_get_gain_bounds (camera, &gainMin, &gainMax);
273 if( (exposureAvailable = arv_camera_is_exposure_time_available(camera)) )
274 arv_camera_get_exposure_time_bounds (camera, &exposureMin, &exposureMax);
275
276 // get initial values
277 pixelFormat = arv_camera_get_pixel_format(camera);
278 exposure = exposureAvailable ? arv_camera_get_exposure_time(camera) : 0;
279 gain = gainAvailable ? arv_camera_get_gain(camera) : 0;
280 fps = arv_camera_get_frame_rate(camera);
281 softwareTriggered = (strcmp(arv_camera_get_trigger_source(camera), "Software") == 0);
282
283 return startCapture();
284 }
285 return false;
286 }
287
grabFrame()288 bool CvCaptureCAM_Aravis::grabFrame()
289 {
290 // remove content of previous frame
291 framebuffer = NULL;
292
293 if(stream) {
294 ArvBuffer *arv_buffer = NULL;
295 int max_tries = 10;
296 int tries = 0;
297 if (softwareTriggered && allowAutoTrigger) {
298 arv_camera_software_trigger (camera);
299 }
300 for(; tries < max_tries; tries ++) {
301 arv_buffer = arv_stream_timeout_pop_buffer (stream, 200000);
302 if (arv_buffer != NULL && arv_buffer_get_status (arv_buffer) != ARV_BUFFER_STATUS_SUCCESS) {
303 arv_stream_push_buffer (stream, arv_buffer);
304 } else break;
305 }
306 if(arv_buffer != NULL && tries < max_tries) {
307 size_t buffer_size;
308 framebuffer = (void*)arv_buffer_get_data (arv_buffer, &buffer_size);
309
310 // retrieve image size properties
311 arv_buffer_get_image_region (arv_buffer, &xoffset, &yoffset, &width, &height);
312
313 // retrieve image ID set by camera
314 frameID = arv_buffer_get_frame_id(arv_buffer);
315
316 arv_stream_push_buffer(stream, arv_buffer);
317 return true;
318 }
319 }
320 return false;
321 }
322
retrieveFrame(int)323 IplImage* CvCaptureCAM_Aravis::retrieveFrame(int)
324 {
325 if(framebuffer) {
326 int depth = 0, channels = 0;
327 switch(pixelFormat) {
328 case ARV_PIXEL_FORMAT_MONO_8:
329 case ARV_PIXEL_FORMAT_BAYER_GR_8:
330 depth = IPL_DEPTH_8U;
331 channels = 1;
332 break;
333 case ARV_PIXEL_FORMAT_MONO_12:
334 case ARV_PIXEL_FORMAT_MONO_16:
335 depth = IPL_DEPTH_16U;
336 channels = 1;
337 break;
338 }
339 if(depth && channels) {
340 IplImage src;
341 cvInitImageHeader( &src, cvSize( width, height ), depth, channels, IPL_ORIGIN_TL, 4 );
342
343 cvSetData( &src, framebuffer, src.widthStep );
344 if( !frame ||
345 frame->width != src.width ||
346 frame->height != src.height ||
347 frame->depth != src.depth ||
348 frame->nChannels != src.nChannels) {
349
350 cvReleaseImage( &frame );
351 frame = cvCreateImage( cvGetSize(&src), src.depth, channels );
352 }
353 cvCopy(&src, frame);
354
355 if(controlExposure && ((frameID - prevFrameID) >= 3)) {
356 // control exposure every third frame
357 // i.e. skip frame taken with previous exposure setup
358 autoExposureControl(frame);
359 }
360
361 return frame;
362 }
363 }
364 return NULL;
365 }
366
autoExposureControl(IplImage * image)367 void CvCaptureCAM_Aravis::autoExposureControl(IplImage* image)
368 {
369 // Software control of exposure parameters utilizing
370 // automatic change of exposure time & gain
371
372 // Priority is set as follows:
373 // - to increase brightness, first increase time then gain
374 // - to decrease brightness, first decrease gain then time
375
376 cv::Mat m = cv::cvarrToMat(image);
377
378 // calc mean value for luminance or green channel
379 double brightness = cv::mean(m)[image->nChannels > 1 ? 1 : 0];
380 if(brightness < 1) brightness = 1;
381
382 // mid point - 100 % means no change
383 static const double dmid = 100;
384
385 // distance from optimal value as a percentage
386 double d = (targetGrey * dmid) / brightness;
387 if(d >= dmid) d = ( d + (dmid * 2) ) / 3;
388
389 prevFrameID = frameID;
390 midGrey = brightness;
391
392 double maxe = 1e6 / fps;
393 double ne = CLIP( ( exposure * d ) / ( dmid * pow(sqrt(2), -2 * exposureCompensation) ), exposureMin, maxe);
394
395 // if change of value requires intervention
396 if(std::fabs(d-dmid) > 5) {
397 double ev, ng = 0;
398
399 if(gainAvailable && autoGain) {
400 ev = log( d / dmid ) / log(2);
401 ng = CLIP( gain + ev + exposureCompensation, gainMin, gainMax);
402
403 if( ng < gain ) {
404 // priority 1 - reduce gain
405 arv_camera_set_gain(camera, (gain = ng));
406 return;
407 }
408 }
409
410 if(exposureAvailable) {
411 // priority 2 - control of exposure time
412 if(std::fabs(exposure - ne) > 2) {
413 // we have not yet reach the max-e level
414 arv_camera_set_exposure_time(camera, (exposure = ne) );
415 return;
416 }
417 }
418
419 if(gainAvailable && autoGain) {
420 if(exposureAvailable) {
421 // exposure at maximum - increase gain if possible
422 if(ng > gain && ng < gainMax && ne >= maxe) {
423 arv_camera_set_gain(camera, (gain = ng));
424 return;
425 }
426 } else {
427 // priority 3 - increase gain
428 arv_camera_set_gain(camera, (gain = ng));
429 return;
430 }
431 }
432 }
433
434 // if gain can be reduced - do it
435 if(gainAvailable && autoGain && exposureAvailable) {
436 if(gain > gainMin && exposure < maxe) {
437 exposure = CLIP( ne * 1.05, exposureMin, maxe);
438 arv_camera_set_exposure_time(camera, exposure );
439 }
440 }
441 }
442
getProperty(int property_id) const443 double CvCaptureCAM_Aravis::getProperty( int property_id ) const
444 {
445 switch(property_id) {
446 case CV_CAP_PROP_POS_MSEC:
447 return (double)frameID/fps;
448
449 case CV_CAP_PROP_FRAME_WIDTH:
450 return width;
451
452 case CV_CAP_PROP_FRAME_HEIGHT:
453 return height;
454
455 case CV_CAP_PROP_AUTO_EXPOSURE:
456 return (controlExposure ? 1 : 0);
457
458 case CV_CAP_PROP_BRIGHTNESS:
459 return exposureCompensation;
460
461 case CV_CAP_PROP_EXPOSURE:
462 if(exposureAvailable) {
463 /* exposure time in seconds, like 1/100 s */
464 return arv_camera_get_exposure_time(camera) / 1e6;
465 }
466 break;
467
468 case CV_CAP_PROP_FPS:
469 if(fpsAvailable) {
470 return arv_camera_get_frame_rate(camera);
471 }
472 break;
473
474 case CV_CAP_PROP_GAIN:
475 if(gainAvailable) {
476 return arv_camera_get_gain(camera);
477 }
478 break;
479
480 case CV_CAP_PROP_FOURCC:
481 {
482 ArvPixelFormat currFormat = arv_camera_get_pixel_format(camera);
483 switch( currFormat ) {
484 case ARV_PIXEL_FORMAT_MONO_8:
485 return MODE_Y800;
486 case ARV_PIXEL_FORMAT_MONO_12:
487 return MODE_Y12;
488 case ARV_PIXEL_FORMAT_MONO_16:
489 return MODE_Y16;
490 case ARV_PIXEL_FORMAT_BAYER_GR_8:
491 return MODE_GRBG;
492 }
493 }
494 break;
495
496 case CV_CAP_PROP_BUFFERSIZE:
497 if(stream) {
498 int in, out;
499 arv_stream_get_n_buffers(stream, &in, &out);
500 // return number of available buffers in Aravis output queue
501 return out;
502 }
503 break;
504
505 case cv::CAP_PROP_ARAVIS_AUTOTRIGGER:
506 {
507 return allowAutoTrigger ? 1. : 0.;
508 }
509 break;
510 }
511 return -1.0;
512 }
513
setProperty(int property_id,double value)514 bool CvCaptureCAM_Aravis::setProperty( int property_id, double value )
515 {
516 switch(property_id) {
517 case CV_CAP_PROP_AUTO_EXPOSURE:
518 if(exposureAvailable || gainAvailable) {
519 if( (controlExposure = (bool)(int)value) ) {
520 exposure = exposureAvailable ? arv_camera_get_exposure_time(camera) : 0;
521 gain = gainAvailable ? arv_camera_get_gain(camera) : 0;
522 }
523 }
524 break;
525 case CV_CAP_PROP_BRIGHTNESS:
526 exposureCompensation = CLIP(value, -3., 3.);
527 break;
528
529 case CV_CAP_PROP_EXPOSURE:
530 if(exposureAvailable) {
531 /* exposure time in seconds, like 1/100 s */
532 value *= 1e6; // -> from s to us
533
534 arv_camera_set_exposure_time(camera, exposure = CLIP(value, exposureMin, exposureMax));
535 break;
536 } else return false;
537
538 case CV_CAP_PROP_FPS:
539 if(fpsAvailable) {
540 arv_camera_set_frame_rate(camera, fps = CLIP(value, fpsMin, fpsMax));
541 break;
542 } else return false;
543
544 case CV_CAP_PROP_GAIN:
545 if(gainAvailable) {
546 if ( (autoGain = (-1 == value) ) )
547 break;
548
549 arv_camera_set_gain(camera, gain = CLIP(value, gainMin, gainMax));
550 break;
551 } else return false;
552
553 case CV_CAP_PROP_FOURCC:
554 {
555 ArvPixelFormat newFormat = pixelFormat;
556 switch((int)value) {
557 case MODE_GREY:
558 case MODE_Y800:
559 newFormat = ARV_PIXEL_FORMAT_MONO_8;
560 targetGrey = 128;
561 break;
562 case MODE_Y12:
563 newFormat = ARV_PIXEL_FORMAT_MONO_12;
564 targetGrey = 2048;
565 break;
566 case MODE_Y16:
567 newFormat = ARV_PIXEL_FORMAT_MONO_16;
568 targetGrey = 32768;
569 break;
570 case MODE_GRBG:
571 newFormat = ARV_PIXEL_FORMAT_BAYER_GR_8;
572 targetGrey = 128;
573 break;
574 }
575 if(newFormat != pixelFormat) {
576 stopCapture();
577 arv_camera_set_pixel_format(camera, pixelFormat = newFormat);
578 startCapture();
579 }
580 }
581 break;
582
583 case CV_CAP_PROP_BUFFERSIZE:
584 {
585 int x = (int)value;
586 if((x > 0) && (x != num_buffers)) {
587 stopCapture();
588 num_buffers = x;
589 startCapture();
590 }
591 }
592 break;
593
594 case cv::CAP_PROP_ARAVIS_AUTOTRIGGER:
595 {
596 allowAutoTrigger = (bool) value;
597 }
598 break;
599
600 default:
601 return false;
602 }
603
604 return true;
605 }
606
stopCapture()607 void CvCaptureCAM_Aravis::stopCapture()
608 {
609 arv_camera_stop_acquisition(camera);
610
611 if(stream) {
612 g_object_unref(stream);
613 stream = NULL;
614 }
615 }
616
startCapture()617 bool CvCaptureCAM_Aravis::startCapture()
618 {
619 if(init_buffers() ) {
620 arv_camera_set_acquisition_mode(camera, ARV_ACQUISITION_MODE_CONTINUOUS);
621 arv_camera_start_acquisition(camera);
622
623 return true;
624 }
625 return false;
626 }
627
create_Aravis_capture(int index)628 cv::Ptr<cv::IVideoCapture> cv::create_Aravis_capture( int index )
629 {
630 CvCaptureCAM_Aravis* capture = new CvCaptureCAM_Aravis;
631
632 if(capture->open(index)) {
633 return cv::makePtr<cv::LegacyCapture>(capture);
634 }
635
636 delete capture;
637 return NULL;
638 }
639 #endif
640