Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Class Members | File Members

redEye.cpp File Reference

#include <qimage.h>
#include <qstring.h>
#include <qapplication.h>
#include "redEye.h"
#include "redEye_internal.h"
#include "../../gui/statusWidget.h"

Include dependency graph for redEye.cpp:

Include dependency graph

Go to the source code of this file.

Defines

#define MIN_RED_VAL   40

Functions

QImage * removeRedeyeRegions (QString filename, QPoint topLeftExtreme, QPoint bottomRightExtreme, StatusWidget *statusWidget)
void findRegionOfInterest (QPoint topLeftExtreme, QPoint bottomRightExtreme)
void pushPixel (int x, int y, int id)
void findBlobs ()
void sortBlobsByDecreasingSize ()
void findBestTwoBlobs ()
bool IDedPixel (int x, int y)
double desaturateAlpha (int x, int y)
void desaturateBlobs ()
void desaturateEntireImage (QPoint topLeftExtreme, QPoint bottomRightExtreme)


Define Documentation

#define MIN_RED_VAL   40
 

Definition at line 107 of file redEye.cpp.


Function Documentation

double desaturateAlpha int  x,
int  y
 

Definition at line 376 of file redEye.cpp.

References IDedPixel().

Referenced by desaturateBlobs().

00377 {
00378   int n = 0;
00379   if( IDedPixel(x  ,y  ) ) n++;
00380   
00381   if(n == 1)
00382     return 1.0;
00383   
00384   if( IDedPixel(x-1,y-1) ) n++;
00385   if( IDedPixel(x  ,y-1) ) n++;
00386   if( IDedPixel(x+1,y-1) ) n++;
00387   if( IDedPixel(x-1,y  ) ) n++;
00388   if( IDedPixel(x+1,y  ) ) n++;
00389   if( IDedPixel(x-1,y+1) ) n++;
00390   if( IDedPixel(x  ,y+1) ) n++;
00391   if( IDedPixel(x+1,y+1) ) n++;
00392   
00393   if( IDedPixel(x-2,y-2) ) n++;
00394   if( IDedPixel(x-1,y-2) ) n++;
00395   if( IDedPixel(x  ,y-2) ) n++;
00396   if( IDedPixel(x+1,y-2) ) n++;
00397   if( IDedPixel(x+2,y-2) ) n++;
00398   
00399   if( IDedPixel(x-2,y-1) ) n++;
00400   if( IDedPixel(x+2,y-1) ) n++;
00401   if( IDedPixel(x-2,y  ) ) n++;
00402   if( IDedPixel(x+2,y  ) ) n++;
00403   if( IDedPixel(x-2,y+1) ) n++;
00404   if( IDedPixel(x+2,y+1) ) n++;
00405   
00406   if( IDedPixel(x-2,y+2) ) n++;
00407   if( IDedPixel(x-1,y+2) ) n++;
00408   if( IDedPixel(x  ,y+2) ) n++;
00409   if( IDedPixel(x+1,y+2) ) n++;
00410   if( IDedPixel(x+2,y+2) ) n++;
00411   
00412   
00413   return ((double)n) / 25;
00414 }

void desaturateBlobs  ) 
 

Definition at line 416 of file redEye.cpp.

References bottomRight, desaturateAlpha(), editedImage, and topLeft.

Referenced by removeRedeyeRegions().

00417 {
00418   //desaturate bad pixels
00419   int x, y;
00420   double r;
00421   QRgb* rgb;
00422   uchar* scanLine;
00423   for( y = QMAX( topLeft.y()-1, 0); 
00424        y<= QMIN( bottomRight.y()+1, editedImage->height()-1 ); 
00425        y++)
00426   {
00427     scanLine = editedImage->scanLine(y);
00428     for( x =  QMAX( topLeft.x()-1, 0); 
00429          x <= QMIN( bottomRight.x()+1, editedImage->width()-1 ); 
00430          x++)
00431     {      
00432       double alpha = desaturateAlpha( x, y );
00433       if( alpha > 0)
00434       {
00435         rgb = ((QRgb*)scanLine+x);
00436         
00437         r = alpha*(0.05*qRed(*rgb) + 0.6*qGreen(*rgb) + 0.3*qBlue(*rgb)) +
00438           (1-alpha)*qRed(*rgb);
00439         *rgb = qRgb( (int)r,
00440                      qGreen(*rgb),
00441                      qBlue(*rgb) );
00442       } //alpha > 0
00443     } //x
00444   } //y  
00445 }

