AlbumShaper  1.0a3
Macros | Functions
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.

Macros

#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)
 

Macro Definition Documentation

§ MIN_RED_VAL

#define MIN_RED_VAL   40

Definition at line 302 of file redEye.cpp.

Referenced by findBlobs(), and findRegionOfInterest().

Function Documentation

§ desaturateAlpha()

double desaturateAlpha ( int  x,
int  y 
)

Definition at line 572 of file redEye.cpp.

References IDedPixel().

Referenced by desaturateBlobs().

573 {
574  int n = 0;
575  if( IDedPixel(x ,y ) ) n++;
576 
577  if(n == 1)
578  return 1.0;
579 
580  if( IDedPixel(x-1,y-1) ) n++;
581  if( IDedPixel(x ,y-1) ) n++;
582  if( IDedPixel(x+1,y-1) ) n++;
583  if( IDedPixel(x-1,y ) ) n++;
584  if( IDedPixel(x+1,y ) ) n++;
585  if( IDedPixel(x-1,y+1) ) n++;
586  if( IDedPixel(x ,y+1) ) n++;
587  if( IDedPixel(x+1,y+1) ) n++;
588 
589  if( IDedPixel(x-2,y-2) ) n++;
590  if( IDedPixel(x-1,y-2) ) n++;
591  if( IDedPixel(x ,y-2) ) n++;
592  if( IDedPixel(x+1,y-2) ) n++;
593  if( IDedPixel(x+2,y-2) ) n++;
594 
595  if( IDedPixel(x-2,y-1) ) n++;
596  if( IDedPixel(x+2,y-1) ) n++;
597  if( IDedPixel(x-2,y ) ) n++;
598  if( IDedPixel(x+2,y ) ) n++;
599  if( IDedPixel(x-2,y+1) ) n++;
600  if( IDedPixel(x+2,y+1) ) n++;
601 
602  if( IDedPixel(x-2,y+2) ) n++;
603  if( IDedPixel(x-1,y+2) ) n++;
604  if( IDedPixel(x ,y+2) ) n++;
605  if( IDedPixel(x+1,y+2) ) n++;
606  if( IDedPixel(x+2,y+2) ) n++;
607 
608 
609  return ((double)n) / 25;
610 }
bool IDedPixel(int x, int y)
Definition: redEye.cpp:561

§ desaturateBlobs()

void desaturateBlobs ( )

Definition at line 612 of file redEye.cpp.

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

Referenced by removeRedeyeRegions().

613 {
614  //desaturate bad pixels
615  int x, y;
616  double r;
617  QRgb* rgb;
618  uchar* scanLine;
619  for( y = QMAX( topLeft.y()-1, 0);
620  y<= QMIN( bottomRight.y()+1, editedImage->height()-1 );
621  y++)
622  {
623  scanLine = editedImage->scanLine(y);
624  for( x = QMAX( topLeft.x()-1, 0);
625  x <= QMIN( bottomRight.x()+1, editedImage->width()-1 );
626  x++)
627  {
628  double alpha = desaturateAlpha( x, y );
629  if( alpha > 0)
630  {
631  rgb = ((QRgb*)scanLine+x);
632 
633  r = alpha*(0.05*qRed(*rgb) + 0.6*qGreen(*rgb) + 0.3*qBlue(*rgb)) +
634  (1-alpha)*qRed(*rgb);
635  *rgb = qRgb( (int)r,
636  qGreen(*rgb),
637  qBlue(*rgb) );
638  } //alpha > 0
639  } //x
640  } //y
641 }
QPoint bottomRight
QPoint topLeft
double desaturateAlpha(int x, int y)
Definition: redEye.cpp:572
QImage * editedImage

§ desaturateEntireImage()

void desaturateEntireImage ( QPoint  topLeftExtreme,
QPoint  bottomRightExtreme 
)

Definition at line 643 of file redEye.cpp.

References editedImage.

Referenced by removeRedeyeRegions().

