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:

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 302 of file redEye.cpp.

Referenced by findBlobs(), and findRegionOfInterest().


Function Documentation

double desaturateAlpha ( int  x,
int  y 
)

Definition at line 572 of file redEye.cpp.

References IDedPixel().

Referenced by desaturateBlobs().

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 }

void desaturateBlobs (  ) 

Definition at line 612 of file redEye.cpp.

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

Referenced by removeRedeyeRegions().

00613 {
00614   //desaturate bad pixels
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       } //alpha > 0
00639     } //x
00640   } //y  
00641 }

void desaturateEntireImage ( QPoint  topLeftExtreme,
QPoint  bottomRightExtreme 
)

Definition at line 643 of file redEye.cpp.

References editedImage.

Referenced by removeRedeyeRegions().

00644 {
00645   //desaturate bad pixels
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       } // > thresh
00661     } //x
00662   } //y
00663 }

void findBestTwoBlobs (  ) 

Definition at line 506 of file redEye.cpp.

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

Referenced by removeRedeyeRegions().

00507 {
00508   id1 = -1;
00509   id2 = -1;
00510   int i;
00511   
00512   //special case: 2 blobs found, both larger than 1 pixel
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       //once we hit blobs that are only one pixel large stop because they are probably just noise
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( //both blobs must be semi-circular, prefer those that are wider
00534           ratios[i] > 0.75 &&   ratios[i] < 2 &&
00535           ratios[i+1] > 0.75 && ratios[i+1] < 2 &&
00536           //both blobs must be similar in shape
00537           QMAX(as2,as1)/QMIN(as2,as1) < 2 &&
00538           //both blobs must be similar in size
00539           ((double)QMAX( sizes[i], sizes[i+1] )) / QMIN( sizes[i], sizes[i+1] ) < 1.5 &&
00540           //both blobs must be above a certain thresh size, this prevents selecting blobs that are very very tiny
00541           //if only tiny blobs are around we'll end up desaturating entire region
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   //Comment this sectionin to see what blobs were found and selected
00552 /* cout << "-----\n";
00553   for(i=0; i<blobCount-1; i++)
00554   {
00555     if( ids[i] == id1 || ids[i] == id2 )
00556       cout << "--->";
00557     cout << "ID: " << ids[i] << "count: " << sizes[i] << " w:h: " << ratios[i] << "\n";      
00558   }*/
00559 }

void findBlobs (  ) 

Definition at line 372 of file redEye.cpp.

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

Referenced by removeRedeyeRegions().

00373 {
00374   //create small matrix for region of interest
00375   regionWidth = bottomRight.x() - topLeft.x() + 1;
00376   regionHeight = bottomRight.y() - topLeft.y() + 1;  
00377   regionOfInterest = new int[ regionWidth * regionHeight ];
00378   
00379   //set all pixels that meet thresh to 1, all others to 0
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   //walk over region of interest and propogate blobs
00407   int nextValidID = 2;
00408   for(x = 0; x<regionWidth; x++)
00409   {
00410     for(y = 0; y<regionHeight; y++)
00411     {
00412       //if any blobs can be propogated handle them first
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       //if this pixel has met thresh and has not yet been assigned a unique ID,
00429       //assign it the next unique id and push all valid neighbors
00430       if( regionOfInterest[ x + y*regionWidth ] == 1 )
00431       {
00432         //print last blob stats
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     } //y
00457   } //x
00458   
00459   //insert last blob stats
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 }

void findRegionOfInterest ( QPoint  topLeftExtreme,
QPoint  bottomRightExtreme 
)

Definition at line 305 of file redEye.cpp.

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

Referenced by removeRedeyeRegions().

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         //first pixel
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       //update status bar if significant progress has been made since last update
00338       newProgress++;
00339       if(newProgress >= updateIncrement)
00340       {
00341         newProgress = 0;
00342         status->incrementProgress();
00343         qApp->processEvents();  
00344       }
00345       
00346     }
00347   }  
00348 }