void desaturateEntireImage QPoint  topLeftExtreme,
QPoint  bottomRightExtreme
 

Definition at line 447 of file redEye.cpp.

References editedImage.

Referenced by removeRedeyeRegions().

00448 {
00449   //desaturate bad pixels
00450   int x, y;
00451   QRgb* rgb;
00452   uchar* scanLine;
00453   for( y=topLeftExtreme.y(); y<=bottomRightExtreme.y(); y++)
00454   {
00455     scanLine = editedImage->scanLine(y);
00456     for( x=topLeftExtreme.x(); x<=bottomRightExtreme.x(); x++)
00457     {
00458       rgb = ((QRgb*)scanLine+x);
00459       if( qRed(*rgb) > 2*qGreen(*rgb) )
00460       {
00461         *rgb = qRgb( (int) (0.05*qRed(*rgb) + 0.6*qGreen(*rgb) + 0.3*qBlue(*rgb)),
00462                      qGreen(*rgb),
00463                      qBlue(*rgb) );
00464       } // > thresh
00465     } //x
00466   } //y
00467 }

void findBestTwoBlobs  ) 
 

Definition at line 310 of file redEye.cpp.

References blobCount, id1, id2, ids, ratios, and sizes.

Referenced by removeRedeyeRegions().

00311 {
00312   id1 = -1;
00313   id2 = -1;
00314   int i;
00315   
00316   //special case: 2 blobs found, both larger than 1 pixel
00317   if(blobCount == 2 &&
00318      sizes[0] > 1 &&
00319      sizes[1] > 1)
00320   {
00321     id1 = ids[0];
00322     id2 = ids[1];
00323   }
00324   else
00325   {
00326     for(i=0; i<blobCount-2; i++)
00327     {
00328       //once we hit blobs that are only one pixel large stop because they are probably just noise
00329       if( sizes[i+1] <= 1 ) break;
00330       
00331       double as1 = ratios[i];
00332       double as2 = ratios[i+1];
00333 
00334       if(as1 < 1) as1 = 1.0/as1;
00335       if(as2 < 1) as2 = 1.0/as2;
00336       
00337       if( //both blobs must be semi-circular, prefer those that are wider
00338           ratios[i] > 0.75 &&   ratios[i] < 2 &&
00339           ratios[i+1] > 0.75 && ratios[i+1] < 2 &&
00340           //both blobs must be similar in shape
00341           QMAX(as2,as1)/QMIN(as2,as1) < 2 &&
00342           //both blobs must be similar in size
00343           ((double)QMAX( sizes[i], sizes[i+1] )) / QMIN( sizes[i], sizes[i+1] ) < 1.5 &&
00344           //both blobs must be above a certain thresh size, this prevents selecting blobs that are very very tiny
00345           //if only tiny blobs are around we'll end up desaturating entire region
00346           QMAX( sizes[i], sizes[i+1] ) > 20 )
00347       {
00348         id1 = ids[i];
00349         id2 = ids[i+1];
00350         break;
00351       }    
00352     }
00353   }
00354   
00355   //Comment this sectionin to see what blobs were found and selected
00356 /* cout << "-----\n";
00357   for(i=0; i<blobCount-1; i++)
00358   {
00359     if( ids[i] == id1 || ids[i] == id2 )
00360       cout << "--->";
00361     cout << "ID: " << ids[i] << "count: " << sizes[i] << " w:h: " << ratios[i] << "\n";      
00362   }*/
00363 }

void findBlobs  ) 
 

Definition at line 177 of file redEye.cpp.

References blobAspectRatios, blobBottomRight, blobIDs, blobPixelCount, blobSizes, blobTopLeft, bottomRight, pushPixel(), rawImage, regionHeight, regionOfInterest, regionWidth, spreadablePixels, and topLeft.

Referenced by removeRedeyeRegions().

