1 /*******************************************************************************
3 * barcam - OpenCV based camera recognition program.
5 * Created for Eurobot 2010 competition.
7 * Petr Kubizňák (kubiznak.petr@gmail.com), 2010
8 * rewriten by Michal Vokac (vokac.m@gmail.com), for Eurobot 2011
10 * This program is used to recognize type of playing elements (pawns, qeens,
11 * kings) in Eurobot 2011 competition.
12 * Program can switch between several modes, also depending on platform - on PPC,
13 * only modes RECOGNIZE and WAIT are present, on PC, there are also VIDEO and
14 * IMAGE modes. Lets introduce them:
15 * RECOGNIZE: Camera image is processed and playing element type is recognized.
16 * The results are published continually using orte. On PC, the result and
17 * some subresults are displayed graphically.
18 * WAIT: Program waits until orte.camera_control.on is set to true, or, on PC,
19 * waiting is interrupted by W key. This is the default mode for PPC.
20 * VIDEO (PC only): Video from camera is just displayed in a window. Default
21 * mode for PC. Use R key to switch to RECOGNIZE mode.
22 * IMAGE (PC only): Pseudo-mode used to analyze *one* image loaded from file.
23 * Is useful also for finding a good threshold value, using +/- keys. To use
24 * this mode, start the program with -i argument (see below). There is no way
25 * to switch from this mode to another one.
28 * Usage: ./barcam [-i filename]
30 * Start in image mode, load filename to analyze.
34 * No arguments possible.
36 ******************************************************************************/
43 /******************************************************************************/
49 #include <semaphore.h>
55 #include <opencv/cv.h>
56 #include <opencv/highgui.h>
59 #include <c2gnuplot.h>
62 #include <roboorte_robottype.h>
66 /******************************************************************************/
67 /***************************** macro definitions ******************************/
68 /******************************************************************************/
70 //uncomment next line to "log" the output frames - save them as pnm to the working directory
74 #define MODE_QUIT 0x01
75 #define MODE_VIDEO 0x02
76 #define MODE_RECOGNIZE 0x04
77 #define MODE_WAIT 0x08
78 #define MODE_IMAGE 0x10
80 // highgui windows names
81 #define WINDOW_ORIG "BARCAM original scene"
82 #define WINDOW_PROD "BARCAM recognition product"
84 // size of graphical windows
85 #define WINDOW_WIDTH 640
86 #define WINDOW_HEIGHT 480
88 //TODO tune size of region of interest (crop camera view)
91 #define ROI_WIDTH WINDOW_WIDTH
92 #define ROI_HEIGHT WINDOW_HEIGHT
96 #define KEY_SPACE 0x20
109 #define KEY_PLUS 0x2B
110 #define KEY_MINUS 0x2D
121 // filename pattern of snapshots (PPC does not support png)
123 #define SNAPSHOTFILENAME "snapshot%02d.png"
125 #define SNAPSHOTFILENAME "snapshot%02d.pnm"
126 #endif /* WITH_GUI */
129 /******************************************************************************/
130 /************************** function declarations *****************************/
131 /******************************************************************************/
133 /********************** multimode graphical functions *************************/
134 int saveFrame(const CvArr *img);
135 void selectROI(CvCapture* capture, CvRect *roi);
137 /******************** multimode computational functions ***********************/
138 int countThreshold(const IplImage *frame);
139 int recognize(const CvMat *frame);
141 /********************************** MODE_IMAGE ********************************/
142 int modeImage(char *imageFilename);
144 /********************************* MODE_VIDEO *********************************/
145 int modeVideo(CvCapture* capture, CvRect *roi);
147 /****************************** MODE_RECOGNIZE ********************************/
148 int recognizeMode_processKeys(IplImage *frame);
149 void displayFrames(IplImage *frame);
150 int modeRecognize(CvCapture* capture, CvRect *roi);
152 /********************************* MODE_WAIT **********************************/
153 int waitMode_processKeys(int previousMode);
154 int modeWait(int previousMode);
156 /******************************** mode manager ********************************/
157 void setAnalyticWindowsVisible(bool visible);
158 void destroyGUI(void);
159 int modeManager(int defaultMode, char *paramC = NULL);
161 /*********************************** orte *************************************/
162 bool getCameraControlOn(void);
163 void send_cmr_res_cb(const ORTESendInfo *info, void *vinstance, void *recvCallBackParam);
164 void rcv_cmr_ctrl_cb(const ORTERecvInfo *info, void *vinstance, void *recvCallBackParam);
165 void rcv_robot_switches_cb(const ORTERecvInfo *info, void *vinstance, void *recvCallBackParam);
167 /********************************* application ********************************/
168 int getParamI(int argc, char *argv[], char *imgFilename);
169 int main(int argc, char *argv[]);
172 /******************************************************************************/
173 /**************************** variable declarations ***************************/
174 /******************************************************************************/
176 struct robottype_orte_data orte;
177 bool camera_control_on = false;
182 /******************************************************************************/
183 /********************** multimode graphical functions *************************/
184 /******************************************************************************/
186 /** Saves current image to a new file in the current directory.
187 * @param img Image to save to file.
188 * @return Value returned by cvSaveImage. */
189 int saveFrame(const CvArr *img)
191 struct stat stFileInfo;
195 //find a non-existent filename (e.g. "snapshot13.png")
197 sprintf(filename, SNAPSHOTFILENAME, i++);
198 } while (!stat(filename, &stFileInfo));
200 fprintf(stdout, "Saving frame to %s...\n", filename);
201 return cvSaveImage(filename, img);
204 /******************************************************************************/
206 /** Setting region of interest in the ROI window.
207 * @param capture Pointer to a camera capture.
208 * @param roi Pointer to ROI rectangle. */
210 void selectROI(CvCapture* capture, CvRect *roi)
212 IplImage* frame = NULL;
216 // wait 10ms for an event
217 switch(cvWaitKey(10) & 0xFF) {
234 // reset ROI to full frame
237 roi->width = WINDOW_WIDTH;
238 roi->height = WINDOW_HEIGHT;
242 // increase left border
248 // decrease left border
254 // increase upper border
260 // decrease upper border
266 // decrease lower border
271 // increase lower border
276 // decrease right border
281 // increase right border
286 step = (step < 0 ? 0 : step);
288 fprintf(stderr, "ROI step: %d x: %d y: %d width: %d height: %d\n",
289 step, roi->x, roi->y, roi->width, roi->height);
291 frame = cvQueryFrame(capture);
293 cvPutText(frame, "Select ROI", cvPoint(frame->width/2 -60,
295 &fontLarge, cvScalar(0,0,255));
297 cvRectangle(frame, cvPoint(roi->x, roi->y), cvPoint(roi->width + roi->x,
298 roi->height + roi->y),
299 cvScalar(0,0,255), 2, 8, 0);
300 cvShowImage(WINDOW_ORIG, frame);
306 void selectROI(CvCapture *capture, CvRect *roi) {}
307 #endif /* WITH_GUI */
309 /******************************************************************************/
310 /******************** multimode computational functions ***********************/
311 /******************************************************************************/
313 /** Returns a threshold computed from the frame.
314 * @param frame Frame to compute a threshold from.
315 * @return Mean of all pixels of the frame. */
316 int countThreshold(const IplImage *frame)
318 return cvAvg(frame).val[0];
321 /******************************************************************************/
323 /** Returns an ordinary number of recognized configuration.
324 * @param frame Frame to be processed.
326 int recognize(IplImage* gray_frame) {
328 // create gnuplot window
329 static gnuplot_window window;
332 int n = gray_frame->height;
336 double correction = (double)255 / (double)n;
341 in = (double*) malloc(sizeof(double) * n);
342 out = (double*) malloc(sizeof(double) * n);
344 rplan = fftw_plan_r2r_1d(n, in, out, FFTW_R2HC, FFTW_ESTIMATE);
346 // we have no imaginary data, so clear idata
347 // memset((void *)out, 0, n * sizeof(double));
349 // fill rdata with actual data
350 for (i = 0; i < n; i++) {
351 in[i] = ((uchar*)(gray_frame->imageData + i*gray_frame->widthStep))[(gray_frame->width/2)*3];
353 // draw black line in the midle of the image where DFT is computed
354 ((uchar*)(gray_frame->imageData + i*gray_frame->widthStep))[(gray_frame->width/2)*3] = 0;
359 // set plot parameters
360 window.plot_data("u 1:2 w p ps 2");
362 // post-process FFT data: make absolute values, and calculate
363 // real frequency of each power line in the spectrum
364 for (i = 1; i < (n/2 - 1); i++) {
365 absval = sqrt((out[i] * out[i]) + (out[n - i] * out[n - i])) / n;
366 cc = (double)m * correction;
369 // plot fft data, do not plot DC value
370 window.data("%f, %f",cc,absval);
373 // plot values from FIFO
378 fftw_destroy_plan(rplan);
386 /******************************************************************************/
387 /********************************** MODE_IMAGE ********************************/
388 /******************************************************************************/
390 /** Simulates MODE_RECOGNIZE over one specified image filename (PC only).
391 * @param imageFilename Image to load and recognize.
392 * @return Code of mode to switch to. */
394 int modeImage(char *imageFilename)
396 IplImage *frame = NULL;
398 frame = cvLoadImage(imageFilename);
401 fprintf(stderr, "File %s cannot be loaded as image.\n", imageFilename);
408 // wait infinitely for a keyboard event
409 switch((char)(cvWaitKey(0) & 0xFF)) {
418 cvReleaseImage(&frame);
423 int modeImage(char *imageFilename) {
424 fprintf(stderr, "Image mode is unsupported on PPC.\nTerminating.\n");
427 #endif /* WITH_GUI */
429 /******************************************************************************/
430 /********************************* MODE_VIDEO *********************************/
431 /******************************************************************************/
433 /** Displays just a real video in a window (only on PC).
434 * @param capture Pointer to a camera capture.
435 * @param roi Pointer to ROI rectangle.
436 * @return Code of mode to switch to. */
438 int modeVideo(CvCapture* capture, CvRect *roi)
440 IplImage *frame = NULL;
443 // wait 10ms for an event
444 switch(cvWaitKey(10) & 0xFF) {
449 // switch to recognize mode
452 return MODE_RECOGNIZE;
454 // pause - switch to wait mode
459 // save frame to file
465 // display ROI select window
468 cvResetImageROI(frame);
469 selectROI(capture, roi);
470 cvSetImageROI(frame, *roi);
475 // get one frame from camera (do not release it!)
476 frame = cvQueryFrame(capture);
479 fprintf(stderr, "NULL frame\n");
483 cvShowImage(WINDOW_ORIG, frame);
490 int modeVideo(CvCapture* capture, CvRect *roi) {
491 fprintf(stderr, "Video mode is unsupported on PPC.\nTerminating.\n");
494 #endif /* WITH_GUI */
496 /******************************************************************************/
497 /****************************** MODE_RECOGNIZE ********************************/
498 /******************************************************************************/
500 /** On PC processes keyboard events, on PPC does nothing.
501 * @param frame Last camera frame.
502 * @return MODE_RECOGNIZE or mode to switch to. */
504 int recognizeMode_processKeys(IplImage *frame)
506 // wait 10ms for an event
507 switch (cvWaitKey(10) & 0xFF) {
520 camera_control_on = 0;
523 // save frame to file
529 return MODE_RECOGNIZE;
533 int recognizeMode_processKeys(IplImage *frame)
535 return MODE_RECOGNIZE;
537 #endif /* WITH_GUI */
539 /******************************************************************************/
541 /** Displays analytic frames on screen (on PC), or saves (input) frame to file
542 * @param frame Input frame from camera.
543 * @param window_name Name of the window to show frame in.
546 void displayFrames(const char* window_name, IplImage *frame) {
547 cvShowImage(window_name, frame);
553 void displayFrames(const char* window_name, IplImage *frame) { /* nothing */ }
554 #endif /* WITH_GUI */
556 /******************************************************************************/
558 /** Implements the camera recognition of corns configurations.
559 * @param capture Pointer to a camera capture.
560 * @param roi Pointer to ROI rectangle.
561 * @return Code of mode to switch to. */
562 int modeRecognize(CvCapture* capture, CvRect *roi)
565 IplImage *frame = NULL;
566 IplImage *roi_frame = cvCreateImage(cvSize(roi->width, roi->height), 8, 1);
568 while (camera_control_on) {
570 if ((ret = recognizeMode_processKeys(roi_frame)) != MODE_RECOGNIZE)
573 //get one frame from camera (do not release!)
574 frame = cvQueryFrame(capture);
577 fprintf(stderr, "NULL frame\n");
580 } //else if(orte.camera_result.error & camera_ERR_NO_FRAME) {
581 // orte.camera_result.error &= ~camera_ERR_NO_FRAME;
582 //ORTEPublicationSend(orte.publication_camera_result);
585 // set region of interest on captured image
586 cvSetImageROI(frame, *roi);
588 // copy ROI part of captured image to roi_frame and convert to grayscale
589 cvCvtColor(frame, roi_frame, CV_BGR2GRAY);
591 //TODO call image processing (do something useful with the image, FFT atc.)
592 ret = recognize(roi_frame);
594 displayFrames(WINDOW_ORIG, frame);
595 displayFrames(WINDOW_PROD, roi_frame);
598 // orte.camera_result.side = sideConfig;
599 // orte.camera_result.center = centerConfig;
600 // orte.camera_result.sideDist = sideDist;
601 // orte.camera_result.centerDist = centerDist;
602 // ORTEPublicationSend(orte.publication_camera_result);
609 /******************************************************************************/
610 /********************************* MODE_WAIT **********************************/
611 /******************************************************************************/
613 /** Returns mode to switch to from wait mode. On PC processes keyboard events,
614 * on PPC just tests for orte.camera_control.on value.
615 * @param previousMode Mode to switch to, if no more waiting is needed.
616 * @return Mode to switch to. */
618 int waitMode_processKeys(int previousMode)
620 // wait 10ms for an event
621 switch(cvWaitKey(10) & 0xFF) {
629 camera_control_on = 1;
639 int waitMode_processKeys(int previousMode) {
640 return (getCameraControlOn() ? previousMode : MODE_WAIT);
642 #endif /* WITH_GUI */
644 /******************************************************************************/
646 /** Waits until orte.camera_control.on is set to true or, on PC P-key
648 * @param previousMode Mode to switch to when waiting stops.
649 * @return Code of mode to switch to. */
650 int modeWait(int previousMode)
654 while((mode = waitMode_processKeys(previousMode)) == MODE_WAIT) {
655 fprintf(stderr, "waiting...\n");
663 /******************************************************************************/
664 /******************************** mode manager ********************************/
665 /******************************************************************************/
667 /** Initializes GUI, displays static video window. */
670 cvNamedWindow(WINDOW_ORIG, CV_WINDOW_AUTOSIZE);
671 cvInitFont(&fontLarge, CV_FONT_HERSHEY_SIMPLEX, 1.0, 1.0, 0, 2);
674 void initGUI(void) {}
675 #endif /* WITH_GUI */
677 /******************************************************************************/
679 /** Destroys highgui windows. */
681 void destroyGUI(void)
683 cvDestroyWindow(WINDOW_ORIG);
684 cvDestroyWindow(WINDOW_PROD);
687 void destroyGUI(void) {}
688 #endif /* WITH_GUI */
690 /******************************************************************************/
692 /** Switches between modes of the program.
693 * @param defaultMode The first mode to run.
694 * @param paramC Used only in MODE_IMAGE mode (store filepath here).
695 * @return Zero on success, error number on fail. */
696 int modeManager(int defaultMode, char *paramC)
698 int mode = defaultMode;
699 int lastMode = MODE_RECOGNIZE;
700 CvCapture* capture = NULL;
705 roi.width = ROI_WIDTH;
706 roi.height = ROI_HEIGHT;
708 if (defaultMode == MODE_IMAGE) {
709 fprintf(stderr, "barcam started in image mode\n");
711 // connect to a camera
712 while (!(capture = cvCaptureFromCAM(-1))) {
713 fprintf(stderr, "NULL capture (is camera connected?)\n");
714 //orte.camera_result.error |= camera_ERR_NO_VIDEO;
715 //ORTEPublicationSend(orte.publication_camera_result);
719 //orte.camera_result.error &= ~camera_ERR_NO_VIDEO;
720 //ORTEPublicationSend(orte.publication_camera_result);
721 fprintf(stderr, "barcam started, camera connected successfully\n");
726 while(!(mode & MODE_QUIT)) {
730 mode = modeVideo(capture, &roi);
731 lastMode = MODE_VIDEO;
735 mode = modeRecognize(capture, &roi);
736 lastMode = MODE_RECOGNIZE;
740 mode = modeWait(lastMode);
744 mode = modeImage(paramC);
745 lastMode = MODE_IMAGE;
748 // jump out of the loop
750 goto _modeManager_end;
755 cvReleaseCapture(&capture);
761 /******************************************************************************/
762 /*********************************** orte *************************************/
763 /******************************************************************************/
765 /** Returns actual state of orte.camera_control.on.
766 * If value changed from previous call, informative output is printed. */
768 inline bool getCameraControlOn(void) {
769 return camera_control_on;
773 inline bool getCameraControlOn(void) {
774 if(orte.camera_control.on!=camera_control_on) {
775 camera_control_on = orte.camera_control.on;
776 fprintf(stderr, "orte: camera_control changed: ctrl %d\n",
779 return camera_control_on;
781 #endif /* WITH_GUI */
783 /******************************************************************************/
785 /** Orte camera result callback function. Does nothing. */
786 void send_cmr_res_cb(const ORTESendInfo *info, void *vinstance,
787 void *recvCallBackParam) { /* nothing */ }
789 /******************************************************************************/
791 /** Orte camera control callback function.
792 * Sets actual value of orte.camera_control.on to camera_control_on. */
793 void rcv_cmr_ctrl_cb(const ORTERecvInfo *info, void *vinstance, void *recvCallBackParam) {
794 struct robottype_orte_data *orte_data = (struct robottype_orte_data *)recvCallBackParam;
796 switch (info->status) {
798 fprintf(stderr, "orte: New camera data: ctrl %d\n",
799 orte_data->camera_control.on);
800 getCameraControlOn();
803 fprintf(stderr, "ORTE deadline occurred - CMR_CTRL receive\n");
809 /******************************************************************************/
810 /********************************* application ********************************/
811 /******************************************************************************/
813 /** If "-i filename" is in argv, filename is stored to imgFilename.
814 * @return Mode to switch to. */
816 int getStartMode(int argc, char *argv[], char *imgFilename) {
819 // scan for program arguments
820 while((opt = getopt(argc, argv, "i:")) != -1) {
824 if(optarg) sprintf(imgFilename, "%s", optarg);
826 fprintf(stderr, "Specify image filename to process: barcam -i filename\n");
834 camera_control_on = true;
839 int getStartMode(int argc, char *argv[], char *imgFilename) {
842 #endif /* WITH_GUI */
844 /******************************************************************************/
846 /** The program may on PC be called with -i argument followed by an image
847 * filename, i.e. rozkuk -i image.png, then the program runs in image mode.
848 * Else call it without arguments. On PC it starts in video mode, to switch
849 * modes keyboard is used (R-recognize, W-wait, Esc-quit). On PPC only two
850 * modes are available - recognize and wait. Orte's camera_control_on variable
851 * is used to switch between them. */
852 int main(int argc, char *argv[])
854 char imgFilename[100];
857 //ret = robottype_roboorte_init(&orte);
860 // fprintf(stderr, "robottype_roboorte_init failed\n");
865 // robottype_publisher_camera_result_create(&orte, send_cmr_res_cb, &orte);
866 // robottype_subscriber_camera_control_create(&orte, rcv_cmr_ctrl_cb, &orte);
867 // robottype_subscriber_robot_switches_create(&orte, rcv_robot_switches_cb, &orte);
869 ret = getStartMode(argc, argv, imgFilename);
870 modeManager(ret, imgFilename);
872 //ret = robottype_roboorte_destroy(&orte);