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

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

References IDedPixel().

Referenced by desaturateBlobs().

◆ desaturateBlobs()

void desaturateBlobs ( )

Definition at line 612 of file redEye.cpp.

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}
double desaturateAlpha(int x, int y)
Definition redEye.cpp:572
QImage * editedImage
QPoint topLeft
QPoint bottomRight

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

Referenced by removeRedeyeRegions().

◆ desaturateEntireImage()

void desaturateEntireImage ( QPoint topLeftExtreme,
QPoint bottomRightExtreme )

Definition at line 643 of file redEye.cpp.

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}

References editedImage.

Referenced by removeRedeyeRegions().

◆ findBestTwoBlobs()

void findBestTwoBlobs ( )

Definition at line 506 of file redEye.cpp.

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 id1
int * ids
double * ratios
int blobCount
int id2
int * sizes

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

Referenced by removeRedeyeRegions().

◆ findBlobs()

void findBlobs ( )

Definition at line 372 of file redEye.cpp.

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) );
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) );
464 blobAspectRatios.push( ((double)(blobBottomRight.x() - blobTopLeft.x()+1)) / (blobBottomRight.y() - blobTopLeft.y()+1) );
465 }
466}
void pushPixel(int x, int y, int id)
Definition redEye.cpp:350
#define MIN_RED_VAL
Definition redEye.cpp:302
Q3ValueStack< double > blobAspectRatios
int regionWidth
QImage rawImage
int * regionOfInterest
Q3ValueStack< int > blobSizes
QPoint blobTopLeft
int blobPixelCount
Q3ValueStack< int > blobIDs
QPoint blobBottomRight
int regionHeight
Q3ValueStack< QPoint > spreadablePixels

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

Referenced by removeRedeyeRegions().

◆ findRegionOfInterest()

void findRegionOfInterest ( QPoint topLeftExtreme,
QPoint bottomRightExtreme )

Definition at line 305 of file redEye.cpp.

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}
void incrementProgress()
Updates the progress bar by one step.
int updateIncrement
StatusWidget * status
int newProgress

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

Referenced by removeRedeyeRegions().

◆ IDedPixel()

bool IDedPixel ( int x,
int y )

Definition at line 561 of file redEye.cpp.

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 ||
570}
int regionIndex(int x, int y)
Definition blur.cpp:227

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

Referenced by desaturateAlpha().

◆ pushPixel()

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

Definition at line 350 of file redEye.cpp.

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
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}

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

Referenced by findBlobs().

◆ removeRedeyeRegions()

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

Definition at line 206 of file redEye.cpp.

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 {
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}
void setStatus(QString message)
Update message.
void showProgressBar(QString message, int numSteps)
Initializes the progress bar.
void sortBlobsByDecreasingSize()
Definition redEye.cpp:468
void desaturateBlobs()
Definition redEye.cpp:612
void findBestTwoBlobs()
Definition redEye.cpp:506
void findRegionOfInterest(QPoint topLeftExtreme, QPoint bottomRightExtreme)
Definition redEye.cpp:305
void findBlobs()
Definition redEye.cpp:372
void desaturateEntireImage(QPoint topLeftExtreme, QPoint bottomRightExtreme)
Definition redEye.cpp:643

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

Referenced by EditingInterface::removeRedeye().

◆ sortBlobsByDecreasingSize()

void sortBlobsByDecreasingSize ( )

Definition at line 468 of file redEye.cpp.

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}

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

Referenced by removeRedeyeRegions().