00178 {
00179   //create small matrix for region of interest
00180   regionWidth = bottomRight.x() - topLeft.x() + 1;
00181   regionHeight = bottomRight.y() - topLeft.y() + 1;  
00182   regionOfInterest = new int[ regionWidth * regionHeight ];
00183   
00184   //set all pixels that meet thresh to 1, all others to 0
00185   int x, y;
00186   int x2, y2;
00187   QRgb* rgb;
00188   uchar* scanLine;
00189   for( y=topLeft.y(); y<=bottomRight.y(); y++)
00190   {
00191     y2 = y - topLeft.y();
00192     
00193     scanLine = rawImage.scanLine(y);
00194     for( x=topLeft.x(); x<=bottomRight.x(); x++)
00195     {
00196     
00197       x2 = x - topLeft.x();
00198       
00199       rgb = ((QRgb*)scanLine+x);
00200       
00201       bool threshMet = qRed(*rgb) > 2*qGreen(*rgb) &&
00202                        qRed(*rgb) > MIN_RED_VAL;
00203       
00204       if(threshMet)
00205         regionOfInterest[ x2 + y2*regionWidth ] = 1;
00206       else
00207         regionOfInterest[ x2 + y2*regionWidth ] = 0;
00208     }
00209   } 
00210   
00211   //walk over region of interest and propogate blobs
00212   int nextValidID = 2;
00213   for(x = 0; x<regionWidth; x++)
00214   {
00215     for(y = 0; y<regionHeight; y++)
00216     {
00217       //if any blobs can be propogated handle them first
00218       while( !spreadablePixels.empty() )
00219       {
00220         QPoint point = spreadablePixels.pop();
00221         int id = regionOfInterest[ point.x() + point.y()*regionWidth ];
00222         
00223         pushPixel( point.x()-1, point.y()-1, id );
00224         pushPixel( point.x(),   point.y()-1, id );
00225         pushPixel( point.x()+1, point.y()-1, id );
00226         pushPixel( point.x()-1, point.y(), id );
00227         pushPixel( point.x()+1, point.y(), id );
00228         pushPixel( point.x()-1, point.y()+1, id );
00229         pushPixel( point.x(),   point.y()+1, id );
00230         pushPixel( point.x()+1, point.y()+1, id );
00231       }
00232       
00233       //if this pixel has met thresh and has not yet been assigned a unique ID,
00234       //assign it the next unique id and push all valid neighbors
00235       if( regionOfInterest[ x + y*regionWidth ] == 1 )
00236       {
00237         //print last blob stats
00238         if( nextValidID > 2)
00239         {
00240           blobIDs.push( (nextValidID - 1) );
00241           blobSizes.push( blobPixelCount );
00242           blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) / (blobBottomRight.y() - blobTopLeft.y()+1) );
00243         }
00244         
00245         regionOfInterest[x + y*regionWidth] = nextValidID;
00246         pushPixel( x-1, y-1, nextValidID );
00247         pushPixel( x,   y-1, nextValidID );
00248         pushPixel( x+1, y-1, nextValidID );
00249         pushPixel( x-1, y, nextValidID );
00250         pushPixel( x+1, y, nextValidID );
00251         pushPixel( x-1, y+1, nextValidID );
00252         pushPixel( x,   y+1, nextValidID );
00253         pushPixel( x+1, y+1, nextValidID );
00254         nextValidID++;        
00255         
00256         blobPixelCount = 1;
00257         blobTopLeft = QPoint( x, y );
00258         blobBottomRight = QPoint( x, y );
00259       }
00260     } //y
00261   } //x
00262   
00263   //insert last blob stats
00264   if( nextValidID > 2)
00265   {
00266     blobIDs.push( (nextValidID - 1) );
00267     blobSizes.push( blobPixelCount );
00268     blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) / (blobBottomRight.y() - blobTopLeft.y()+1) );
00269   }
00270 }

void findRegionOfInterest QPoint  topLeftExtreme,
QPoint  bottomRightExtreme
 

Definition at line 110 of file redEye.cpp.

References bottomRight, StatusWidget::incrementProgress(), newProgress, rawImage, status, and topLeft.

Referenced by removeRedeyeRegions().

