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