Main Page | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members

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 Sun Mar 4 19:42:56 2007 for AlbumShaper by doxygen 1.3.7