bool IDedPixel ( int  x,
int  y 
)

Definition at line 561 of file redEye.cpp.

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

Referenced by desaturateAlpha().

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 }

void pushPixel ( int  x,
int  y,
int  id 
)

Definition at line 350 of file redEye.cpp.

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

Referenced by findBlobs().

00351 {
00352   //if pixel off image or below thresh ignore push attempt
00353   if(  x < 0  || 
00354        y <  0 ||
00355        x >= regionWidth ||
00356        y >= regionHeight ||
00357        regionOfInterest[ x + y*regionWidth ] != 1 )
00358     return;
00359   
00360   //passes! set id and actually put pixel onto stack
00361   regionOfInterest[ x + y*regionWidth] = id;  
00362   spreadablePixels.push( QPoint( x, y ) );
00363   
00364   //increase blob pixel count and update topLeft and bottomRight
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 }

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

Definition at line 206 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().

00209 {
00210   //store handle to status widget
00211   status = statusWidget;
00212   
00213   //load original image
00214   rawImage = QImage( filename );
00215   
00216   //sanity check: unable to load image
00217   if(rawImage.isNull()) { return NULL; }
00218 
00219   //convert to 32-bit depth if necessary
00220   if( rawImage.depth() < 32 ) { rawImage = rawImage.convertDepth( 32, Qt::AutoColor ); }
00221    
00222   //sanity check: make sure topLeftExtreme and bottomRightExtreme are within image boundary
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   //setup progress bar
00229   QString statusMessage = qApp->translate( "removeRedeyeRegions", "Removing Red-Eye:" );
00230   status->showProgressBar( statusMessage, 100 );
00231   qApp->processEvents();  
00232   
00233   //update progress bar for every 1% of completion
00234   updateIncrement = (int) ( 0.01 * 
00235                             ( bottomRightExtreme.x() - topLeftExtreme.x() + 1 ) *
00236                             ( bottomRightExtreme.y() - topLeftExtreme.y() + 1 ) );
00237   newProgress = 0;   
00238 
00239   //find region of interest: constrain search box to boundary that actually contains red enough pixels
00240   findRegionOfInterest(topLeftExtreme, bottomRightExtreme);
00241 
00242   //if no pixels were found then immediately return a NULL pointer signaling no change
00243   if(topLeft.x() == -1) 
00244   { 
00245     //hide progress bar
00246     status->setStatus( "" );
00247     qApp->processEvents();
00248 
00249     return NULL; 
00250   }
00251 
00252   //load an editing image
00253   //two images mus be loaded becuase pixel values are replaced
00254   //using a compbination of niehgbors and their own in order
00255   //to avoid sharp lines at the edge of the saturated region
00256   editedImage = new QImage( filename );
00257   
00258   //sanity check: unable to allocated edited image
00259   if( editedImage == NULL) 
00260   { 
00261     //hide progress bar
00262     status->setStatus( "" );
00263     qApp->processEvents();
00264 
00265     return NULL; 
00266   }
00267 
00268   //convert to 32-bit depth if necessary
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   //if we found two good blobs then desaturate those only
00281   if(id1 != -1)
00282   {
00283     desaturateBlobs();
00284   }
00285   //else desaturate all pixels above thresh within selection area
00286   else
00287   {
00288     desaturateEntireImage(topLeftExtreme, bottomRightExtreme);
00289   }
00290 
00291   //remove status bar
00292   status->setStatus( "" );
00293   qApp->processEvents();
00294 
00295   //return pointer to edited image
00296   return editedImage;      
00297 }

void sortBlobsByDecreasingSize (  ) 

Definition at line 468 of file redEye.cpp.

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

Referenced by removeRedeyeRegions().

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   //quick and dirty bubble sort
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 }


Generated on Wed Jan 24 05:38:30 2007 for AlbumShaper by  doxygen 1.5.1