#include <qimage.h>
#include <qstring.h>
#include <qapplication.h>
#include <cstdlib>
#include <time.h>
#include <math.h>
#include "mosaic.h"
#include "manipulationOptions.h"
#include "../tools/imageTools.h"
#include "../../gui/statusWidget.h"
#include <iostream>
Include dependency graph for mosaic.cpp:

Go to the source code of this file.
Namespaces | |
| namespace | std |
Classes | |
| struct | Tile |
| struct | TileSet |
Defines | |
| #define | MAX_TILES 216 |
Functions | |
| void | constructColorTiles (QSize tileSize) |
| void | constructImageTiles (QStringList files, QSize tileSize) |
| void | splatBestTile (QImage *image, QPoint topLeftCorner, TileSet *tileSet) |
| QImage * | mosaicEffect (QString filename, MosaicOptions *options) |
Variables | |
| TileSet | colorTiles |
| TileSet | imageTiles |
|
|
Definition at line 256 of file mosaic.cpp. Referenced by constructColorTiles(). |
|
|
Definition at line 375 of file mosaic.cpp. References Tile::avgColor, Tile::avgL, Tile::avgS, b, colorTiles, Tile::image, MAX_TILES, TileSet::numInitialized, and TileSet::tiles. Referenced by mosaicEffect(). 00376 {
00377 //max tiles must be allocated across all colors, so find resolution we'll have for each color
00378 //channel (e.g. if max tiles is 100, 100^(1/3) ~= 4.6 so we'll use 4 unique red, green, and
00379 //blue color values for constructing tiles and use 4^3=64 tiles out of the 100 allocated
00380 int colorRes = (int)pow( MAX_TILES, 1.0/3 );
00381
00382 //always include 0 and 255 so increment is always totalSpan/(count-1)
00383 int colorIncrement = 255 / (colorRes-1);
00384
00385 colorIncrement = 51;
00386
00387 //create actual tiles
00388 int tile=0;
00389 int r,g,b;
00390 for(r=0; r<=255; r+=colorIncrement)
00391 {
00392 for(g=0; g<=255; g+=colorIncrement)
00393 {
00394 for(b=0; b<=255; b+=colorIncrement)
00395 {
00396 colorTiles.tiles[tile].image.create( tileSize.width(), tileSize.height(), 32);
00397 colorTiles.tiles[tile].image.fill( qRgb(r, g, b) );
00398
00399 colorTiles.tiles[tile].avgColor = QColor(r,g,b);
00400
00401 int h;
00402 QColor(r,g,b).getHsv( &h, &(colorTiles.tiles[tile].avgS), &(colorTiles.tiles[tile].avgL) );
00403 tile++;
00404 }
00405 }
00406 }
00407
00408 //setup number of initialized tiles
00409 colorTiles.numInitialized = tile;
00410 }
|
|
||||||||||||
|
Definition at line 413 of file mosaic.cpp. References Tile::avgColor, Tile::avgL, Tile::avgS, getImageSize(), Tile::image, imageTiles, TileSet::numInitialized, scaleImage(), and TileSet::tiles. Referenced by mosaicEffect(). 00414 {
00415 //---------------------------------
00416 //setup number of initialized tiles
00417 imageTiles.numInitialized = QMIN(files.size(), MAX_TILES);
00418 //---------------------------------
00419 //create file index list, we'll use this to construct a
00420 //list of indices to the randomply picked files from the master list
00421 int* fileIndices = new int[imageTiles.numInitialized];
00422 int* fileIndicesUsed = new int[files.size()];
00423 int i;
00424 for(i=0; i<imageTiles.numInitialized; i++) { fileIndices[i] = -1; }
00425 for(i=0; i<((int)files.size()); i++) { fileIndicesUsed[i] = 0; }
00426 //---------------------------------
00427 //pick the random files, updating the file indices list
00428 for(i=0; i<imageTiles.numInitialized; i++)
00429 {
00430 double percentage = ((double)rand()) / RAND_MAX;
00431 int fileNum = (int) ( (files.size() - (i+1)) * percentage);
00432
00433 //correct index by offsetting by all files that have been picked before this one
00434 int j = 0;
00435 int realFileNum = fileNum;
00436 while( fileNum >= 0)
00437 {
00438 if( fileIndicesUsed[j] == 1 ) { realFileNum++; }
00439 else { fileNum--; }
00440
00441 j++;
00442 }
00443
00444 //record file index into list
00445 fileIndices[i] = realFileNum;
00446 fileIndicesUsed[realFileNum] = 1;
00447 }
00448
00449 //---------------------------------
00450 //sort the file index list - bubble sort is fast enough right? :-)
00451 int j;
00452 for( i=imageTiles.numInitialized-1; i>0; i--)
00453 {
00454 for( j=0; j<i; j++)
00455 {
00456 if( fileIndices[j] > fileIndices[j+1] )
00457 {
00458 int tmp = fileIndices[j+1];
00459 fileIndices[j+1] = fileIndices[j];
00460 fileIndices[j] = tmp;
00461 }
00462 }
00463 }
00464 //---------------------------------
00465 //construct truncated list of files that we'll use
00466 QStringList chosenFiles;
00467 QStringList::iterator it;
00468 int curFileIndex = 0;
00469 int nextDesiredFileIndex = 0;
00470 for(it = files.begin(); it != files.end(); it++ )
00471 {
00472 if( curFileIndex == fileIndices[nextDesiredFileIndex] )
00473 {
00474 chosenFiles.append( *it );
00475 nextDesiredFileIndex++;
00476
00477 if( nextDesiredFileIndex >= imageTiles.numInitialized ) break;
00478 }
00479
00480 curFileIndex++;
00481 }
00482
00483 //resetting numInitialized should not be necessary, we should have the right
00484 //number of files in chosenFiles, but as a sanity check, we'll reset it here again.
00485 imageTiles.numInitialized = QMIN((int)chosenFiles.size(), imageTiles.numInitialized);
00486
00487 //---------------------------------
00488 //free up the temporary index list, it's nolonger needed since we now have an
00489 //actual list of the chosen files
00490 delete fileIndices;
00491 delete fileIndicesUsed;
00492 fileIndices = NULL;
00493 fileIndicesUsed = NULL;
00494 //---------------------------------
00495 //ok, we now have a list of files we actually want to use to create tiles from, that have
00496 //been randomly chosen from the huge list we were given. now actually create the tiles
00497 int tile = 0;
00498
00499 for(it = chosenFiles.begin(); it != chosenFiles.end(); it++ )
00500 {
00501 //scale image to definately fill a tileSizeW x tileSizeH region, we'll crop down afterwards
00502 QSize imageRes;
00503 getImageSize( *it, imageRes );
00504
00505 int intermediateWidth = -1;
00506 int intermediateHeight = -1;
00507 if( ((double)imageRes.width()) / tileSize.width() > ((double)imageRes.height()) / tileSize.height() )
00508 {
00509 intermediateHeight = tileSize.height();
00510 intermediateWidth = (int) ( ((1.0*intermediateHeight*imageRes.width()) / imageRes.height()) + 0.5 );
00511 }
00512 else
00513 {
00514 intermediateWidth = tileSize.width();
00515 intermediateHeight = (int) ( ((1.0*intermediateWidth*imageRes.height()) / imageRes.width()) + 0.5 );
00516 }
00517
00518 QImage scaledImage;
00519 scaleImage( *it, scaledImage, intermediateWidth, intermediateHeight );
00520
00521 //scaleImage does not like to scale more than 2x, so if image is not the right size scale it up again
00522 if( scaledImage.width() != tileSize.width() || scaledImage.height() != tileSize.height() )
00523 scaledImage = scaledImage.scale( tileSize, QImage::ScaleFree );
00524
00525 //construct tile image
00526 imageTiles.tiles[tile].image.create( tileSize.width(), tileSize.height(), 32);
00527 imageTiles.tiles[tile].image.fill( qRgb(255,255,255) );
00528
00529 //crop scaledimage to tileSizeW x tileSizeH - simultaniously compute statistics about tile
00530 int xOffset = (scaledImage.width() - tileSize.width())/2;
00531 int yOffset = (scaledImage.height() - tileSize.height())/2;
00532 int x, y;
00533 uchar* scaledScanLine;
00534 uchar* croppedScanLine;
00535 QRgb* scaledRgb;
00536 QRgb* croppedRgb;
00537
00538 double avgR=0; double avgG=0; double avgB=0;
00539 double avgS=0; double avgL=0;
00540
00541 //sometimes corrupt images can get through, so this check
00542 //bulletproofs the code
00543 if( scaledImage.isNull() )
00544 {
00545 avgR = avgG = avgB = 255;
00546 avgS = avgL = 255;
00547 }
00548 else
00549 {
00550 for( y=0; y<tileSize.height(); y++)
00551 {
00552 scaledScanLine = scaledImage.scanLine(y + yOffset);
00553 croppedScanLine = imageTiles.tiles[tile].image.scanLine(y);
00554
00555 for( x=0; x<tileSize.width(); x++)
00556 {
00557 scaledRgb = ((QRgb*) scaledScanLine) +x + xOffset;
00558 croppedRgb = ((QRgb*) croppedScanLine) + x;
00559
00560 //copy pixel color over
00561 *croppedRgb = *scaledRgb;
00562
00563 //update statistics
00564 QColor color( *croppedRgb );
00565
00566 avgR += color.red();
00567 avgG += color.green();
00568 avgB += color.blue();
00569
00570 int h,s,l;
00571 color.getHsv( &h, &s, &l );
00572 avgS += s;
00573 avgL += l;
00574 }
00575 }
00576
00577 //average red, green, blue, saturation, and luminance sums
00578 int pixelCount = tileSize.width()*tileSize.height();
00579 avgR /= pixelCount;
00580 avgG /= pixelCount;
00581 avgB /= pixelCount;
00582 avgS /= pixelCount;
00583 avgL /= pixelCount;
00584 }
00585 //store statistics
00586 imageTiles.tiles[tile].avgColor = QColor( (int)avgR, (int)avgG, (int)avgB );
00587 imageTiles.tiles[tile].avgS = (int)avgS;
00588 imageTiles.tiles[tile].avgL = (int)avgL;
00589
00590 //move on to next tile
00591 tile++;
00592 }
00593 //---------------------------------
00594 }
|
|
||||||||||||
|
Definition at line 290 of file mosaic.cpp. References constructColorTiles(), constructImageTiles(), editedImage, MosaicOptions::getFileList(), ManipulationOptions::getStatus(), MosaicOptions::getTileSize(), StatusWidget::incrementProgress(), newProgress, StatusWidget::showProgressBar(), splatBestTile(), status, and updateIncrement. Referenced by EditingInterface::applyEffect(). 00291 {
00292 //load image
00293 QImage* editedImage = new QImage( filename );
00294
00295 //convert to 32-bit depth if necessary
00296 if( editedImage->depth() < 32 )
00297 {
00298 QImage* tmp = editedImage;
00299 editedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) );
00300 delete tmp; tmp=NULL;
00301 }
00302
00303 //determine if busy indicators will be used
00304 bool useBusyIndicators = false;
00305 StatusWidget* status = NULL;
00306 if( options != NULL && options->getStatus() != NULL )
00307 {
00308 useBusyIndicators = true;
00309 status = options->getStatus();
00310 }
00311
00312 //intialize seed using current time
00313 srand( unsigned(time(NULL)) );
00314
00315 //determine tile size
00316 QSize tileSize;
00317 if(options == NULL) tileSize = QSize(6,6); //6 is big enough to be visible, but not so blocky the image looks bad
00318 else tileSize =options->getTileSize();
00319
00320 //construct tile set
00321 TileSet* tileSet = NULL;
00322 if( options != NULL && options->getFileList().size() > 0 )
00323 {
00324 constructImageTiles(options->getFileList(), tileSize);
00325 tileSet = &imageTiles;
00326 }
00327 else
00328 {
00329 constructColorTiles(tileSize);
00330 tileSet = &colorTiles;
00331 }
00332
00333 //setup progress bar
00334 if(useBusyIndicators)
00335 {
00336 QString statusMessage = qApp->translate( "mosaicEffect", "Applying Mosaic Effect:" );
00337 status->showProgressBar( statusMessage, 100 );
00338 qApp->processEvents();
00339 }
00340
00341 //update progress bar for every 1% of completion
00342 const int updateIncrement = (int) ( (0.01 * editedImage->width() * editedImage->height()) /
00343 (tileSize.width() * tileSize.height()) );
00344 int newProgress = 0;
00345
00346 //iterate over each selected scanline
00347 int x, y;
00348 for(y=0; y<editedImage->height(); y+=tileSize.height())
00349 {
00350 for( x=0; x<editedImage->width(); x+=tileSize.width())
00351 {
00352 //splat the best tile
00353 splatBestTile( editedImage, QPoint(x,y), tileSet );
00354
00355 //update status bar if significant progress has been made since last update
00356 if(useBusyIndicators)
00357 {
00358 newProgress++;
00359 if(newProgress >= updateIncrement)
00360 {
00361 newProgress = 0;
00362 status->incrementProgress();
00363 qApp->processEvents();
00364 }
00365 }
00366
00367 }
00368 }
00369
00370 //return pointer to edited image
00371 return editedImage;
00372 }
|
|
||||||||||||||||
|
Definition at line 598 of file mosaic.cpp. References Tile::avgColor, Tile::avgL, Tile::avgS, Tile::image, TileSet::numInitialized, and TileSet::tiles. Referenced by mosaicEffect(). 00599 {
00600 int x, y;
00601 QRgb* imageRgb;
00602 QRgb* tileRgb;
00603 uchar* imageScanLine;
00604 uchar* tileScanLine;
00605 //------------------------------
00606 //dermine boundary we'll be iterating over
00607 int xMin = 0;
00608 int xMax = QMIN( tileSet->tiles[0].image.width(), image->width() - topLeftCorner.x() );
00609 int yMin = 0;
00610 int yMax = QMIN( tileSet->tiles[0].image.height(), image->height() - topLeftCorner.y() );
00611 //------------------------------
00612 //find most common hue, and average color, saturation and luminance for this portion of the image
00613 double avgR=0; double avgG=0; double avgB=0;
00614 int hueHist[361];
00615 int i;
00616 for(i=0; i<361; i++) { hueHist[i] = 0; }
00617 double avgS=0; double avgL=0;
00618
00619 for( y=yMin; y<yMax; y++)
00620 {
00621 imageScanLine = image->scanLine(y+topLeftCorner.y());
00622 for( x=xMin; x<xMax; x++)
00623 {
00624 imageRgb = ((QRgb*)imageScanLine+x+topLeftCorner.x());
00625 QColor color( *imageRgb );
00626
00627 avgR += color.red();
00628 avgG += color.green();
00629 avgB += color.blue();
00630
00631 int h,s,l;
00632 color.getHsv( &h, &s, &l );
00633 hueHist[ QMIN( QMAX(h,0), 360 ) ]++;
00634 avgS += s;
00635 avgL += l;
00636 }
00637 }
00638
00639 //average red, green, blue, saturation, and luminance sums
00640 int pixelCount = (yMax-yMin) * (xMax-xMin);
00641 avgR /= pixelCount;
00642 avgG /= pixelCount;
00643 avgB /= pixelCount;
00644 avgS /= pixelCount;
00645 avgL /= pixelCount;
00646
00647 //walk through hue histogram and find most common hue
00648 int mostCommonHue = 0;
00649 for(i=1; i<361; i++)
00650 {
00651 if( hueHist[i] > hueHist[mostCommonHue] ) { mostCommonHue = i; }
00652 }
00653
00654 //------------------------------
00655 //compute distance between this region and all initialized tiles
00656 double* distances = new double[tileSet->numInitialized];
00657
00658 double dR, dG, dB;
00659 double rBar;
00660 for(i=0; i<tileSet->numInitialized; i++)
00661 {
00662 dR = tileSet->tiles[i].avgColor.red() - avgR;
00663 dG = tileSet->tiles[i].avgColor.green() - avgG;
00664 dB = tileSet->tiles[i].avgColor.blue() - avgB;
00665 rBar = 0.5* (tileSet->tiles[i].avgColor.red() + avgR);
00666
00667 //we could find the distance between this region and the tile by comparing the colors
00668 //directly as 3d points (sqrt(dR*dR + dG*dG + dB*dB)) but this would not
00669 //take into account their reltive perceptual weights. I found
00670 //some work by Thiadmer Riemersma that suggest I use this equation instead...
00671 //http://www.compuphase.com/cmetric.htm
00672 distances[i] = ((2+(rBar/256)) * dR * dR) +
00673 (4 * dG * dG) +
00674 ((2 + ((255.0-rBar)/256)) * dB * dB);
00675 }
00676 //------------------------------
00677 //pick tile using pseudo-random distance biased approach
00678
00679 //take reciprocol of all distances and find sum
00680 double sum = 0;
00681 double epsilon = 0.000000001;
00682 for(i=0; i<tileSet->numInitialized; i++)
00683 {
00684 distances[i] = 1.0 / QMAX(distances[i], epsilon);
00685 sum += distances[i];
00686 }
00687
00688 //get a random number and find appropriate tile
00689 double percentage = ((double)rand()) / RAND_MAX;
00690 double number = sum * percentage;
00691 int TILE = 0;
00692 sum = 0;
00693 for(i =0; i<tileSet->numInitialized; i++)
00694 {
00695 sum += distances[i];
00696 if( sum >= number)
00697 {
00698 TILE = i; break;
00699 }
00700 }
00701
00702 delete distances;
00703 distances = NULL;
00704 //------------------------------
00705 //determine saturation and luminance multipliers
00706 double sInc = avgS - tileSet->tiles[TILE].avgS;
00707 double lInc = avgL - tileSet->tiles[TILE].avgL;
00708 //------------------------------
00709
00710 //finally, splat the tile
00711 for( y=yMin; y<yMax; y++ )
00712 {
00713 //iterate over each selected pixel in scanline
00714 imageScanLine = image->scanLine( (y+topLeftCorner.y()) );
00715 tileScanLine = tileSet->tiles[TILE].image.scanLine(y);
00716 for( x=xMin; x<xMax; x++)
00717 {
00718 //get the tile color
00719 tileRgb = ((QRgb*) tileScanLine) + x;;
00720 QColor color( *tileRgb );
00721
00722 //convert to hsl
00723 int h,s,l;
00724 color.getHsv( &h, &s, &l );
00725
00726 //replace hue with the most common hue from this region of the target image
00727 h = mostCommonHue;
00728
00729 //adjust saturation and luminance to more closely match the average values
00730 //found in this region of the target image.
00731 s = (int)QMIN( QMAX( s+sInc, 0), 255 );
00732 l = (int)QMIN( QMAX( l+lInc, 0), 255 );
00733
00734 //convert back to rgb
00735 color.setHsv( mostCommonHue, s, l );
00736
00737 //splat the adjusted tile color onto the image
00738 imageRgb = ((QRgb*)imageScanLine) + x + topLeftCorner.x();
00739
00740 *imageRgb = color.rgb();
00741 }
00742 }
00743
00744 }
|
|
|
Definition at line 282 of file mosaic.cpp. Referenced by constructColorTiles(). |
|
|
Definition at line 283 of file mosaic.cpp. Referenced by constructImageTiles(). |
1.3.9.1