644 {
645  //desaturate bad pixels
646  int x, y;
647  QRgb* rgb;
648  uchar* scanLine;
649  for( y=topLeftExtreme.y(); y<=bottomRightExtreme.y(); y++)
650  {
651  scanLine = editedImage->scanLine(y);
652  for( x=topLeftExtreme.x(); x<=bottomRightExtreme.x(); x++)
653  {
654  rgb = ((QRgb*)scanLine+x);
655  if( qRed(*rgb) > 2*qGreen(*rgb) )
656  {
657  *rgb = qRgb( (int) (0.05*qRed(*rgb) + 0.6*qGreen(*rgb) + 0.3*qBlue(*rgb)),
658  qGreen(*rgb),
659  qBlue(*rgb) );
660  } // > thresh
661  } //x
662  } //y
663 }
QImage * editedImage

§ findBestTwoBlobs()

void findBestTwoBlobs ( )

Definition at line 506 of file redEye.cpp.

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

Referenced by removeRedeyeRegions().

507 {
508  id1 = -1;
509  id2 = -1;
510  int i;
511 
512  //special case: 2 blobs found, both larger than 1 pixel
513  if(blobCount == 2 &&
514  sizes[0] > 1 &&
515  sizes[1] > 1)
516  {
517  id1 = ids[0];
518  id2 = ids[1];
519  }
520  else
521  {
522  for(i=0; i<blobCount-2; i++)
523  {
524  //once we hit blobs that are only one pixel large stop because they are probably just noise
525  if( sizes[i+1] <= 1 ) break;
526 
527  double as1 = ratios[i];
528  double as2 = ratios[i+1];
529 
530  if(as1 < 1) as1 = 1.0/as1;
531  if(as2 < 1) as2 = 1.0/as2;
532 
533  if( //both blobs must be semi-circular, prefer those that are wider
534  ratios[i] > 0.75 && ratios[i] < 2 &&
535  ratios[i+1] > 0.75 && ratios[i+1] < 2 &&
536  //both blobs must be similar in shape
537  QMAX(as2,as1)/QMIN(as2,as1) < 2 &&
538  //both blobs must be similar in size
539  ((double)QMAX( sizes[i], sizes[i+1] )) / QMIN( sizes[i], sizes[i+1] ) < 1.5 &&
540  //both blobs must be above a certain thresh size, this prevents selecting blobs that are very very tiny
541  //if only tiny blobs are around we'll end up desaturating entire region
542  QMAX( sizes[i], sizes[i+1] ) > 20 )
543  {
544  id1 = ids[i];
545  id2 = ids[i+1];
546  break;
547  }
548  }
549  }
550 
551  //Comment this sectionin to see what blobs were found and selected
552 /* cout << "-----\n";
553  for(i=0; i<blobCount-1; i++)
554  {
555  if( ids[i] == id1 || ids[i] == id2 )
556  cout << "--->";
557  cout << "ID: " << ids[i] << "count: " << sizes[i] << " w:h: " << ratios[i] << "\n";
558  }*/
559 }
int id2
int * ids
int blobCount
int * sizes
double * ratios
int id1

§ findBlobs()

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().

