00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include <qimage.h>
00013 #include <qstring.h>
00014 #include <qapplication.h>
00015
00016
00017 #include "redEye.h"
00018 #include "redEye_internal.h"
00019 #include "../../gui/statusWidget.h"
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206 QImage* removeRedeyeRegions( QString filename,
00207 QPoint topLeftExtreme, QPoint bottomRightExtreme,
00208 StatusWidget* statusWidget )
00209 {
00210
00211 status = statusWidget;
00212
00213
00214 rawImage = QImage( filename );
00215
00216
00217 if(rawImage.isNull()) { return NULL; }
00218
00219
00220 if( rawImage.depth() < 32 ) { rawImage = rawImage.convertDepth( 32, Qt::AutoColor ); }
00221
00222
00223 topLeftExtreme.setX( QMAX( topLeftExtreme.x(), 0 ) );
00224 topLeftExtreme.setY( QMAX( topLeftExtreme.y(), 0 ) );
00225 bottomRightExtreme.setX( QMIN( bottomRightExtreme.x(), rawImage.width()-1 ) );
00226 bottomRightExtreme.setY( QMIN( bottomRightExtreme.y(), rawImage.height()-1 ) );
00227
00228
00229 QString statusMessage = qApp->translate( "removeRedeyeRegions", "Removing Red-Eye:" );
00230 status->showProgressBar( statusMessage, 100 );
00231 qApp->processEvents();
00232
00233
00234 updateIncrement = (int) ( 0.01 *
00235 ( bottomRightExtreme.x() - topLeftExtreme.x() + 1 ) *
00236 ( bottomRightExtreme.y() - topLeftExtreme.y() + 1 ) );
00237 newProgress = 0;
00238
00239
00240 findRegionOfInterest(topLeftExtreme, bottomRightExtreme);
00241
00242
00243 if(topLeft.x() == -1)
00244 {
00245
00246 status->setStatus( "" );
00247 qApp->processEvents();
00248
00249 return NULL;
00250 }
00251
00252
00253
00254
00255
00256 editedImage = new QImage( filename );
00257
00258
00259 if( editedImage == NULL)
00260 {
00261
00262 status->setStatus( "" );
00263 qApp->processEvents();
00264
00265 return NULL;
00266 }
00267
00268
00269 if( editedImage->depth() < 32 )
00270 {
00271 QImage* tmp = editedImage;
00272 editedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) );
00273 delete tmp; tmp=NULL;
00274 }
00275
00276 findBlobs();
00277 sortBlobsByDecreasingSize();
00278 findBestTwoBlobs();
00279
00280
00281 if(id1 != -1)
00282 {
00283 desaturateBlobs();
00284 }
00285
00286 else
00287 {
00288 desaturateEntireImage(topLeftExtreme, bottomRightExtreme);
00289 }
00290
00291
00292 status->setStatus( "" );
00293 qApp->processEvents();
00294
00295
00296 return editedImage;
00297 }
00298
00299
00300
00301
00302 #define MIN_RED_VAL 40
00303
00304
00305 void findRegionOfInterest(QPoint topLeftExtreme, QPoint bottomRightExtreme)
00306 {
00307 topLeft = QPoint(-1,-1);
00308 bottomRight = QPoint(-1,-1);
00309
00310 int x, y;
00311 QRgb* rgb;
00312 uchar* scanLine;
00313 for( y=topLeftExtreme.y(); y<=bottomRightExtreme.y(); y++)
00314 {
00315 scanLine = rawImage.scanLine(y);
00316 for( x=topLeftExtreme.x(); x<=bottomRightExtreme.x(); x++)
00317 {
00318 rgb = ((QRgb*)scanLine+x);
00319
00320 bool threshMet = qRed(*rgb) > 2*qGreen(*rgb) &&
00321 qRed(*rgb) > MIN_RED_VAL;
00322 if(threshMet)
00323 {
00324
00325 if(topLeft.x() == -1)
00326 {
00327 topLeft = QPoint(x,y);
00328 bottomRight = QPoint(x,y);
00329 }
00330
00331 if(x < topLeft.x() ) topLeft.setX( x );
00332 if(y < topLeft.y() ) topLeft.setY( y );
00333 if(x > bottomRight.x() ) bottomRight.setX( x );
00334 if(y > bottomRight.y() ) bottomRight.setY( y );
00335 }
00336
00337
00338 newProgress++;
00339 if(newProgress >= updateIncrement)
00340 {
00341 newProgress = 0;
00342 status->incrementProgress();
00343 qApp->processEvents();
00344 }
00345
00346 }
00347 }
00348 }
00349
00350 void pushPixel(int x, int y, int id)
00351 {
00352
00353 if( x < 0 ||
00354 y < 0 ||
00355 x >= regionWidth ||
00356 y >= regionHeight ||
00357 regionOfInterest[ x + y*regionWidth ] != 1 )
00358 return;
00359
00360
00361 regionOfInterest[ x + y*regionWidth] = id;
00362 spreadablePixels.push( QPoint( x, y ) );
00363
00364
00365 blobPixelCount++;
00366 blobTopLeft.setX( QMIN( x, blobTopLeft.x() ) );
00367 blobTopLeft.setY( QMIN( y, blobTopLeft.y() ) );
00368 blobBottomRight.setX( QMAX( x, blobBottomRight.x() ) );
00369 blobBottomRight.setY( QMAX( y, blobBottomRight.y() ) );
00370 }
00371
00372 void findBlobs()
00373 {
00374
00375 regionWidth = bottomRight.x() - topLeft.x() + 1;
00376 regionHeight = bottomRight.y() - topLeft.y() + 1;
00377 regionOfInterest = new int[ regionWidth * regionHeight ];
00378
00379
00380 int x, y;
00381 int x2, y2;
00382 QRgb* rgb;
00383 uchar* scanLine;
00384 for( y=topLeft.y(); y<=bottomRight.y(); y++)
00385 {
00386 y2 = y - topLeft.y();
00387
00388 scanLine = rawImage.scanLine(y);
00389 for( x=topLeft.x(); x<=bottomRight.x(); x++)
00390 {
00391
00392 x2 = x - topLeft.x();
00393
00394 rgb = ((QRgb*)scanLine+x);
00395
00396 bool threshMet = qRed(*rgb) > 2*qGreen(*rgb) &&
00397 qRed(*rgb) > MIN_RED_VAL;
00398
00399 if(threshMet)
00400 regionOfInterest[ x2 + y2*regionWidth ] = 1;
00401 else
00402 regionOfInterest[ x2 + y2*regionWidth ] = 0;
00403 }
00404 }
00405
00406
00407 int nextValidID = 2;
00408 for(x = 0; x<regionWidth; x++)
00409 {
00410 for(y = 0; y<regionHeight; y++)
00411 {
00412
00413 while( !spreadablePixels.empty() )
00414 {
00415 QPoint point = spreadablePixels.pop();
00416 int id = regionOfInterest[ point.x() + point.y()*regionWidth ];
00417
00418 pushPixel( point.x()-1, point.y()-1, id );
00419 pushPixel( point.x(), point.y()-1, id );
00420 pushPixel( point.x()+1, point.y()-1, id );
00421 pushPixel( point.x()-1, point.y(), id );
00422 pushPixel( point.x()+1, point.y(), id );
00423 pushPixel( point.x()-1, point.y()+1, id );
00424 pushPixel( point.x(), point.y()+1, id );
00425 pushPixel( point.x()+1, point.y()+1, id );
00426 }
00427
00428
00429
00430 if( regionOfInterest[ x + y*regionWidth ] == 1 )
00431 {
00432
00433 if( nextValidID > 2)
00434 {
00435 blobIDs.push( (nextValidID - 1) );
00436 blobSizes.push( blobPixelCount );
00437 blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) /
00438 (blobBottomRight.y() - blobTopLeft.y()+1) );
00439 }
00440
00441 regionOfInterest[x + y*regionWidth] = nextValidID;
00442 pushPixel( x-1, y-1, nextValidID );
00443 pushPixel( x, y-1, nextValidID );
00444 pushPixel( x+1, y-1, nextValidID );
00445 pushPixel( x-1, y, nextValidID );
00446 pushPixel( x+1, y, nextValidID );
00447 pushPixel( x-1, y+1, nextValidID );
00448 pushPixel( x, y+1, nextValidID );
00449 pushPixel( x+1, y+1, nextValidID );
00450 nextValidID++;
00451
00452 blobPixelCount = 1;
00453 blobTopLeft = QPoint( x, y );
00454 blobBottomRight = QPoint( x, y );
00455 }
00456 }
00457 }
00458
00459
00460 if( nextValidID > 2)
00461 {
00462 blobIDs.push( (nextValidID - 1) );
00463 blobSizes.push( blobPixelCount );
00464 blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) / (blobBottomRight.y() - blobTopLeft.y()+1) );
00465 }
00466 }
00467
00468 void sortBlobsByDecreasingSize()
00469 {
00470 blobCount = blobIDs.count();
00471 ids = new int[blobCount];
00472 sizes = new int[blobCount];
00473 ratios = new double[blobCount];
00474
00475 int i,j;
00476 for(i=0; i<blobCount; i++)
00477 {
00478 ids[i] = blobIDs.pop();
00479 sizes[i] = blobSizes.pop();
00480 ratios[i] = blobAspectRatios.pop();
00481 }
00482
00483
00484 for(j = blobCount-1; j>0; j--)
00485 {
00486 for(i=0; i<j; i++)
00487 {
00488 if( sizes[i+1] > sizes[i] )
00489 {
00490 int t = sizes[i+1];
00491 sizes[i+1] = sizes[i];
00492 sizes[i] = t;
00493
00494 t = ids[i+1];
00495 ids[i+1] = ids[i];
00496 ids[i] = t;
00497
00498 double tR = ratios[i+1];
00499 ratios[i+1] = ratios[i];
00500 ratios[i] = tR;
00501 }
00502 }
00503 }
00504 }
00505
00506 void findBestTwoBlobs()
00507 {
00508 id1 = -1;
00509 id2 = -1;
00510 int i;
00511
00512
00513 if(blobCount == 2 &&
00514 sizes[0] > 1 &&
00515 sizes[1] > 1)
00516 {
00517 id1 = ids[0];
00518 id2 = ids[1];
00519 }
00520 else
00521 {
00522 for(i=0; i<blobCount-2; i++)
00523 {
00524
00525 if( sizes[i+1] <= 1 ) break;
00526
00527 double as1 = ratios[i];
00528 double as2 = ratios[i+1];
00529
00530 if(as1 < 1) as1 = 1.0/as1;
00531 if(as2 < 1) as2 = 1.0/as2;
00532
00533 if(
00534 ratios[i] > 0.75 && ratios[i] < 2 &&
00535 ratios[i+1] > 0.75 && ratios[i+1] < 2 &&
00536
00537 QMAX(as2,as1)/QMIN(as2,as1) < 2 &&
00538
00539 ((double)QMAX( sizes[i], sizes[i+1] )) / QMIN( sizes[i], sizes[i+1] ) < 1.5 &&
00540
00541
00542 QMAX( sizes[i], sizes[i+1] ) > 20 )
00543 {
00544 id1 = ids[i];
00545 id2 = ids[i+1];
00546 break;
00547 }
00548 }
00549 }
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559 }
00560
00561 bool IDedPixel( int x, int y)
00562 {
00563 if( x < topLeft.x() || y < topLeft.y() ||
00564 x > bottomRight.x() || y > bottomRight.y() )
00565 return false;
00566
00567 int regionIndex = x - topLeft.x() + (y-topLeft.y())*regionWidth;
00568 return ( regionOfInterest[regionIndex] == id1 ||
00569 regionOfInterest[regionIndex] == id2 );
00570 }
00571
00572 double desaturateAlpha(int x, int y)
00573 {
00574 int n = 0;
00575 if( IDedPixel(x ,y ) ) n++;
00576
00577 if(n == 1)
00578 return 1.0;
00579
00580 if( IDedPixel(x-1,y-1) ) n++;
00581 if( IDedPixel(x ,y-1) ) n++;
00582 if( IDedPixel(x+1,y-1) ) n++;
00583 if( IDedPixel(x-1,y ) ) n++;
00584 if( IDedPixel(x+1,y ) ) n++;
00585 if( IDedPixel(x-1,y+1) ) n++;
00586 if( IDedPixel(x ,y+1) ) n++;
00587 if( IDedPixel(x+1,y+1) ) n++;
00588
00589 if( IDedPixel(x-2,y-2) ) n++;
00590 if( IDedPixel(x-1,y-2) ) n++;
00591 if( IDedPixel(x ,y-2) ) n++;
00592 if( IDedPixel(x+1,y-2) ) n++;
00593 if( IDedPixel(x+2,y-2) ) n++;
00594
00595 if( IDedPixel(x-2,y-1) ) n++;
00596 if( IDedPixel(x+2,y-1) ) n++;
00597 if( IDedPixel(x-2,y ) ) n++;
00598 if( IDedPixel(x+2,y ) ) n++;
00599 if( IDedPixel(x-2,y+1) ) n++;
00600 if( IDedPixel(x+2,y+1) ) n++;
00601
00602 if( IDedPixel(x-2,y+2) ) n++;
00603 if( IDedPixel(x-1,y+2) ) n++;
00604 if( IDedPixel(x ,y+2) ) n++;
00605 if( IDedPixel(x+1,y+2) ) n++;
00606 if( IDedPixel(x+2,y+2) ) n++;
00607
00608
00609 return ((double)n) / 25;
00610 }
00611
00612 void desaturateBlobs()
00613 {
00614
00615 int x, y;
00616 double r;
00617 QRgb* rgb;
00618 uchar* scanLine;
00619 for( y = QMAX( topLeft.y()-1, 0);
00620 y<= QMIN( bottomRight.y()+1, editedImage->height()-1 );
00621 y++)
00622 {
00623 scanLine = editedImage->scanLine(y);
00624 for( x = QMAX( topLeft.x()-1, 0);
00625 x <= QMIN( bottomRight.x()+1, editedImage->width()-1 );
00626 x++)
00627 {
00628 double alpha = desaturateAlpha( x, y );
00629 if( alpha > 0)
00630 {
00631 rgb = ((QRgb*)scanLine+x);
00632
00633 r = alpha*(0.05*qRed(*rgb) + 0.6*qGreen(*rgb) + 0.3*qBlue(*rgb)) +
00634 (1-alpha)*qRed(*rgb);
00635 *rgb = qRgb( (int)r,
00636 qGreen(*rgb),
00637 qBlue(*rgb) );
00638 }
00639 }
00640 }
00641 }
00642
00643 void desaturateEntireImage(QPoint topLeftExtreme, QPoint bottomRightExtreme)
00644 {
00645
00646 int x, y;
00647 QRgb* rgb;
00648 uchar* scanLine;
00649 for( y=topLeftExtreme.y(); y<=bottomRightExtreme.y(); y++)
00650 {
00651 scanLine = editedImage->scanLine(y);
00652 for( x=topLeftExtreme.x(); x<=bottomRightExtreme.x(); x++)
00653 {
00654 rgb = ((QRgb*)scanLine+x);
00655 if( qRed(*rgb) > 2*qGreen(*rgb) )
00656 {
00657 *rgb = qRgb( (int) (0.05*qRed(*rgb) + 0.6*qGreen(*rgb) + 0.3*qBlue(*rgb)),
00658 qGreen(*rgb),
00659 qBlue(*rgb) );
00660 }
00661 }
00662 }
00663 }
00664
00665
00666
00667
00668
00669