00001 #include "TePDIColorTransform.hpp"
00002
00003 #include "../kernel/TeAgnostic.h"
00004 #include "TePDIUtils.hpp"
00005 #include "TePDIMatrix.hpp"
00006 #include "../kernel/TeDefines.h"
00007
00008 #ifndef M_PI
00009 #define M_PI 3.14159265358979323846
00010 #endif
00011
00012
00013 TePDIColorTransform::TePDIColorTransform()
00014 {
00015 }
00016
00017
00018 TePDIColorTransform::~TePDIColorTransform()
00019 {
00020 }
00021
00022
00023 void TePDIColorTransform::ResetState( const TePDIParameters& )
00024 {
00025 }
00026
00027
00028 bool TePDIColorTransform::CheckParameters(
00029 const TePDIParameters& parameters ) const
00030 {
00031
00032
00033 ColorTransfTypes transf_type;
00034 TEAGN_TRUE_OR_RETURN( parameters.GetParameter( "transf_type", transf_type ),
00035 "Missing parameter: transf_type" );
00036 TEAGN_TRUE_OR_RETURN(
00037 ( ( transf_type == Rgb2Ihs ) || ( transf_type == Ihs2Rgb ) ),
00038 "Invalid parameter: transf_type" );
00039
00040
00041
00042 TePDITypes::TePDIRasterVectorType input_rasters;
00043 TEAGN_TRUE_OR_RETURN(
00044 parameters.GetParameter( "input_rasters", input_rasters ),
00045 "Missing parameter: input_rasters" );
00046
00047 TEAGN_TRUE_OR_RETURN( ( input_rasters.size() == 3 ),
00048 "Invalid number of input rasters" );
00049
00050 std::vector< int > input_channels;
00051 TEAGN_TRUE_OR_RETURN(
00052 parameters.GetParameter( "input_channels", input_channels ),
00053 "Missing parameter: input_channels" );
00054
00055 TEAGN_TRUE_OR_RETURN( ( input_channels.size() == 3 ),
00056 "Invalid number of input channels" );
00057
00058 for( unsigned int index = 0 ; index < input_rasters.size() ; ++index ) {
00059 TEAGN_TRUE_OR_RETURN( input_rasters[ index ].isActive(),
00060 "Invalid parameter: raster " +
00061 Te2String( index ) + " inactive" );
00062
00063 TEAGN_TRUE_OR_RETURN(
00064 input_rasters[ index ]->params().status_ != TeRasterParams::TeNotReady,
00065 "Invalid parameter: raster " +
00066 Te2String( index ) + " not ready" );
00067
00068 TEAGN_TRUE_OR_RETURN(
00069 input_rasters[ 0 ]->params().nlines_ ==
00070 input_rasters[ index ]->params().nlines_,
00071 "Lines number mismatch between raster 0 and raster " +
00072 Te2String( index ) );
00073
00074 TEAGN_TRUE_OR_RETURN(
00075 input_rasters[ 0 ]->params().ncols_ ==
00076 input_rasters[ index ]->params().ncols_,
00077 "Columns number mismatch between raster 0 and raster " +
00078 Te2String( index ) );
00079
00080 TEAGN_TRUE_OR_RETURN(
00081 input_channels[ index ] >= 0, "Invalid channel number (" +
00082 Te2String( index ) + ")" );
00083 TEAGN_TRUE_OR_RETURN(
00084 input_channels[ index ] < input_rasters[ index ]->nBands(),
00085 "Invalid channel number (" +
00086 Te2String( index ) + ")" );
00087
00088
00089
00090 TEAGN_TRUE_OR_RETURN( (
00091 ( input_rasters[ index ]->params().photometric_[
00092 input_channels[ index ] ] == TeRasterParams::TeRGB ) ||
00093 ( input_rasters[ index ]->params().photometric_[
00094 input_channels[ index ] ] == TeRasterParams::TeMultiBand ) ),
00095 "Invalid parameter - rasters (invalid photometric "
00096 "interpretation)" );
00097 }
00098
00099
00100
00101 TePDITypes::TePDIRasterVectorType output_rasters;
00102 TEAGN_TRUE_OR_RETURN(
00103 parameters.GetParameter( "output_rasters", output_rasters ),
00104 "Missing parameter: output_rasters" );
00105
00106 TEAGN_TRUE_OR_RETURN( ( ( output_rasters.size() == 3 ) ||
00107 ( output_rasters.size() == 1 ) ),
00108 "Invalid number of output rasters" );
00109
00110 for( unsigned int index = 0 ; index < output_rasters.size() ; ++index ) {
00111 TEAGN_TRUE_OR_RETURN( output_rasters[ index ].isActive(),
00112 "Invalid parameter: output raster " +
00113 Te2String( index ) + " inactive" );
00114
00115 TEAGN_TRUE_OR_RETURN(
00116 output_rasters[ index ]->params().status_ != TeRasterParams::TeNotReady,
00117 "Invalid parameter: output raster " +
00118 Te2String( index ) + " not ready" );
00119
00120 if( ( index != 0 ) && ( output_rasters.size() != 1 ) ) {
00121 TEAGN_TRUE_OR_RETURN(
00122 ( output_rasters[ 0 ] != output_rasters[ index ] ),
00123 "Cannot use the same output raster two more times" );
00124 }
00125
00126
00127
00128 if( transf_type == Rgb2Ihs ) {
00129 TEAGN_TRUE_OR_RETURN(
00130 (
00131 (
00132 output_rasters[ index ]->params().dataType_[ 0 ] == TeDOUBLE
00133 )
00134 ||
00135 (
00136 output_rasters[ index ]->params().dataType_[ 0 ] == TeFLOAT
00137 )
00138 ), "Invalid output rasters data type" );
00139 }
00140 }
00141
00142
00143
00144 if( ( transf_type == Ihs2Rgb ) || ( transf_type == Rgb2Ihs ) ) {
00145 double rgb_channels_min = 0;
00146 TEAGN_TRUE_OR_RETURN(
00147 parameters.GetParameter( "rgb_channels_min", rgb_channels_min ),
00148 "Missing parameter: rgbs_channels_min" );
00149
00150 double rgb_channels_max = 0;
00151 TEAGN_TRUE_OR_RETURN(
00152 parameters.GetParameter( "rgb_channels_max", rgb_channels_max ),
00153 "Missing parameter: rgb_channels_max" );
00154
00155 TEAGN_TRUE_OR_RETURN(
00156 ( rgb_channels_max > rgb_channels_min ),
00157 "Invalid parameters: rgb_channels_max - rgb_channels_min" );
00158 }
00159
00160 return true;
00161 }
00162
00163
00164 bool TePDIColorTransform::RunImplementation()
00165 {
00166 TePDITypes::TePDIRasterVectorType input_rasters;
00167 params_.GetParameter( "input_rasters", input_rasters );
00168
00169 std::vector< int > input_channels;
00170 params_.GetParameter( "input_channels", input_channels );
00171
00172 TePDITypes::TePDIRasterVectorType output_rasters;
00173 params_.GetParameter( "output_rasters", output_rasters );
00174
00175 ColorTransfTypes transf_type;
00176 params_.GetParameter( "transf_type", transf_type );
00177
00178 std::vector< int > output_channels;
00179
00180
00181
00182 TeRaster& ref_input_raster = *(input_rasters[ 0 ].nakedPointer() );
00183
00184 if( output_rasters.size() == 1 ) {
00185 TeRaster& output_raster = *( output_rasters[ 0 ].nakedPointer() );
00186
00187
00188
00189 TeRasterParams output_raster_params = output_raster.params();
00190
00191 output_raster_params.nBands( 3 );
00192 if( ref_input_raster.projection() != 0 ) {
00193 output_raster_params.projection( ref_input_raster.projection() );
00194 }
00195 output_raster_params.boxLinesColumns(
00196 ref_input_raster.params().box().x1(),
00197 ref_input_raster.params().box().y1(),
00198 ref_input_raster.params().box().x2(),
00199 ref_input_raster.params().box().y2(),
00200 ref_input_raster.params().nlines_,
00201 ref_input_raster.params().ncols_ );
00202
00203 switch( transf_type ) {
00204 case Rgb2Ihs :
00205 {
00206 output_raster_params.setPhotometric( TeRasterParams::TeMultiBand, -1 );
00207
00208 break;
00209 }
00210 case Ihs2Rgb :
00211 {
00212 output_raster_params.setPhotometric( TeRasterParams::TeRGB, -1 );
00213
00214 break;
00215 }
00216 default :
00217 {
00218 TEAGN_LOG_AND_RETURN( "Invalid transformation type" );
00219 break;
00220 }
00221 }
00222
00223 TEAGN_TRUE_OR_RETURN( output_raster.init( output_raster_params ),
00224 "Output raster reset error" );
00225
00226
00227
00228 output_channels.push_back( 0 );
00229 output_channels.push_back( 1 );
00230 output_channels.push_back( 2 );
00231
00232
00233
00234 output_rasters.push_back( output_rasters[ 0 ] );
00235 output_rasters.push_back( output_rasters[ 0 ] );
00236 } else {
00237
00238
00239 for( unsigned int index = 0 ; index < output_rasters.size() ; ++index ) {
00240 TeRaster& output_raster = *( output_rasters[ index ].nakedPointer() );
00241
00242
00243
00244 TeRasterParams output_raster_params = output_raster.params();
00245
00246 output_raster_params.nBands( 1 );
00247 if( ref_input_raster.projection() != 0 ) {
00248 output_raster_params.projection( ref_input_raster.projection() );
00249 }
00250 output_raster_params.boxLinesColumns(
00251 ref_input_raster.params().box().x1(),
00252 ref_input_raster.params().box().y1(),
00253 ref_input_raster.params().box().x2(),
00254 ref_input_raster.params().box().y2(),
00255 ref_input_raster.params().nlines_,
00256 ref_input_raster.params().ncols_ );
00257 switch( transf_type ) {
00258 case Rgb2Ihs :
00259 {
00260 output_raster_params.setPhotometric( TeRasterParams::TeMultiBand, -1 );
00261
00262 break;
00263 }
00264 case Ihs2Rgb :
00265 {
00266 output_raster_params.setPhotometric( TeRasterParams::TeRGB, -1 );
00267
00268 break;
00269 }
00270 default :
00271 {
00272 TEAGN_LOG_AND_RETURN( "Invalid transformation type" );
00273 break;
00274 }
00275 }
00276
00277 TEAGN_TRUE_OR_RETURN( output_raster.init( output_raster_params ),
00278 "Output raster reset error" );
00279 }
00280
00281
00282
00283 output_channels.push_back( 0 );
00284 output_channels.push_back( 0 );
00285 output_channels.push_back( 0 );
00286 }
00287
00288
00289
00290 switch( transf_type ) {
00291 case Rgb2Ihs :
00292 {
00293 double rgb_channels_min = 0;
00294 params_.GetParameter( "rgb_channels_min", rgb_channels_min );
00295
00296 double rgb_channels_max = 0;
00297 params_.GetParameter( "rgb_channels_max", rgb_channels_max );
00298
00299 return RunRgb2Ihs( input_rasters, input_channels, output_rasters,
00300 output_channels, rgb_channels_min, rgb_channels_max );
00301 break;
00302 }
00303 case Ihs2Rgb :
00304 {
00305 double rgb_channels_min = 0;
00306 params_.GetParameter( "rgb_channels_min", rgb_channels_min );
00307
00308 double rgb_channels_max = 0;
00309 params_.GetParameter( "rgb_channels_max", rgb_channels_max );
00310
00311 return RunIhs2Rgb( input_rasters, input_channels, output_rasters,
00312 output_channels, rgb_channels_min, rgb_channels_max );
00313 break;
00314 }
00315 default :
00316 {
00317 TEAGN_LOG_AND_RETURN( "Invalid transformation type" );
00318 break;
00319 }
00320 }
00321
00322 return false;
00323 }
00324
00325
00326 bool TePDIColorTransform::RunRgb2Ihs(
00327 TePDITypes::TePDIRasterVectorType& input_rasters,
00328 std::vector< int >& input_channels,
00329 TePDITypes::TePDIRasterVectorType& output_rasters,
00330 std::vector< int >& output_channels,
00331 const double rgb_channels_min, const double rgb_channels_max )
00332 {
00333 TEAGN_DEBUG_CONDITION( ( input_rasters.size() == 3 ),
00334 "Invalid vector size" )
00335 TEAGN_DEBUG_CONDITION( ( input_channels.size() == 3 ),
00336 "Invalid vector size" )
00337 TEAGN_DEBUG_CONDITION( ( output_rasters.size() == 3 ),
00338 "Invalid vector size" )
00339 TEAGN_DEBUG_CONDITION( ( output_channels.size() == 3 ),
00340 "Invalid vector size" )
00341 TEAGN_DEBUG_CONDITION( ( rgb_channels_max >= rgb_channels_min ),
00342 "Invalid rgb channels max/min" )
00343
00344
00345
00346 TeRaster& input_raster0 = *( input_rasters[ 0 ].nakedPointer() );
00347 TeRaster& input_raster1 = *( input_rasters[ 1 ].nakedPointer() );
00348 TeRaster& input_raster2 = *( input_rasters[ 2 ].nakedPointer() );
00349
00350 const int input_channel0 = input_channels[ 0 ];
00351 const int input_channel1 = input_channels[ 1 ];
00352 const int input_channel2 = input_channels[ 2 ];
00353
00354 TeRaster& output_raster0 = *( output_rasters[ 0 ].nakedPointer() );
00355 TeRaster& output_raster1 = *( output_rasters[ 1 ].nakedPointer() );
00356 TeRaster& output_raster2 = *( output_rasters[ 2 ].nakedPointer() );
00357
00358 const int output_channel0 = output_channels[ 0 ];
00359 const int output_channel1 = output_channels[ 1 ];
00360 const int output_channel2 = output_channels[ 2 ];
00361
00362 const unsigned int lines = ( unsigned int ) input_raster0.params().nlines_;
00363 const unsigned int columns = ( unsigned int )
00364 input_raster0.params().ncols_;
00365
00366 const double rgb_channels_diff = rgb_channels_max - rgb_channels_min;
00367 const double rgb_channels_norm_fac = ( rgb_channels_diff != 0.0 ) ?
00368 rgb_channels_diff : 1.0;
00369
00370
00371
00372 double out_raster_dummy = 0;
00373 if( output_raster0.params().useDummy_ ) {
00374 out_raster_dummy = output_raster0.params().dummy_[ 0 ];
00375 }
00376
00377
00378
00379 double red = 0, green = 0, blue = 0;
00380 double hue = 0, sat = 0, light = 0;
00381 unsigned int line = 0;
00382 unsigned int column = 0;
00383 double teta = 0;
00384 double red_norm = 0, green_norm = 0, blue_norm = 0;
00385 double r_minus_g = 0, r_minus_b = 0;
00386 double rgb_sum = 0;
00387 double cosvalue = 0;
00388 const double two_pi = 2.0 * ((double)M_PI);
00389 const double pi_rat_2 = ((double)M_PI) / 2.0;
00390
00391 TePDIPIManager progress( "Converting RGB -> IHS...", lines,
00392 progress_enabled_ );
00393
00394 for( line = 0 ; line < lines ; ++line ) {
00395 for( column = 0 ; column < columns ; ++column ) {
00396 if( input_raster0.getElement( column, line, red, input_channel0 ) &&
00397 input_raster1.getElement( column, line, green, input_channel1 ) &&
00398 input_raster2.getElement( column, line, blue, input_channel2 ) )
00399 {
00400 if( ( red == green ) && ( green == blue ) )
00401 {
00402
00403
00404
00405
00406
00407
00408 hue = sat = 0.0;
00409 light = ( red / rgb_channels_norm_fac );
00410 }
00411 else
00412 {
00413 red_norm = ( red - rgb_channels_min ) / rgb_channels_norm_fac;
00414 green_norm = ( green - rgb_channels_min ) / rgb_channels_norm_fac;
00415 blue_norm = ( blue - rgb_channels_min ) / rgb_channels_norm_fac;
00416
00417 r_minus_g = red_norm - green_norm;
00418 r_minus_b = red_norm - blue_norm;
00419
00420 cosvalue = sqrt( ( r_minus_g * r_minus_g ) + ( r_minus_b *
00421 ( green_norm - blue_norm ) ) );
00422
00423 if( cosvalue == 0.0 )
00424 {
00425 teta = pi_rat_2;
00426 }
00427 else
00428 {
00429 cosvalue = ( 0.5 * ( r_minus_g + r_minus_b ) ) /
00430 cosvalue;
00431 teta = acos( cosvalue );
00432 }
00433
00434 TEAGN_DEBUG_CONDITION( ( cosvalue >= (-1.0) ) &&
00435 ( cosvalue <= (1.0) ), "Invalid cosvalue value"
00436 " cosvalue=" + Te2String( cosvalue, 9 )
00437 + " red_norm=" + Te2String( red_norm, 9 )
00438 + " green_norm=" + Te2String( green_norm, 9 )
00439 + " blue_norm=" + Te2String( blue_norm, 9 )
00440 + " r_minus_g=" + Te2String( r_minus_g, 9 )
00441 + " r_minus_b=" + Te2String( r_minus_b, 9 )
00442 + " rgb_channels_min=" + Te2String( rgb_channels_min, 9 )
00443 + " rgb_channels_norm_fac=" + Te2String( rgb_channels_norm_fac, 9 )
00444 );
00445
00446 if( blue_norm > green_norm )
00447 {
00448 hue = two_pi - teta;
00449 }
00450 else
00451 {
00452 hue = teta;
00453 }
00454
00455 rgb_sum = red_norm + green_norm + blue_norm;
00456
00457 sat = 1.0 - ( 3 * MIN( MIN( red_norm, green_norm ), blue_norm ) /
00458 rgb_sum );
00459
00460 light = rgb_sum / 3.0;
00461 }
00462 } else {
00463 hue = sat = light = 0.0;
00464 }
00465
00466 TEAGN_TRUE_OR_RETURN( output_raster0.setElement(
00467 column, line, light, output_channel0 ),
00468 "Unable to write intensity channel for output_image" );
00469 TEAGN_TRUE_OR_RETURN( output_raster1.setElement(
00470 column, line, hue, output_channel1 ),
00471 "Unable to write hue channel for output_image" );
00472 TEAGN_TRUE_OR_RETURN( output_raster2.setElement(
00473 column, line, sat, output_channel2 ),
00474 "Unable to write saturation channel for output_image" );
00475 }
00476
00477 TEAGN_FALSE_OR_RETURN( progress.Increment(),
00478 "Canceled by the user" );
00479 }
00480
00481 return true;
00482 }
00483
00484
00485 bool TePDIColorTransform::RunIhs2Rgb(
00486 TePDITypes::TePDIRasterVectorType& input_rasters,
00487 std::vector< int >& input_channels,
00488 TePDITypes::TePDIRasterVectorType& output_rasters,
00489 std::vector< int >& output_channels,
00490 const double rgb_channels_min, const double rgb_channels_max )
00491 {
00492 TEAGN_TRUE_OR_THROW( ( input_rasters.size() == 3 ),
00493 "Invalid vector size" )
00494 TEAGN_TRUE_OR_THROW( ( input_channels.size() == 3 ),
00495 "Invalid vector size" )
00496 TEAGN_TRUE_OR_THROW( ( output_rasters.size() == 3 ),
00497 "Invalid vector size" )
00498 TEAGN_TRUE_OR_THROW( ( output_channels.size() == 3 ),
00499 "Invalid vector size" )
00500
00501
00502
00503 TeRaster& input_raster0 = *( input_rasters[ 0 ].nakedPointer() );
00504 TeRaster& input_raster1 = *( input_rasters[ 1 ].nakedPointer() );
00505 TeRaster& input_raster2 = *( input_rasters[ 2 ].nakedPointer() );
00506
00507 const int input_channel0 = input_channels[ 0 ];
00508 const int input_channel1 = input_channels[ 1 ];
00509 const int input_channel2 = input_channels[ 2 ];
00510
00511 TeRaster& output_raster0 = *( output_rasters[ 0 ].nakedPointer() );
00512 TeRaster& output_raster1 = *( output_rasters[ 1 ].nakedPointer() );
00513 TeRaster& output_raster2 = *( output_rasters[ 2 ].nakedPointer() );
00514
00515 int output_channel0 = output_channels[ 0 ];
00516 int output_channel1 = output_channels[ 1 ];
00517 int output_channel2 = output_channels[ 2 ];
00518
00519 unsigned int lines = ( unsigned int ) input_raster0.params().nlines_;
00520 unsigned int columns = ( unsigned int ) input_raster0.params().ncols_;
00521
00522 const double rgb_channels_diff = rgb_channels_max - rgb_channels_min;
00523 const double rgb_channels_norm_fac = ( rgb_channels_diff != 0.0 ) ?
00524 rgb_channels_diff : 1.0;
00525
00526
00527
00528 double out_raster_dummy = 0;
00529 if( output_raster0.params().useDummy_ ) {
00530 out_raster_dummy = output_raster0.params().dummy_[ 0 ];
00531 }
00532
00533
00534
00535 const double pi_rat3 = M_PI / 3.0;
00536 const double two_pi_rat3 = 2.0 * M_PI / 3.0;
00537 const double four_pi_rat3 = 4.0 * M_PI / 3.0;
00538
00539 double red = 0, green = 0, blue = 0;
00540 double hue = 0, sat = 0, lig = 0;
00541
00542 StartProgInt( "Converting IHS -> RGB...", lines );
00543
00544 for( unsigned int line = 0 ; line < lines ; ++line ) {
00545 TEAGN_FALSE_OR_RETURN( UpdateProgInt( line ), "Canceled by the user" );
00546
00547 for( unsigned int column = 0 ; column < columns ; ++column ) {
00548 if( input_raster0.getElement( column, line, lig, input_channel0 ) &&
00549 input_raster1.getElement( column, line, hue, input_channel1 ) &&
00550 input_raster2.getElement( column, line, sat, input_channel2 ) )
00551 {
00552 if( ( hue == 0.0 ) && ( sat == 0.0 ) )
00553 {
00554 red = green = blue = ( lig * rgb_channels_norm_fac );
00555 }
00556 else
00557 {
00558
00559 if( hue < two_pi_rat3 )
00560 {
00561 blue = lig * ( 1.0 - sat );
00562 red = lig * ( 1.0 + ( sat * cos( hue ) /
00563 cos( pi_rat3 - hue ) ) );
00564 green = ( 3.0 * lig ) - ( red + blue );
00565 }
00566 else if( hue < four_pi_rat3 )
00567 {
00568
00569 hue -= two_pi_rat3;
00570
00571 red = lig * ( 1.0 - sat );
00572 green = lig * ( 1.0 + ( sat * cos( hue ) /
00573 cos( pi_rat3 - hue ) ) );
00574 blue = ( 3.0 * lig ) - ( red + green );
00575 }
00576 else
00577 {
00578
00579 hue -= four_pi_rat3;
00580
00581 green = lig * ( 1.0 - sat );
00582 blue = lig * ( 1.0 + ( sat * cos( hue ) /
00583 cos( pi_rat3 - hue ) ) );
00584 red = ( 3.0 * lig ) - ( green + blue );
00585 }
00586
00587 red = ( red * rgb_channels_norm_fac ) + rgb_channels_min;
00588 green = ( green * rgb_channels_norm_fac ) + rgb_channels_min;
00589 blue = ( blue * rgb_channels_norm_fac ) + rgb_channels_min;
00590 }
00591
00592 red = MIN( red, rgb_channels_max );
00593 green = MIN( green, rgb_channels_max );
00594 blue = MIN( blue, rgb_channels_max );
00595
00596 red = MAX( red, rgb_channels_min );
00597 green = MAX( green, rgb_channels_min );
00598 blue = MAX( blue, rgb_channels_min );
00599
00600 TEAGN_TRUE_OR_RETURN( output_raster0.setElement(
00601 column, line, red, output_channel0 ),
00602 "Unable to write red channel for output_image" );
00603 TEAGN_TRUE_OR_RETURN( output_raster1.setElement(
00604 column, line, green, output_channel1 ),
00605 "Unable to write green channel for output_image" );
00606 TEAGN_TRUE_OR_RETURN( output_raster2.setElement(
00607 column, line, blue, output_channel2 ),
00608 "Unable to write blue channel for output_image" );
00609 } else {
00610 TEAGN_TRUE_OR_RETURN( output_raster0.setElement(
00611 column, line, out_raster_dummy, output_channel0 ),
00612 "Unable to write red channel for output_image" );
00613 TEAGN_TRUE_OR_RETURN( output_raster1.setElement(
00614 column, line, out_raster_dummy, output_channel1 ),
00615 "Unable to write green channel for output_image" );
00616 TEAGN_TRUE_OR_RETURN( output_raster2.setElement(
00617 column, line, out_raster_dummy, output_channel2 ),
00618 "Unable to write blue channel for output_image" );
00619 }
00620 }
00621 }
00622
00623 return true;
00624 }
00625