373 {
374  //create small matrix for region of interest
375  regionWidth = bottomRight.x() - topLeft.x() + 1;
376  regionHeight = bottomRight.y() - topLeft.y() + 1;
378 
379  //set all pixels that meet thresh to 1, all others to 0
380  int x, y;
381  int x2, y2;
382  QRgb* rgb;
383  uchar* scanLine;
384  for( y=topLeft.y(); y<=bottomRight.y(); y++)
385  {
386  y2 = y - topLeft.y();
387 
388  scanLine = rawImage.scanLine(y);
389  for( x=topLeft.x(); x<=bottomRight.x(); x++)
390  {
391 
392  x2 = x - topLeft.x();
393 
394  rgb = ((QRgb*)scanLine+x);
395 
396  bool threshMet = qRed(*rgb) > 2*qGreen(*rgb) &&
397  qRed(*rgb) > MIN_RED_VAL;
398 
399  if(threshMet)
400  regionOfInterest[ x2 + y2*regionWidth ] = 1;
401  else
402  regionOfInterest[ x2 + y2*regionWidth ] = 0;
403  }
404  }
405 
406  //walk over region of interest and propogate blobs
407  int nextValidID = 2;
408  for(x = 0; x<regionWidth; x++)
409  {
410  for(y = 0; y<regionHeight; y++)
411  {
412  //if any blobs can be propogated handle them first
413  while( !spreadablePixels.empty() )
414  {
415  QPoint point = spreadablePixels.pop();
416  int id = regionOfInterest[ point.x() + point.y()*regionWidth ];
417 
418  pushPixel( point.x()-1, point.y()-1, id );
419  pushPixel( point.x(), point.y()-1, id );
420  pushPixel( point.x()+1, point.y()-1, id );
421  pushPixel( point.x()-1, point.y(), id );
422  pushPixel( point.x()+1, point.y(), id );
423  pushPixel( point.x()-1, point.y()+1, id );
424  pushPixel( point.x(), point.y()+1, id );
425  pushPixel( point.x()+1, point.y()+1, id );
426  }
427 
428  //if this pixel has met thresh and has not yet been assigned a unique ID,
429  //assign it the next unique id and push all valid neighbors
430  if( regionOfInterest[ x + y*regionWidth ] == 1 )
431  {
432  //print last blob stats
433  if( nextValidID > 2)
434  {
435  blobIDs.push( (nextValidID - 1) );
436  blobSizes.push( blobPixelCount );
437  blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) /
438  (blobBottomRight.y() - blobTopLeft.y()+1) );
439  }
440 
441  regionOfInterest[x + y*regionWidth] = nextValidID;
442  pushPixel( x-1, y-1, nextValidID );
443  pushPixel( x, y-1, nextValidID );
444  pushPixel( x+1, y-1, nextValidID );
445  pushPixel( x-1, y, nextValidID );
446  pushPixel( x+1, y, nextValidID );
447  pushPixel( x-1, y+1, nextValidID );
448  pushPixel( x, y+1, nextValidID );
449  pushPixel( x+1, y+1, nextValidID );
450  nextValidID++;
451 
452  blobPixelCount = 1;
453  blobTopLeft = QPoint( x, y );
454  blobBottomRight = QPoint( x, y );
455  }
456  } //y
457  } //x
458 
459  //insert last blob stats
460  if( nextValidID > 2)
461  {
462  blobIDs.push( (nextValidID - 1) );
463  blobSizes.push( blobPixelCount );
464  blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) / (blobBottomRight.y() - blobTopLeft.y()+1) );
465  }
466 }
QPoint bottomRight
QPoint topLeft
Q3ValueStack< double > blobAspectRatios
Q3ValueStack< int > blobIDs
int * regionOfInterest
QImage rawImage
int regionHeight
Q3ValueStack< QPoint > spreadablePixels
void pushPixel(int x, int y, int id)
Definition: redEye.cpp:350
QPoint blobTopLeft
int blobPixelCount
QPoint blobBottomRight
int regionWidth
Q3ValueStack< int > blobSizes
#define MIN_RED_VAL
Definition: redEye.cpp:302

§ findRegionOfInterest()

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().

