AlbumShaper 1.0a3
painting.cpp
Go to the documentation of this file.
1//==============================================
2// copyright : (C) 2003-2005 by Will Stokes
3//==============================================
4// This program is free software; you can redistribute it
5// and/or modify it under the terms of the GNU General
6// Public License as published by the Free Software
7// Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//==============================================
10
11//Systemwide includes
12#include <qimage.h>
13#include <qstring.h>
14#include <qapplication.h>
15#include <math.h>
16
17#define MIN(x,y) ((x) < (y) ? (x) : (y))
18#define MAX(x,y) ((x) < (y) ? (x) : (y))
19
20//Projectwide includes
21#include "painting.h"
22#include "manipulationOptions.h"
24
25//----------------------------------------------
26// Inputs:
27// -------
28// QString filename - location of original image on disk
29// StatusWidget* status - widget for making progress visible to user
30//
31// Outputs:
32// --------
33// QImage* returned - constructed image
34//
35// Description:
36// ------------
37// This method constructs an oil painting version of
38// the image by replacing each pixel with an average of the
39// original pixel color and the most common pixel color within a local radius.
40//
41// A histogram of color values (which fall in the 0-255 range)
42// is constructed at each pixel for all pixels without a given
43// radius. The most commonly occuring red, green, and blue color
44// values are found and used in combination with the current color
45// to produce the oil effect. This is done because in oil painting
46// (and water colors) color bleeds out from a given area across the
47// canvass. By averaging with the most common color in a given
48// neighborhood the larger blobs spread and the higher frequency
49// information (details) fade into the background.
50//
51// TODO:
52// The local area idealy would be circular, but currently is square.
53//
54// TODO:
55// Experiment adaptively adjusting the oil radius using local image contrast measure?
56//
57// TODO:
58// Come up with method for avoiding strange color shifts near object boundaries.
59//----------------------------------------------
60
61//==============================================
62struct Triplet
63{ int r,g,b; };
64//----------------------------------------------
66{
67 //histogram data
69
70 //index of highest count for each component
72};
73//----------------------------------------------
75//----------------------------------------------
77{
78 static int i;
79 for(i=0;i<256;i++)
80 {
81 histogram.values[i].r = 0;
82 histogram.values[i].g = 0;
83 histogram.values[i].b = 0;
84 }
88}
89//----------------------------------------------
91{
92 static int i;
93 for(i = 1; i<256; i++)
94 {
97
100
103 }
104}
105//----------------------------------------------
106QImage* oilPaintingEffect( QString filename, ManipulationOptions* options )
107{
108 //load original image
109 QImage originalImage( filename );
110
111 //convert to 32-bit depth if necessary
112 if( originalImage.depth() < 32 ) { originalImage = originalImage.convertDepth( 32, Qt::AutoColor ); }
113
114//determine if busy indicators will be used
115 bool useBusyIndicators = false;
116 StatusWidget* status = NULL;
117 if( options != NULL && options->getStatus() != NULL )
118 {
119 useBusyIndicators = true;
120 status = options->getStatus();
121 }
122
123 //setup progress bar
124 if(useBusyIndicators)
125 {
126 QString statusMessage = qApp->translate( "oilPaintingEffect", "Applying Oil Painting Effect:" );
127 status->showProgressBar( statusMessage, 100 );
128 qApp->processEvents();
129 }
130
131 //update progress bar for every 1% of completion
132 const int updateIncrement = (int) ( 0.01 * originalImage.width() * originalImage.height() );
133 int newProgress = 0;
134
135 //construct edited image
136 QImage* editedImage = new QImage( filename );
137
138 //convert to 32-bit depth if necessary
139 if( editedImage->depth() < 32 )
140 {
141 QImage* tmp = editedImage;
142 editedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) );
143 delete tmp; tmp=NULL;
144 }
145
146 //compute the radius using image resolution
147 double minDimen = (double) MIN( editedImage->width(), editedImage->height() );
148 const int RADIUS = (int) MAX( 2, (sqrt(minDimen)/4) );
149
150 //iterate over image
151 int originalImageX, originalImageY;
152 int editedImageX, editedImageY;
153 int clampedX, clampedY;
154 int trailingEdgeY, leadingEdgeY;
155
156 QRgb* rgb;
157 uchar* scanLine;
158 uchar* trailingScanLine;
159 uchar* leadingScanLine;
160
161 //iterate over columns
162 for( editedImageX=0; editedImageX < editedImage->width(); editedImageX++)
163 {
164 //------------------
165 //reset histogram object
167 //------------------
168 //fill histogram with data that would have results from Y=-1
169 for(originalImageY = 0 - 1 - RADIUS;
170 originalImageY <= 0 - 1 + RADIUS;
171 originalImageY++)
172 {
173 clampedY = MAX( MIN( originalImageY, originalImage.height() - 1 ), 0 );
174 scanLine = originalImage.scanLine( clampedY );
175
176 for(originalImageX = editedImageX - RADIUS;
177 originalImageX <= editedImageX + RADIUS;
178 originalImageX++)
179 {
180 clampedX = MAX( MIN( originalImageX, originalImage.width() - 1 ), 0 );
181
182 //get rgb value
183 rgb = ((QRgb*)scanLine+clampedX);
184
185 //update counts for this r/g/b value
186 histogram.values[ qRed(*rgb) ].r++;
187 histogram.values[ qGreen(*rgb) ].g++;
188 histogram.values[ qBlue(*rgb) ].b++;
189 } //originalX
190 } //originalY
191 //------------------
192
193 //now iterate over rows by simply removing trailing edge data and adding leading edge data
194 for( editedImageY=0; editedImageY < editedImage->height(); editedImageY++)
195 {
196 trailingEdgeY = MAX( MIN( editedImageY-1-RADIUS, originalImage.height() - 1 ), 0 );
197 leadingEdgeY = MAX( MIN( editedImageY+RADIUS, originalImage.height() - 1 ), 0 );
198
199 trailingScanLine = originalImage.scanLine( trailingEdgeY );
200 leadingScanLine = originalImage.scanLine( leadingEdgeY );
201
202 for(originalImageX = editedImageX - RADIUS;
203 originalImageX <= editedImageX + RADIUS;
204 originalImageX++)
205 {
206 clampedX = MAX( MIN( originalImageX, originalImage.width() - 1 ), 0 );
207
208 //remove trail edge data
209 rgb = ((QRgb*)trailingScanLine+clampedX);
210 histogram.values[ qRed(*rgb) ].r--;
211 histogram.values[ qGreen(*rgb) ].g--;
212 histogram.values[ qBlue(*rgb) ].b--;
213
214 //add leading edge data
215 rgb = ((QRgb*)leadingScanLine+clampedX);
216 histogram.values[ qRed(*rgb) ].r++;
217 histogram.values[ qGreen(*rgb) ].g++;
218 histogram.values[ qBlue(*rgb) ].b++;
219 } //originalX
220
221 //find highest color counts
223
224 //replace each color channel value with average of
225 //current value and most occuring value within neighborhood
226 scanLine = editedImage->scanLine( editedImageY );
227 rgb = ((QRgb*)scanLine+editedImageX);
228 *rgb = qRgb( (qRed(*rgb) + histogram.highestCountIndex.r) / 2,
229 (qGreen(*rgb) + histogram.highestCountIndex.g) / 2,
230 (qBlue(*rgb) + histogram.highestCountIndex.b) / 2 );
231
232
233 //update status bar if significant progress has been made since last update
234 if(useBusyIndicators)
235 {
236 newProgress++;
238 {
239 newProgress = 0;
241 qApp->processEvents();
242 }
243 }
244
245 } //editedImageX
246 } //editedImageY
247
248 //return pointer to edited image
249 return editedImage;
250}
251//==============================================
StatusWidget * getStatus()
void showProgressBar(QString message, int numSteps)
Initializes the progress bar.
void incrementProgress()
Updates the progress bar by one step.
void resetHistogram()
Definition painting.cpp:76
#define MIN(x, y)
Definition painting.cpp:17
void findHighestCounts()
Definition painting.cpp:90
#define MAX(x, y)
Definition painting.cpp:18
QImage * oilPaintingEffect(QString filename, ManipulationOptions *options)
Definition painting.cpp:106
Histogram histogram
Definition painting.cpp:74
int updateIncrement
QImage * editedImage
StatusWidget * status
int newProgress
Triplet highestCountIndex
Definition painting.cpp:71
Triplet values[256]
Definition painting.cpp:68