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