306 {
307  topLeft = QPoint(-1,-1);
308  bottomRight = QPoint(-1,-1);
309 
310  int x, y;
311  QRgb* rgb;
312  uchar* scanLine;
313  for( y=topLeftExtreme.y(); y<=bottomRightExtreme.y(); y++)
314  {
315  scanLine = rawImage.scanLine(y);
316  for( x=topLeftExtreme.x(); x<=bottomRightExtreme.x(); x++)
317  {
318  rgb = ((QRgb*)scanLine+x);
319 
320  bool threshMet = qRed(*rgb) > 2*qGreen(*rgb) &&
321  qRed(*rgb) > MIN_RED_VAL;
322  if(threshMet)
323  {
324  //first pixel
325  if(topLeft.x() == -1)
326  {
327  topLeft = QPoint(x,y);
328  bottomRight = QPoint(x,y);
329  }
330 
331  if(x < topLeft.x() ) topLeft.setX( x );
332  if(y < topLeft.y() ) topLeft.setY( y );
333  if(x > bottomRight.x() ) bottomRight.setX( x );
334  if(y > bottomRight.y() ) bottomRight.setY( y );
335  }
336 
337  //update status bar if significant progress has been made since last update
338  newProgress++;
340  {
341  newProgress = 0;
343  qApp->processEvents();
344  }
345 
346  }
347  }
348 }
QPoint bottomRight
int updateIncrement
QPoint topLeft
QImage rawImage
void incrementProgress()
Updates the progress bar by one step.
StatusWidget * status
#define MIN_RED_VAL
Definition: redEye.cpp:302
int newProgress

§ IDedPixel()

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().

562 {
563  if( x < topLeft.x() || y < topLeft.y() ||
564  x > bottomRight.x() || y > bottomRight.y() )
565  return false;
566 
567  int regionIndex = x - topLeft.x() + (y-topLeft.y())*regionWidth;
568  return ( regionOfInterest[regionIndex] == id1 ||
569  regionOfInterest[regionIndex] == id2 );
570 }
QPoint bottomRight
QPoint topLeft
int * regionOfInterest
int regionIndex(int x, int y)
Definition: blur.cpp:227
int id2
int regionWidth
int id1

§ pushPixel()

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().

351 {
352  //if pixel off image or below thresh ignore push attempt
353  if( x < 0 ||
354  y < 0 ||
355  x >= regionWidth ||
356  y >= regionHeight ||
357  regionOfInterest[ x + y*regionWidth ] != 1 )
358  return;
359 
360  //passes! set id and actually put pixel onto stack
361  regionOfInterest[ x + y*regionWidth] = id;
362  spreadablePixels.push( QPoint( x, y ) );
363 
364  //increase blob pixel count and update topLeft and bottomRight
365  blobPixelCount++;
366  blobTopLeft.setX( QMIN( x, blobTopLeft.x() ) );
367  blobTopLeft.setY( QMIN( y, blobTopLeft.y() ) );
368  blobBottomRight.setX( QMAX( x, blobBottomRight.x() ) );
369  blobBottomRight.setY( QMAX( y, blobBottomRight.y() ) );
370 }
int * regionOfInterest
int regionHeight
Q3ValueStack< QPoint > spreadablePixels
QPoint blobTopLeft
int blobPixelCount
QPoint blobBottomRight
int regionWidth

§ removeRedeyeRegions()

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().