00111 {
00112   topLeft = QPoint(-1,-1);
00113   bottomRight = QPoint(-1,-1);
00114   
00115   int x, y;
00116   QRgb* rgb;
00117   uchar* scanLine;
00118   for( y=topLeftExtreme.y(); y<=bottomRightExtreme.y(); y++)
00119   {
00120     scanLine = rawImage.scanLine(y);
00121     for( x=topLeftExtreme.x(); x<=bottomRightExtreme.x(); x++)
00122     {
00123       rgb = ((QRgb*)scanLine+x);
00124       
00125       bool threshMet = qRed(*rgb) > 2*qGreen(*rgb) &&
00126         qRed(*rgb) > MIN_RED_VAL;
00127       if(threshMet)
00128       {
00129         //first pixel
00130         if(topLeft.x() == -1) 
00131         {
00132           topLeft = QPoint(x,y);
00133           bottomRight = QPoint(x,y);
00134         }
00135         
00136         if(x < topLeft.x() ) topLeft.setX( x );
00137         if(y < topLeft.y() ) topLeft.setY( y );
00138         if(x > bottomRight.x() ) bottomRight.setX( x );
00139         if(y > bottomRight.y() ) bottomRight.setY( y );
00140       }
00141       
00142       //update status bar if significant progress has been made since last update
00143       newProgress++;
00144       if(newProgress >= updateIncrement)
00145       {
00146         newProgress = 0;
00147         status->incrementProgress();
00148         qApp->processEvents();  
00149       }
00150       
00151     }
00152   }  
00153 }

bool IDedPixel int  x,
int  y
 

Definition at line 365 of file redEye.cpp.

References bottomRight, id1, regionIndex(), regionOfInterest, and topLeft.

Referenced by desaturateAlpha().

00366 {
00367   if( x < topLeft.x() || y < topLeft.y() ||
00368       x > bottomRight.x() || y > bottomRight.y() )
00369     return false;
00370   
00371   int regionIndex = x - topLeft.x() + (y-topLeft.y())*regionWidth;
00372   return ( regionOfInterest[regionIndex] == id1 ||
00373            regionOfInterest[regionIndex] == id2 );
00374 }

void pushPixel int  x,
int  y,
int  id
 

Definition at line 155 of file redEye.cpp.

References blobBottomRight, blobPixelCount, blobTopLeft, regionHeight, regionOfInterest, regionWidth, and spreadablePixels.

Referenced by findBlobs().

00156 {
00157   //if pixel off image or below thresh ignore push attempt
00158   if(  x < 0  || 
00159        y <  0 ||
00160        x >= regionWidth ||
00161        y >= regionHeight ||
00162        regionOfInterest[ x + y*regionWidth ] != 1 )
00163     return;
00164   
00165   //passes! set id and actually put pixel onto stack
00166   regionOfInterest[ x + y*regionWidth] = id;  
00167   spreadablePixels.push( QPoint( x, y ) );
00168   
00169   //increase blob pixel count and update topLeft and bottomRight
00170   blobPixelCount++;
00171   blobTopLeft.setX( QMIN( x, blobTopLeft.x() ) );
00172   blobTopLeft.setY( QMIN( y, blobTopLeft.y() ) );
00173   blobBottomRight.setX( QMAX( x, blobBottomRight.x() ) );
00174   blobBottomRight.setY( QMAX( y, blobBottomRight.y() ) );
00175 }

QImage* removeRedeyeRegions QString  filename,
QPoint  topLeftExtreme,
QPoint  bottomRightExtreme,
StatusWidget statusWidget
 

Definition at line 22 of file redEye.cpp.

References desaturateBlobs(), desaturateEntireImage(), editedImage, findBestTwoBlobs(), findBlobs(), findRegionOfInterest(), id1, newProgress, rawImage, StatusWidget::setStatus(), StatusWidget::showProgressBar(), sortBlobsByDecreasingSize(), status, topLeft, and updateIncrement.

Referenced by EditingInterface::removeRedeye().

