Fawkes API  Fawkes Development Version
gradient.cpp
00001 /***************************************************************************
00002  *  gradient.h - Class defining a gradient (color) classifier
00003  *
00004  *  Created: Tue Jun 10 11:48:00 2008
00005  *  Copyright  2008 Christof Rath <christof.rath@gmail.com>
00006  *
00007  ****************************************************************************/
00008 
00009 /*  This program is free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version. A runtime exception applies to
00013  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00021  */
00022 
00023 #include "gradient.h"
00024 #include <core/exceptions/software.h>
00025 
00026 using std::list;
00027 using std::iterator;
00028 
00029 using fawkes::point_t;
00030 
00031 namespace firevision {
00032 #if 0 /* just to make Emacs auto-indent happy */
00033 }
00034 #endif
00035 
00036 /** @class GradientClassifier <classifiers/gradient.h>
00037  * Gradient classifier.
00038  * Uses the difference of the current and the last value.
00039  */
00040 
00041 /** Constructor.
00042  * @param scanlines list of scanline models (Does only work with ScanlineGrid)
00043  * @param q Qualifier for a single pixel (The qualifier gets deleted by this class)
00044  * @param threshold minimum rise required for classification
00045  * @param max_size of an object to be detected (if 0 value will be ignored)
00046  * @param use_rising_flank 
00047  *           if true the classification can start on a rising flank
00048  * @param use_falling_flank 
00049  *           if true the classification can start on a falling flank
00050  */
00051 GradientClassifier::GradientClassifier(std::list<ScanlineGrid* >* scanlines, 
00052                                        Qualifier* q,
00053                                        unsigned int threshold, unsigned int max_size, 
00054                                        bool use_rising_flank, bool use_falling_flank)
00055   : Classifier("GradientClassifier")
00056 {
00057   if (!scanlines)
00058     throw fawkes::NullPointerException("GradientClassifier: scanlines may not be null!");
00059   if (!q)
00060     throw fawkes::NullPointerException("GradientClassifier: the Qualifier may not be null!");
00061 
00062   _scanlines = scanlines;
00063   _q = q;
00064 
00065   _max_size = 999999; //Infinite...
00066   set_threshold(threshold, max_size);
00067   set_edges(use_rising_flank, use_falling_flank);
00068 }
00069 
00070 /** Destructor.
00071  */
00072 GradientClassifier::~GradientClassifier()
00073 {
00074   if (_q)
00075     delete _q;
00076 }
00077 
00078 /** Threshold setter.
00079  * @param threshold minimum rise required for classification
00080  * @param max_size of an object to be detected (if 0 value will not be set)
00081  */
00082 void
00083 GradientClassifier::set_threshold(unsigned int threshold, unsigned int max_size)
00084 {
00085   _threshold = threshold;
00086 
00087   if (max_size)
00088     _max_size = max_size;
00089 }
00090 
00091 /** Edge setter.
00092  * @param use_rising_edge 
00093  *           if true the classification can start on a rising edge
00094  * @param use_falling_edge 
00095  *           if true the classification can start on a falling edge
00096  */
00097 void 
00098 GradientClassifier::set_edges(bool use_rising_edge, bool use_falling_edge)
00099 {
00100   _use_rising_edge  = use_rising_edge;
00101   _use_falling_edge = use_falling_edge;
00102 }
00103 
00104 /** Set source buffer.
00105  * @param yuv422_planar a YUV422 planar buffer with the source image to
00106  * classify. The classifier may NOT modify the image in any way. If that is
00107  * required the classifier shall make a copy of the image.
00108  * @param width width of buffer in pixels
00109  * @param height height of buffer in pixels
00110  */
00111 void
00112 GradientClassifier::set_src_buffer(unsigned char *yuv422_planar, 
00113                                    unsigned int width, unsigned int height)
00114 {
00115   Classifier::set_src_buffer(yuv422_planar, width, height);
00116 
00117   _q->set_buffer(yuv422_planar, width, height);
00118 }
00119 
00120 std::list< ROI > *
00121 GradientClassifier::classify()
00122 {
00123   if (_q->get_buffer() == NULL) 
00124   {
00125     //cout << "GradientClassifier: ERROR, src buffer not set. NOT classifying." << endl;
00126     return new std::list< ROI >;
00127   }
00128 
00129   list< ROI > *rv = new list< ROI >;
00130   int cur_val, cur_diff, direction = 0;
00131   point_t cur_pos, edge_start;
00132   cur_pos.x = cur_pos.y = edge_start.x = edge_start.y = 0;
00133 
00134   unsigned int jumpSize = 0;
00135 
00136   ROI current;
00137 
00138   for (list<ScanlineGrid*>::iterator it = _scanlines->begin(); it != _scanlines->end(); it++)
00139   {
00140     ScanlineGrid* slm = (*it);
00141     slm->reset();
00142 
00143     _last_pos = *(*slm);
00144     _last_val = _q->get(_last_pos);
00145 
00146     while(!slm->finished())
00147     {
00148       cur_pos = *(++(*slm));
00149       cur_val =  _q->get(cur_pos);
00150       cur_diff = cur_val - _last_val;
00151 
00152       if ((cur_pos.x < _last_pos.x || cur_pos.y < _last_pos.y) //new scan line
00153           || (current.pixel_step && ((cur_pos.x - current.start.x) > _max_size //area found is too big
00154                                      || (cur_pos.y - current.start.y) > _max_size)))
00155       {
00156         current.set_pixel_step(0);
00157 
00158         edge_start.x = edge_start.y = direction = jumpSize  = 0;
00159       }
00160 
00161       int curDir = (cur_diff < 0 ? -1 : (cur_diff > 0 ? 1 : 0));
00162       switch (curDir)
00163       {
00164       case -1:
00165         switch (direction)
00166         {
00167         case -1: //drop continues
00168           jumpSize -= cur_diff;
00169           break;
00170         case 0: //new drop
00171           jumpSize = -cur_diff;
00172           edge_start = cur_pos;
00173           break;
00174         case 1: 
00175           if (jumpSize < _threshold)//spike reset ramp
00176           {
00177             jumpSize = -cur_diff;
00178             edge_start = cur_pos;
00179           }
00180           else // found edge!
00181           {
00182             if (current.pixel_step) //this is a line end
00183             {
00184               current.set_width(_last_pos.x - current.start.x);
00185               current.set_height(_last_pos.y - current.start.y);
00186 
00187               rv->push_back(ROI(current));
00188 
00189               current.set_pixel_step(0);
00190             }
00191             else if (_use_falling_edge) 
00192             {
00193               current.set_pixel_step(1);
00194               current.set_start(edge_start);
00195             }
00196                                                         
00197             edge_start = cur_pos;
00198             jumpSize = -cur_diff;
00199           }
00200           break;
00201         }
00202         direction = -1;
00203         break;
00204 
00205 
00206       case 0:
00207         switch (direction)
00208         {
00209         case -1: //ramp end
00210         case 1:  //ramp end
00211           if (jumpSize >= _threshold) //found edge!
00212           {
00213             if (current.pixel_step) //this is a line end
00214             {
00215               current.set_width(_last_pos.x - current.start.x);
00216               current.set_height(_last_pos.y - current.start.y);
00217 
00218               rv->push_back(ROI(current));
00219 
00220               current.set_pixel_step(0);
00221             }
00222             else 
00223             {
00224               if ((_use_falling_edge && direction == 1) || (_use_rising_edge && direction == -1))
00225               {
00226                 current.set_pixel_step(1);
00227                 current.set_start(edge_start);
00228               }
00229             }
00230           }
00231           break;
00232 
00233         case 0:
00234           break;
00235         }
00236         direction = jumpSize = 0;
00237         edge_start.x = edge_start.y = 0;
00238         break;
00239 
00240 
00241       case 1:
00242         switch (direction)
00243         {
00244         case 1: //climb continues
00245           jumpSize += cur_diff;
00246           break;
00247         case 0: //new climb
00248           jumpSize = cur_diff;
00249           edge_start = cur_pos;
00250           break;
00251         case -1: 
00252           if (jumpSize < _threshold)//spike reset ramp
00253           {
00254             jumpSize = cur_diff;
00255             edge_start = cur_pos;
00256           }
00257           else // found edge!
00258           {
00259             if (current.pixel_step) //this is a line end
00260             {
00261               current.set_width(_last_pos.x - current.start.x);
00262               current.set_height(_last_pos.y - current.start.y);
00263 
00264               rv->push_back(ROI(current));
00265 
00266               current.set_pixel_step(0);
00267             }
00268             else if (_use_rising_edge) 
00269             {
00270               current.set_pixel_step(1);
00271               current.set_start(edge_start);
00272             }
00273 
00274             edge_start = cur_pos;
00275             jumpSize = cur_diff;
00276           }
00277           break;
00278         }
00279         direction = 1;
00280         break;
00281       }
00282 
00283 
00284       _last_val = cur_val;
00285       _last_pos = cur_pos;
00286     }
00287   } //END: For all scanline models
00288         
00289   return rv;
00290 }
00291 
00292 } // end namespace firevision