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