00025 {
00026   //store handle to status widget
00027   status = statusWidget;
00028   
00029   //load original image
00030   rawImage = QImage( filename );
00031   
00032   //sanity check: unable to load image
00033   if(rawImage.isNull()) { return NULL; }
00034 
00035   //sanity check: make sure topLeftExtreme and bottomRightExtreme are within image boundary
00036   topLeftExtreme.setX( QMAX( topLeftExtreme.x(), 0 ) );
00037   topLeftExtreme.setY( QMAX( topLeftExtreme.y(), 0 ) );
00038   bottomRightExtreme.setX( QMIN( bottomRightExtreme.x(), rawImage.width()-1 ) );
00039   bottomRightExtreme.setY( QMIN( bottomRightExtreme.y(), rawImage.height()-1 ) );
00040 
00041   //setup progress bar
00042   QString statusMessage = qApp->translate( "removeRedeyeRegions", "Removing Red-Eye:" );
00043   status->showProgressBar( statusMessage, 100 );
00044   qApp->processEvents();  
00045   
00046   //update progress bar for every 1% of completion
00047   updateIncrement = (int) ( 0.01 * 
00048                             ( bottomRightExtreme.x() - topLeftExtreme.x() + 1 ) *
00049                             ( bottomRightExtreme.y() - topLeftExtreme.y() + 1 ) );
00050   newProgress = 0;   
00051 
00052   //find region of interest: constrain search box to boundary that actually contains red enough pixels
00053   findRegionOfInterest(topLeftExtreme, bottomRightExtreme);
00054 
00055   //if no pixels were found then immediately return a NULL pointer signaling no change
00056   if(topLeft.x() == -1) 
00057   { 
00058     //hide progress bar
00059     status->setStatus( "" );
00060     qApp->processEvents();
00061 
00062     return NULL; 
00063   }
00064 
00065   //load an editing image
00066   //two images mus be loaded becuase pixel values are replaced
00067   //using a compbination of niehgbors and their own in order
00068   //to avoid sharp lines at the edge of the saturated region
00069   editedImage = new QImage( filename );
00070   
00071   //sanity check: unable to allocated edited image
00072   if( editedImage == NULL) 
00073   { 
00074     //hide progress bar
00075     status->setStatus( "" );
00076     qApp->processEvents();
00077 
00078     return NULL; 
00079   }
00080 
00081   findBlobs();
00082   sortBlobsByDecreasingSize();
00083   findBestTwoBlobs();
00084 
00085   //if we found two good blobs then desaturate those only
00086   if(id1 != -1)
00087   {
00088     desaturateBlobs();
00089   }
00090   //else desaturate all pixels above thresh within selection area
00091   else
00092   {
00093     desaturateEntireImage(topLeftExtreme, bottomRightExtreme);
00094   }
00095 
00096   //remove status bar
00097   status->setStatus( "" );
00098   qApp->processEvents();
00099 
00100   //return pointer to edited image
00101   return editedImage;      
00102 }

void sortBlobsByDecreasingSize  ) 
 

Definition at line 272 of file redEye.cpp.

References blobAspectRatios, blobCount, blobIDs, blobSizes, ids, ratios, and sizes.

Referenced by removeRedeyeRegions().

00273 {
00274   blobCount = blobIDs.count();
00275   ids = new int[blobCount];
00276   sizes = new int[blobCount];
00277   ratios = new double[blobCount];
00278   
00279   int i,j;
00280   for(i=0; i<blobCount; i++)
00281   {
00282     ids[i] = blobIDs.pop();
00283     sizes[i] = blobSizes.pop();
00284     ratios[i] = blobAspectRatios.pop();
00285   }
00286   
00287   //quick and dirty bubble sort
00288   for(j = blobCount-1; j>0; j--)
00289   {
00290     for(i=0; i<j; i++)
00291     {
00292       if( sizes[i+1] > sizes[i] )
00293       {
00294         int t = sizes[i+1];
00295         sizes[i+1] = sizes[i];
00296         sizes[i] = t;
00297         
00298         t = ids[i+1];
00299         ids[i+1] = ids[i];
00300         ids[i] = t;
00301         
00302         double tR = ratios[i+1];
00303         ratios[i+1] = ratios[i];
00304         ratios[i] = tR;        
00305       }
00306     }
00307   }
00308 }


Generated on Sat Apr 2 05:44:34 2005 for AlbumShaper by  doxygen 1.3.9.1