OmniFastLookupTable.cpp

Go to the documentation of this file.
00001 /*
00002  *  Copyright (C) Massimo Cora' 2005 <maxcvs@email.it>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 
00021 #include "OmniFastLookupTable.hh"
00022 
00023 
00024 //-------------------------------------------------------------------------
00025 //
00026 
00027 OmniFastLookupTable::OmniFastLookupTable( int min_radius,
00028                                                                                   int max_radius,
00029                                                                                   int omni_center_x_pixel,
00030                                                                                   int omni_center_y_pixel,
00031                                                                                   double theta_step, /* = 0 */
00032                                                                                   bool flipped_mode /* = false */ ) :
00033                                                                 _min_radius( min_radius ),
00034                                                                 _max_radius( max_radius ),
00035                                                                 _omni_center_x_rgb( omni_center_x_pixel*3 ),
00036                                                                 _omni_center_y_rgb( omni_center_y_pixel ),
00037                                                                 _omni_center_x( omni_center_x_pixel ),
00038                                                                 _omni_center_y( omni_center_y_pixel ),
00039                                                                 _flipped_mode( flipped_mode )
00040 {
00041         _is_valid = false;
00042 
00043         if ( theta_step == 0 ) {
00044                 if ( max_radius == 0 || min_radius == 0 )
00045                         return;
00046                 
00047                 // let's calculate it
00048                 _theta_step = 1 / (double)max_radius;
00049         }
00050 
00051         // be sure to have max_radius and min_radius even
00052         if ( _max_radius % 2 != 0 )
00053                 _max_radius++;
00054         if ( _min_radius % 2 != 0 )
00055                 _min_radius++;
00056 
00057         _min_to_max_radius_range = _max_radius - _min_radius;
00058         _theta_max = (int)ceil( 2 * CV_PI * (double)_max_radius );
00059         
00060         // computes values
00061         _is_valid = omnipano_lookup_table_make();
00062         
00063 }
00064 
00065 //-------------------------------------------------------------------------
00066 // free up the allocated memory and tables.
00067 
00068 OmniFastLookupTable::~OmniFastLookupTable() {
00069 
00070         int i;
00071 
00072         // free the omni_data pointers
00073         for ( i=0; i < omni_bounds.theta_length; i++ ) {
00074                         free( omni_bounds.omni_data[i] );
00075         }
00076         // and the omni_data theta array
00077         free( omni_bounds.omni_data );
00078 
00079         // now it's the turn of pano_data
00080         for ( i=0; i < pano_bounds.width; i++ ) {
00081                 free( pano_bounds.pano_data[i] );
00082         }
00083         free( pano_bounds.pano_data );
00084 
00085 }
00086 
00087 
00088 //-------------------------------------------------------------------------
00089 // return whether he lookup table has been correctly initialized or not
00090 
00091 bool OmniFastLookupTable::is_initialized () {
00092         return _is_valid;
00093 }
00094 
00095 
00096 //-------------------------------------------------------------------------
00097 //
00098 
00099 void OmniFastLookupTable::angle_type_conversion( const OmniAngle<double>& angle_in, 
00100                                                                                                  OmniAngle<int>& angle_out ) {
00101 
00102         int theta, steps;
00103         double rad_angle_start, rad_angle_end;
00104 
00105         rad_angle_start = angle_in.angle_start;
00106         rad_angle_end = angle_in.angle_end;
00107     
00108         // check whether the lookup table was initialized correctly
00109         if ( !is_initialized () )
00110                 return;
00111 
00112         // we need a positive interval.
00113         // So check for a negative angle
00114         if ( rad_angle_start < 0 )
00115                 rad_angle_start = 2 * CV_PI + rad_angle_start;
00116         
00117         if ( rad_angle_end < 0 )
00118                 rad_angle_end = 2 * CV_PI + rad_angle_end;
00119 
00120         // case in which we want to take an angle for example from 3/2pi to pi/2.
00121         if ( rad_angle_end < rad_angle_start ) {
00122                 theta = (int)ceil( rad_angle_start * (double)_max_radius );
00123 
00124                 // rad_angle_start + (2 pi angle - rad_angle_end)
00125                 steps = (int)ceil( rad_angle_end *(double)_max_radius ) +
00126                         (_theta_max - theta);
00127         }
00128         else if ( rad_angle_end > rad_angle_start ) {
00129                 theta = (int)ceil( rad_angle_start * (double)_max_radius );
00130                 steps = (int)ceil( rad_angle_end * (double)_max_radius ) - theta;
00131         }
00132         else {                  // normal case
00133                 steps = _theta_max;
00134                 theta = 0;
00135         }
00136 
00137         // now fill in the struct
00138         angle_out.range = steps;
00139         angle_out.angle_start = theta;
00140         angle_out.angle_end = (theta + steps) % _theta_max;
00141 }
00142 
00143 //-------------------------------------------------------------------------
00144 // transform an omnidirectional frame into a panorama frame. Can receive also
00145 // a slice of angles: it will convert only the part of the omnidirectional
00146 // image between the two angles.
00147 
00148 IplImage* OmniFastLookupTable::omni2pano( IplImage *frame, 
00149                                                                                   const OmniAngle<int>& IN_interval ) {
00150 
00151         IplImage *image;
00152         IplImage *result ;
00153         int radius, theta;
00154         int steps, step_count;
00155         
00156         // check whether the lookup table was initialized correctly
00157         if ( !is_initialized () ) {
00158                 DEBUG_PRINT ("lookup table not initialized\n");
00159                 return NULL;
00160         }
00161 
00162         image = frame;
00163         theta = IN_interval.angle_start;
00164         steps = IN_interval.range;
00165 
00166         int pano_coord_tmp = (PANO_COORD( steps-1, _max_radius-_min_radius -1 ).x_rgb)/3;
00167 
00168         //
00169         // adjust the width size to the next 32 divisor: this is for image alignment correctness
00170         // and mainly if you want to stream that image with ffmpeg, that has problems and incompatibility
00171         // against OpenCv and IplImages.
00172         // The worst thing that can happen is to have a black line on the right of the resulting pano image
00173         //
00174         int adjusted_pano_coord = pano_coord_tmp;
00175         int tmp_val = pano_coord_tmp % 32;
00176         if ( tmp_val != 0 )
00177                 adjusted_pano_coord += 32 - tmp_val;
00178 
00179         result = cvCreateImage( cvSize( adjusted_pano_coord, _max_radius - _min_radius ),
00180                                                         IPL_DEPTH_8U, 3 );
00181 
00182 
00183         // Note: 231 steps are about 60 degrees
00184 
00185         // go on with a O(2) algorithm... is there something better to do?
00186         for ( theta, step_count = 0; 
00187                 step_count < steps; 
00188                 step_count++, theta = (++theta % _theta_max) ) {
00189 
00190                 if ( theta == _theta_max )
00191                         continue;
00192 
00193                 for ( radius = 0; radius < (_max_radius - _min_radius); radius++ ) { 
00194                         unsigned char* tmp_ptr;
00195                         int original_r;
00196             int original_g;
00197             int original_b;
00198                         int omni_x, omni_y, pano_x, pano_y;
00199 
00200                         omni_x = OMNI_COORD( theta, radius ).x_rgb;
00201             omni_y = OMNI_COORD( theta, radius ).y_rgb;
00202 
00203                         // take the three rgb 3 values from source image
00204                         tmp_ptr = &(CV_IMAGE_ELEM (image, unsigned char, omni_y, omni_x));
00205             original_r = tmp_ptr[0];
00206             original_g = tmp_ptr[1];
00207             original_b = tmp_ptr[2];
00208 
00209             pano_x = PANO_COORD( step_count, radius ).x_rgb;
00210                         if ( _flipped_mode )
00211                                 pano_y = PANO_COORD( step_count, (_min_to_max_radius_range -1) - radius ).y_rgb;
00212                         else 
00213                                 pano_y = PANO_COORD( step_count, radius ).y_rgb;
00214                         
00215             // and then put them in the result image
00216                         tmp_ptr = &(CV_IMAGE_ELEM (result, unsigned char, pano_y, pano_x));
00217                         tmp_ptr[0] = original_r;
00218                         tmp_ptr[1] = original_g;
00219                         tmp_ptr[2] = original_b;
00220                 }
00221         }
00222 
00223         return result;
00224 }
00225 
00226 
00227 
00228 //-------------------------------------------------------------------------
00229 // pre-computes values for conversion pano->omni and vice-versa.
00230 // With this method we can increase the computing performances up to 50%
00231 
00232 bool OmniFastLookupTable::omnipano_lookup_table_make( ) {
00233         int i, j;
00234 
00235         if ( _max_radius <= _min_radius ) {
00236                 DEBUG_PRINT("returning false from OmniFastLookupTable::omnipano_lookup_table_make,"
00237                                         "_max_radius %d <= _min_radius %d\n", _max_radius, _min_radius);
00238                 return false;
00239         }
00240         // let's calculate the number of theta steps to store
00241         int steps = (int)ceil( 2 * CV_PI / _theta_step );
00242         
00243         omni_bounds.theta_length = steps;
00244         omni_bounds.radius_length = _max_radius - _min_radius;
00245 
00246         // create the omni_data matrix
00247         omni_bounds.omni_data = (omni_coord_xy**)calloc( steps, sizeof(omni_coord_xy*));
00248         for ( i=0; i < steps; i++ ) {
00249                 omni_bounds.omni_data[i] = 
00250                         (omni_coord_xy*)calloc( omni_bounds.radius_length, sizeof(omni_coord_xy));
00251         }
00252 
00253         // and the pano_data's matrix
00254         pano_bounds.width = (int)ceil(2 * CV_PI * _max_radius);
00255         pano_bounds.height = _max_radius - _min_radius;
00256 
00257         pano_bounds.pano_data = 
00258                 (pano_coord_xy**)calloc( pano_bounds.width*3, sizeof(pano_coord_xy*) * pano_bounds.width );
00259         for ( i=0; i < pano_bounds.width*3; i++ ) {
00260                 pano_bounds.pano_data[i] = 
00261                         (pano_coord_xy*)calloc( pano_bounds.height, sizeof(pano_coord_xy));
00262         }
00263 
00264         // compute the omni data
00265         for ( i=0; i < steps; i++ ) {           // theta
00266                 for ( j=_min_radius; j < _max_radius; j++ ) {           // radius
00267                         double curr_theta = i * _theta_step;
00268                         omni_bounds.omni_data[i][j - _min_radius].x_rgb = 
00269                                 (int)(j * cos(curr_theta) + _omni_center_x_rgb)*3;
00270                         omni_bounds.omni_data[i][j - _min_radius].y_rgb = 
00271                                 (int)(j * sin(curr_theta) + _omni_center_y_rgb);
00272                 }
00273         }
00274 
00275         // compute the pano data
00276         int tmp_pano_x = 0;
00277         for ( i=0; i < pano_bounds.width; i++ ) {                       // theta
00278                 for ( j=0; j < pano_bounds.height; j++ ) {                      // radius
00279                         int curr_pano_x = (int)(i * _theta_step *j);
00280                         tmp_pano_x = MAX( curr_pano_x, tmp_pano_x );
00281                         pano_bounds.pano_data[i][j].x_rgb = tmp_pano_x*3;
00282 
00283             pano_bounds.pano_data[i][j].y_rgb = j;
00284                 }
00285         }
00286 
00287         return true;
00288 }
00289 
00290 
00291 //------------------------------------------------------------------
00292 // converts a panoimage xy coord to a omniimage xy coord.
00293 // This function is useful if you want to pass from a omni2pano's image point with 
00294 // a start_angle theta back to the point of the original omni image.
00295 // Output: omni_point.
00296 // note: perhaps this function does not return the *exact* point. 
00297 // Should be revised and tested more.
00298 
00299 void OmniFastLookupTable::pano2omni_xy( CvPoint *pano_point, 
00300                                                                                 int theta_start, 
00301                                                                                 CvPoint *omni_point ) 
00302 {
00303         omni_point->x = (int)((double)(pano_point->y  + _min_radius ) * 
00304                 cos( (double)(theta_start + pano_point->x) / (double)_max_radius ));  
00305 
00306         omni_point->y = (int)((double)(pano_point->y + _min_radius ) * 
00307                 sin( (double)(theta_start + pano_point->x) / (double)_max_radius ));  
00308 
00309         omni_point->x +=  _omni_center_x;
00310         omni_point->y +=  _omni_center_y;
00311 }
00312 
00313 
00314 //------------------------------------------------------------------
00315 //
00316 
00317 int OmniFastLookupTable::get_min_radius() {
00318         return _min_radius;
00319 }
00320 
00321 //------------------------------------------------------------------
00322 //
00323 
00324 int OmniFastLookupTable::get_max_radius() {
00325         return _max_radius;
00326 }
00327 
00328 //------------------------------------------------------------------
00329 //
00330 
00331 int OmniFastLookupTable::get_omni_center_x() {
00332         return _omni_center_x;
00333 }
00334 
00335 //------------------------------------------------------------------
00336 //
00337 
00338 int OmniFastLookupTable::get_omni_center_y() {
00339         return _omni_center_y;
00340 }
00341 
00342 

Generated on Tue Dec 26 10:32:38 2006 for Omnimeeting by  doxygen 1.4.7