1 /* Panorama_Tools - Generate, Edit and Convert Panoramic Images
2 Copyright (C) 1998,1999 - Helmut Dersch der@fh-furtwangen.de
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this software; see the file COPYING. If not, a copy
16 can be downloaded from http://www.gnu.org/licenses/gpl.html, or
17 obtained by writing to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 /*------------------------------------------------------------*/
21
22 //begin Rik's mask-from-focus hacking (ZComb <stuff>)
23
24 // This is experimental code introduced by Rik Littlefield,
25 // rj.littlefield@computer.org . See postings on ptX archives
26 // http://www.email-lists.org/pipermail/ptx/
27 // and Panorama Tools forum
28 // http://groups.yahoo.com/group/PanoTools/
29 // on 6/18/2004 and surrounding for more info.
30
31 #include "filter.h"
32 #include "ZComb.h"
33
34 #define ZCOMBLOGFILENAME "zcom_log.txt"
35
36 static struct { // ZComb parameters
37 int enabled;
38 int passNum;
39 int fnameSet;
40 int width;
41 int height;
42 int currentImageNum;
43 char firstFname[1024]; // name of first file
44 float *accumFocus; // array width*height of estimated focus accumulated across all images
45 float *estFocus; // array width*height of estimated focus for current image
46 int *bestLevel; // array width*height of image number of best focus
47 int masktype;
48 int focusWindowHalfwidth;
49 int smoothingWindowHalfwidth;
50 } ZComb;
51
ZCombSetDisabled()52 void ZCombSetDisabled() {
53 ZComb.enabled = 0;
54 }
55
ZCombSetEnabled()56 void ZCombSetEnabled() {
57 ZComb.enabled = 1;
58 ZComb.masktype = 2;
59 ZComb.focusWindowHalfwidth = 4;
60 ZComb.smoothingWindowHalfwidth = 4;
61 }
62
ZCombSetMaskType(int mt)63 void ZCombSetMaskType(int mt) {
64 ZComb.masktype = mt;
65 }
66
ZCombSetFocusWindowHalfwidth(int fwh)67 void ZCombSetFocusWindowHalfwidth(int fwh) {
68 ZComb.focusWindowHalfwidth = fwh;
69 }
70
ZCombSetSmoothingWindowHalfwidth(int swh)71 void ZCombSetSmoothingWindowHalfwidth(int swh) {
72 ZComb.smoothingWindowHalfwidth = swh;
73 }
74
ZCombLogMsg(char * fmt,char * sarg)75 void ZCombLogMsg(char *fmt, char *sarg) {
76 FILE* logfile;
77 char* logfileName = ZCOMBLOGFILENAME;
78 if ((logfile = fopen(logfileName, "a")) == NULL) {
79 PrintError("can't open %s\n", logfileName);
80 return;
81 }
82 fprintf (logfile,fmt,sarg);
83 fclose (logfile);
84 }
85
ZCombInit()86 void ZCombInit() {
87 ZComb.fnameSet = 0;
88 ZComb.passNum = 1;
89 ZComb.currentImageNum = 0;
90 }
91
ZCombInitStats(int width,int height)92 int ZCombInitStats(int width, int height) {
93 int row, col;
94 ZComb.width = width;
95 ZComb.height = height;
96 if (ZComb.accumFocus != NULL) {
97 free(ZComb.accumFocus);
98 free(ZComb.estFocus);
99 free(ZComb.bestLevel);
100 }
101 ZComb.accumFocus = malloc(width*height*sizeof(float));
102 ZComb.estFocus = malloc(width*height*sizeof(float));
103 ZComb.bestLevel = malloc(width*height*sizeof(int));
104 if (ZComb.accumFocus == NULL || ZComb.estFocus == NULL || ZComb.bestLevel == NULL) {
105 PrintError("Not enough memory");
106 ZCombLogMsg("Insufficient memory in ZCombInitStats\n",NULL);
107 return -1;
108 }
109 for (row=0; row < height; row++) {
110 for (col=0; col < width; col++) {
111 ZComb.accumFocus[row*width+col] = 0.0;
112 ZComb.bestLevel[row*width+col] = 1;
113 }
114 }
115 return 0;
116 }
117
118 #define VARIANCEFUDGE 0.0 // increase in variance that we require to say that we have better focus.
119 // early experiments show better results with variance fudge = 0,
120 // so this is not (yet) brought out to be runtime settable
ZCombAccumEstFocus()121 void ZCombAccumEstFocus() {
122 int row, col;
123 int width = ZComb.width;
124 int height = ZComb.height;
125 for (row=0; row < height; row++) {
126 for (col=0; col < width; col++) {
127 if (ZComb.accumFocus[row*width+col] < ZComb.estFocus[row*width+col] - VARIANCEFUDGE) {
128 ZComb.accumFocus[row*width+col] = ZComb.estFocus[row*width+col];
129 ZComb.bestLevel[row*width+col] = ZComb.currentImageNum;
130 }
131 }
132 }
133 }
134
ZCombGetSmoothedLevel(int row,int col)135 float ZCombGetSmoothedLevel(int row, int col) {
136 int n; /* number of pixels used in kernel */
137 int sumLevels;
138 int kr, kc;
139 int height = ZComb.height;
140 int width = ZComb.width;
141 int khw = ZComb.smoothingWindowHalfwidth;
142
143 sumLevels = 0;
144 n = 0;
145 for (kr = row-khw; kr <= row+khw; kr++) {
146 for (kc = col-khw; kc <= col+khw; kc++) {
147 if (kr < 0 || kr >= height || kc < 0 || kc >= width) {
148 continue; /* Out of bounds */
149 } else {
150 sumLevels += ZComb.bestLevel[kr*width+kc];
151 n++;
152 }
153 }
154 }
155 if (n == 0) {
156 PrintError("ZCombGetSmoothedLevel: n==0");
157 return 0.0;
158 }
159 return (float)sumLevels / (float)n;
160 }
161
162 /* Estimate focus for current image,
163 produce result as float array in ZComb.estFocus.
164 In this version, the estimated focus is computed from whatever
165 pixel values are sitting in "red" channel of the ARGB
166 input image. These appear to actually be the red channel of
167 the RGB image, although the green and blue channels are not
168 those of the RGB data. The estimate is the local variance.
169 */
ZCombEstimateFocus(Image * im)170 void ZCombEstimateFocus(Image *im) {
171 int row;
172 int col;
173 #define KERNELSPARSENESS 1 // make larger than 1 to run faster but more approximate
174 int n; /* number of pixels used in kernel */
175 uint8_t *pg; /* pointer to gray (red) value */
176 uint8_t g; /* gray value */
177 uint8_t *pa; /* pointer to alpha value */
178 int sumg, sumgsq; /* sum of gray values and gray values squared */
179 int ming, maxg; /* minimum and maximum gray levels in each region */
180 int kr, kc;
181 int height = im->height;
182 int width = im->width;
183 int khw = ZComb.focusWindowHalfwidth; /* kernel halfwidth */
184
185 for (row = 0; row < height; row++) {
186 for (col = 0; col < width; col++) {
187 sumg = 0;
188 sumgsq = 0;
189 n = 0;
190 ming = 256;
191 maxg = 0;
192 for (kr = row-khw; kr <= row+khw; kr+=KERNELSPARSENESS) {
193 for (kc = col-khw; kc <= col+khw; kc+=KERNELSPARSENESS) {
194 if (kr < 0 || kr >= height || kc < 0 || kc >= width) {
195 continue; /* Out of bounds */
196 } else {
197 pg = *(im->data) + kr*(im->bytesPerLine) + kc*4 + 1; /* ARGB */
198 g = *pg; /* pixel value */
199 // if (g < ming) ming = g;
200 // if (g > maxg) maxg = g;
201 pa = *(im->data) + kr*(im->bytesPerLine) + kc*4 + 2; /* ARGB */
202 if (*pa != 0) { // include only valid pixels in contrast estimate
203 sumg += g;
204 sumgsq += g*g;
205 n++;
206 }
207 }
208 }
209 }
210 pa = *(im->data) + row*(im->bytesPerLine) + col*4 + 2; /* ARGB */
211 if (*pa == 0) { // do not store contrast estimate at invalid pixels
212 ZComb.estFocus[row*width+col] = 0.0;
213 }
214 else
215 if (n <= 1 || maxg == ming) {
216 ZComb.estFocus[row*width+col] = 0.0;
217 } else {
218 /* variance squared */
219 ZComb.estFocus[row*width+col] = ((n*sumgsq - sumg*sumg) / (float)(n*(n-1)));
220 // /((maxg-ming)*(maxg-ming));
221 }
222 }
223 }
224 }
225
ZCombCopyEstFocusToBlue(Image * im)226 void ZCombCopyEstFocusToBlue(Image *im) {
227 int row;
228 int col;
229 uint8_t *pg; /* pointer to gray (blue) value */
230 uint8_t g; /* gray value */
231 int height = im->height;
232 int width = im->width;
233
234 float maxEst = 0.0;
235 for (row = 0; row < height; row++) {
236 for (col = 0; col < width; col++) {
237 if (ZComb.estFocus[row*width+col] > maxEst) {
238 maxEst = ZComb.estFocus[row*width+col];
239 }
240 }
241 }
242
243 for (row = 0; row < height; row++) {
244 for (col = 0; col < width; col++) {
245 pg = *(im->data) + row*(im->bytesPerLine) + col*4 + 3; /* ARGB */
246 g = (int) (255.0*ZComb.estFocus[row*width+col]/maxEst);
247 *pg = g;
248 }
249 }
250 }
251
ZCombSetMaskFromFocusData(Image * im)252 void ZCombSetMaskFromFocusData(Image *im) {
253 int row;
254 int col;
255 uint8_t *pg; /* pointer to gray (blue) value */
256 // uint8_t g; /* gray value */
257 int height = im->height;
258 int width = im->width;
259 float flev;
260
261 for (row = 0; row < height; row++) {
262 for (col = 0; col < width; col++) {
263 pg = *(im->data) + row*(im->bytesPerLine) + col*4 + 0; /* ARGB */
264 if (ZComb.masktype == 0 || ZComb.masktype == 1) {
265 // generate hard-edged masks using unsmoothed depth level -- select strictly pixels belonging to best-focused image
266 if ((ZComb.masktype == 0 && ZComb.currentImageNum == ZComb.bestLevel[row*width+col]) // generate non-nested masks
267 || (ZComb.masktype == 1 && ZComb.currentImageNum <= ZComb.bestLevel[row*width+col]) // generate a stack of nested masks
268 ) {
269 *pg = 255;
270 } else {
271 *pg = 0;
272 }
273 } else if (ZComb.masktype == 2) {
274 // generate blending masks using smoothed depth level -- typically makes much better looking images
275 flev = ZCombGetSmoothedLevel(row,col);
276 if (ZComb.currentImageNum <= flev+0.01) {
277 *pg = 255;
278 } else if (ZComb.currentImageNum > flev+1.01) {
279 *pg = 0;
280 } else {
281 *pg = 255 - (uint8_t) (255.0*(ZComb.currentImageNum - (flev+0.01)));
282 }
283 } else {
284 *pg = 255; // unrecognized masktype, just keep from crashing
285 }
286 // following is a hack that forces nominally binary masks to actually have a single
287 // intermediate value, which keeps Jim Watter's PSD modifications from incorrectly
288 // assigning shape mask = visibility mask.
289 if (row == 0 && col == 0) {
290 if (*pg == 0) *pg = 1;
291 if (*pg == 255) *pg = 254;
292 }
293 }
294 }
295 }
296
ZCombSetGreenTo255(Image * im)297 void ZCombSetGreenTo255(Image *im) {
298 int row;
299 int col;
300 uint8_t *pg; /* pointer to gray (green) value */
301 int height = im->height;
302 int width = im->width;
303
304 for (row = 0; row < height; row++) {
305 for (col = 0; col < width; col++) {
306 pg = *(im->data) + row*(im->bytesPerLine) + col*4 + 2; /* ARGB */
307 *pg = 255;
308 }
309 }
310 }
311
ZCombSeeImage(Image * im,char * filename)312 int ZCombSeeImage(Image *im, char *filename) {
313 if (!ZComb.enabled) {
314 ZCombLogMsg ("Z-combining disabled\n",NULL);
315 return 0;
316 }
317 ZCombLogMsg ("Z-combining enabled\n",NULL);
318 ZCombLogMsg ("writeTIFF called on file %s\n",filename);
319 ZCombLogMsg (" image name = %s\n",im->name);
320 if (ZComb.fnameSet && strcmp(ZComb.firstFname,filename) == 0) {
321 ZCombLogMsg ("Starting second pass\n",NULL);
322 ZComb.passNum = 2;
323 ZComb.currentImageNum = 0;
324 }
325 if (!ZComb.fnameSet) {
326 ZComb.fnameSet = 1;
327 ZComb.passNum = 1;
328 strcpy(ZComb.firstFname,filename);
329 ZCombLogMsg(" initialFname set to %s\n",ZComb.firstFname);
330 if (ZCombInitStats(im->width,im->height)) {
331 return -1;
332 }
333 }
334 ZComb.currentImageNum++;
335 if (ZComb.passNum == 1) {
336 ZCombEstimateFocus(im);
337 ZCombAccumEstFocus();
338 // ZCombCopyEstFocusToBlue(im); // for debugging purposes
339 // ZCombSetGreenTo255(im);
340 }
341 if (ZComb.passNum == 2) {
342 ZCombEstimateFocus(im);
343 ZCombSetMaskFromFocusData(im);
344 }
345 return 0;
346 }
347
348 // end Rik's mask-from-focus hacking
349
350