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 /** @class GradientClassifier <classifiers/gradient.h>
00032  * Gradient classifier.
00033  * Uses the difference of the current and the last value.
00034  */
00035 
00036 /** Constructor.
00037  * @param scanlines list of scanline models (Does only work with ScanlineGrid)
00038  * @param q Qualifier for a single pixel (The qualifier gets deleted by this class)
00039  * @param threshold minimum rise required for classification
00040  * @param max_size of an object to be detected (if 0 value will be ignored)
00041  * @param use_rising_flank 
00042  *           if true the classification can start on a rising flank
00043  * @param use_falling_flank 
00044  *           if true the classification can start on a falling flank
00045  */
00046 GradientClassifier::GradientClassifier(std::list<ScanlineGrid* >* scanlines,
00047                                        Qualifier* q,
00048                                        unsigned int threshold, unsigned int max_size,
00049                                        bool use_rising_flank, bool use_falling_flank)
00050   : Classifier("GradientClassifier")
00051 {
00052   if (!scanlines)
00053     throw fawkes::NullPointerException("GradientClassifier: scanlines may not be null!");
00054   if (!q)
00055     throw fawkes::NullPointerException("GradientClassifier: the Qualifier may not be null!");
00056
00057   _scanlines = scanlines;
00058   _q = q;
00059
00060   _max_size = 999999; //Infinite...
00061   set_threshold(threshold, max_size);
00062   set_edges(use_rising_flank, use_falling_flank);
00063 }
00064 
00065 /** Destructor.
00066  */
00067 GradientClassifier::~GradientClassifier()
00068 {
00069   if (_q)
00070     delete _q;
00071 }
00072 
00073 /** Threshold setter.
00074  * @param threshold minimum rise required for classification
00075  * @param max_size of an object to be detected (if 0 value will not be set)
00076  */
00077 void
00078 GradientClassifier::set_threshold(unsigned int threshold, unsigned int max_size)
00079 {
00080   _threshold = threshold;
00081
00082   if (max_size)
00083     _max_size = max_size;
00084 }
00085 
00086 /** Edge setter.
00087  * @param use_rising_edge 
00088  *           if true the classification can start on a rising edge
00089  * @param use_falling_edge 
00090  *           if true the classification can start on a falling edge
00091  */
00092 void
00093 GradientClassifier::set_edges(bool use_rising_edge, bool use_falling_edge)
00094 {
00095   _use_rising_edge  = use_rising_edge;
00096   _use_falling_edge = use_falling_edge;
00097 }
00098 
00099 /** Set source buffer.
00100  * @param yuv422_planar a YUV422 planar buffer with the source image to
00101  * classify. The classifier may NOT modify the image in any way. If that is
00102  * required the classifier shall make a copy of the image.
00103  * @param width width of buffer in pixels
00104  * @param height height of buffer in pixels
00105  */
00106 void
00107 GradientClassifier::set_src_buffer(unsigned char *yuv422_planar,
00108                                    unsigned int width, unsigned int height)
00109 {
00110   Classifier::set_src_buffer(yuv422_planar, width, height);
00111
00112   _q->set_buffer(yuv422_planar, width, height);
00113 }
00114 
00115 /** Performs the classification.
00116  */
00117 std::list< ROI > *
00118 GradientClassifier::classify()
00119 {
00120   if (_q->get_buffer() == NULL)
00121   {
00122     //cout << "GradientClassifier: ERROR, src buffer not set. NOT classifying." << endl;
00123     return new std::list< ROI >;
00124   }
00125
00126   list< ROI > *rv = new list< ROI >;
00127   int cur_val, cur_diff, direction = 0;
00128   point_t cur_pos, edge_start;
00129   cur_pos.x = cur_pos.y = edge_start.x = edge_start.y = 0;
00130
00131   unsigned int jumpSize = 0;
00132
00133   ROI current;
00134
00135   for (list<ScanlineGrid*>::iterator it = _scanlines->begin(); it != _scanlines->end(); it++)
00136   {
00137     ScanlineGrid* slm = (*it);
00138     slm->reset();
00139
00140     _last_pos = *(*slm);
00141     _last_val = _q->get(_last_pos);
00142
00143     while(!slm->finished())
00144     {
00145       cur_pos = *(++(*slm));
00146       cur_val =  _q->get(cur_pos);
00147       cur_diff = cur_val - _last_val;
00148
00149       if ((cur_pos.x < _last_pos.x || cur_pos.y < _last_pos.y) //new scan line
00150           || (current.pixel_step && ((cur_pos.x - current.start.x) > _max_size //area found is too big
00151                                      || (cur_pos.y - current.start.y) > _max_size)))
00152       {
00153         current.set_pixel_step(0);
00154
00155         edge_start.x = edge_start.y = direction = jumpSize  = 0;
00156       }
00157
00158       int curDir = (cur_diff < 0 ? -1 : (cur_diff > 0 ? 1 : 0));
00159       switch (curDir)
00160       {
00161       case -1:
00162         switch (direction)
00163         {
00164         case -1: //drop continues
00165           jumpSize -= cur_diff;
00166           break;
00167         case 0: //new drop
00168           jumpSize = -cur_diff;
00169           edge_start = cur_pos;
00170           break;
00171         case 1:
00172           if (jumpSize < _threshold)//spike reset ramp
00173           {
00174             jumpSize = -cur_diff;
00175             edge_start = cur_pos;
00176           }
00177           else // found edge!
00178           {
00179             if (current.pixel_step) //this is a line end
00180             {
00181               current.set_width(_last_pos.x - current.start.x);
00182               current.set_height(_last_pos.y - current.start.y);
00183
00184               rv->push_back(ROI(current));
00185
00186               current.set_pixel_step(0);
00187             }
00188             else if (_use_falling_edge)
00189             {
00190               current.set_pixel_step(1);
00191               current.set_start(edge_start);
00192             }
00193
00194             edge_start = cur_pos;
00195             jumpSize = -cur_diff;
00196           }
00197           break;
00198         }
00199         direction = -1;
00200         break;
00201
00202
00203       case 0:
00204         switch (direction)
00205         {
00206         case -1: //ramp end
00207         case 1:  //ramp end
00208           if (jumpSize >= _threshold) //found edge!
00209           {
00210             if (current.pixel_step) //this is a line end
00211             {
00212               current.set_width(_last_pos.x - current.start.x);
00213               current.set_height(_last_pos.y - current.start.y);
00214
00215               rv->push_back(ROI(current));
00216
00217               current.set_pixel_step(0);
00218             }
00219             else
00220             {
00221               if ((_use_falling_edge && direction == 1) || (_use_rising_edge && direction == -1))
00222               {
00223                 current.set_pixel_step(1);
00224                 current.set_start(edge_start);
00225               }
00226             }
00227           }
00228           break;
00229
00230         case 0:
00231           break;
00232         }
00233         direction = jumpSize = 0;
00234         edge_start.x = edge_start.y = 0;
00235         break;
00236
00237
00238       case 1:
00239         switch (direction)
00240         {
00241         case 1: //climb continues
00242           jumpSize += cur_diff;
00243           break;
00244         case 0: //new climb
00245           jumpSize = cur_diff;
00246           edge_start = cur_pos;
00247           break;
00248         case -1:
00249           if (jumpSize < _threshold)//spike reset ramp
00250           {
00251             jumpSize = cur_diff;
00252             edge_start = cur_pos;
00253           }
00254           else // found edge!
00255           {
00256             if (current.pixel_step) //this is a line end
00257             {
00258               current.set_width(_last_pos.x - current.start.x);
00259               current.set_height(_last_pos.y - current.start.y);
00260
00261               rv->push_back(ROI(current));
00262
00263               current.set_pixel_step(0);
00264             }
00265             else if (_use_rising_edge)
00266             {
00267               current.set_pixel_step(1);
00268               current.set_start(edge_start);
00269             }
00270
00271             edge_start = cur_pos;
00272             jumpSize = cur_diff;
00273           }
00274           break;
00275         }
00276         direction = 1;
00277         break;
00278       }
00279
00280
00281       _last_val = cur_val;
00282       _last_pos = cur_pos;
00283     }
00284   } //END: For all scanline models
00285
00286   return rv;
00287 }
00288