pointillism.cpp

Go to the documentation of this file.
00001 //==============================================
00002 //  copyright            : (C) 2003-2005 by Will Stokes
00003 //==============================================
00004 //  This program is free software; you can redistribute it
00005 //  and/or modify it under the terms of the GNU General
00006 //  Public License as published by the Free Software
00007 //  Foundation; either version 2 of the License, or
00008 //  (at your option) any later version.
00009 //==============================================
00010 
00011 //Systemwide includes
00012 #include <qimage.h>
00013 #include <qstring.h>
00014 #include <cstdlib>
00015 #include <time.h>
00016 
00017 //Projectwide includes
00018 #include "pointillism.h"
00019 #include "blackWhite.h"
00020 #include "manipulationOptions.h"
00021 
00022 //----------------------------------------------
00023 // Inputs:
00024 // -------
00025 // QString filename   - location of original image on disk
00026 // StatusWidget* status - widget for making progress visible to user
00027 //
00028 // Outputs:
00029 // --------
00030 // QImage* returned - constructed image
00031 //
00032 // Description:
00033 // ------------
00034 // This method constructs an image using a pointillism approach using luminance
00035 // data from the original image.
00036 //
00037 // This effect is under heavy development and will be documented 
00038 // in full when it is complete.
00039 //----------------------------------------------
00040 
00041 //==============================================
00042 void pickRandomPixelWithinBlock( int width, int height, 
00043                                  int blockX, int blockY,
00044                                  int BLOCK_SIZE,
00045                                  int &x, int &y )
00046 {
00047   int dx = rand() % BLOCK_SIZE;
00048   int dy = rand() % BLOCK_SIZE;
00049   x = blockX*BLOCK_SIZE + dx;
00050   y = blockY*BLOCK_SIZE + dy;
00051   
00052   if(x < 0) x = 0;
00053   if(y < 0) y = 0;
00054   if(x >= width ) x = width-1;
00055   if(y >= height) y = height-1;  
00056 }
00057 //----------------------------------------------
00058 bool pixelValid( QImage* image, int x, int y )
00059 {
00060   return (
00061           x >= 0 &&
00062           y >= 0 &&
00063           x < image->width() &&
00064           x < image->height() );
00065 }
00066 //----------------------------------------------
00067 double computeLocalGrayVal( QImage* image, int x, int y )
00068 {
00069   int weights[3][3] = { {1,2,1}, {2,4,2}, {1,2,1} };
00070   
00071   int divisorSum = 0;
00072   double sum = 0;
00073   int xp, yp;
00074   for(yp = QMAX( y-1, 0); yp < QMIN( image->height()-1, y+1 ); yp++)
00075   {
00076     uchar* scanLine = image->scanLine(yp);
00077     
00078     for(xp = QMAX( x-1, 0); xp< QMIN( image->width()-1, x+1 ); xp++)
00079     {
00080       //compute dx and dy values
00081       int dx = xp - x;
00082       int dy = yp - y;
00083       
00084       //compute weight index
00085       int weightX = dx+1;
00086       int weightY = dy+1;
00087       
00088       //update sum and divisor count      
00089       sum+= (weights[weightX][weightY] * qGray( *((QRgb*)scanLine+xp) ) );
00090       divisorSum+= weights[weightX][weightY];
00091     }
00092   }
00093   
00094   //return weighted average  
00095   return sum/divisorSum; 
00096 }
00097 //----------------------------------------------
00098 void drawDotAt( QImage* image, int x, int y, int )
00099 {
00100   //TODO: antialias over grid, for now
00101   //just update this pixel value
00102   uchar* scanLine = image->scanLine(y);
00103   QRgb* rgb = ((QRgb*)scanLine+x);
00104   int red = qRed(*rgb);
00105   red = (int) (0.6*red);
00106   *rgb = qRgb( red, red, red);
00107 }
00108 //----------------------------------------------
00109 QImage* pointillismEffect( QString filename, ManipulationOptions* )
00110 {
00111   //intialize seed using current time
00112   srand( unsigned(time(NULL)) );
00113 
00114   //load original image and convert to grayscale
00115   QImage* originalImage = blackWhiteEffect( filename, NULL );
00116   
00117   //construct edited image
00118   QImage* editedImage = new QImage( originalImage->width(),
00119                                     originalImage->height(),
00120                                     originalImage->depth() );
00121 
00122   //fill with white since we'll be drawing black/color dots on top
00123   editedImage->fill( qRgb(255,255,255) );
00124 
00125   //break image into BLOCK_SIZE x BLOCK_SIZE blocks. iterate over
00126   //each block and pick a random pixel within. Local
00127   //average gray value in edited image is > originalImage + thresh
00128   //then draw a dot at pixel. continue doing this for each block
00129   //and repeat until ???
00130   const int BLOCK_SIZE = 8;
00131 
00132   //compute image size in blocks
00133   int blocksWide = editedImage->width() / BLOCK_SIZE;
00134   if(blocksWide*BLOCK_SIZE < editedImage->width())
00135   { blocksWide++; }
00136   
00137   int blocksTall = editedImage->height() / BLOCK_SIZE;
00138   if(blocksTall*BLOCK_SIZE < editedImage->height())
00139   { blocksTall++; }
00140 
00141   //iterate over image say 100 times, we'll need to fix this outer loop to be smarter?
00142   int bx,by,x,y;
00143   for(int i=0; i<10; i++)
00144   {
00145     //iterate over all blocks
00146     for(bx=0; bx<blocksWide; bx++)
00147     {
00148       for(by=0; by<blocksTall; by++)
00149       {
00150         //pick random pixel within block
00151         pickRandomPixelWithinBlock( editedImage->width(),
00152                                     editedImage->height(),
00153                                     bx, by,
00154                                     BLOCK_SIZE,
00155                                     x, y );
00156         
00157         double curGrayVal = computeLocalGrayVal( editedImage, x, y );
00158         double goalGrayVal = computeLocalGrayVal( originalImage, x, y );
00159 
00160         //too bright -> draw dot
00161         if( curGrayVal > goalGrayVal )
00162         { drawDotAt( editedImage, x, y, 5 ); }
00163       }
00164     }
00165   }
00166 
00167   //free grayscale form of original image
00168   delete originalImage;
00169   originalImage = NULL;
00170   
00171   //return pointer to edited image
00172   return editedImage;      
00173 }
00174 //==============================================

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