appkit  1.5.1
RoadNarrows Robotics Application Kit
ut-Camera.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: RoadNarrows Robotics Application Tool Kit
4 //
5 // Unit Test: Test Camera Class.
6 //
7 /*! \file
8  *
9  * $LastChangedDate: 2013-07-13 14:13:21 -0600 (Sat, 13 Jul 2013) $
10  * $Rev: 3127 $
11  *
12  * \ingroup appkit_ut
13  *
14  * \brief Unit test for librnr_appkit Camera classes.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \par Copyright
19  * \h_copy 2013-2017. RoadNarrows LLC.\n
20  * http://www.roadnarrows.com\n
21  * All Rights Reserved
22  */
23 /*
24  * @EulaBegin@
25  *
26  * Permission is hereby granted, without written agreement and without
27  * license or royalty fees, to use, copy, modify, and distribute this
28  * software and its documentation for any purpose, provided that
29  * (1) The above copyright notice and the following two paragraphs
30  * appear in all copies of the source code and (2) redistributions
31  * including binaries reproduces these notices in the supporting
32  * documentation. Substantial modifications to this software may be
33  * copyrighted by their authors and need not follow the licensing terms
34  * described here, provided that the new terms are clearly indicated in
35  * all files where they apply.
36  *
37  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
38  * OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
39  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
40  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
41  * EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
42  * THE POSSIBILITY OF SUCH DAMAGE.
43  *
44  * THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
45  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
46  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
47  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
48  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
49  *
50  * @EulaEnd@
51  */
52 
53 #include <stdio.h>
54 #include <ctype.h>
55 
56 #include <iostream>
57 #include <string>
58 
59 #include "rnr/rnrconfig.h"
60 #include "rnr/log.h"
61 
62 #include "rnr/CameraCv.h"
63 #include "rnr/CameraGst.h"
64 
65 #include "gtest/gtest.h"
66 
67 #include "opencv/cv.h"
68 #include "opencv/highgui.h"
69 
70 #include <gtk/gtk.h>
71 #include <gdk/gdkkeysyms.h>
72 #include <gdk/gdkx.h>
73 
74 using namespace ::std;
75 using namespace ::rnr;
76 using namespace ::cv;
77 
78 #define KEY_L_SHIFT 0xe1 ///< left shift key code
79 #define KEY_R_SHIFT 0xe2 ///< right shift key code
80 #define KEY_L_CTRL 0xe3 ///< left ctrl key code
81 #define KEY_R_CTRL 0xe4 ///< right ctrl key code
82 #define KEY_L_ALT 0xe9 ///< left alt key code
83 #define KEY_R_ALT 0xea ///< right alt key code
84 
85 
86 /*!
87  * \ingroup appkit_ut
88  * \defgroup appkit_ut_camera Camera Unit Tests
89  * \brief Fine-grained testing of Camera classes.
90  * \{
91  */
92 
93 static const char *TestMenu =
94  "\nTest Menu\n"
95  " 'h' print help\n"
96  " 'q' quit with success\n"
97  " 's' start video\n"
98  " 'p' stop video\n"
99  " 'c' click snapshot\n"
100  " '+/-' increase/decrease video resolution\n"
101  " '>/<' increase/decrease image resolution\n";
102 
103 /*!
104  * \brief Increment resolution to the next comman resolution.
105  *
106  * \param resCur Current resolution.
107  *
108  * \param Returns next higher resolution.
109  */
110 static CamRes incRes(const CamRes &resCur)
111 {
112  CamRes resNew;
113 
114  switch( resCur.width )
115  {
116  case 320:
117  resNew = CamResVGA;
118  break;
119  case 640:
120  resNew = CamRes1024x768;
121  break;
122  case 1024:
123  resNew = CamRes1440x1080;
124  break;
125  case 1440:
126  resNew = CamRes1600x1200;
127  break;
128  case 1600:
129  resNew = CamRes2048x1536;
130  break;
131  case 2048:
132  resNew = CamRes2592x1944;
133  break;
134  default:
135  resNew = resCur;
136  break;
137  }
138 
139  return resNew;
140 }
141 
142 /*!
143  * \brief Decrement resolution to the previous comman resolution.
144  *
145  * \param resCur Current resolution.
146  *
147  * \param Returns previous lower resolution.
148  */
149 static CamRes decRes(const CamRes &resCur)
150 {
151  CamRes resNew;
152 
153  switch( resCur.width )
154  {
155  case 2592:
156  resNew = CamRes2048x1536;
157  break;
158  case 2048:
159  resNew = CamRes1600x1200;
160  break;
161  case 1600:
162  resNew = CamRes1440x1080;
163  break;
164  case 1440:
165  resNew = CamRes1024x768;
166  break;
167  case 1024:
168  resNew = CamResVGA;
169  break;
170  case 640:
171  resNew = CamResQVGA;
172  break;
173  default:
174  resNew = resCur;
175  break;
176  }
177 
178  return resNew;
179 }
180 
181 
182 // .............................................................................
183 // OpenCv Camera Test
184 // .............................................................................
185 
186 /*!
187  * \brief Test OpenCv CameraCv class.
188  *
189  * \return Returns 0 if test succeeds, else returns \h_lt 0.
190  */
191 static int testCameraCv()
192 {
193  string strVideoWinName("ut-CameraCv - Video");
194  string strImageWinName("ut-CameraCv - Image");
195  CamRes resVideo = CamResVGA;
196  CamRes resImage = CamResVGA;
197  Mat frame;
198  Mat snapshot;
199  bool bRun = true;
200  int c;
201  int rc = 0;
202 
203  // manually set rnr log level
204  LOG_SET_THRESHOLD(LOG_LEVEL_DIAG3);
205 
206  CameraCv cam;
207 
208  if( cam.isFatal() )
209  {
210  return -1;
211  }
212 
213  printf("%s", TestMenu);
214 
215  namedWindow(strVideoWinName);
216 
217  while( bRun )
218  {
219  c = waitKey(10);
220 
221  // strip off shift bit
222  if( c != -1 )
223  {
224  c &= 0x00ff;
225  }
226 
227  switch( c )
228  {
229  case 'h':
230  printf("%s", TestMenu);
231  break;
232 
233  case 'q':
234  rc = 0;
235  bRun = false;
236  break;
237 
238  case 's':
239  if( cam.startVideo(resVideo) < 0 )
240  {
241  rc = -1;
242  bRun = false;
243  }
244  break;
245 
246  case 'p':
247  if( cam.stopVideo() < 0 )
248  {
249  rc = -1;
250  bRun = false;
251  }
252  break;
253 
254  case 'c':
255  if( cam.clickImage(snapshot, resImage) == OK )
256  {
257  namedWindow(strImageWinName);
258  imshow(strImageWinName, snapshot);
259  }
260  break;
261 
262  case '+':
263  resVideo = incRes(resVideo);
264  printf("%dx%d video resolution.\n", resVideo.width, resVideo.height);
265  if( cam.isCameraRunning() )
266  {
267  cam.startVideo(resVideo);
268  }
269  break;
270 
271  case '-':
272  resVideo = decRes(resVideo);
273  printf("%dx%d video resolution.\n", resVideo.width, resVideo.height);
274  if( cam.isCameraRunning() )
275  {
276  cam.startVideo(resVideo);
277  }
278  break;
279 
280  case '>':
281  resImage = incRes(resImage);
282  printf("%dx%d image resolution.\n", resImage.width, resImage.height);
283  break;
284 
285  case '<':
286  resImage = decRes(resImage);
287  printf("%dx%d image resolution.\n", resImage.width, resImage.height);
288  break;
289 
290  case -1:
291  if( cam.isCameraRunning() && !cam.isTakingAnImage() )
292  {
293  if( cam.grabFrame(frame) == OK )
294  {
295  imshow(strVideoWinName, frame);
296  }
297  }
298  break;
299 
300  case KEY_L_SHIFT:
301  case KEY_R_SHIFT:
302  case KEY_L_CTRL:
303  case KEY_R_CTRL:
304  case KEY_L_ALT:
305  case KEY_R_ALT:
306  break;
307 
308  default:
309  printf("0x%02x huh?\n", c);
310  break;
311  }
312  }
313 
314  destroyWindow(strVideoWinName);
315  destroyWindow(strImageWinName);
316 
317  LOG_SET_THRESHOLD(LOG_LEVEL_OFF);
318 
319  return rc;
320 }
321 
322 
323 // .............................................................................
324 // GStreamer Camera Test
325 // .............................................................................
326 
327 static gulong GstWinXid = 0;
328 static uint_t GtkLastKey = 0;
329 
330 /*!
331  * \brief Realize GStreamer video window callback.
332  *
333  * Once the window has been realized, the X-Window id can be obtained. The id
334  * is critical for renders gst video and images to the gtk widget.
335  *
336  * \param w Gtk draw widget where video will be overlaied.
337  * \param user_data Supplied user data (this).
338  */
339 void GtkOnRealizeGstWin(GtkWidget *w, gpointer user_data)
340 {
341 #if GTK_CHECK_VERSION(2,18,0)
342  //
343  // I copied this code. Kept it for meta-pedagogical reasons.
344  //
345  // This is here just for pedagogical purposes, GDK_WINDOW_XID will call
346  // it as well in newer Gtk versions.
347  //
348  if( !gdk_window_ensure_native(w->window) )
349  {
350  LOGERROR("Couldn't create native window needed for GstXOverlay!");
351  }
352 #endif
353 
354 #ifdef GDK_WINDOWING_X11
355  GstWinXid = GDK_WINDOW_XID(gtk_widget_get_window(w));
356 #endif
357 }
358 
359 /*!
360  * \brief Timeout expiry callback.
361  *
362  * The supplied user data is set to 1 (true).
363  *
364  * \param user_data Pointer to user supplied expiry flag.
365  *
366  * \return Returns FALSE.
367  */
368 gboolean GtkAlarm(gpointer user_data)
369 {
370  *(int *)user_data = 1;
371  return FALSE;
372 }
373 
374 /*!
375  * \brief Wait for keypress or timeout.
376  *
377  * GTK widgets can be updated during this wait.
378  *
379  * \param delay Timeout delay in millseconds. Set to 0 for no timeout.
380  *
381  * \return Returns code of last key pressed or -1 if timedout.
382  */
383 int GtkWaitKey(int delay)
384 {
385  int expired = 0;
386  guint timer = 0;
387  uint_t key;
388 
389  if( delay > 0 )
390  {
391  timer = g_timeout_add(delay, GtkAlarm, &expired);
392  }
393 
394  while( gtk_main_iteration_do(FALSE) &&
395  (GtkLastKey == 0) &&
396  !expired );
397 
398  if( delay > 0 && !expired )
399  {
400  g_source_remove(timer);
401  }
402 
403  key = GtkLastKey;
404 
405  GtkLastKey = 0;
406 
407  return key;
408 }
409 
410 /*!
411  * \brief Keyboard press event handler.
412  *
413  * If registered, the application callback function will be called.
414  *
415  * \param w Widget where keyboard event occurred.
416  * \param event Keyboard event.
417  * \param user_data Supplied user data (this).
418  *
419  * \return Returns FALSE.
420  */
421 gboolean GtkOnKeyPress(GtkWidget *w,
422  GdkEventKey *event,
423  gpointer *user_data)
424 {
425  int code = 0;
426 
427  switch( event->keyval )
428  {
429  case GDK_Escape:
430  code = 27;
431  break;
432  case GDK_Return:
433  case GDK_Linefeed:
434  code = '\n';
435  break;
436  case GDK_Tab:
437  code = '\t';
438  break;
439  default:
440  code = event->keyval;
441  }
442 
443  GtkLastKey = (code & 0xffff) | (event->state << 16);
444 
445  return FALSE;
446 }
447 
448 
449 /*!
450  * \brief Test GStreamer CameraGst class.
451  *
452  * \return Returns 0 if test succeeds, else returns \h_lt 0.
453  */
454 static int testCameraGst()
455 {
456  static const char *TestMenuAddOn = " 't' text overlay\n";
457 
458  string strVideoWinName("ut-CameraGst - Video");
459  string strImageWinName("ut-CameraGst - Image");
460  CamRes resVideo = CamResVGA;
461  CamRes resImage = CamResVGA;
462  Mat frame;
463  Mat snapshot;
464 
465  GtkWidget *wMain;
466  GtkWindow *wWin;
467  GtkWidget *wGst;
468  gint nIdRealize;
469  gint nIdKeyPress;
470 
471  bool bRun = true;
472  bool bEnterText = false;
473  int c;
474  char bufText[256];
475  int n;
476  int rc = 0;
477 
478  // manually set rnr log level
479  LOG_SET_THRESHOLD(LOG_LEVEL_DIAG3);
480 
481  CameraGst cam;
482 
483  if( cam.isFatal() )
484  {
485  return -1;
486  }
487 
488  printf("%s", TestMenu);
489  printf("%s", TestMenuAddOn);
490 
491  gtk_init(0, NULL);
492 
493  //
494  // Create a new gtk window
495  //
496  wMain = gtk_window_new(GTK_WINDOW_TOPLEVEL);
497  wWin = GTK_WINDOW(wMain);
498 
499  //
500  // Configure window's look and feel
501  //
502  gtk_window_set_title(wWin, strVideoWinName.c_str());
503  gtk_window_resize(wWin, 640, 480);
504  gtk_window_set_decorated(wWin, TRUE);
505 
506  //---
507  // Video widget
508  //---
509  wGst = gtk_drawing_area_new();
510  g_object_set(G_OBJECT(wGst),
511  "width-request", 640,
512  "height-request", 480,
513  NULL);
514  nIdRealize = g_signal_connect(wGst, "realize",
515  G_CALLBACK(GtkOnRealizeGstWin), NULL);
516  gtk_widget_set_double_buffered(wGst, FALSE);
517  gtk_container_add(GTK_CONTAINER(wMain), wGst);
518 
519  //
520  // Event handlers
521  //
522  nIdKeyPress = gtk_signal_connect(GTK_OBJECT(wMain), "key-press-event",
523  GTK_SIGNAL_FUNC(GtkOnKeyPress), NULL);
524 
525  gtk_widget_show_all(wMain);
526 
527  cam.setXid(GstWinXid);
528 
529  while( bRun )
530  {
531  c = GtkWaitKey(30);
532 
533  // strip off shift bit
534  if( c != -1 )
535  {
536  c &= 0x00ff;
537  }
538 
539  if( bEnterText )
540  {
541  switch( c )
542  {
543  case '\n':
544  case '\r':
545  printf("\n");
546  fflush(stdout);
547  bufText[n] = 0;
548  if( n > 0 )
549  {
550  cam.setTextOverlay(bufText);
551  }
552  else
553  {
554  cam.clearTextOverlay();
555  }
556  bEnterText = false;
557  break;
558  default:
559  if( isprint(c) )
560  {
561  bufText[n++] = c;
562  printf("%c", c);
563  fflush(stdout);
564  }
565  break;
566  }
567  continue;
568  }
569 
570  switch( c )
571  {
572  case 'h':
573  printf("%s", TestMenu);
574  printf("%s", TestMenuAddOn);
575  break;
576 
577  case 'q':
578  rc = 0;
579  bRun = false;
580  break;
581 
582  case 's':
583  if( cam.startVideo(resVideo) < 0 )
584  {
585  rc = -1;
586  bRun = false;
587  }
588  break;
589 
590  case 'p':
591  if( cam.stopVideo() < 0 )
592  {
593  rc = -1;
594  bRun = false;
595  }
596  break;
597 
598  case 'c':
599  if( cam.clickImage(snapshot, resImage) == OK )
600  {
601  namedWindow(strImageWinName);
602  imshow(strImageWinName, snapshot);
603  }
604  break;
605 
606  case '+':
607  resVideo = incRes(resVideo);
608  printf("%dx%d video resolution.\n", resVideo.width, resVideo.height);
609  if( cam.isCameraRunning() )
610  {
611  cam.startVideo(resVideo);
612  }
613  break;
614 
615  case '-':
616  resVideo = decRes(resVideo);
617  printf("%dx%d video resolution.\n", resVideo.width, resVideo.height);
618  if( cam.isCameraRunning() )
619  {
620  cam.startVideo(resVideo);
621  }
622  break;
623 
624  case '>':
625  resImage = incRes(resImage);
626  printf("%dx%d image resolution.\n", resImage.width, resImage.height);
627  break;
628 
629  case '<':
630  resImage = decRes(resImage);
631  printf("%dx%d image resolution.\n", resImage.width, resImage.height);
632  break;
633 
634  case 't':
635  bEnterText = true;
636  n = 0;
637  printf("Text: ");
638  fflush(stdout);
639  break;
640 
641  case 0:
642  break;
643 
644  case KEY_L_SHIFT:
645  case KEY_R_SHIFT:
646  case KEY_L_CTRL:
647  case KEY_R_CTRL:
648  case KEY_L_ALT:
649  case KEY_R_ALT:
650  break;
651 
652  default:
653  printf("0x%02x huh?\n", c);
654  break;
655  }
656  }
657 
658  cam.stopVideo();
659 
660  //gtk_signal_disconnect(GTK_OBJECT(wMain), nIdRealize);
661  gtk_signal_disconnect(GTK_OBJECT(wMain), nIdKeyPress);
662  gtk_widget_destroy(wMain);
663  destroyWindow(strImageWinName);
664 
665  gtk_main_iteration_do(FALSE);
666 
667  while( gtk_events_pending() )
668  {
669  gtk_main_iteration_do(FALSE);
670  }
671 
672  LOG_SET_THRESHOLD(LOG_LEVEL_OFF);
673 
674  return rc;
675 }
676 
677 
678 #ifndef JENKINS
679 
680 /*!
681  * \brief Test OpenCv camera class.
682  *
683  * \par The Test:
684  * Construct CameraCv object.\n
685  * run video.\n
686  * take image.\n
687  * change resolution.\n
688  * Destroy CameraCv object.
689  */
690 TEST(Camera, CameraCv)
691 {
692  EXPECT_TRUE( testCameraCv() == 0 );
693 }
694 
695 /*!
696  * \brief Test OpenGst camera class.
697  *
698  * \par The Test:
699  * Construct CameraGst object.\n
700  * run video.\n
701  * take image.\n
702  * change resolution.\n
703  * overlay text.\n
704  * Destroy CameraGst object.
705  */
706 TEST(Camera, CameraGst)
707 {
708  EXPECT_TRUE( testCameraGst() == 0 );
709 }
710 
711 #endif // JENKINS
712 
713 
714 /*!
715  * \}
716  */
const CamRes CamRes1440x1080
1440 x 1080 resolution
Definition: Camera.h:93
const CamRes CamRes2592x1944
2592 x 1944 resolution
Definition: Camera.h:96
const CamRes CamRes1600x1200
1600 x 1200 resolution
Definition: Camera.h:94
gboolean GtkOnKeyPress(GtkWidget *w, GdkEventKey *event, gpointer *user_data)
Keyboard press event handler.
Definition: ut-Camera.cxx:421
#define KEY_L_SHIFT
left shift key code
Definition: ut-Camera.cxx:78
gboolean GtkAlarm(gpointer user_data)
Timeout expiry callback.
Definition: ut-Camera.cxx:368
const CamRes CamRes1024x768
1024 x 768 resolution
Definition: Camera.h:92
const CamRes CamResQVGA
Quarter VGA 320 x 240 res.
Definition: Camera.h:90
#define KEY_L_CTRL
left ctrl key code
Definition: ut-Camera.cxx:80
void GtkOnRealizeGstWin(GtkWidget *w, gpointer user_data)
Realize GStreamer video window callback.
Definition: ut-Camera.cxx:339
static CamRes incRes(const CamRes &resCur)
Increment resolution to the next comman resolution.
Definition: ut-Camera.cxx:110
int GtkWaitKey(int delay)
Wait for keypress or timeout.
Definition: ut-Camera.cxx:383
static const char * TestMenu
Test menu.
Definition: ut-sm.cxx:102
static int testCameraCv()
Test OpenCv CameraCv class.
Definition: ut-Camera.cxx:191
#define KEY_R_SHIFT
right shift key code
Definition: ut-Camera.cxx:79
static CamRes decRes(const CamRes &resCur)
Decrement resolution to the previous comman resolution.
Definition: ut-Camera.cxx:149
TEST(Camera, CameraCv)
Test OpenCv camera class.
Definition: ut-Camera.cxx:690
const CamRes CamRes2048x1536
2048 x 1536 resolution
Definition: Camera.h:95
#define KEY_R_CTRL
right ctrl key code
Definition: ut-Camera.cxx:81
RoadNarrows Robotics.
Definition: Camera.h:74
#define KEY_L_ALT
left alt key code
Definition: ut-Camera.cxx:82
#define KEY_R_ALT
right alt key code
Definition: ut-Camera.cxx:83
const CamRes CamResVGA
VGA 640 x 480 resolution.
Definition: Camera.h:91
static int testCameraGst()
Test GStreamer CameraGst class.
Definition: ut-Camera.cxx:454