209 {
210  //store handle to status widget
211  status = statusWidget;
212 
213  //load original image
214  rawImage = QImage( filename );
215 
216  //sanity check: unable to load image
217  if(rawImage.isNull()) { return NULL; }
218 
219  //convert to 32-bit depth if necessary
220  if( rawImage.depth() < 32 ) { rawImage = rawImage.convertDepth( 32, Qt::AutoColor ); }
221 
222  //sanity check: make sure topLeftExtreme and bottomRightExtreme are within image boundary
223  topLeftExtreme.setX( QMAX( topLeftExtreme.x(), 0 ) );
224  topLeftExtreme.setY( QMAX( topLeftExtreme.y(), 0 ) );
225  bottomRightExtreme.setX( QMIN( bottomRightExtreme.x(), rawImage.width()-1 ) );
226  bottomRightExtreme.setY( QMIN( bottomRightExtreme.y(), rawImage.height()-1 ) );
227 
228  //setup progress bar
229  QString statusMessage = qApp->translate( "removeRedeyeRegions", "Removing Red-Eye:" );
230  status->showProgressBar( statusMessage, 100 );
231  qApp->processEvents();
232 
233  //update progress bar for every 1% of completion
234  updateIncrement = (int) ( 0.01 *
235  ( bottomRightExtreme.x() - topLeftExtreme.x() + 1 ) *
236  ( bottomRightExtreme.y() - topLeftExtreme.y() + 1 ) );
237  newProgress = 0;
238 
239  //find region of interest: constrain search box to boundary that actually contains red enough pixels
240  findRegionOfInterest(topLeftExtreme, bottomRightExtreme);
241 
242  //if no pixels were found then immediately return a NULL pointer signaling no change
243  if(topLeft.x() == -1)
244  {
245  //hide progress bar
246  status->setStatus( "" );
247  qApp->processEvents();
248 
249  return NULL;
250  }
251 
252  //load an editing image
253  //two images mus be loaded becuase pixel values are replaced
254  //using a compbination of niehgbors and their own in order
255  //to avoid sharp lines at the edge of the saturated region
256  editedImage = new QImage( filename );
257 
258  //sanity check: unable to allocated edited image
259  if( editedImage == NULL)
260  {
261  //hide progress bar
262  status->setStatus( "" );
263  qApp->processEvents();
264 
265  return NULL;
266  }
267 
268  //convert to 32-bit depth if necessary
269  if( editedImage->depth() < 32 )
270  {
271  QImage* tmp = editedImage;
272  editedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) );
273  delete tmp; tmp=NULL;
274  }
275 
276  findBlobs();
279 
280  //if we found two good blobs then desaturate those only
281  if(id1 != -1)
282  {
283  desaturateBlobs();
284  }
285  //else desaturate all pixels above thresh within selection area
286  else
287  {
288  desaturateEntireImage(topLeftExtreme, bottomRightExtreme);
289  }
290 
291  //remove status bar
292  status->setStatus( "" );
293  qApp->processEvents();
294 
295  //return pointer to edited image
296  return editedImage;
297 }
int updateIncrement
QPoint topLeft
void findBestTwoBlobs()
Definition: redEye.cpp:506
QImage rawImage
void showProgressBar(QString message, int numSteps)
Initializes the progress bar.
void findRegionOfInterest(QPoint topLeftExtreme, QPoint bottomRightExtreme)
Definition: redEye.cpp:305
void setStatus(QString message)
Update message.
StatusWidget * status
void findBlobs()
Definition: redEye.cpp:372
QImage * editedImage
void sortBlobsByDecreasingSize()
Definition: redEye.cpp:468
int newProgress
void desaturateBlobs()
Definition: redEye.cpp:612
int id1
void desaturateEntireImage(QPoint topLeftExtreme, QPoint bottomRightExtreme)
Definition: redEye.cpp:643

§ sortBlobsByDecreasingSize()

void sortBlobsByDecreasingSize ( )

Definition at line 468 of file redEye.cpp.

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

Referenced by removeRedeyeRegions().

469 {
470  blobCount = blobIDs.count();
471  ids = new int[blobCount];
472  sizes = new int[blobCount];
473  ratios = new double[blobCount];
474 
475  int i,j;
476  for(i=0; i<blobCount; i++)
477  {
478  ids[i] = blobIDs.pop();
479  sizes[i] = blobSizes.pop();
480  ratios[i] = blobAspectRatios.pop();
481  }
482 
483  //quick and dirty bubble sort
484  for(j = blobCount-1; j>0; j--)
485  {
486  for(i=0; i<j; i++)
487  {
488  if( sizes[i+1] > sizes[i] )
489  {
490  int t = sizes[i+1];
491  sizes[i+1] = sizes[i];
492  sizes[i] = t;
493 
494  t = ids[i+1];
495  ids[i+1] = ids[i];
496  ids[i] = t;
497 
498  double tR = ratios[i+1];
499  ratios[i+1] = ratios[i];
500  ratios[i] = tR;
501  }
502  }
503  }
504 }
Q3ValueStack< double > blobAspectRatios
Q3ValueStack< int > blobIDs
int * ids
int blobCount
int * sizes
Q3ValueStack< int > blobSizes
double * ratios