AlbumShaper  1.0a3
emboss.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 "emboss.h"
22 #include "manipulationOptions.h"
23 #include "../tools/imageTools.h"
24 #include "../../gui/statusWidget.h"
25 
26 //----------------------------------------------
27 // Inputs:
28 // -------
29 // QString filename - location of original image on disk
30 // StatusWidget* status - widget for making progress visible to user
31 //
32 // Outputs:
33 // --------
34 // QImage* returned - constructed image
35 //
36 // Description:
37 // ------------
38 // This method constructs an embossed version of
39 // the image by modifying the luminance of each pixel
40 // by taking a weighted average of pixel luminance within
41 // a local neighborhood. Most embossing algorithms convert an
42 // image to grayscale and then combine a pixel's gray value with
43 // its neighbors using the following convolution matrix:
44 //
45 // [ -1 -1 0
46 // -1 1 1
47 // 0 1 1 ]
48 //
49 // This apporach has two problems. First, all color information
50 // is lost. Second, the convolution filter is not a function of image
51 // size, and as a result embossing results on high resolution images are
52 // barely when viewed on a high resolution screen or printed at high DPI.
53 //
54 // My solution to this problem is to:
55 //
56 // 1.) Apply the emboss effect on pixel luminance and leave pixel
57 // color untouched. The varying lightness that results produces
58 // the same effect, and if a grayscale image is really desired the
59 // image can be converted to grayscale either before or after the
60 // emboss effect takes place.
61 //
62 // 2.) using neighboring pixels that are a distance D from the
63 // pixel being modified in the X or Y dimensions. This simply
64 // modification allows the embossing procedure to run at the
65 // same speed (larger convolution matrices would slow the algorithm
66 // to at best O(N^2 * M^2) where there are N^2 pixels in the image
67 // and the convolution matrix is M^2 in size) while providing the
68 // same effective emboss effect at any image resolution. This is
69 // done by computing distance D using the minimum image dimension:
70 //
71 // D = MIN( width, height )
72 //
73 // At each pixel, we compute an average luminance of the 6
74 // neighbors and combine these values with a 50% luminance value for
75 // the pixel in question. This luminance value is used to replace
76 // the current pixel luminance before converting it back to RGB
77 // space and storing in the edited image object.
78 //
79 // Neighbor pixels that are clamped to be on the image plane to
80 // prevent lookups of neighbor pixels with negative coordinates or
81 // coordinates beyond the width/height of the image in question.
82 //----------------------------------------------
83 
84 //==============================================
85 QImage* embossEffect( QString filename, ManipulationOptions* options )
86 {
87  //load original image
88  QImage originalImage( filename );
89 
90  //convert to 32-bit depth if necessary
91  if( originalImage.depth() < 32 ) { originalImage = originalImage.convertDepth( 32, Qt::AutoColor ); }
92 
93  //create edited image
94  QImage* editedImage = new QImage( filename );
95 
96  //convert to 32-bit depth if necessary
97  if( editedImage->depth() < 32 )
98  {
99  QImage* tmp = editedImage;
100  editedImage = new QImage( tmp->convertDepth( 32, Qt::AutoColor ) );
101  delete tmp; tmp=NULL;
102  }
103 
104  //determine if busy indicators will be used
105  bool useBusyIndicators = false;
106  StatusWidget* status = NULL;
107  if( options != NULL && options->getStatus() != NULL )
108  {
109  useBusyIndicators = true;
110  status = options->getStatus();
111  }
112 
113  //setup progress bar
114  if(useBusyIndicators)
115  {
116  QString statusMessage = qApp->translate( "embossEffect", "Applying Emboss Effect:" );
117  status->showProgressBar( statusMessage, 100 );
118  qApp->processEvents();
119  }
120 
121  //update progress bar for every 1% of completion
122  const int updateIncrement = (int) ( 0.01 * originalImage.width() * originalImage.height() );
123  int newProgress = 0;
124 
125  //iterate over each selected scanline
126  int x, y;
127  QRgb* rgb;
128  uchar* scanLine;
129 
130  int yPrev, yNext, xPrev, xNext;
131 
132  //compute the radius using image resolution
133  double minDimen = (double) MIN( editedImage->width(), editedImage->height() );
134  const int embossRadius = (int) MAX( 1, (sqrt(minDimen)/8) );
135 
136  for( y=0; y<editedImage->height(); y++)
137  {
138  scanLine = originalImage.scanLine(y);
139 
140  //compute previous and next y pixel coordinates
141  yPrev = MAX( y-embossRadius, 0 );
142  yNext = MIN( y+embossRadius, editedImage->height() - 1 );
143 
144  //iterate over each selected pixel in scanline
145  for( x=0; x<editedImage->width(); x++)
146  {
147  //compute previous and next x pixel coordinates
148  xPrev = MAX( x-embossRadius, 0 );
149  xNext = MIN( x+embossRadius, editedImage->width() - 1 );
150 
151  //start with a default luminance of 128 (50% luminance)
152  int sum = 128;
153 
154  //sum weighted gray values of neighbors
155  scanLine = originalImage.scanLine( yPrev );
156  sum-= qGray( *((QRgb*)scanLine + xPrev ) );
157  sum-= qGray( *((QRgb*)scanLine + x ) );
158 
159  scanLine = originalImage.scanLine( y );
160  sum-= qGray( *((QRgb*)scanLine + xPrev ) );
161  sum+= qGray( *((QRgb*)scanLine + xNext ) );
162 
163  scanLine = originalImage.scanLine( yNext );
164  sum+= qGray( *((QRgb*)scanLine + x ) );
165  sum+= qGray( *((QRgb*)scanLine + xNext ) );
166 
167  //clamp sum to within 0-255 range
168  sum = MAX( MIN( sum, 255), 0 );
169 
170  //get original pixel color in HSV space
171  scanLine = editedImage->scanLine(y);
172  rgb = ((QRgb*)scanLine+x);
173  double r = ((double)qRed(*rgb) )/255.0;
174  double g = ((double)qGreen(*rgb) )/255.0;
175  double b = ((double)qBlue(*rgb) )/255.0;
176 
177  //convert to hsv
178  double h,s,v;
179  RGBtoHSV(r,g,b,&h,&s,&v);
180 
181  //reset v
182  v = ((double)sum)/255;
183 
184  //convert adjusted color back to rgb colorspace and clamp
185  HSVtoRGB( &r,&g,&b, h,s,v);
186  int rp = (int) MIN( MAX((r*255), 0), 255 );
187  int gp = (int) MIN( MAX((g*255), 0), 255 );
188  int bp = (int) MIN( MAX((b*255), 0), 255 );
189 
190  //set adjusted color value
191  *rgb = qRgb(rp,gp,bp);
192 
193  //update status bar if significant progress has been made since last update
194  if(useBusyIndicators)
195  {
196  newProgress++;
197  if(newProgress >= updateIncrement)
198  {
199  newProgress = 0;
200  status->incrementProgress();
201  qApp->processEvents();
202  }
203  }
204 
205  }
206  }
207 
208  //return pointer to edited image
209  return editedImage;
210 }
211 //==============================================
int updateIncrement
#define MIN(x, y)
Definition: emboss.cpp:17
void HSVtoRGB(double *r, double *g, double *b, double h, double s, double v)
Convert a HSV color triplet to RGB.
Definition: imageTools.cpp:264
StatusWidget * getStatus()
void incrementProgress()
Updates the progress bar by one step.
void showProgressBar(QString message, int numSteps)
Initializes the progress bar.
long b
Definition: jpegInternal.h:125
StatusWidget * status
QImage * embossEffect(QString filename, ManipulationOptions *options)
Definition: emboss.cpp:85
void RGBtoHSV(double r, double g, double b, double *h, double *s, double *v)
Convert a RGB color triplet to HSV.
Definition: imageTools.cpp:231
QImage * editedImage
#define MAX(x, y)
Definition: emboss.cpp:18
int newProgress