~ubuntu-branches/ubuntu/vivid/fotoxx/vivid

« back to all changes in this revision

Viewing changes to fotoxx_comp.cc

  • Committer: Package Import Robot
  • Author(s): Santiago Torres Batan
  • Date: 2011-11-07 16:37:55 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20111107163755-2fdevt5hu7tcguhx
Tags: 11.11.1-1
* New upstream release
  
* debian/watch
  -Fix lintian warning: debian_watch_contains_dh_make_template

* debian/rules
  -Remove -XTRANLATIONS -XCHANGES params in dh_compress.
  -Fix lintian warning: debian_rules_missing_recommends_targets
  -Add symlink to avoid duplicate documentation at intalldocs rule.
  -Remove unnecessary files.
 
* debian/control
 -Bump Standars-Version to 3.9.2
 -Remov libfreeimage-dev, libimage-exiftool-perf from
   Build-Depends
 -Add xgd-utils and libimage-exiftool-perf to Recommends
 -Add brasero to Suggests

* debian/copyright
  -Update copyright to 2010, 2011
 
* debian/patches/ 
  -Fix lintian warning format-3.0-but-debian-changes-patch and
   (closes: #643119) by creating:
  -docs_dir.patch: changes upstream sources for the program to
   know where documentation is installed
  -makefile_changes.patch: edit makefile for installation
   Removes the depencies check by dependencies.sh. (closes: #622315)
   Fix FTBFS with binutils-gold. (closes: #610545)
   fotoxxfotoxx.desktop was created by mistake. (closes: #610544)
   Thanks to Mahyuddin Susanto.

* debian/doc-base
  -Register fotoxx user guide documentation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**************************************************************************
 
2
 
 
3
   Fotoxx      edit photos and manage collections
 
4
 
 
5
   Copyright 2007 2008 2009 2010 2011  Michael Cornelison
 
6
   Source URL: http://kornelix.squarespace.com/fotoxx
 
7
   Contact: kornelix2@googlemail.com
 
8
   
 
9
   This program is free software: you can redistribute it and/or modify
 
10
   it under the terms of the GNU General Public License as published by
 
11
   the Free Software Foundation, either version 3 of the License, or
 
12
   (at your option) any later version.
 
13
 
 
14
   This program is distributed in the hope that it will be useful,
 
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
   GNU General Public License for more details.
 
18
 
 
19
   You should have received a copy of the GNU General Public License
 
20
   along with this program. If not, see http://www.gnu.org/licenses/.
 
21
 
 
22
***************************************************************************/
 
23
 
 
24
#define EX extern                                                          //  enable extern declarations
 
25
#include "fotoxx.h"
 
26
 
 
27
 
 
28
/**************************************************************************
 
29
 
 
30
   Fotoxx image edit functions - composite functions
 
31
 
 
32
***************************************************************************/
 
33
 
 
34
 
 
35
//   File scope variables and functions for composite images
 
36
//      used by HDR, HDF, STP, STN, Panorama.
 
37
 
 
38
 
 
39
int      cimNF;                                                   //  image count, <= 10
 
40
char     *cimFile[10];                                            //  image files
 
41
PXM      *cimPXMf[10];                                            //  original images
 
42
PXM      *cimPXMs[10];                                            //  alignment images, scaled and curved (pano)
 
43
PXM      *cimPXMw[10];                                            //  alignment images, warped
 
44
 
 
45
struct cimoffs  {                                                 //  image alignment offsets
 
46
   double   xf, yf, tf;                                           //  x, y, theta offsets
 
47
   double   wx[4], wy[4];                                         //  x/y corner warps, 0=NW, 1=NE, 2=SE, 3=SW
 
48
};
 
49
cimoffs  cimOffs[10];                                             //  image alignment data in E3 output image
 
50
 
 
51
double   cimScale;                                                //  alignment image size relative to full image
 
52
double   cimBlend;                                                //  image blend width at overlap, pixels (pano)
 
53
double   cimSearchRange;                                          //  alignment search range, pixels
 
54
double   cimSearchStep;                                           //  alignment search step, vpixels
 
55
double   cimWarpRange;                                            //  alignment corner warp range, pixels
 
56
double   cimWarpStep;                                             //  alignment corner warp step, vpixels
 
57
double   cimSampSize;                                             //  pixel sample size
 
58
int      cimOv1xlo, cimOv1xhi, cimOv1ylo, cimOv1yhi;              //  rectangle enclosing overlap area,
 
59
int      cimOv2xlo, cimOv2xhi, cimOv2ylo, cimOv2yhi;              //    image 1 and image2 coordinates
 
60
double   cimRGBmf1[3][65536];                                     //  RGB matching factors for pixel comparisons:
 
61
double   cimRGBmf2[3][65536];                                     //  cimRGBmf1[*][pix1[*]] == cimRGBmf2[*][pix2[*]]
 
62
char     *cimRedpix = 0;                                          //  maps high-contrast pixels for alignment
 
63
int      cimRedImage;                                             //  which image has red pixels
 
64
int      cimNsearch;                                              //  alignment search counter
 
65
int      cimShowIm1, cimShowIm2;                                  //  two images for cim_show_images()
 
66
int      cimShowAll;                                              //  if > 0, show all images
 
67
int      cimShrink;                                               //  image shrinkage from pano image curving
 
68
int      cimPano;                                                 //  pano mode flag for cim_align_image()
 
69
int      cimPanoV;                                                //  vertical pano flag
 
70
 
 
71
int      cim_load_files();                                        //  load and check selected files
 
72
void     cim_scale_image(int im, PXM **);                         //  scale image, 1.0 to cimScale (normally < 1)
 
73
double   cim_get_overlap(int im1, int im2, PXM **);               //  get overlap area for images (horiz or vert)
 
74
void     cim_match_colors(int im1, int im2, PXM **);              //  match image RGB levels >> match data
 
75
void     cim_adjust_colors(PXM *pxm, int fwhich);                 //  adjust RGB levels from match data
 
76
void     cim_get_redpix(int im1);                                 //  find high-contrast pixels in overlap area
 
77
void     cim_curve_image(int im);                                 //  curve cimPXMs[im] using lens parameters
 
78
void     cim_curve_Vimage(int im);                                //  vertical pano version
 
79
void     cim_warp_image(int im);                                  //  warp image corners: cimPXMs[im] >> cimPXMw[im]
 
80
void     cim_warp_image_pano(int im, int fblend);                 //  pano version, all / left side / blend stripe
 
81
void     cim_warp_image_Vpano(int im, int fblend);                //  vertical pans version: bottom side corners
 
82
void     cim_align_image(int im1, int im2);                       //  align image im2 to im1, modify im2 offsets
 
83
double   cim_match_images(int im1, int im2);                      //  compute match for overlapped images
 
84
void     cim_show_images(int fnew, int fblend);                   //  combine images >> E3pxm16 >> main window
 
85
void     cim_show_Vimages(int fnew, int fblend);                  //  vertical pano version
 
86
void     cim_trim();                                              //  cut-off edges where all images do not overlap
 
87
void     cim_dump_offsets(cchar *text);                           //  diagnostic tool
 
88
 
 
89
 
 
90
//  load image file into pixmaps cimPXMf[*] and check for errors
 
91
//  returns 0 if error
 
92
 
 
93
int cim_load_files()                                                       //  v.10.7
 
94
{
 
95
   PXM      *pxm;
 
96
 
 
97
   for (int imx = 0; imx < cimNF; imx++)
 
98
   {
 
99
      PXM_free(cimPXMf[imx]);
 
100
      pxm = f_load(cimFile[imx],16);                                       //  will diagnose errors
 
101
      if (! pxm) return 0;
 
102
      cimPXMf[imx] = pxm;
 
103
      
 
104
      PXM_fixblue(pxm);                                                    //  blue=0 >> blue=2 for vpixel()   v.11.07
 
105
   }
 
106
 
 
107
   return 1;
 
108
}
 
109
 
 
110
 
 
111
//  scale image from full size) to cimScale (normally < 1.0)
 
112
 
 
113
void cim_scale_image(int im, PXM** pxmout)                                 //  v.10.7
 
114
{
 
115
   int      ww, hh;
 
116
   
 
117
   ww = cimScale * cimPXMf[im]->ww;
 
118
   hh = cimScale * cimPXMf[im]->hh;
 
119
 
 
120
   PXM_free(pxmout[im]);   
 
121
   pxmout[im] = PXM_rescale(cimPXMf[im],ww,hh);
 
122
   
 
123
   PXM_fixblue(pxmout[im]);                                                //  blue=0 >> blue=2 for vpixel()   v.11.07
 
124
 
 
125
   return;
 
126
}
 
127
 
 
128
 
 
129
//  get overlap area for a pair of images im1 and im2
 
130
//  outputs are coordinates of overlap area in im1 and in im2
 
131
//  returns overlap width as fraction of image width <= 1.0
 
132
 
 
133
double cim_get_overlap(int im1, int im2, PXM **pxmx)                       //  v.11.04
 
134
{
 
135
   double      x1, y1, t1, x2, y2, t2;
 
136
   double      xoff, yoff, toff, costf, sintf;
 
137
   int         ww1, ww2, hh1, hh2, pxM;
 
138
   PXM         *pxm1, *pxm2;
 
139
 
 
140
   x1 = cimOffs[im1].xf;                                                   //  im1, im2 absolute offsets
 
141
   y1 = cimOffs[im1].yf;
 
142
   t1 = cimOffs[im1].tf;
 
143
   x2 = cimOffs[im2].xf;
 
144
   y2 = cimOffs[im2].yf;
 
145
   t2 = cimOffs[im2].tf;
 
146
   
 
147
   xoff = (x2 - x1) * cos(t1) + (y2 - y1) * sin(t1);                       //  offset of im2 relative to im1
 
148
   yoff = (y2 - y1) * cos(t1) - (x2 - x1) * sin(t1);
 
149
   toff = t2 - t1;
 
150
 
 
151
   costf = cos(toff);
 
152
   sintf = sin(toff);
 
153
 
 
154
   pxm1 = pxmx[im1];
 
155
   pxm2 = pxmx[im2];
 
156
 
 
157
   ww1 = pxm1->ww;
 
158
   hh1 = pxm1->hh;
 
159
   ww2 = pxm2->ww;
 
160
   hh2 = pxm2->hh;
 
161
   
 
162
   cimOv1xlo = 0;                                                          //  lowest x overlap
 
163
   if (xoff > 0) cimOv1xlo = xoff;
 
164
 
 
165
   cimOv1xhi = ww1-1;                                                      //  highest x overlap
 
166
   if (cimOv1xhi > xoff + ww2-1) cimOv1xhi = xoff + ww2-1;
 
167
 
 
168
   cimOv1ylo = 0;                                                          //  lowest y overlap
 
169
   if (yoff > 0) cimOv1ylo = yoff;
 
170
 
 
171
   cimOv1yhi = hh1-1;                                                      //  highest y overlap
 
172
   if (cimOv1yhi > yoff + hh2-1) cimOv1yhi = yoff + hh2-1;
 
173
   
 
174
   if (toff < 0) cimOv1xlo -= toff * (cimOv1yhi - cimOv1ylo);              //  reduce for theta offset
 
175
   if (toff < 0) cimOv1yhi += toff * (cimOv1xhi - cimOv1xlo);
 
176
   if (toff > 0) cimOv1xhi -= toff * (cimOv1yhi - cimOv1ylo);
 
177
   if (toff > 0) cimOv1ylo += toff * (cimOv1xhi - cimOv1xlo);
 
178
   
 
179
   cimOv1xlo += cimShrink + 3;                                             //  account for void areas from
 
180
   cimOv1xhi += - cimShrink - 3;                                           //    image shrinkage from 
 
181
   cimOv1ylo += cimShrink + 3;                                             //      cim_curve_image()
 
182
   cimOv1yhi += - cimShrink - 3;                                           //  v.11.04
 
183
   
 
184
   if (cimPanoV) {
 
185
      if (cimBlend && cimBlend < (cimOv1yhi - cimOv1ylo)) {                //  reduce y range to cimBlend   v.11.04
 
186
         pxM = (cimOv1yhi + cimOv1ylo) / 2;
 
187
         cimOv1ylo = pxM - cimBlend / 2;
 
188
         cimOv1yhi = pxM + cimBlend / 2;
 
189
      }
 
190
   }
 
191
   else {
 
192
      if (cimBlend && cimBlend < (cimOv1xhi - cimOv1xlo)) {                //  reduce x range to cimBlend
 
193
         pxM = (cimOv1xhi + cimOv1xlo) / 2;
 
194
         cimOv1xlo = pxM - cimBlend / 2;
 
195
         cimOv1xhi = pxM + cimBlend / 2;
 
196
      }
 
197
   }
 
198
 
 
199
   cimOv2xlo = costf * (cimOv1xlo - xoff) + sintf * (cimOv1ylo - yoff);    //  overlap area in im2 coordinates
 
200
   cimOv2xhi = costf * (cimOv1xhi - xoff) + sintf * (cimOv1yhi - yoff);
 
201
   cimOv2ylo = costf * (cimOv1ylo - yoff) + sintf * (cimOv1xlo - xoff);
 
202
   cimOv2yhi = costf * (cimOv1yhi - yoff) + sintf * (cimOv1xhi - xoff);
 
203
 
 
204
   if (cimOv1xlo < 0) cimOv1xlo = 0;                                       //  take care of limits
 
205
   if (cimOv1ylo < 0) cimOv1ylo = 0;
 
206
   if (cimOv2xlo < 0) cimOv2xlo = 0;
 
207
   if (cimOv2ylo < 0) cimOv2ylo = 0;
 
208
   if (cimOv1xhi > ww1-1) cimOv1xhi = ww1-1;
 
209
   if (cimOv1yhi > hh1-1) cimOv1yhi = hh1-1;
 
210
   if (cimOv2xhi > ww2-1) cimOv2xhi = ww2-1;
 
211
   if (cimOv2yhi > hh2-1) cimOv2yhi = hh2-1;
 
212
   
 
213
   if (cimPanoV) return 1.0 * (cimOv1yhi - cimOv1ylo) / hh1;               //  return overlap height <= 1.0  v.11.04
 
214
   else return 1.0 * (cimOv1xhi - cimOv1xlo) / ww1;                        //  return overlap width <= 1.0  v.11.03
 
215
}
 
216
 
 
217
 
 
218
//  Get the RGB brightness distribution in the overlap area for each image.
 
219
//  Compute matching factors to compare pixels within the overlap area.
 
220
//  compare  cimRGBmf1[rgb][pix1[rgb]]  to  cimRGBmf2[rgb][pix2[rgb]]
 
221
 
 
222
void cim_match_colors(int im1, int im2, PXM **pxmx)                        //  v.10.7
 
223
{
 
224
   double      Bratios1[3][256];                //  image2/image1 brightness ratio per color per level
 
225
   double      Bratios2[3][256];                //  image1/image2 brightness ratio per color per level
 
226
 
 
227
   uint16      *pix1, vpix2[3];
 
228
   int         vstat2, px1, py1;
 
229
   int         ii, jj, rgb;
 
230
   int         npix, npix1, npix2, npix3;
 
231
   int         brdist1[3][256], brdist2[3][256];
 
232
   double      x1, y1, t1, x2, y2, t2;
 
233
   double      xoff, yoff, toff, costf, sintf;
 
234
   double      px2, py2;
 
235
   double      brlev1[3][256], brlev2[3][256];
 
236
   double      a1, a2, b1, b2, bratio = 1;
 
237
   double      r256 = 1.0 / 256.0;
 
238
   PXM         *pxm1, *pxm2;
 
239
   
 
240
   pxm1 = pxmx[im1];
 
241
   pxm2 = pxmx[im2];
 
242
   
 
243
   x1 = cimOffs[im1].xf;                                                   //  im1, im2 absolute offsets
 
244
   y1 = cimOffs[im1].yf;
 
245
   t1 = cimOffs[im1].tf;
 
246
   x2 = cimOffs[im2].xf;
 
247
   y2 = cimOffs[im2].yf;
 
248
   t2 = cimOffs[im2].tf;
 
249
   
 
250
   xoff = (x2 - x1) * cos(t1) + (y2 - y1) * sin(t1);                       //  offset of im2 relative to im1
 
251
   yoff = (y2 - y1) * cos(t1) - (x2 - x1) * sin(t1);
 
252
   toff = t2 - t1;
 
253
 
 
254
   costf = cos(toff);
 
255
   sintf = sin(toff);
 
256
 
 
257
   for (rgb = 0; rgb < 3; rgb++)                                           //  clear distributions
 
258
   for (ii = 0; ii < 256; ii++)
 
259
      brdist1[rgb][ii] = brdist2[rgb][ii] = 0;
 
260
 
 
261
   npix = 0;
 
262
 
 
263
   for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++)                           //  loop overlapped rows
 
264
   for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++)                           //  loop overlapped columns
 
265
   {
 
266
      pix1 = PXMpix(pxm1,px1,py1);                                         //  image1 pixel
 
267
      if (! pix1[2]) continue;                                             //  ignore void pixels
 
268
 
 
269
      px2 = costf * (px1 - xoff) + sintf * (py1 - yoff);                   //  corresponding image2 pixel
 
270
      py2 = costf * (py1 - yoff) - sintf * (px1 - xoff);
 
271
      vstat2 = vpixel(pxm2,px2,py2,vpix2);
 
272
      if (! vstat2) continue;                                              //  does not exist
 
273
 
 
274
      ++npix;                                                              //  count overlapping pixels
 
275
      
 
276
      for (rgb = 0; rgb < 3; rgb++)                                        //  accumulate distributions
 
277
      {                                                                    //    by color in 256 bins
 
278
         ++brdist1[rgb][int(r256*pix1[rgb])];
 
279
         ++brdist2[rgb][int(r256*vpix2[rgb])];
 
280
      }
 
281
   }
 
282
   
 
283
   npix1 = npix / 256;                                                     //  1/256th of total pixels
 
284
   
 
285
   for (rgb = 0; rgb < 3; rgb++)                                           //  get brlev1[rgb][N] = mean bright
 
286
   for (ii = jj = 0; jj < 256; jj++)                                       //    for Nth group of image1 pixels
 
287
   {                                                                       //      for color rgb
 
288
      brlev1[rgb][jj] = 0;
 
289
      npix2 = npix1;                                                       //  1/256th of total pixels
 
290
 
 
291
      while (npix2 > 0 && ii < 256)                                        //  next 1/256th group from distr,
 
292
      {
 
293
         npix3 = brdist1[rgb][ii];
 
294
         if (npix3 == 0) { ++ii; continue; }
 
295
         if (npix3 > npix2) npix3 = npix2;
 
296
         brlev1[rgb][jj] += ii * npix3;                                    //  brightness * (pixels with)
 
297
         brdist1[rgb][ii] -= npix3;
 
298
         npix2 -= npix3;
 
299
      }
 
300
 
 
301
      brlev1[rgb][jj] = brlev1[rgb][jj] / npix1;                           //  mean brightness for group, 0-255
 
302
   }
 
303
 
 
304
   for (rgb = 0; rgb < 3; rgb++)                                           //  do same for image2
 
305
   for (ii = jj = 0; jj < 256; jj++)
 
306
   {
 
307
      brlev2[rgb][jj] = 0;
 
308
      npix2 = npix1;
 
309
 
 
310
      while (npix2 > 0 && ii < 256)
 
311
      {
 
312
         npix3 = brdist2[rgb][ii];
 
313
         if (npix3 == 0) { ++ii; continue; }
 
314
         if (npix3 > npix2) npix3 = npix2;
 
315
         brlev2[rgb][jj] += ii * npix3;
 
316
         brdist2[rgb][ii] -= npix3;
 
317
         npix2 -= npix3;
 
318
      }
 
319
 
 
320
      brlev2[rgb][jj] = brlev2[rgb][jj] / npix1;
 
321
   }
 
322
 
 
323
   for (rgb = 0; rgb < 3; rgb++)                                           //  color
 
324
   for (ii = jj = 0; ii < 256; ii++)                                       //  brlev1 brightness, 0 to 255
 
325
   {
 
326
      if (ii == 0) bratio = 1;
 
327
      while (ii > brlev2[rgb][jj] && jj < 256) ++jj;                       //  find matching brlev2 brightness
 
328
      a2 = brlev2[rgb][jj];                                                //  next higher value
 
329
      b2 = brlev1[rgb][jj];
 
330
      if (a2 > 0 && b2 > 0) {
 
331
         if (jj > 0) {
 
332
            a1 = brlev2[rgb][jj-1];                                        //  next lower value
 
333
            b1 = brlev1[rgb][jj-1];
 
334
         }
 
335
         else   a1 = b1 = 0;
 
336
         if (ii == 0)  bratio = b2 / a2;
 
337
         else   bratio = (b1 + (ii-a1)/(a2-a1) * (b2-b1)) / ii;            //  interpolate
 
338
      }
 
339
 
 
340
      if (bratio < 0.2) bratio = 0.2;                                      //  contain outliers
 
341
      if (bratio > 5) bratio = 5;
 
342
      Bratios2[rgb][ii] = bratio;
 
343
   }
 
344
 
 
345
   for (rgb = 0; rgb < 3; rgb++)                                           //  color
 
346
   for (ii = jj = 0; ii < 256; ii++)                                       //  brlev2 brightness, 0 to 255
 
347
   {
 
348
      if (ii == 0) bratio = 1;
 
349
      while (ii > brlev1[rgb][jj] && jj < 256) ++jj;                       //  find matching brlev1 brightness
 
350
      a2 = brlev1[rgb][jj];                                                //  next higher value
 
351
      b2 = brlev2[rgb][jj];
 
352
      if (a2 > 0 && b2 > 0) {
 
353
         if (jj > 0) {
 
354
            a1 = brlev1[rgb][jj-1];                                        //  next lower value
 
355
            b1 = brlev2[rgb][jj-1];
 
356
         }
 
357
         else   a1 = b1 = 0;
 
358
         if (ii == 0)  bratio = b2 / a2;
 
359
         else   bratio = (b1 + (ii-a1)/(a2-a1) * (b2-b1)) / ii;            //  interpolate
 
360
      }
 
361
 
 
362
      if (bratio < 0.2) bratio = 0.2;                                      //  contain outliers
 
363
      if (bratio > 5) bratio = 5;
 
364
      Bratios1[rgb][ii] = bratio;
 
365
   }
 
366
   
 
367
   for (ii = 0; ii < 65536; ii++)                                          //  convert brightness ratios into
 
368
   {                                                                       //    conversion factors
 
369
      jj = ii / 256;
 
370
 
 
371
      for (rgb = 0; rgb < 3; rgb++)
 
372
      {
 
373
         cimRGBmf1[rgb][ii] = sqrt(Bratios1[rgb][jj]) * ii;                //  use sqrt(ratio) so that adjustment
 
374
         cimRGBmf2[rgb][ii] = sqrt(Bratios2[rgb][jj]) * ii;                //    can be applied to both images
 
375
      }
 
376
   }
 
377
   
 
378
   return;
 
379
}
 
380
 
 
381
 
 
382
//  Use color match data from cim_match_colors() to 
 
383
//    modify images so the colors match.
 
384
 
 
385
void cim_adjust_colors(PXM *pxm, int fwhich)                               //  v.10.7
 
386
{
 
387
   int         ww, hh, px, py;
 
388
   int         red, green, blue, max;
 
389
   uint16      *pix;
 
390
   double      f1;
 
391
   
 
392
   ww = pxm->ww;
 
393
   hh = pxm->hh;
 
394
 
 
395
   for (py = 0; py < hh; py++)
 
396
   for (px = 0; px < ww; px++)
 
397
   {
 
398
      pix = PXMpix(pxm,px,py);
 
399
      red = pix[0];
 
400
      green = pix[1];
 
401
      blue = pix[2];
 
402
      if (! blue) continue;
 
403
 
 
404
      if (fwhich == 1) {
 
405
         red = cimRGBmf1[0][red];
 
406
         green = cimRGBmf1[1][green];
 
407
         blue = cimRGBmf1[2][blue];
 
408
      }
 
409
 
 
410
      if (fwhich == 2) {
 
411
         red = cimRGBmf2[0][red];
 
412
         green = cimRGBmf2[1][green];
 
413
         blue = cimRGBmf2[2][blue];
 
414
      }
 
415
 
 
416
      if (red > 65535 || green > 65535 || blue > 65535) {
 
417
         max = red;
 
418
         if (green > max) max = green;
 
419
         if (blue > max) max = blue;
 
420
         f1 = 65535.0 / max;
 
421
         red = red * f1;
 
422
         green = green * f1;
 
423
         blue = blue * f1;
 
424
      }
 
425
      
 
426
      if (! blue) blue = 1;                                                //  avoid 0    v.10.7
 
427
 
 
428
      pix[0] = red;
 
429
      pix[1] = green;
 
430
      pix[2] = blue;
 
431
   }
 
432
 
 
433
   return;
 
434
}
 
435
 
 
436
 
 
437
//  find pixels of greatest contrast within overlap area
 
438
//  flag high-contrast pixels to use in each image compare region
 
439
 
 
440
void cim_get_redpix(int im1)                                               //  v.10.7
 
441
{
 
442
   int         ww, hh, samp, xzone, yzone;
 
443
   int         pxL, pxH, pyL, pyH;
 
444
   int         px, py, ii, jj, npix;
 
445
   int         red1, green1, blue1, red2, green2, blue2, tcon;
 
446
   int         ov1xlo, ov1xhi, ov1ylo, ov1yhi;
 
447
   int         Hdist[256], Vdist[256], Hmin, Vmin;
 
448
   double      s8 = 1.0 / 770.0;
 
449
   double      zsamp[16] = { 4,6,6,4,6,9,9,6,6,9,9,6,4,6,6,4 };            //  % sample per zone, sum = 100
 
450
   uchar       *Hcon, *Vcon;
 
451
   uint16      *pix1, *pix2;
 
452
   PXM         *pxm;
 
453
   
 
454
   pxm = cimPXMs[im1];                                                     //  v.11.04
 
455
   ww = pxm->ww;
 
456
   hh = pxm->hh;
 
457
 
 
458
   if (cimRedpix) zfree(cimRedpix);                                        //  clear prior
 
459
   cimRedpix = zmalloc(ww*hh,"cimRedpix");
 
460
   memset(cimRedpix,0,ww*hh);
 
461
 
 
462
   cimRedImage = im1;                                                      //  image with red pixels
 
463
   
 
464
   ov1xlo = cimOv1xlo + cimSearchRange;                                    //  stay within x/y search range
 
465
   ov1xhi = cimOv1xhi - cimSearchRange;                                    //    so that red pixels persist
 
466
   ov1ylo = cimOv1ylo + cimSearchRange;                                    //      over offset changes
 
467
   ov1yhi = cimOv1yhi - cimSearchRange;
 
468
   
 
469
   for (yzone = 0; yzone < 4; yzone++)                                     //  loop 16 zones       v.10.8
 
470
   for (xzone = 0; xzone < 4; xzone++)
 
471
   {
 
472
      pxL = ov1xlo + 0.25 * xzone     * (ov1xhi - ov1xlo);                 //  px and py zone limits
 
473
      pxH = ov1xlo + 0.25 * (xzone+1) * (ov1xhi - ov1xlo);
 
474
      pyL = ov1ylo + 0.25 * yzone     * (ov1yhi - ov1ylo);
 
475
      pyH = ov1ylo + 0.25 * (yzone+1) * (ov1yhi - ov1ylo);
 
476
      
 
477
      npix = (pxH - pxL) * (pyH - pyL);                                    //  zone pixels
 
478
      Hcon = (uchar *) zmalloc(npix,"cimRedpix");                          //  horizontal pixel contrast 0-255
 
479
      Vcon = (uchar *) zmalloc(npix,"cimRedpix");                          //  vertical pixel contrast 0-255
 
480
      
 
481
      ii = 4 * yzone + xzone;
 
482
      samp = cimSampSize * 0.01 * zsamp[ii];                               //  sample size for zone
 
483
      if (samp > 0.1 * npix) samp = 0.1 * npix;                            //  limit to 10% of zone pixels
 
484
      
 
485
      for (py = pyL; py < pyH; py++)                                       //  scan image pixels in zone
 
486
      for (px = pxL; px < pxH; px++)
 
487
      {
 
488
         ii = (py-pyL) * (pxH-pxL) + (px-pxL);
 
489
         Hcon[ii] = Vcon[ii] = 0;                                          //  horiz. = vert. contrast = 0
 
490
 
 
491
         if (py < 8 || py > hh-9) continue;                                //  keep away from image edges
 
492
         if (px < 8 || px > ww-9) continue;
 
493
 
 
494
         pix1 = PXMpix(pxm,px,py-6);                                       //  verify not near void areas
 
495
         if (! pix1[2]) continue;
 
496
         pix1 = PXMpix(pxm,px+6,py);
 
497
         if (! pix1[2]) continue;
 
498
         pix1 = PXMpix(pxm,px,py+6);
 
499
         if (! pix1[2]) continue;
 
500
         pix1 = PXMpix(pxm,px-6,py);
 
501
         if (! pix1[2]) continue;
 
502
 
 
503
         pix1 = PXMpix(pxm,px,py);                                         //  candidate red pixel
 
504
         red1 = pix1[0];
 
505
         green1 = pix1[1];
 
506
         blue1 = pix1[2];
 
507
 
 
508
         pix2 = PXMpix(pxm,px+2,py);                                       //  2 pixels to right
 
509
         red2 = pix2[0];
 
510
         green2 = pix2[1];
 
511
         blue2 = pix2[2];
 
512
 
 
513
         tcon = abs(red1-red2) + abs(green1-green2) + abs(blue1-blue2);    //  horizontal contrast
 
514
         Hcon[ii] = int(tcon * s8);                                        //  scale  0 - 255
 
515
 
 
516
         pix2 = PXMpix(pxm,px,py+2);                                       //  2 pixels below
 
517
         red2 = pix2[0];
 
518
         green2 = pix2[1];
 
519
         blue2 = pix2[2];
 
520
 
 
521
         tcon = abs(red1-red2) + abs(green1-green2) + abs(blue1-blue2);    //  vertical contrast
 
522
         Vcon[ii] = int(tcon * s8);
 
523
      }
 
524
 
 
525
      for (ii = 0; ii < 256; ii++) Hdist[ii] = Vdist[ii] = 0;              //  clear contrast distributions
 
526
 
 
527
      for (py = pyL; py < pyH; py++)                                       //  scan image pixels
 
528
      for (px = pxL; px < pxH; px++)
 
529
      {                                                                    //  build contrast distributions
 
530
         ii = (py-pyL) * (pxH-pxL) + (px-pxL);
 
531
         ++Hdist[Hcon[ii]];
 
532
         ++Vdist[Vcon[ii]];
 
533
      }
 
534
      
 
535
      for (npix = 0, ii = 255; ii > 0; ii--)                               //  find minimum contrast needed to get
 
536
      {                                                                    //    enough pixels for sample size
 
537
         npix += Hdist[ii];                                                //      (horizontal contrast pixels)
 
538
         if (npix > samp) break; 
 
539
      }
 
540
      Hmin = ii; 
 
541
 
 
542
      for (npix = 0, ii = 255; ii > 0; ii--)                               //  (verticle contrast pixels)
 
543
      {
 
544
         npix += Vdist[ii];
 
545
         if (npix > samp) break;
 
546
      }
 
547
      Vmin = ii;
 
548
 
 
549
      for (py = pyL; py < pyH; py++)                                       //  scan zone pixels
 
550
      for (px = pxL; px < pxH; px++)
 
551
      {
 
552
         ii = (py-pyL) * (pxH-pxL) + (px-pxL);
 
553
         jj = py * ww + px;
 
554
         if (Hcon[ii] > Hmin) cimRedpix[jj] = 1;                           //  flag pixels above min. contrast
 
555
         if (Vcon[ii] > Vmin) cimRedpix[jj] = 1;
 
556
      }
 
557
 
 
558
      zfree(Hcon);
 
559
      zfree(Vcon);
 
560
 
 
561
      for (py = pyL; py < pyH; py++)                                       //  scan zone pixels
 
562
      for (px = pxL; px < pxH; px++)
 
563
      {
 
564
         ii = (py-pyL) * (pxH-pxL) + (px-pxL);
 
565
         jj = py * ww + px;
 
566
         if (! cimRedpix[jj]) continue;
 
567
         npix = cimRedpix[jj-1] + cimRedpix[jj+1];                         //  eliminate flagged pixels with no
 
568
         npix += cimRedpix[jj-ww] + cimRedpix[jj+ww];                      //    neighboring flagged pixels
 
569
         npix += cimRedpix[jj-ww-1] + cimRedpix[jj+ww-1];                  //  v.11.03
 
570
         npix += cimRedpix[jj-ww+1] + cimRedpix[jj+ww+1];
 
571
         if (npix < 2) cimRedpix[jj] = 0;
 
572
      }
 
573
 
 
574
      for (py = pyL; py < pyH; py++)                                       //  scan zone pixels
 
575
      for (px = pxL; px < pxH; px++)
 
576
      {
 
577
         ii = (py-pyL) * (pxH-pxL) + (px-pxL);
 
578
         jj = py * ww + px;
 
579
 
 
580
         if (cimRedpix[jj] == 1) {                                         //  flag horizontal group of 3
 
581
            cimRedpix[jj+1] = 2;
 
582
            cimRedpix[jj+2] = 2;
 
583
            cimRedpix[jj+ww] = 2;                                          //  and vertical group of 3
 
584
            cimRedpix[jj+2*ww] = 2;
 
585
         }
 
586
      }
 
587
   }
 
588
   
 
589
   return;
 
590
}
 
591
 
 
592
 
 
593
//  curve image based on lens parameters (pano)
 
594
//  replaces cimPXMs[im] with curved version
 
595
 
 
596
void cim_curve_image(int im)                                               //  overhauled    v.11.03
 
597
{
 
598
   int         px, py, ww, hh, vstat;
 
599
   double      ww2, hh2;
 
600
   double      dx, dy;
 
601
   double      F = lens_mm;                                                //  lens focal length, 35mm equivalent
 
602
   double      S = 35.0;                                                   //  corresponding image width
 
603
   double      R1, R2, G, T, bow;
 
604
   PXM         *pxmin, *pxmout;
 
605
   uint16      vpix[3], *pix;
 
606
   
 
607
   pxmin = cimPXMs[im];                                                    //  input and output image
 
608
   ww = pxmin->ww;                  //    200
 
609
   hh = pxmin->hh;
 
610
   ww2 = 0.5 * ww;                  //    100
 
611
   hh2 = 0.5 * hh;
 
612
   
 
613
   if (hh > ww) S = S * ww / hh;                                           //  vertical format
 
614
   F = F / S;                       //    28 / 35                          //  scale to image dimensions
 
615
   S = ww2;                         //    100
 
616
   F = F * ww;                      //    160
 
617
   R1 = F;                                                                 //  cylinder tangent to image plane
 
618
   
 
619
   bow = -lens_bow * 0.01 / hh2 / hh2;                                     //  lens bow % to fraction
 
620
   if (hh > ww) 
 
621
      bow = -lens_bow * 0.01 / ww2 / ww2;
 
622
 
 
623
   pxmout = PXM_make(ww,hh,16);                                            //  temp. output PXM
 
624
   
 
625
   for (py = 0; py < hh; py++)                                             //  cylindrical projection    v.11.03
 
626
   for (px = 0; px < ww; px++)
 
627
   {
 
628
      dx = px - ww2;
 
629
      dy = py - hh2;
 
630
      T = dx / R1;
 
631
      dx = F * tan(T);
 
632
      R2 = sqrt(dx * dx + F * F);
 
633
      G = R1 - R2;
 
634
      dy = (dy * R2) / (R2 + G);
 
635
      dx += bow * dx * dy * dy;                                            //  barrel distortion
 
636
      dx += ww2;
 
637
      dy += hh2;
 
638
      vstat = vpixel(pxmin,dx,dy,vpix);                                    //  input virtual pixel
 
639
      pix = PXMpix(pxmout,px,py);                                          //  output real pixel
 
640
      if (vstat) {  
 
641
         pix[0] = vpix[0];
 
642
         pix[1] = vpix[1];
 
643
         pix[2] = vpix[2];
 
644
      }
 
645
      else pix[0] = pix[1] = pix[2] = 0;                                   //  voided pixels are (0,0,0)
 
646
   }
 
647
 
 
648
   for (px = 1; px < ww2; px++) {                                          //  compute image shrinkage
 
649
      pix = PXMpix(pxmout,px,hh/2);
 
650
      if (pix[2]) break;
 
651
   }
 
652
   cimShrink = px-1;                                                       //  = 0 if no curvature
 
653
   
 
654
   PXM_free(pxmin);                                                        //  replace input with output PXM
 
655
   cimPXMs[im] = pxmout;
 
656
 
 
657
   return;
 
658
}
 
659
 
 
660
 
 
661
//  version for vertical panorama
 
662
 
 
663
void cim_curve_Vimage(int im)                                              //  v.11.04
 
664
{
 
665
   int         px, py, ww, hh, vstat;
 
666
   double      ww2, hh2;
 
667
   double      dx, dy;
 
668
   double      F = lens_mm;                                                //  lens focal length, 35mm equivalent
 
669
   double      S = 35.0;                                                   //  corresponding image width
 
670
   double      R1, R2, G, T, bow;
 
671
   PXM         *pxmin, *pxmout;
 
672
   uint16      vpix[3], *pix;
 
673
   
 
674
   pxmin = cimPXMs[im];                                                    //  input and output image
 
675
   ww = pxmin->ww;                  //    200
 
676
   hh = pxmin->hh;
 
677
   ww2 = 0.5 * ww;                  //    100
 
678
   hh2 = 0.5 * hh;
 
679
   
 
680
   if (hh > ww) S = S * ww / hh;                                           //  vertical format
 
681
   F = F / S;                       //    28 / 35                          //  scale to image dimensions
 
682
   S = ww2;                         //    100
 
683
   F = F * ww;                      //    160
 
684
   R1 = F;                                                                 //  cylinder tangent to image plane
 
685
   
 
686
   bow = -lens_bow * 0.01 / hh2 / hh2;                                     //  lens bow % to fraction
 
687
   if (hh > ww) 
 
688
      bow = -lens_bow * 0.01 / ww2 / ww2;
 
689
 
 
690
   pxmout = PXM_make(ww,hh,16);                                            //  temp. output PXM
 
691
   
 
692
   for (py = 0; py < hh; py++)                                             //  cylindrical projection    v.11.03
 
693
   for (px = 0; px < ww; px++)
 
694
   {
 
695
      dx = px - ww2;
 
696
      dy = py - hh2;
 
697
      T = dy / R1;
 
698
      dy = F * tan(T);
 
699
      R2 = sqrt(dy * dy + F * F);
 
700
      G = R1 - R2;
 
701
      dx = (dx * R2) / (R2 + G);
 
702
      dy += bow * dy * dx * dx;                                            //  barrel distortion
 
703
      dx += ww2;
 
704
      dy += hh2;
 
705
      vstat = vpixel(pxmin,dx,dy,vpix);                                    //  input virtual pixel
 
706
      pix = PXMpix(pxmout,px,py);                                          //  output real pixel
 
707
      if (vstat) {  
 
708
         pix[0] = vpix[0];
 
709
         pix[1] = vpix[1];
 
710
         pix[2] = vpix[2];
 
711
      }
 
712
      else pix[0] = pix[1] = pix[2] = 0;                                   //  voided pixels are (0,0,0)
 
713
   }
 
714
 
 
715
   for (py = 1; py < hh2; py++) {                                          //  compute image shrinkage
 
716
      pix = PXMpix(pxmout,ww/2,py);
 
717
      if (pix[2]) break;
 
718
   }
 
719
   cimShrink = py-1;                                                       //  = 0 if no curvature
 
720
   
 
721
   PXM_free(pxmin);                                                        //  replace input with output PXM
 
722
   cimPXMs[im] = pxmout;
 
723
 
 
724
   return;
 
725
}
 
726
 
 
727
 
 
728
//  Warp 4 image corners according to cimOffs[im].wx[ii] and .wy[ii]
 
729
//  corner = 0 = NW,  1 = NE,  2 = SE,  3 = SW
 
730
//  4 corners move by these pixel amounts and center does not move.
 
731
//  input: cimPXMs[im] (flat or curved) output: cimPXMw[im]
 
732
 
 
733
namespace cim_warp_image_names {
 
734
   PXM         *pxmin, *pxmout;
 
735
   double      ww, hh, wwi, hhi;
 
736
   double      wx0, wy0, wx1, wy1, wx2, wy2, wx3, wy3;
 
737
}
 
738
 
 
739
void cim_warp_image(int im)                                                //  caller function     v.10.8
 
740
{
 
741
   using namespace cim_warp_image_names;
 
742
 
 
743
   void * cim_warp_image_wthread(void *arg);
 
744
   
 
745
   pxmin = cimPXMs[im];                                                    //  input and output pixmaps
 
746
   pxmout = cimPXMw[im];
 
747
   
 
748
   PXM_free(pxmout);                                                       //  v.11.04
 
749
   pxmout = PXM_copy(pxmin);
 
750
   cimPXMw[im] = pxmout;
 
751
 
 
752
   ww = pxmin->ww;
 
753
   hh = pxmin->hh;
 
754
   wwi = 1.0 / ww;
 
755
   hhi = 1.0 / hh;
 
756
 
 
757
   wx0 = cimOffs[im].wx[0];                                                //  corner warps
 
758
   wy0 = cimOffs[im].wy[0];
 
759
   wx1 = cimOffs[im].wx[1];
 
760
   wy1 = cimOffs[im].wy[1];
 
761
   wx2 = cimOffs[im].wx[2];
 
762
   wy2 = cimOffs[im].wy[2];
 
763
   wx3 = cimOffs[im].wx[3];
 
764
   wy3 = cimOffs[im].wy[3];
 
765
 
 
766
   for (int ii = 0; ii < Nwt; ii++)                                        //  start worker threads
 
767
      start_wthread(cim_warp_image_wthread,&wtnx[ii]);
 
768
   wait_wthreads();                                                        //  wait for completion
 
769
 
 
770
   return;
 
771
}
 
772
 
 
773
 
 
774
void * cim_warp_image_wthread(void *arg)                                   //  worker thread function  v.10.8
 
775
{
 
776
   using namespace cim_warp_image_names;
 
777
 
 
778
   int         index = *((int *) arg);
 
779
   int         pxm, pym, vstat;
 
780
   uint16      vpix[3], *pixm;
 
781
   double      px, py, dx, dy, coeff;
 
782
   
 
783
   for (pym = index; pym < hh; pym += Nwt)                                 //  loop all pixels for this thread
 
784
   for (pxm = 0; pxm < ww; pxm++)
 
785
   {
 
786
      dx = dy = 0.0;
 
787
 
 
788
      coeff = (1.0 - pym * hhi - pxm * wwi);                               //  corner 0  NW
 
789
      if (coeff > 0) {
 
790
         dx += coeff * wx0;
 
791
         dy += coeff * wy0;
 
792
      }
 
793
      coeff = (1.0 - pym * hhi - (ww - pxm) * wwi);                        //  corner 1  NE
 
794
      if (coeff > 0) {
 
795
         dx += coeff * wx1;
 
796
         dy += coeff * wy1;
 
797
      }
 
798
      coeff = (1.0 - (hh - pym) * hhi - (ww - pxm) * wwi);                 //  corner 2  SE
 
799
      if (coeff > 0) {
 
800
         dx += coeff * wx2;
 
801
         dy += coeff * wy2;
 
802
      }
 
803
      coeff = (1.0 - (hh - pym) * hhi - pxm * wwi);                        //  corner 3  SW
 
804
      if (coeff > 0) {
 
805
         dx += coeff * wx3;
 
806
         dy += coeff * wy3;
 
807
      }
 
808
 
 
809
      px = pxm + dx;                                                       //  source pixel location
 
810
      py = pym + dy;
 
811
 
 
812
      vstat = vpixel(pxmin,px,py,vpix);                                    //  input virtual pixel
 
813
      pixm = PXMpix(pxmout,pxm,pym);                                       //  output real pixel
 
814
 
 
815
      if (vstat) {  
 
816
         pixm[0] = vpix[0];
 
817
         pixm[1] = vpix[1];
 
818
         pixm[2] = vpix[2];
 
819
      }
 
820
      else pixm[0] = pixm[1] = pixm[2] = 0;
 
821
   }
 
822
   
 
823
   exit_wthread();
 
824
   return 0;                                                               //  not executed, avoid gcc warning
 
825
}
 
826
 
 
827
 
 
828
//  warp image for pano, left side corners only, reduced warp range
 
829
//  input: cimPXMs[im] (curved) 
 
830
//  output: cimPXMw[im] (warped)
 
831
//  fblend: 0 = process entire image
 
832
//          1 = process left half only
 
833
//          2 = process blend stripe only
 
834
 
 
835
void cim_warp_image_pano(int im, int fblend)                               //  v.10.8
 
836
{
 
837
   int         ww, hh, ww2, hh2, pxL, pxH;
 
838
   int         pxm, pym, vstat;
 
839
   uint16      vpix[3], *pixm;
 
840
   double      ww2i, hh2i, pxs, pys, xdisp, ydisp;
 
841
   double      wx0, wy0, wx3, wy3;
 
842
   PXM         *pxmin, *pxmout;
 
843
   
 
844
   pxmin = cimPXMs[im];                                                    //  input and output pixmaps
 
845
   pxmout = cimPXMw[im];
 
846
   
 
847
   PXM_free(pxmout);                                                       //  v.11.04
 
848
   pxmout = PXM_copy(pxmin);
 
849
   cimPXMw[im] = pxmout;
 
850
 
 
851
   ww = pxmin->ww;
 
852
   hh = pxmin->hh;
 
853
 
 
854
   ww2 = ww / 2;
 
855
   hh2 = hh / 2;
 
856
 
 
857
   ww2i = 1.0 / ww2;                                                       //  v.10.8
 
858
   hh2i = 1.0 / hh2;
 
859
 
 
860
   wx0 = cimOffs[im].wx[0];                                                //  NW corner warp
 
861
   wy0 = cimOffs[im].wy[0];
 
862
 
 
863
   wx3 = cimOffs[im].wx[3];                                                //  SW corner warp
 
864
   wy3 = cimOffs[im].wy[3];
 
865
 
 
866
   pxL = 0;                                                                //  entire image        v.10.8
 
867
   pxH = ww;
 
868
   
 
869
   if (fblend == 1)                                                        //  left half
 
870
      pxH = ww2;
 
871
 
 
872
   if (fblend == 2) {
 
873
      pxL = cimOv2xlo;                                                     //  limit to overlap/blend width
 
874
      pxH = cimOv2xhi;
 
875
   }
 
876
   
 
877
   for (pym = 0; pym < hh; pym++)                                          //  loop all output pixels
 
878
   for (pxm = pxL; pxm < pxH; pxm++)
 
879
   {
 
880
      pixm = PXMpix(pxmout,pxm,pym);                                       //  output pixel
 
881
      
 
882
      xdisp = (pxm - ww2) * ww2i;                                          //  -1 ... 0 ... +1
 
883
      ydisp = (pym - hh2) * hh2i;                                          //  v.11.04
 
884
      
 
885
      if (xdisp > 0) {                                                     //  right half, no warp
 
886
         pxs = pxm;
 
887
         pys = pym;
 
888
      }
 
889
      else if (ydisp < 0) {                                                //  use NW corner warp
 
890
         pxs = pxm + wx0 * xdisp * ydisp;
 
891
         pys = pym + wy0 * xdisp * ydisp;
 
892
      }
 
893
      else {                                                               //  use SW corner warp
 
894
         pxs = pxm + wx3 * xdisp * ydisp;
 
895
         pys = pym + wy3 * xdisp * ydisp;
 
896
      }
 
897
 
 
898
      vstat = vpixel(pxmin,pxs,pys,vpix);                                  //  input virtual pixel
 
899
 
 
900
      if (vstat) {  
 
901
         pixm[0] = vpix[0];
 
902
         pixm[1] = vpix[1];
 
903
         pixm[2] = vpix[2];
 
904
      }
 
905
      else pixm[0] = pixm[1] = pixm[2] = 0;
 
906
   }
 
907
 
 
908
   for (pxm = 1; pxm < ww2; pxm++) {                                       //  compute image shrinkage
 
909
      pixm = PXMpix(pxmout,pxm,hh2);                                       //    used by cim_get_overlap()
 
910
      if (pixm[2]) break;
 
911
   }
 
912
   cimShrink = pxm-1;
 
913
 
 
914
   return;
 
915
}
 
916
 
 
917
 
 
918
//  vertical pano version - warp top side corners (NW, NE)
 
919
 
 
920
void cim_warp_image_Vpano(int im, int fblend)                              //  v.11.04
 
921
{
 
922
   int         ww, hh, ww2, hh2, pyL, pyH;
 
923
   int         pxm, pym, vstat;
 
924
   uint16      vpix[3], *pixm;
 
925
   double      ww2i, hh2i, pxs, pys, xdisp, ydisp;
 
926
   double      wx0, wy0, wx1, wy1;
 
927
   PXM         *pxmin, *pxmout;
 
928
   
 
929
   pxmin = cimPXMs[im];                                                    //  input and output pixmaps
 
930
   pxmout = cimPXMw[im];
 
931
   
 
932
   PXM_free(pxmout);                                                       //  v.11.04
 
933
   pxmout = PXM_copy(pxmin);
 
934
   cimPXMw[im] = pxmout;
 
935
 
 
936
   ww = pxmin->ww;
 
937
   hh = pxmin->hh;
 
938
 
 
939
   ww2 = ww / 2;
 
940
   hh2 = hh / 2;
 
941
 
 
942
   ww2i = 1.0 / ww2;                                                       //  v.10.8
 
943
   hh2i = 1.0 / hh2;
 
944
 
 
945
   wx0 = cimOffs[im].wx[0];                                                //  NW corner warp
 
946
   wy0 = cimOffs[im].wy[0];
 
947
 
 
948
   wx1 = cimOffs[im].wx[1];                                                //  NE corner warp
 
949
   wy1 = cimOffs[im].wy[1];
 
950
 
 
951
   pyL = 0;                                                                //  entire image        v.10.8
 
952
   pyH = hh;
 
953
   
 
954
   if (fblend == 1)                                                        //  top half
 
955
      pyH = hh2;
 
956
 
 
957
   if (fblend == 2) {
 
958
      pyL = cimOv2ylo;                                                     //  limit to overlap/blend width
 
959
      pyH = cimOv2yhi;
 
960
   }
 
961
   
 
962
   for (pym = pyL; pym < pyH; pym++)                                       //  loop all output pixels
 
963
   for (pxm = 0; pxm < ww; pxm++)
 
964
   {
 
965
      pixm = PXMpix(pxmout,pxm,pym);                                       //  output pixel
 
966
      
 
967
      xdisp = (pxm - ww2) * ww2i;                                          //  -1 ... 0 ... +1
 
968
      ydisp = (pym - hh2) * hh2i;
 
969
      
 
970
      if (ydisp > 0) {                                                     //  bottom half, no warp
 
971
         pxs = pxm;
 
972
         pys = pym;
 
973
      }
 
974
      else if (xdisp < 0) {                                                //  use NW corner warp
 
975
         pxs = pxm + wx0 * xdisp * ydisp;
 
976
         pys = pym + wy0 * xdisp * ydisp;
 
977
      }
 
978
      else {                                                               //  use NE corner warp
 
979
         pxs = pxm + wx1 * xdisp * ydisp;
 
980
         pys = pym + wy1 * xdisp * ydisp;
 
981
      }
 
982
 
 
983
      vstat = vpixel(pxmin,pxs,pys,vpix);                                  //  input virtual pixel
 
984
 
 
985
      if (vstat) {  
 
986
         pixm[0] = vpix[0];
 
987
         pixm[1] = vpix[1];
 
988
         pixm[2] = vpix[2];
 
989
      }
 
990
      else pixm[0] = pixm[1] = pixm[2] = 0;
 
991
   }
 
992
 
 
993
   for (pym = 1; pym < hh2; pym++) {                                       //  compute image shrinkage
 
994
      pixm = PXMpix(pxmout,ww2,pym);                                       //    used by cim_get_overlap()
 
995
      if (pixm[2]) break;
 
996
   }
 
997
   cimShrink = pym-1;
 
998
 
 
999
   return;
 
1000
}
 
1001
 
 
1002
 
 
1003
//  fine-align a pair of images im1 and im2
 
1004
//  cimPXMs[im2] is aligned with cimPXMs[im1]
 
1005
//  inputs are cimOffs[im1] and cimOffs[im2] (x/y/t and corner offsets)
 
1006
//  output is adjusted offsets and corner warp values for im2 only
 
1007
//  (im1 is used as-is without corner warps)
 
1008
 
 
1009
void cim_align_image(int im1, int im2)                                     //  speedup v.11.03
 
1010
{
 
1011
   int         ii, corner1, cornerstep, cornerN, pass;
 
1012
   double      xyrange, xystep, trange, tstep, wrange, wstep;
 
1013
   double      xfL, xfH, yfL, yfH, tfL, tfH;
 
1014
   double      wxL, wxH, wyL, wyH;
 
1015
   double      match, matchB;
 
1016
   cimoffs     offsets0, offsetsB;
 
1017
   
 
1018
   offsets0 = cimOffs[im2];                                                //  initial offsets
 
1019
   offsetsB = offsets0;                                                    //  = best offsets so far
 
1020
   matchB = cim_match_images(im1,im2);                                     //  = best image match level
 
1021
 
 
1022
   if (cimPanoV) cim_show_Vimages(0,0);                                    //  v.11.04
 
1023
   else cim_show_images(0,0);                                              //  show with 50/50 blend
 
1024
 
 
1025
   for (pass = 1; pass <=2; pass++)                                        //  main pass and 2nd pass    v.10.8
 
1026
   {
 
1027
      xyrange = cimSearchRange;                                            //  x/y search range and step
 
1028
      xystep = cimSearchStep;   
 
1029
 
 
1030
      trange = xyrange / (cimOv1yhi - cimOv1ylo);                          //  angle range, radians
 
1031
      tstep = trange * xystep / xyrange;
 
1032
      
 
1033
      if (pass == 2) {
 
1034
         xyrange = 0.5 * xyrange;                                          //  2nd pass, reduce range and step
 
1035
         xystep = 0.5 * xystep;                                            //  v.11.04
 
1036
         trange = 0.5 * trange;
 
1037
         tstep = 0.5 * tstep;
 
1038
      }
 
1039
      
 
1040
      //  search x/y/t range for best match
 
1041
 
 
1042
      xfL = cimOffs[im2].xf - xyrange;
 
1043
      xfH = cimOffs[im2].xf + xyrange + 0.5 * xystep;
 
1044
      yfL = cimOffs[im2].yf - xyrange;
 
1045
      yfH = cimOffs[im2].yf + xyrange + 0.5 * xystep;
 
1046
      tfL = cimOffs[im2].tf - trange;
 
1047
      tfH = cimOffs[im2].tf + trange + 0.5 * tstep;
 
1048
 
 
1049
      for (cimOffs[im2].xf = xfL; cimOffs[im2].xf < xfH; cimOffs[im2].xf += xystep)
 
1050
      for (cimOffs[im2].yf = yfL; cimOffs[im2].yf < yfH; cimOffs[im2].yf += xystep)
 
1051
      for (cimOffs[im2].tf = tfL; cimOffs[im2].tf < tfH; cimOffs[im2].tf += tstep)
 
1052
      {
 
1053
         match = cim_match_images(im1,im2);                                //  get match level
 
1054
 
 
1055
         if (sigdiff(match,matchB,0.00001) > 0) {
 
1056
            matchB = match;                                                //  save best match
 
1057
            offsetsB = cimOffs[im2];
 
1058
         }
 
1059
 
 
1060
         sprintf(SB_text,"align: %d  match: %.5f",cimNsearch++,matchB);    //  update status bar
 
1061
         zmainloop();                                                      //  v.11.11.1
 
1062
      }
 
1063
      
 
1064
      cimOffs[im2] = offsetsB;                                             //  restore best match
 
1065
 
 
1066
      if (cimPanoV) cim_show_Vimages(0,0);                                 //  v.11.04
 
1067
      else cim_show_images(0,0);
 
1068
      
 
1069
      //  warp corners and search for best match
 
1070
 
 
1071
      wrange = cimWarpRange;                                               //  corner warp range and step
 
1072
      wstep = cimWarpStep;
 
1073
      if (! wrange) continue;
 
1074
 
 
1075
      if (pass == 2) {                                                     //  2nd pass, 1/4 range and 1/2 step
 
1076
         wrange = wrange / 4;
 
1077
         wstep = wstep / 2;
 
1078
      }
 
1079
      
 
1080
      corner1 = 0;                                                         //  process all 4 corners
 
1081
      cornerN = 3;
 
1082
      cornerstep = 1;
 
1083
 
 
1084
      if (cimPano) {
 
1085
         corner1 = 0;                                                      //  left side corners 0, 3
 
1086
         cornerN = 3;
 
1087
         cornerstep = 3;
 
1088
      }
 
1089
      
 
1090
      if (cimPanoV) {
 
1091
         corner1 = 0;                                                      //  top side corners 0, 1   v.11.04
 
1092
         cornerN = 1;
 
1093
         cornerstep = 1;
 
1094
      }
 
1095
 
 
1096
      matchB = cim_match_images(im1,im2);                                  //  initial image match level
 
1097
      
 
1098
      for (ii = corner1; ii <= cornerN; ii += cornerstep)                  //  modify one corner at a time
 
1099
      {
 
1100
         wxL = cimOffs[im2].wx[ii] - wrange;
 
1101
         wxH = cimOffs[im2].wx[ii] + wrange + 0.5 * wstep;
 
1102
         wyL = cimOffs[im2].wy[ii] - wrange;
 
1103
         wyH = cimOffs[im2].wy[ii] + wrange + 0.5 * wstep;
 
1104
 
 
1105
         for (cimOffs[im2].wx[ii] = wxL; cimOffs[im2].wx[ii] < wxH; cimOffs[im2].wx[ii] += wstep)
 
1106
         for (cimOffs[im2].wy[ii] = wyL; cimOffs[im2].wy[ii] < wyH; cimOffs[im2].wy[ii] += wstep)
 
1107
         {
 
1108
            match = cim_match_images(im1,im2);                             //  get match level
 
1109
 
 
1110
            if (sigdiff(match,matchB,0.00001) > 0) {
 
1111
               matchB = match;                                             //  save best match
 
1112
               offsetsB = cimOffs[im2];
 
1113
            }
 
1114
 
 
1115
            sprintf(SB_text,"warp: %d  match: %.5f",cimNsearch++,matchB);
 
1116
            zmainloop();                                                   //  v.11.11.1
 
1117
         }
 
1118
 
 
1119
         cimOffs[im2] = offsetsB;                                          //  restore best match
 
1120
      }
 
1121
      
 
1122
      if (cimPano) cim_warp_image_pano(im2,1);                             //  apply corner warps     v.11.04
 
1123
      else if (cimPanoV) cim_warp_image_Vpano(im2,1);
 
1124
      else  cim_warp_image(im2);
 
1125
 
 
1126
      if (cimPanoV) cim_show_Vimages(0,0);                                 //  v.11.04
 
1127
      else cim_show_images(0,0);
 
1128
   }
 
1129
 
 
1130
   return;
 
1131
}
 
1132
 
 
1133
 
 
1134
//  Compare 2 pixels using precalculated brightness ratios
 
1135
//  1.0 = perfect match   0 = total mismatch (black/white)
 
1136
 
 
1137
inline double cim_match_pixels(uint16 *pix1, uint16 *pix2)                 //  v.10.7
 
1138
{
 
1139
   double      red1, green1, blue1, red2, green2, blue2;
 
1140
   double      reddiff, greendiff, bluediff, match;
 
1141
   double      ff = 1.0 / 65536.0;
 
1142
 
 
1143
   red1 = pix1[0];
 
1144
   green1 = pix1[1];
 
1145
   blue1 = pix1[2];
 
1146
 
 
1147
   red2 = pix2[0];
 
1148
   green2 = pix2[1];
 
1149
   blue2 = pix2[2];
 
1150
 
 
1151
   reddiff = ff * fabs(red1-red2);                                         //  0 = perfect match
 
1152
   greendiff = ff * fabs(green1-green2);                                   //  1 = total mismatch
 
1153
   bluediff = ff * fabs(blue1-blue2);
 
1154
   
 
1155
   match = (1.0 - reddiff) * (1.0 - greendiff) * (1.0 - bluediff);         //  1 = perfect match
 
1156
   return match;
 
1157
}
 
1158
 
 
1159
 
 
1160
//  Compare two images in overlapping areas.
 
1161
//  Use the high-contrast pixels from cim_get_redpix()
 
1162
//  return: 1 = perfect match, 0 = total mismatch (black/white)
 
1163
//  cimPXMs[im1] is matched to cimPXMs[im2] + virtual warps
 
1164
 
 
1165
double cim_match_images(int im1, int im2)                                  //  v.11.03
 
1166
{
 
1167
   uint16      *pix1, vpix2[3];
 
1168
   int         ww, hh, ww2, hh2;
 
1169
   int         px1, py1, ii, vstat;
 
1170
   double      wwi, hhi, ww2i, hh2i, xdisp, ydisp;
 
1171
   double      wx0, wy0, wx1, wy1, wx2, wy2, wx3, wy3;
 
1172
   double      dx, dy, px2, py2;
 
1173
   double      x1, y1, t1, x2, y2, t2;
 
1174
   double      xoff, yoff, toff, costf, sintf, coeff;
 
1175
   double      match, cmatch, maxcmatch;
 
1176
   PXM         *pxm1, *pxm2;
 
1177
   
 
1178
   x1 = cimOffs[im1].xf;                                                   //  im1, im2 absolute offsets
 
1179
   y1 = cimOffs[im1].yf;
 
1180
   t1 = cimOffs[im1].tf;
 
1181
   x2 = cimOffs[im2].xf;
 
1182
   y2 = cimOffs[im2].yf;
 
1183
   t2 = cimOffs[im2].tf;
 
1184
   
 
1185
   xoff = (x2 - x1) * cos(t1) + (y2 - y1) * sin(t1);                       //  offset of im2 relative to im1
 
1186
   yoff = (y2 - y1) * cos(t1) - (x2 - x1) * sin(t1);
 
1187
   toff = t2 - t1;
 
1188
 
 
1189
   costf = cos(toff);
 
1190
   sintf = sin(toff);
 
1191
 
 
1192
   wx0 = cimOffs[im2].wx[0];                                               //  im2 corner warps
 
1193
   wy0 = cimOffs[im2].wy[0];
 
1194
   wx1 = cimOffs[im2].wx[1];
 
1195
   wy1 = cimOffs[im2].wy[1];
 
1196
   wx2 = cimOffs[im2].wx[2];
 
1197
   wy2 = cimOffs[im2].wy[2];
 
1198
   wx3 = cimOffs[im2].wx[3];
 
1199
   wy3 = cimOffs[im2].wy[3];
 
1200
 
 
1201
   pxm1 = cimPXMs[im1];                                                    //  base image
 
1202
   pxm2 = cimPXMs[im2];                                                    //  comparison image (virtual warps)
 
1203
 
 
1204
   ww = pxm1->ww;   
 
1205
   hh = pxm1->hh;
 
1206
   ww2 = ww / 2;
 
1207
   hh2 = hh / 2;
 
1208
   
 
1209
   wwi = 1.0 / ww;
 
1210
   hhi = 1.0 / hh;
 
1211
   ww2i = 1.0 / ww2;
 
1212
   hh2i = 1.0 / hh2;
 
1213
 
 
1214
   cmatch = 0;
 
1215
   maxcmatch = 1;
 
1216
   
 
1217
   if (cimPano)
 
1218
   {
 
1219
      for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++)                        //  loop overlapping pixels
 
1220
      for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++)
 
1221
      {
 
1222
         ii = py1 * ww + px1;                                              //  skip low-contrast pixels
 
1223
         if (! cimRedpix[ii]) continue;
 
1224
 
 
1225
         pix1 = PXMpix(pxm1,px1,py1);                                      //  image1 pixel
 
1226
         if (! pix1[2]) continue;                                          //  ignore void pixels
 
1227
 
 
1228
         px2 = costf * (px1 - xoff) + sintf * (py1 - yoff);                //  corresponding image2 pixel
 
1229
         py2 = costf * (py1 - yoff) - sintf * (px1 - xoff);
 
1230
         
 
1231
         dx = dy = 0.0;                                                    //  corner warp
 
1232
 
 
1233
         xdisp = (px2 - ww2) * ww2i;                                       //  -1 ... 0 ... +1
 
1234
         ydisp = (py2 - hh2) * hh2i;                                       //  v.11.04
 
1235
         
 
1236
         if (xdisp > 0)                                                    //  right half, no warp
 
1237
            dx = dy = 0;
 
1238
 
 
1239
         else if (ydisp < 0) {                                             //  use NW corner warp
 
1240
            dx = wx0 * xdisp * ydisp;
 
1241
            dy = wy0 * xdisp * ydisp;
 
1242
         }
 
1243
 
 
1244
         else {                                                            //  use SW corner warp
 
1245
            dx = wx3 * xdisp * ydisp;
 
1246
            dy = wy3 * xdisp * ydisp;
 
1247
         }
 
1248
 
 
1249
         px2 += dx;                                                        //  source pixel location
 
1250
         py2 += dy;                                                        //    after corner warps
 
1251
         
 
1252
         vstat = vpixel(pxm2,px2,py2,vpix2);
 
1253
         if (! vstat) continue;
 
1254
 
 
1255
         match = cim_match_pixels(pix1,vpix2);                             //  compare brightness adjusted
 
1256
         cmatch += match;                                                  //  accumulate total match
 
1257
         maxcmatch += 1.0;
 
1258
      }
 
1259
   }
 
1260
 
 
1261
   else if (cimPanoV)
 
1262
   {
 
1263
      for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++)                        //  loop overlapping pixels
 
1264
      for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++)
 
1265
      {
 
1266
         ii = py1 * ww + px1;                                              //  skip low-contrast pixels
 
1267
         if (! cimRedpix[ii]) continue;
 
1268
 
 
1269
         pix1 = PXMpix(pxm1,px1,py1);                                      //  image1 pixel
 
1270
         if (! pix1[2]) continue;                                          //  ignore void pixels
 
1271
 
 
1272
         px2 = costf * (px1 - xoff) + sintf * (py1 - yoff);                //  corresponding image2 pixel
 
1273
         py2 = costf * (py1 - yoff) - sintf * (px1 - xoff);
 
1274
         
 
1275
         dx = dy = 0.0;                                                    //  corner warp
 
1276
 
 
1277
         xdisp = (px2 - ww2) * ww2i;                                       //  -1 ... 0 ... +1
 
1278
         ydisp = (py2 - hh2) * hh2i;
 
1279
         
 
1280
         if (ydisp > 0)                                                    //  bottom half, no warp
 
1281
            dx = dy = 0;
 
1282
 
 
1283
         else if (xdisp < 0) {                                             //  use NW corner warp
 
1284
            dx = wx0 * xdisp * ydisp;
 
1285
            dy = wy0 * xdisp * ydisp;
 
1286
         }
 
1287
 
 
1288
         else {                                                            //  use NE corner warp
 
1289
            dx = wx1 * xdisp * ydisp;
 
1290
            dy = wy1 * xdisp * ydisp;
 
1291
         }
 
1292
 
 
1293
         px2 += dx;                                                        //  source pixel location
 
1294
         py2 += dy;                                                        //    after corner warps
 
1295
         
 
1296
         vstat = vpixel(pxm2,px2,py2,vpix2);
 
1297
         if (! vstat) continue;
 
1298
 
 
1299
         match = cim_match_pixels(pix1,vpix2);                             //  compare brightness adjusted
 
1300
         cmatch += match;                                                  //  accumulate total match
 
1301
         maxcmatch += 1.0;
 
1302
      }
 
1303
   }
 
1304
 
 
1305
   else
 
1306
   {
 
1307
      for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++)                        //  loop overlapping pixels
 
1308
      for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++)
 
1309
      {
 
1310
         ii = py1 * ww + px1;                                              //  skip low-contrast pixels
 
1311
         if (! cimRedpix[ii]) continue;
 
1312
 
 
1313
         pix1 = PXMpix(pxm1,px1,py1);                                      //  image1 pixel
 
1314
         if (! pix1[2]) continue;                                          //  ignore void pixels
 
1315
 
 
1316
         px2 = costf * (px1 - xoff) + sintf * (py1 - yoff);                //  corresponding image2 pixel
 
1317
         py2 = costf * (py1 - yoff) - sintf * (px1 - xoff);
 
1318
         
 
1319
         dx = dy = 0.0;                                                    //  corner warp
 
1320
 
 
1321
         coeff = (1.0 - py2 * hhi - px2 * wwi);                            //  corner 0  NW
 
1322
         if (coeff > 0) {
 
1323
            dx += coeff * wx0;
 
1324
            dy += coeff * wy0;
 
1325
         }
 
1326
         coeff = (1.0 - py2 * hhi - (ww - px2) * wwi);                     //  corner 1  NE
 
1327
         if (coeff > 0) {
 
1328
            dx += coeff * wx1;
 
1329
            dy += coeff * wy1;
 
1330
         }
 
1331
         coeff = (1.0 - (hh - py2) * hhi - (ww - px2) * wwi);              //  corner 2  SE
 
1332
         if (coeff > 0) {
 
1333
            dx += coeff * wx2;
 
1334
            dy += coeff * wy2;
 
1335
         }
 
1336
         coeff = (1.0 - (hh - py2) * hhi - px2 * wwi);                     //  corner 3  SW
 
1337
         if (coeff > 0) {
 
1338
            dx += coeff * wx3;
 
1339
            dy += coeff * wy3;
 
1340
         }
 
1341
 
 
1342
         px2 += dx;                                                        //  source pixel location
 
1343
         py2 += dy;                                                        //    after corner warps
 
1344
         
 
1345
         vstat = vpixel(pxm2,px2,py2,vpix2);
 
1346
         if (! vstat) continue;
 
1347
 
 
1348
         match = cim_match_pixels(pix1,vpix2);                             //  compare brightness adjusted
 
1349
         cmatch += match;                                                  //  accumulate total match
 
1350
         maxcmatch += 1.0;
 
1351
      }
 
1352
   }
 
1353
   
 
1354
   return cmatch / maxcmatch;
 
1355
}
 
1356
 
 
1357
 
 
1358
//  combine and show all images
 
1359
//  fnew >> make new E3 output image and adjust x and y offsets
 
1360
//  cimPXMw[*] >> E3pxm16 >> main window
 
1361
//  fblend:  0 > 50/50 blend,  1 > gradual blend
 
1362
 
 
1363
namespace  cim_show_images_names {
 
1364
   int         im1, im2, iminc, fblendd;
 
1365
   int         wwlo[10], wwhi[10];
 
1366
   int         hhlo[10], hhhi[10];
 
1367
   double      costf[10], sintf[10];
 
1368
}
 
1369
 
 
1370
void cim_show_images(int fnew, int fblend)                                 //  v.10.7
 
1371
{
 
1372
   using namespace cim_show_images_names;
 
1373
 
 
1374
   void * cim_show_images_wthread(void *arg);
 
1375
 
 
1376
   int         imx, pxr, pyr, ii, px3, py3;
 
1377
   int         ww, hh, wwmin, wwmax, hhmin, hhmax, bmid;
 
1378
   double      xf, yf, tf;
 
1379
   uint16      *pix3;
 
1380
   
 
1381
   mutex_lock(&Fpixmap_lock);                                              //  stop window updates
 
1382
   
 
1383
   fblendd = fblend;                                                       //  blend 50/50 or gradual ramp
 
1384
 
 
1385
   im1 = cimShowIm1;                                                       //  two images to show
 
1386
   im2 = cimShowIm2;
 
1387
   iminc = im2 - im1;                                                      //  v.10.9
 
1388
   
 
1389
   if (cimShowAll) {                                                       //  show all images     v.10.9
 
1390
      im1 = 0;
 
1391
      im2 = cimNF-1;
 
1392
      iminc = 1;
 
1393
   }
 
1394
 
 
1395
   for (imx = 0; imx < cimNF; imx++) {                                     //  pre-calculate
 
1396
      costf[imx] = cos(cimOffs[imx].tf);
 
1397
      sintf[imx] = sin(cimOffs[imx].tf);
 
1398
   }
 
1399
 
 
1400
   if (fnew) PXM_free(E3pxm16);                                            //  force new output pixmap
 
1401
   
 
1402
   if (! E3pxm16)                                                          //  allocate output pixmap
 
1403
   {
 
1404
      wwmin = hhmin = 9999;                                                //  initial values
 
1405
      wwmax = cimPXMw[im2]->ww;
 
1406
      hhmax = cimPXMw[im2]->hh;
 
1407
      
 
1408
      for (imx = im1; imx <= im2; imx += iminc)                            //  find min and max ww and hh extents
 
1409
      {
 
1410
         xf = cimOffs[imx].xf;
 
1411
         yf = cimOffs[imx].yf;
 
1412
         tf = cimOffs[imx].tf;
 
1413
         ww = cimPXMw[imx]->ww;
 
1414
         hh = cimPXMw[imx]->hh;
 
1415
         if (xf < wwmin) wwmin = xf;
 
1416
         if (xf - tf * hh < wwmin) wwmin = xf + tf * hh;
 
1417
         if (xf + ww > wwmax) wwmax = xf + ww;
 
1418
         if (xf + ww - tf * hh > wwmax) wwmax = xf + ww - tf * hh;
 
1419
         if (yf < hhmin) hhmin = yf;
 
1420
         if (yf + tf * ww < hhmin) hhmin = yf + tf * ww;
 
1421
         if (yf + hh > hhmax) hhmax = yf + hh;
 
1422
         if (yf + hh + tf * ww > hhmax) hhmax = yf + hh + tf * ww;
 
1423
      }
 
1424
 
 
1425
      for (imx = im1; imx <= im2; imx += iminc) {                          //  align to top and left edges
 
1426
         cimOffs[imx].xf -= wwmin;
 
1427
         cimOffs[imx].yf -= hhmin;
 
1428
      }
 
1429
      wwmax = wwmax - wwmin;
 
1430
      hhmax = hhmax - hhmin;
 
1431
      wwmin = hhmin = 0;
 
1432
 
 
1433
      if (cimPano) {
 
1434
         for (imx = im1; imx <= im2; imx += iminc)                         //  deliberate margins      v.11.03
 
1435
            cimOffs[imx].yf += 10;
 
1436
         hhmax += 20;
 
1437
      }
 
1438
 
 
1439
      if (cimPanoV) {
 
1440
         for (imx = im1; imx <= im2; imx += iminc)                         //  deliberate margins      v.11.04
 
1441
            cimOffs[imx].xf += 10;
 
1442
         wwmax += 20;
 
1443
      }
 
1444
 
 
1445
      E3pxm16 = PXM_make(wwmax,hhmax,16);                                  //  allocate output image
 
1446
      E3ww = wwmax;
 
1447
      E3hh = hhmax;
 
1448
   }
 
1449
 
 
1450
   for (imx = im1; imx <= im2; imx += iminc)                               //  get ww range of each image
 
1451
   {
 
1452
      ww = cimPXMw[imx]->ww;
 
1453
      hh = cimPXMw[imx]->hh;
 
1454
      tf = cimOffs[imx].tf;
 
1455
      wwlo[imx] = cimOffs[imx].xf;
 
1456
      wwhi[imx] = wwlo[imx] + ww;
 
1457
      wwlo[imx] -= 0.5 * tf * hh;                                          //  use midpoint of sloping edges
 
1458
      wwhi[imx] -= 0.5 * tf * hh;
 
1459
   }
 
1460
 
 
1461
   if (cimBlend) {                                                         //  blend width active
 
1462
      for (imx = im1; imx <= im2-1; imx += iminc)                          //  reduce for blend width
 
1463
      {
 
1464
         if (wwhi[imx] - wwlo[imx+1] > cimBlend) {
 
1465
            bmid = (wwhi[imx] + wwlo[imx+1]) / 2;
 
1466
            wwlo[imx+1] = bmid - cimBlend / 2;
 
1467
            wwhi[imx] = bmid + cimBlend / 2;
 
1468
         }
 
1469
      }
 
1470
   }
 
1471
 
 
1472
   for (ii = 0; ii < Nwt; ii++)                                            //  start worker threads   v.10.7
 
1473
      start_wthread(cim_show_images_wthread,&wtnx[ii]);
 
1474
   wait_wthreads();                                                        //  wait for completion
 
1475
 
 
1476
   if (cimRedpix) 
 
1477
   {
 
1478
      imx = cimRedImage;                                                   //  paint red pixels for current image
 
1479
      ww = cimPXMw[imx]->ww;                                               //    being aligned
 
1480
      hh = cimPXMw[imx]->hh;
 
1481
 
 
1482
      for (ii = 0; ii < ww * hh; ii++)
 
1483
      {
 
1484
         if (cimRedpix[ii]) {
 
1485
            pyr = ii / ww;                                                 //  red pixel
 
1486
            pxr = ii - pyr * ww;
 
1487
            px3 = cimOffs[imx].xf + pxr * costf[imx] - pyr * sintf[imx] + 0.5;
 
1488
            py3 = cimOffs[imx].yf + pyr * costf[imx] + pxr * sintf[imx] + 0.5;
 
1489
            pix3 = PXMpix(E3pxm16,px3,py3);
 
1490
            pix3[0] = 65535; pix3[1] = pix3[2] = 1;
 
1491
         }
 
1492
      }
 
1493
   }
 
1494
 
 
1495
   mutex_unlock(&Fpixmap_lock);
 
1496
   mwpaint2();                                                             //  update window
 
1497
   zmainloop();                                                            //  v.11.11.1
 
1498
 
 
1499
   return;
 
1500
}
 
1501
 
 
1502
 
 
1503
void * cim_show_images_wthread(void *arg)                                  //  working thread   v.10.7
 
1504
{
 
1505
   using namespace cim_show_images_names;
 
1506
 
 
1507
   int         index = *((int *) (arg));
 
1508
   int         imx, imy;
 
1509
   int         px3, py3;
 
1510
   int         vstat, vstat1, vstat2;
 
1511
   int         red1, green1, blue1;
 
1512
   int         red2, green2, blue2;
 
1513
   int         red3, green3, blue3;
 
1514
   double      f1, f2, px, py;
 
1515
   uint16      vpix[3], *pix3;
 
1516
   
 
1517
   red1 = green1 = blue1 = 0;
 
1518
 
 
1519
   f1 = f2 = 0.5;                                                          //  to use if no fblend flag
 
1520
 
 
1521
   for (py3 = index; py3 < E3hh; py3 += Nwt)                               //  loop E3 rows
 
1522
   for (px3 = 0; px3 < E3ww; px3++)                                        //  loop E3 columns
 
1523
   {
 
1524
      vstat1 = vstat2 = 0;
 
1525
      
 
1526
      for (imx = imy = im1; imx <= im2; imx += iminc)                      //  find which images overlap this pixel
 
1527
      {
 
1528
         if (px3 < wwlo[imx] || px3 > wwhi[imx]) continue;
 
1529
         px = costf[imx] * (px3 - cimOffs[imx].xf) + sintf[imx] * (py3 - cimOffs[imx].yf);
 
1530
         py = costf[imx] * (py3 - cimOffs[imx].yf) - sintf[imx] * (px3 - cimOffs[imx].xf);
 
1531
         vstat = vpixel(cimPXMw[imx],px,py,vpix);
 
1532
         if (! vstat) continue;
 
1533
 
 
1534
         if (! vstat1) {                                                   //  first overlapping image
 
1535
            vstat1 = 1;
 
1536
            imy = imx;
 
1537
            red1 = vpix[0];
 
1538
            green1 = vpix[1];
 
1539
            blue1 = vpix[2];
 
1540
         }
 
1541
         else {                                                            //  second image
 
1542
            vstat2 = 1;
 
1543
            red2 = vpix[0];
 
1544
            green2 = vpix[1];
 
1545
            blue2 = vpix[2];
 
1546
            break;
 
1547
         }
 
1548
      }
 
1549
      
 
1550
      imx = imy;                                                           //  first of 1 or 2 overlapping images
 
1551
 
 
1552
      if (vstat1) {
 
1553
         if (! vstat2) {
 
1554
            red3 = red1;                                                   //  use image1 pixel
 
1555
            green3 = green1;
 
1556
            blue3 = blue1; 
 
1557
         }
 
1558
         else {                                                            //  use blended image1 + image2 pixels
 
1559
            if (fblendd) {
 
1560
               f1 = wwhi[imx] - px3;                                       //  gradual blend
 
1561
               f2 = px3 - wwlo[imx+1];
 
1562
               f1 = f1 / (f1 + f2);
 
1563
               f2 = 1.0 - f1;
 
1564
            }
 
1565
            red3 = f1 * red1 + f2 * red2 + 0.5;
 
1566
            green3 = f1 * green1 + f2 * green2 + 0.5;
 
1567
            blue3 = f1 * blue1 + f2 * blue2 + 0.5;
 
1568
         }
 
1569
      }
 
1570
 
 
1571
      else red3 = green3 = blue3 = 0;                                      //  no overlapping image, use black pixel
 
1572
 
 
1573
      pix3 = PXMpix(E3pxm16,px3,py3);                                      //  output pixel
 
1574
      pix3[0] = red3;
 
1575
      pix3[1] = green3;
 
1576
      pix3[2] = blue3;
 
1577
   }
 
1578
 
 
1579
   exit_wthread();
 
1580
   return 0;                                                               //  not executed, avoid gcc warning
 
1581
}
 
1582
 
 
1583
 
 
1584
//  version for vertical panorama
 
1585
 
 
1586
void cim_show_Vimages(int fnew, int fblend)                                //  v.11.04
 
1587
{
 
1588
   using namespace cim_show_images_names;
 
1589
 
 
1590
   void * cim_show_Vimages_wthread(void *arg);
 
1591
 
 
1592
   int         imx, pxr, pyr, ii, px3, py3;
 
1593
   int         ww, hh, wwmin, wwmax, hhmin, hhmax, bmid;
 
1594
   double      xf, yf, tf;
 
1595
   uint16      *pix3;
 
1596
   
 
1597
   mutex_lock(&Fpixmap_lock);                                              //  stop window updates
 
1598
   
 
1599
   fblendd = fblend;                                                       //  blend 50/50 or gradual ramp
 
1600
 
 
1601
   im1 = 0;                                                                //  show all images (pano)
 
1602
   im2 = cimNF-1;
 
1603
 
 
1604
   for (imx = 0; imx < cimNF; imx++) {                                     //  pre-calculate
 
1605
      costf[imx] = cos(cimOffs[imx].tf);
 
1606
      sintf[imx] = sin(cimOffs[imx].tf);
 
1607
   }
 
1608
 
 
1609
   if (fnew) PXM_free(E3pxm16);                                            //  force new output pixmap
 
1610
   
 
1611
   if (! E3pxm16)                                                          //  allocate output pixmap
 
1612
   {
 
1613
      wwmin = hhmin = 9999;
 
1614
      wwmax = hhmax = 0;
 
1615
      
 
1616
      for (imx = im1; imx <= im2; imx++)                                   //  find min and max ww and hh extents
 
1617
      {
 
1618
         xf = cimOffs[imx].xf;
 
1619
         yf = cimOffs[imx].yf;
 
1620
         tf = cimOffs[imx].tf;
 
1621
         ww = cimPXMw[imx]->ww;
 
1622
         hh = cimPXMw[imx]->hh;
 
1623
         if (xf < wwmin) wwmin = xf;
 
1624
         if (xf - tf * hh < wwmin) wwmin = xf + tf * hh;
 
1625
         if (xf + ww > wwmax) wwmax = xf + ww;
 
1626
         if (xf + ww - tf * hh > wwmax) wwmax = xf + ww - tf * hh;
 
1627
         if (yf < hhmin) hhmin = yf;
 
1628
         if (yf + tf * ww < hhmin) hhmin = yf + tf * ww;
 
1629
         if (yf + hh > hhmax) hhmax = yf + hh;
 
1630
         if (yf + hh + tf * ww > hhmax) hhmax = yf + hh + tf * ww;
 
1631
      }
 
1632
 
 
1633
      for (imx = im1; imx <= im2; imx++) {                                 //  align to top and left edges
 
1634
         cimOffs[imx].xf -= wwmin;
 
1635
         cimOffs[imx].yf -= hhmin;
 
1636
      }
 
1637
      wwmax = wwmax - wwmin;
 
1638
      hhmax = hhmax - hhmin;
 
1639
      wwmin = hhmin = 0;
 
1640
 
 
1641
      for (imx = im1; imx <= im2; imx++)                                   //  deliberate margins
 
1642
         cimOffs[imx].xf += 10;
 
1643
      wwmax += 20;
 
1644
 
 
1645
      E3pxm16 = PXM_make(wwmax,hhmax,16);                                  //  allocate output image
 
1646
      E3ww = wwmax;
 
1647
      E3hh = hhmax;
 
1648
   }
 
1649
 
 
1650
   for (imx = im1; imx <= im2; imx++)                                      //  get hh range of each image
 
1651
   {
 
1652
      ww = cimPXMw[imx]->ww;
 
1653
      hh = cimPXMw[imx]->hh;
 
1654
      tf = cimOffs[imx].tf;
 
1655
      hhlo[imx] = cimOffs[imx].yf;
 
1656
      hhhi[imx] = hhlo[imx] + hh;
 
1657
      hhlo[imx] += 0.5 * tf * ww;                                          //  use midpoint of sloping edges
 
1658
      hhhi[imx] += 0.5 * tf * ww;
 
1659
   }
 
1660
 
 
1661
   if (cimBlend) {                                                         //  blend width active
 
1662
      for (imx = im1; imx <= im2-1; imx++)                                 //  reduce for blend width
 
1663
      {
 
1664
         if (hhhi[imx] - hhlo[imx+1] > cimBlend) {
 
1665
            bmid = (hhhi[imx] + hhlo[imx+1]) / 2;
 
1666
            hhlo[imx+1] = bmid - cimBlend / 2;
 
1667
            hhhi[imx] = bmid + cimBlend / 2;
 
1668
         }
 
1669
      }
 
1670
   }
 
1671
 
 
1672
   for (ii = 0; ii < Nwt; ii++)                                            //  start worker threads   v.10.7
 
1673
      start_wthread(cim_show_Vimages_wthread,&wtnx[ii]);
 
1674
   wait_wthreads();                                                        //  wait for completion
 
1675
 
 
1676
   if (cimRedpix) 
 
1677
   {
 
1678
      imx = cimRedImage;                                                   //  paint red pixels for current image
 
1679
      ww = cimPXMw[imx]->ww;                                               //    being aligned
 
1680
      hh = cimPXMw[imx]->hh;
 
1681
 
 
1682
      for (ii = 0; ii < ww * hh; ii++)
 
1683
      {
 
1684
         if (cimRedpix[ii]) {
 
1685
            pyr = ii / ww;                                                 //  red pixel
 
1686
            pxr = ii - pyr * ww;
 
1687
            px3 = cimOffs[imx].xf + pxr * costf[imx] - pyr * sintf[imx] + 0.5;
 
1688
            py3 = cimOffs[imx].yf + pyr * costf[imx] + pxr * sintf[imx] + 0.5;
 
1689
            pix3 = PXMpix(E3pxm16,px3,py3);
 
1690
            pix3[0] = 65535; pix3[1] = pix3[2] = 1;
 
1691
         }
 
1692
      }
 
1693
   }
 
1694
 
 
1695
   mutex_unlock(&Fpixmap_lock);
 
1696
   mwpaint2();                                                             //  update window
 
1697
   zmainloop();                                                            //  v.11.11.1
 
1698
   return;
 
1699
}
 
1700
 
 
1701
 
 
1702
void * cim_show_Vimages_wthread(void *arg)                                 //  working thread   v.11.04
 
1703
{
 
1704
   using namespace cim_show_images_names;
 
1705
 
 
1706
   int         index = *((int *) (arg));
 
1707
   int         imx, imy;
 
1708
   int         px3, py3;
 
1709
   int         vstat, vstat1, vstat2;
 
1710
   int         red1, green1, blue1;
 
1711
   int         red2, green2, blue2;
 
1712
   int         red3, green3, blue3;
 
1713
   double      f1, f2, px, py;
 
1714
   uint16      vpix[3], *pix3;
 
1715
   
 
1716
   red1 = green1 = blue1 = 0;
 
1717
 
 
1718
   f1 = f2 = 0.5;                                                          //  to use if no fblend flag
 
1719
 
 
1720
   for (py3 = index; py3 < E3hh; py3 += Nwt)                               //  loop E3 rows
 
1721
   for (px3 = 0; px3 < E3ww; px3++)                                        //  loop E3 columns
 
1722
   {
 
1723
      vstat1 = vstat2 = 0;
 
1724
      
 
1725
      for (imx = imy = im1; imx <= im2; imx++)                             //  find which images overlap this pixel
 
1726
      {
 
1727
         if (py3 < hhlo[imx] || py3 > hhhi[imx]) continue;
 
1728
         px = costf[imx] * (px3 - cimOffs[imx].xf) + sintf[imx] * (py3 - cimOffs[imx].yf);
 
1729
         py = costf[imx] * (py3 - cimOffs[imx].yf) - sintf[imx] * (px3 - cimOffs[imx].xf);
 
1730
         vstat = vpixel(cimPXMw[imx],px,py,vpix);
 
1731
         if (! vstat) continue;
 
1732
 
 
1733
         if (! vstat1) {                                                   //  first overlapping image
 
1734
            vstat1 = 1;
 
1735
            imy = imx;
 
1736
            red1 = vpix[0];
 
1737
            green1 = vpix[1];
 
1738
            blue1 = vpix[2];
 
1739
         }
 
1740
         else {                                                            //  second image
 
1741
            vstat2 = 1;
 
1742
            red2 = vpix[0];
 
1743
            green2 = vpix[1];
 
1744
            blue2 = vpix[2];
 
1745
            break;
 
1746
         }
 
1747
      }
 
1748
      
 
1749
      imx = imy;                                                           //  first of 1 or 2 overlapping images
 
1750
 
 
1751
      if (vstat1) {
 
1752
         if (! vstat2) {
 
1753
            red3 = red1;                                                   //  use image1 pixel
 
1754
            green3 = green1;
 
1755
            blue3 = blue1; 
 
1756
         }
 
1757
         else {                                                            //  use blended image1 + image2 pixels
 
1758
            if (fblendd) {
 
1759
               f1 = hhhi[imx] - py3;                                       //  gradual blend
 
1760
               f2 = py3 - hhlo[imx+1];
 
1761
               f1 = f1 / (f1 + f2);
 
1762
               f2 = 1.0 - f1;
 
1763
            }
 
1764
            red3 = f1 * red1 + f2 * red2 + 0.5;
 
1765
            green3 = f1 * green1 + f2 * green2 + 0.5;
 
1766
            blue3 = f1 * blue1 + f2 * blue2 + 0.5;
 
1767
         }
 
1768
      }
 
1769
 
 
1770
      else red3 = green3 = blue3 = 0;                                      //  no overlapping image, use black pixel
 
1771
 
 
1772
      pix3 = PXMpix(E3pxm16,px3,py3);                                      //  output pixel
 
1773
      pix3[0] = red3;
 
1774
      pix3[1] = green3;
 
1775
      pix3[2] = blue3;
 
1776
   }
 
1777
 
 
1778
   exit_wthread();
 
1779
   return 0;                                                               //  not executed, avoid gcc warning
 
1780
}
 
1781
 
 
1782
 
 
1783
//  cut-off edges of output image where all input images do not overlap
 
1784
//  (HDR HDF Stack)
 
1785
 
 
1786
void cim_trim()                                                            //  v.10.9
 
1787
{
 
1788
   int      edgex[8] =  {  0, 1,  2, 2,  2, 1,  0, 0 };                    //  4 corners and 4 midpoints of rectangle
 
1789
   int      edgey[8] =  {  0, 0,  0, 1,  2, 2,  2, 1 };                    //  0 and 2 mark corners, 1 marks midpoints
 
1790
   int      edgewx[4] = { +1, -1, -1, +1 };
 
1791
   int      edgewy[4] = { +1, +1, -1, -1 };
 
1792
 
 
1793
   int      imx, ii, jj, ww, hh, px3, py3, px9, py9;
 
1794
   int      wwmin, wwmax, hhmin, hhmax;
 
1795
   double   xf, yf, tf, sintf, costf, px, py, wx, wy;
 
1796
   uint16   *pix3, *pix9;
 
1797
 
 
1798
   wwmin = hhmin = 0;
 
1799
   wwmax = E3ww;
 
1800
   hhmax = E3hh;
 
1801
   
 
1802
   for (imx = 0; imx < cimNF; imx++)                                       //  loop all images
 
1803
   {
 
1804
      ww = cimPXMw[imx]->ww;                                               //  image size
 
1805
      hh = cimPXMw[imx]->hh;
 
1806
      xf = cimOffs[imx].xf;                                                //  alignment offsets
 
1807
      yf = cimOffs[imx].yf;
 
1808
      tf = cimOffs[imx].tf;
 
1809
      sintf = sin(tf);
 
1810
      costf = cos(tf);
 
1811
      
 
1812
      for (ii = 0; ii < 8; ii++)                                           //  8 points around image rectangle
 
1813
      {
 
1814
         px = ww * edgex[ii] / 2;                                          //  coordinates before warping
 
1815
         py = hh * edgey[ii] / 2;
 
1816
         
 
1817
         if (edgex[ii] != 1 && edgey[ii] != 1) {                           //  if a corner
 
1818
            jj = ii / 2;
 
1819
            wx = cimOffs[imx].wx[jj];                                      //  corner warp
 
1820
            wy = cimOffs[imx].wy[jj];
 
1821
            if (edgewx[jj] > 0 && wx < 0) px -= wx;                        //  if warp direction inwards,
 
1822
            if (edgewx[jj] < 0 && wx > 0) px -= wx;                        //    reduce px/py by warp
 
1823
            if (edgewy[jj] > 0 && wy < 0) py -= wy;
 
1824
            if (edgewy[jj] < 0 && wy > 0) py -= wy;
 
1825
         }
 
1826
 
 
1827
         px3 = xf + px * costf - py * sintf;                               //  map px/py to output image px3/py3
 
1828
         py3 = yf + py * costf + px * sintf;
 
1829
         
 
1830
         if (edgex[ii] != 1) {
 
1831
            if (px3 < ww/2 && px3 > wwmin) wwmin = px3;                    //  remember px3/py3 extremes
 
1832
            if (px3 > ww/2 && px3 < wwmax) wwmax = px3;
 
1833
         }
 
1834
 
 
1835
         if (edgey[ii] != 1) {
 
1836
            if (py3 < hh/2 && py3 > hhmin) hhmin = py3;
 
1837
            if (py3 > hh/2 && py3 < hhmax) hhmax = py3;
 
1838
         }
 
1839
      }
 
1840
   }
 
1841
   
 
1842
   wwmin += 2;                                                             //  compensate rounding
 
1843
   wwmax -= 2;
 
1844
   hhmin += 2;
 
1845
   hhmax -= 2;
 
1846
 
 
1847
   ww = wwmax - wwmin;                                                     //  new image size
 
1848
   hh = hhmax - hhmin;
 
1849
 
 
1850
   if (ww < 0.7 * E3ww) return;                                            //  sanity check
 
1851
   if (hh < 0.7 * E3hh) return;
 
1852
   
 
1853
   E9pxm16 = PXM_make(ww,hh,16);
 
1854
 
 
1855
   for (py3 = hhmin; py3 < hhmax; py3++)                                   //  E9 = trimmed E3
 
1856
   for (px3 = wwmin; px3 < wwmax; px3++)
 
1857
   {
 
1858
      px9 = px3 - wwmin;
 
1859
      py9 = py3 - hhmin;
 
1860
      pix3 = PXMpix(E3pxm16,px3,py3);
 
1861
      pix9 = PXMpix(E9pxm16,px9,py9);
 
1862
      pix9[0] = pix3[0];
 
1863
      pix9[1] = pix3[1];
 
1864
      pix9[2] = pix3[2];
 
1865
   }
 
1866
 
 
1867
   PXM_free(E3pxm16);                                                      //  E3 = E9
 
1868
   E3pxm16 = E9pxm16;
 
1869
   E9pxm16 = 0;
 
1870
   E3ww = ww;
 
1871
   E3hh = hh;
 
1872
 
 
1873
   return;
 
1874
}
 
1875
 
 
1876
 
 
1877
//  dump offsets to stdout - diagnostic tool
 
1878
 
 
1879
void cim_dump_offsets(cchar *text)
 
1880
{
 
1881
   printf("\n offsets: %s \n",text);
 
1882
 
 
1883
   for (int imx = 0; imx < cimNF; imx++)
 
1884
   {
 
1885
      printf(" imx %d  x/y/t: %.1f %.1f %.4f  w0: %.1f %.1f  w1: %.1f %.1f  w2: %.1f %.1f  w3: %.1f %.1f \n",
 
1886
          imx, cimOffs[imx].xf, cimOffs[imx].yf, cimOffs[imx].tf,
 
1887
               cimOffs[imx].wx[0], cimOffs[imx].wy[0], cimOffs[imx].wx[1], cimOffs[imx].wy[1], 
 
1888
               cimOffs[imx].wx[2], cimOffs[imx].wy[2], cimOffs[imx].wx[3], cimOffs[imx].wy[3]);
 
1889
   }
 
1890
 
 
1891
   return;
 
1892
}
 
1893
 
 
1894
 
 
1895
/**************************************************************************
 
1896
 
 
1897
   Make an HDR (high dynamic range) image from several images of the same
 
1898
   subject with different exposure levels. The composite image has better
 
1899
   visibility of detail in both the brightest and darkest areas.
 
1900
 
 
1901
***************************************************************************/
 
1902
 
 
1903
int      HDRstat;                                                          //  1 = OK, 0 = failed or canceled
 
1904
double   HDRinitAlignSize = 160;                                           //  initial align image size
 
1905
double   HDRimageIncrease = 1.6;                                           //  image size increase per align cycle
 
1906
double   HDRsampSize = 6000;                                               //  pixel sample size   11.03
 
1907
 
 
1908
double   HDRinitSearchRange = 8.0;                                         //  initial search range, +/- pixels
 
1909
double   HDRinitSearchStep = 1.0;                                          //  initial search step, pixels 
 
1910
double   HDRinitWarpRange = 3.0;                                           //  initial corner warp range, +/- pixels
 
1911
double   HDRinitWarpStep = 0.67;                                           //  initial corner warp step, pixels 
 
1912
double   HDRsearchRange = 2.0;                                             //  normal search range, +/- pixels
 
1913
double   HDRsearchStep = 0.67;                                             //  normal search step, pixels 
 
1914
double   HDRwarpRange = 2.0;                                               //  normal corner warp range, +/- pixels
 
1915
double   HDRwarpStep = 0.67;                                               //  normal corner warp step, pixels
 
1916
 
 
1917
float    *HDRbright = 0;                                                   //  maps brightness per pixel
 
1918
zdialog  *HDRzd = 0;                                                       //  tweak dialog
 
1919
double   HDR_respfac[10][1000];                                            //  contribution / image / pixel brightness
 
1920
 
 
1921
void * HDR_align_thread(void *);                                           //  align 2 images
 
1922
void   HDR_brightness();                                                   //  compute pixel brightness levels
 
1923
void   HDR_tweak();                                                        //  adjust image contribution curves
 
1924
void * HDR_combine_thread(void *);                                         //  combine images per contribution curves
 
1925
 
 
1926
editfunc    EFhdr;                                                         //  edit function data
 
1927
 
 
1928
 
 
1929
//  menu function
 
1930
 
 
1931
void m_HDR(GtkWidget *, cchar *)                                           //  v.10.7
 
1932
{
 
1933
   char        **flist, *ftemp;
 
1934
   int         imx, jj, err, px, py, ww, hh;
 
1935
   double      diffw, diffh;
 
1936
   double      fbright[10], btemp;
 
1937
   double      pixsum, fnorm = 3.0 / 65536.0;
 
1938
   uint16      *pixel;
 
1939
   PXM         *pxmtemp;
 
1940
   
 
1941
   zfuncs::F1_help_topic = "HDR";                                          //  help topic
 
1942
 
 
1943
   if (mod_keep()) return;                                                 //  warn unsaved changes
 
1944
   if (! menulock(1)) return;                                              //  test menu lock            v.11.07
 
1945
   menulock(0);
 
1946
 
 
1947
   for (imx = 0; imx < 10; imx++)
 
1948
   {                                                                       //  clear all file and PXM data
 
1949
      cimFile[imx] = 0;
 
1950
      cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0;
 
1951
   }
 
1952
 
 
1953
   cimNF = 0;   
 
1954
   HDRbright = 0;
 
1955
 
 
1956
   flist = zgetfileN(ZTX("Select 2 to 9 files"),"openN",curr_file);        //  select images to combine
 
1957
   if (! flist) return;
 
1958
 
 
1959
   for (imx = 0; flist[imx]; imx++);                                       //  count selected files
 
1960
   if (imx < 2 || imx > 9) {
 
1961
      zmessageACK(mWin,ZTX("Select 2 to 9 files"));
 
1962
      goto cleanup;
 
1963
   }
 
1964
 
 
1965
   cimNF = imx;                                                            //  file count
 
1966
   for (imx = 0; imx < cimNF; imx++)
 
1967
      cimFile[imx] = strdupz(flist[imx],0,"HDR");                          //  set up file list
 
1968
   
 
1969
   if (! cim_load_files()) goto cleanup;                                   //  load and check all files
 
1970
 
 
1971
   ww = cimPXMf[0]->ww;
 
1972
   hh = cimPXMf[0]->hh;
 
1973
   
 
1974
   for (imx = 1; imx < cimNF; imx++)                                       //  check image compatibility
 
1975
   {
 
1976
      diffw = abs(ww - cimPXMf[imx]->ww);
 
1977
      diffw = diffw / ww;
 
1978
      diffh = abs(hh - cimPXMf[imx]->hh);
 
1979
      diffh = diffh / hh;
 
1980
 
 
1981
      if (diffw > 0.02 || diffh > 0.02) {
 
1982
         zmessageACK(mWin,ZTX("Images are not all the same size"));
 
1983
         goto cleanup;
 
1984
      }
 
1985
   }
 
1986
   
 
1987
   free_resources();                                                       //  ready to commit
 
1988
 
 
1989
   err = f_open(cimFile[0],0);                                             //  curr_file = 1st file in list
 
1990
   if (err) goto cleanup;
 
1991
 
 
1992
   EFhdr.funcname = "HDR";
 
1993
   if (! edit_setup(EFhdr)) goto cleanup;                                  //  setup edit (will lock)
 
1994
 
 
1995
   for (imx = 0; imx < cimNF; imx++)                                       //  compute image brightness levels
 
1996
   {
 
1997
      pixsum = 0;
 
1998
      for (py = 0; py < Fhh; py++)
 
1999
      for (px = 0; px < Fww; px++)
 
2000
      {
 
2001
         pixel = PXMpix(cimPXMf[imx],px,py);
 
2002
         pixsum += fnorm * (pixel[0] + pixel[1] + pixel[2]);
 
2003
      }
 
2004
      fbright[imx] = pixsum / (Fww * Fhh);
 
2005
   }
 
2006
 
 
2007
   for (imx = 0; imx < cimNF; imx++)                                       //  sort file and pixmap lists
 
2008
   for (jj = imx+1; jj < cimNF; jj++)                                      //    by decreasing brightness
 
2009
   {
 
2010
      if (fbright[jj] > fbright[imx]) {                                    //  bubble sort
 
2011
         btemp = fbright[jj];
 
2012
         fbright[jj] = fbright[imx];
 
2013
         fbright[imx] = btemp;
 
2014
         ftemp = cimFile[jj];
 
2015
         cimFile[jj] = cimFile[imx];
 
2016
         cimFile[imx] = ftemp;
 
2017
         pxmtemp = cimPXMf[jj];
 
2018
         cimPXMf[jj] = cimPXMf[imx];
 
2019
         cimPXMf[imx] = pxmtemp;
 
2020
      }
 
2021
   }
 
2022
   
 
2023
   start_thread(HDR_align_thread,0);                                       //  align each pair of images
 
2024
   wrapup_thread(0);                                                       //  wait for completion
 
2025
   if (HDRstat != 1) goto cancel;
 
2026
 
 
2027
   HDR_brightness();                                                       //  compute pixel brightness levels
 
2028
   if (HDRstat != 1) goto cancel;
 
2029
   
 
2030
   HDR_tweak();                                                            //  combine images based on user inputs
 
2031
   if (HDRstat != 1) goto cancel;
 
2032
 
 
2033
   CEF->Fmod = 1;                                                          //  done
 
2034
   edit_done(EFhdr);
 
2035
   goto cleanup;
 
2036
 
 
2037
cancel:
 
2038
   edit_cancel(EFhdr);
 
2039
 
 
2040
cleanup:
 
2041
 
 
2042
   if (flist) {
 
2043
      for (imx = 0; flist[imx]; imx++)                                     //  free file list
 
2044
         zfree(flist[imx]);
 
2045
      zfree(flist);
 
2046
   }
 
2047
 
 
2048
   for (imx = 0; imx < cimNF; imx++) {                                     //  free cim file and PXM data
 
2049
      if (cimFile[imx]) zfree(cimFile[imx]);
 
2050
      if (cimPXMf[imx]) PXM_free(cimPXMf[imx]);
 
2051
      if (cimPXMs[imx]) PXM_free(cimPXMs[imx]);
 
2052
      if (cimPXMw[imx]) PXM_free(cimPXMw[imx]);
 
2053
   }
 
2054
   
 
2055
   if (HDRbright) zfree(HDRbright);
 
2056
   *SB_text = 0;
 
2057
 
 
2058
   return;
 
2059
}
 
2060
 
 
2061
 
 
2062
//  HDR align each pair of input images, output combined image to E3pxm16
 
2063
//  cimPXMf[*]  original image
 
2064
//  cimPXMs[*]  scaled and color adjusted for pixel comparisons
 
2065
//  cimPXMw[*]  warped for display
 
2066
 
 
2067
void * HDR_align_thread(void *)                                            //  v.10.7
 
2068
{
 
2069
   int         imx, im1, im2, ww, hh, ii, nn;
 
2070
   double      R, maxtf, mintf, midtf;
 
2071
   double      xoff, yoff, toff, dxoff, dyoff;
 
2072
   cimoffs     offsets[10];                                                //  x/y/t offsets after alignment
 
2073
   
 
2074
   Fzoom = 0;                                                              //  fit to window if big
 
2075
   Fblowup = 1;                                                            //  scale up to window if small
 
2076
   Ffuncbusy++;                                                            //  v.11.01
 
2077
   cimShrink = 0;                                                          //  no warp shrinkage (pano)
 
2078
   cimPano = cimPanoV = 0;                                                 //  no pano mode
 
2079
 
 
2080
   for (imx = 0; imx < cimNF; imx++)                                       //  bugfix     v.10.8
 
2081
      memset(&offsets[imx],0,sizeof(cimoffs));
 
2082
 
 
2083
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  loop each pair of images
 
2084
   {
 
2085
      im2 = im1 + 1;
 
2086
 
 
2087
      memset(&cimOffs[im1],0,sizeof(cimoffs));                             //  initial image offsets = 0
 
2088
      memset(&cimOffs[im2],0,sizeof(cimoffs));
 
2089
 
 
2090
      ww = cimPXMf[im1]->ww;                                               //  image dimensions
 
2091
      hh = cimPXMf[im1]->hh;
 
2092
 
 
2093
      nn = ww;                                                             //  use larger of ww, hh
 
2094
      if (hh > ww) nn = hh;
 
2095
      cimScale = HDRinitAlignSize / nn;                                    //  initial align image size
 
2096
      if (cimScale > 1.0) cimScale = 1.0;
 
2097
 
 
2098
      cimBlend = 0;                                                        //  no blend width (use all)
 
2099
      cim_get_overlap(im1,im2,cimPXMf);                                    //  get overlap area
 
2100
      cim_match_colors(im1,im2,cimPXMf);                                   //  get color matching factors
 
2101
 
 
2102
      cimSearchRange = HDRinitSearchRange;                                 //  initial align search range
 
2103
      cimSearchStep = HDRinitSearchStep;                                   //  initial align search step
 
2104
      cimWarpRange = HDRinitWarpRange;                                     //  initial align corner warp range
 
2105
      cimWarpStep = HDRinitWarpStep;                                       //  initial align corner warp step
 
2106
      cimSampSize = HDRsampSize;                                           //  pixel sample size for align/compare
 
2107
      cimNsearch = 0;                                                      //  reset align search counter
 
2108
 
 
2109
      while (true)                                                         //  loop, increasing image size
 
2110
      {
 
2111
         cim_scale_image(im1,cimPXMs);                                     //  scale images to cimScale
 
2112
         cim_scale_image(im2,cimPXMs);
 
2113
 
 
2114
         cim_adjust_colors(cimPXMs[im1],1);                                //  apply color adjustments
 
2115
         cim_adjust_colors(cimPXMs[im2],2);
 
2116
 
 
2117
         cim_warp_image(im1);                                              //  make warped images to show
 
2118
         cim_warp_image(im2);      
 
2119
 
 
2120
         cimShowIm1 = im1;                                                 //  show two images with 50/50 blend
 
2121
         cimShowIm2 = im2;
 
2122
         cimShowAll = 0;
 
2123
         cim_show_images(1,0);                                             //  (x/y offsets can change)
 
2124
 
 
2125
         cim_get_overlap(im1,im2,cimPXMs);                                 //  get overlap area             v.11.04
 
2126
         cim_get_redpix(im1);                                              //  get high-contrast pixels
 
2127
 
 
2128
         cim_align_image(im1,im2);                                         //  align im2 to im1
 
2129
 
 
2130
         zfree(cimRedpix);                                                 //  clear red pixels
 
2131
         cimRedpix = 0;
 
2132
 
 
2133
         if (cimScale == 1.0) break;                                       //  done
 
2134
 
 
2135
         R = HDRimageIncrease;                                             //  next larger image size
 
2136
         cimScale = cimScale * R;
 
2137
         if (cimScale > 0.85) {                                            //  if close to end, jump to end
 
2138
            R = R / cimScale;
 
2139
            cimScale = 1.0;
 
2140
         }
 
2141
 
 
2142
         cimOffs[im1].xf *= R;                                             //  scale offsets for larger image
 
2143
         cimOffs[im1].yf *= R;
 
2144
         cimOffs[im2].xf *= R;
 
2145
         cimOffs[im2].yf *= R;
 
2146
 
 
2147
         for (ii = 0; ii < 4; ii++) {
 
2148
            cimOffs[im1].wx[ii] *= R;
 
2149
            cimOffs[im1].wy[ii] *= R;
 
2150
            cimOffs[im2].wx[ii] *= R;
 
2151
            cimOffs[im2].wy[ii] *= R;
 
2152
         }
 
2153
 
 
2154
         cimSearchRange = HDRsearchRange;                                  //  align search range
 
2155
         cimSearchStep = HDRsearchStep;                                    //  align search step size
 
2156
         cimWarpRange = HDRwarpRange;                                      //  align corner warp range
 
2157
         cimWarpStep = HDRwarpStep;                                        //  align corner warp step size
 
2158
      }
 
2159
 
 
2160
      offsets[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf;                 //  save im2 offsets from im1
 
2161
      offsets[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf;
 
2162
      offsets[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf;
 
2163
      
 
2164
      for (ii = 0; ii < 4; ii++) {
 
2165
         offsets[im2].wx[ii] = cimOffs[im2].wx[ii] - cimOffs[im1].wx[ii];
 
2166
         offsets[im2].wy[ii] = cimOffs[im2].wy[ii] - cimOffs[im1].wy[ii];
 
2167
      }
 
2168
   }
 
2169
   
 
2170
   for (imx = 0; imx < cimNF; imx++)                                       //  offsets[*] >> cimOffs[*]
 
2171
      cimOffs[imx] = offsets[imx];
 
2172
 
 
2173
   cimOffs[0].xf = cimOffs[0].yf = cimOffs[0].tf = 0;                      //  image 0 at (0,0,0)
 
2174
 
 
2175
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  absolute offsets for image 1 to last
 
2176
   {
 
2177
      im2 = im1 + 1;
 
2178
      cimOffs[im2].xf += cimOffs[im1].xf;                                  //  x/y/t offsets are additive
 
2179
      cimOffs[im2].yf += cimOffs[im1].yf;
 
2180
      cimOffs[im2].tf += cimOffs[im1].tf;
 
2181
 
 
2182
      for (ii = 0; ii < 4; ii++) {                                         //  corner warps are additive
 
2183
         cimOffs[im2].wx[ii] += cimOffs[im1].wx[ii];
 
2184
         cimOffs[im2].wy[ii] += cimOffs[im1].wy[ii];
 
2185
      }
 
2186
   }
 
2187
 
 
2188
   for (imx = 1; imx < cimNF; imx++)                                       //  re-warp to absolute       v.10.8
 
2189
      cim_warp_image(imx);
 
2190
 
 
2191
   toff = cimOffs[0].tf;                                                   //  balance +/- thetas
 
2192
   maxtf = mintf = toff;
 
2193
   for (imx = 1; imx < cimNF; imx++) {
 
2194
      toff = cimOffs[imx].tf;
 
2195
      if (toff > maxtf) maxtf = toff;
 
2196
      if (toff < mintf) mintf = toff;
 
2197
   }
 
2198
   midtf = 0.5 * (maxtf + mintf);
 
2199
   
 
2200
   for (imx = 0; imx < cimNF; imx++)
 
2201
      cimOffs[imx].tf -= midtf;
 
2202
 
 
2203
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  adjust x/y offsets for images after im1
 
2204
   for (im2 = im1+1; im2 < cimNF; im2++)                                   //    due to im1 theta offset
 
2205
   {
 
2206
      toff = cimOffs[im1].tf;
 
2207
      xoff = cimOffs[im2].xf - cimOffs[im1].xf;
 
2208
      yoff = cimOffs[im2].yf - cimOffs[im1].yf;
 
2209
      dxoff = yoff * sin(toff);
 
2210
      dyoff = xoff * sin(toff);
 
2211
      cimOffs[im2].xf -= dxoff;
 
2212
      cimOffs[im2].yf += dyoff;
 
2213
   }
 
2214
 
 
2215
   Fzoom = Fblowup = 0;
 
2216
   Ffuncbusy--;
 
2217
   HDRstat = 1;
 
2218
   thread_exit();
 
2219
   return 0;                                                               //  not executed
 
2220
}
 
2221
 
 
2222
 
 
2223
//  Compute mean image pixel brightness levels.
 
2224
//  (basis for setting image contributions per brightness level)
 
2225
 
 
2226
void HDR_brightness()                                                      //  v.10.7
 
2227
{
 
2228
   int         px3, py3, ww, hh, imx, kk, vstat;
 
2229
   double      px, py, red, green, blue;
 
2230
   double      bright, maxbright, minbright;
 
2231
   double      xoff, yoff, sintf[10], costf[10];
 
2232
   double      norm, fnorm = 1.0 / 65536.0;
 
2233
   uint16      vpix[3], *pix3;
 
2234
 
 
2235
   cimScale = 1.0;
 
2236
 
 
2237
   for (imx = 0; imx < cimNF; imx++)                                       //  replace alignment images
 
2238
   {                                                                       //   (color adjusted for pixel matching)
 
2239
      PXM_free(cimPXMs[imx]);                                              //    with the original images
 
2240
      cimPXMs[imx] = PXM_copy(cimPXMf[imx]);
 
2241
      cim_warp_image(imx);                                                 //  re-apply warps
 
2242
   }
 
2243
 
 
2244
   for (imx = 0; imx < cimNF; imx++)                                       //  pre-calculate trig functions
 
2245
   {
 
2246
      sintf[imx] = sin(cimOffs[imx].tf);
 
2247
      costf[imx] = cos(cimOffs[imx].tf);
 
2248
   }
 
2249
   
 
2250
   ww = E3pxm16->ww;
 
2251
   hh = E3pxm16->hh;
 
2252
 
 
2253
   HDRbright = (float *) zmalloc(ww*hh*sizeof(int),"HDR");                 //  get memory for brightness array
 
2254
   
 
2255
   minbright = 1.0;
 
2256
   maxbright = 0.0;
 
2257
   
 
2258
   for (py3 = 0; py3 < hh; py3++)                                          //  step through all output pixels
 
2259
   for (px3 = 0; px3 < ww; px3++)
 
2260
   {
 
2261
      red = green = blue = 0;
 
2262
      vstat = 0;
 
2263
 
 
2264
      for (imx = 0; imx < cimNF; imx++)                                    //  step through all input images
 
2265
      {
 
2266
         xoff = cimOffs[imx].xf;
 
2267
         yoff = cimOffs[imx].yf;
 
2268
 
 
2269
         px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);       //  image N pixel, after offsets
 
2270
         py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);
 
2271
         vstat = vpixel(cimPXMw[imx],px,py,vpix);
 
2272
         if (! vstat) break;
 
2273
         
 
2274
         red += fnorm * vpix[0];                                           //  sum input pixels
 
2275
         green += fnorm * vpix[1];
 
2276
         blue += fnorm * vpix[2];
 
2277
      }
 
2278
      
 
2279
      if (! vstat) {                                                       //  pixel outside some image
 
2280
         pix3 = PXMpix(E3pxm16,px3,py3);                                   //  output pixel = black
 
2281
         pix3[0] = pix3[1] = pix3[2] = 0;
 
2282
         kk = py3 * ww + px3;
 
2283
         HDRbright[kk] = 0;
 
2284
         continue;
 
2285
      }
 
2286
      
 
2287
      bright = (red + green + blue) / (3 * cimNF);                         //  mean pixel brightness, 0.0 to 1.0
 
2288
      kk = py3 * ww + px3;
 
2289
      HDRbright[kk] = bright;
 
2290
      
 
2291
      if (bright > maxbright) maxbright = bright;
 
2292
      if (bright < minbright) minbright = bright;
 
2293
 
 
2294
      pix3 = PXMpix(E3pxm16,px3,py3);                                      //  output pixel
 
2295
      pix3[0] = red * 65535.0 / cimNF;
 
2296
      pix3[1] = green * 65535.0 / cimNF;
 
2297
      pix3[2] = blue * 65535.0 / cimNF;
 
2298
   }
 
2299
   
 
2300
   norm = 0.999 / (maxbright - minbright);                                 //  normalize to range 0.0 to 0.999   
 
2301
 
 
2302
   for (int ii = 0; ii < ww * hh; ii++)
 
2303
      HDRbright[ii] = (HDRbright[ii] - minbright) * norm;
 
2304
 
 
2305
   mwpaint2();                                                             //  update window
 
2306
   return;
 
2307
}
 
2308
 
 
2309
 
 
2310
//  Dialog for user to control the contributions of each input image
 
2311
//  while watching the output image which is updated in real time.
 
2312
 
 
2313
void HDR_tweak()                                                           //  v.10.7
 
2314
{
 
2315
   int    HDR_tweak_event(zdialog *zd, cchar *event);
 
2316
   void   HDR_curvedit(int);
 
2317
 
 
2318
   int         imx;
 
2319
   double      cww = 1.0 / (cimNF-1);
 
2320
   
 
2321
   HDRzd = zdialog_new(ZTX("Adjust Image Contributions"),mWin,Bdone,Bcancel,null);
 
2322
   zdialog_add_widget(HDRzd,"frame","brframe","dialog",0,"expand|space=2");
 
2323
   zdialog_add_widget(HDRzd,"hbox","hb1","dialog",0);
 
2324
   zdialog_add_widget(HDRzd,"label","lab11","hb1",ZTX("dark pixels"),"space=3");
 
2325
   zdialog_add_widget(HDRzd,"label","lab12","hb1",0,"expand");
 
2326
   zdialog_add_widget(HDRzd,"label","lab13","hb1",ZTX("light pixels"),"space=3");
 
2327
   zdialog_add_widget(HDRzd,"hbox","hb2","dialog",0,"space=3");
 
2328
   zdialog_add_widget(HDRzd,"label","labf1","hb2",ZTX("file:"),"space=3");
 
2329
   zdialog_add_widget(HDRzd,"label","labf2","hb2","*");
 
2330
 
 
2331
   zdialog_add_widget(HDRzd,"hbox","hbcf","dialog",0,"space=5");
 
2332
   zdialog_add_widget(HDRzd,"label","labcf","hbcf",Bcurvefile,"space=5");
 
2333
   zdialog_add_widget(HDRzd,"button","load","hbcf",Bopen,"space=5");
 
2334
   zdialog_add_widget(HDRzd,"button","save","hbcf",Bsave,"space=5");
 
2335
 
 
2336
   GtkWidget *brframe = zdialog_widget(HDRzd,"brframe");                   //  set up curve edit
 
2337
   spldat *sd = splcurve_init(brframe,HDR_curvedit);                       //  v.11.01
 
2338
   EFhdr.curves = sd;
 
2339
 
 
2340
   sd->Nspc = cimNF;                                                       //  no. curves = no. files
 
2341
   
 
2342
   for (imx = 0; imx < cimNF; imx++)                                       //  set up initial response curve
 
2343
   {                                                                       //    anchor points
 
2344
      sd->vert[imx] = 0;
 
2345
      sd->nap[imx] = 2;
 
2346
      sd->apx[imx][0] = 0.01;                                              //  flatter curves, v.9.3
 
2347
      sd->apx[imx][1] = 0.99;
 
2348
      sd->apy[imx][0] = 0.9 - imx * 0.8 * cww;
 
2349
      sd->apy[imx][1] = 0.1 + imx * 0.8 * cww;
 
2350
      splcurve_generate(sd,imx);
 
2351
   }
 
2352
   
 
2353
   start_thread(HDR_combine_thread,0);                                     //  start working thread
 
2354
   signal_thread();
 
2355
 
 
2356
   zdialog_resize(HDRzd,400,360);
 
2357
   zdialog_run(HDRzd,HDR_tweak_event,"-10/20");                            //  run dialog             v.11.07
 
2358
   zdialog_wait(HDRzd);                                                    //  wait for completion
 
2359
 
 
2360
   return;
 
2361
}
 
2362
 
 
2363
 
 
2364
//  dialog event and completion callback function
 
2365
 
 
2366
int HDR_tweak_event(zdialog *zd, cchar *event)
 
2367
{
 
2368
   spldat *sd = EFhdr.curves;
 
2369
 
 
2370
   if (strEqu(event,"load")) {                                             //  load saved curve       v.11.02
 
2371
      splcurve_load(sd);
 
2372
      zdialog_stuff(HDRzd,"labf2","*");
 
2373
      signal_thread();
 
2374
      return 0;
 
2375
   }
 
2376
 
 
2377
   if (strEqu(event,"save")) {                                             //  save curve to file     v.11.02
 
2378
      splcurve_save(sd);
 
2379
      return 0;
 
2380
   }
 
2381
 
 
2382
   if (zd->zstat)                                                          //  dialog complete
 
2383
   {
 
2384
      wrapup_thread(8);
 
2385
      if (zd->zstat == 1) HDRstat = 1;
 
2386
      else HDRstat = 0;
 
2387
      zdialog_free(HDRzd);
 
2388
      if (HDRstat == 1) cim_trim();                                        //  cut-off edges       v.10.9
 
2389
   }
 
2390
   
 
2391
   return 1;
 
2392
}
 
2393
 
 
2394
 
 
2395
//  this function is called when a curve is edited
 
2396
 
 
2397
void HDR_curvedit(int spc)
 
2398
{
 
2399
   cchar  *pp; 
 
2400
   
 
2401
   pp = strrchr(cimFile[spc],'/');
 
2402
   zdialog_stuff(HDRzd,"labf2",pp+1);
 
2403
   signal_thread();
 
2404
   return;
 
2405
}
 
2406
 
 
2407
 
 
2408
//  Combine all input images >> E3pxm16 based on image response curves.
 
2409
 
 
2410
void * HDR_combine_thread(void *)
 
2411
{
 
2412
   void * HDR_combine_wthread(void *arg);
 
2413
 
 
2414
   int         imx, ii, kk;
 
2415
   double      xlo, xhi, xval, yval, sumrf;
 
2416
   spldat      *sd = EFhdr.curves;
 
2417
 
 
2418
   while (true)
 
2419
   {
 
2420
      thread_idle_loop();                                                  //  wait for work or exit request
 
2421
      
 
2422
      for (imx = 0; imx < cimNF; imx++)                                    //  loop input images
 
2423
      {
 
2424
         ii = sd->nap[imx];                                                //  get low and high anchor points
 
2425
         xlo = sd->apx[imx][0];                                            //    for image response curve
 
2426
         xhi = sd->apx[imx][ii-1];
 
2427
         if (xlo < 0.02) xlo = 0;                                          //  snap-to scale end points
 
2428
         if (xhi > 0.98) xhi = 1;
 
2429
 
 
2430
         for (ii = 0; ii < 1000; ii++)                                     //  loop all brightness levels
 
2431
         {
 
2432
            HDR_respfac[imx][ii] = 0;
 
2433
            xval = 0.001 * ii;
 
2434
            if (xval < xlo || xval > xhi) continue;                        //  no influence for brightness level
 
2435
            kk = 1000 * xval;                                              //  speedup    v.11.06
 
2436
            yval = sd->yval[imx][kk];
 
2437
            HDR_respfac[imx][ii] = yval;                                   //    = contribution of this input image
 
2438
         }
 
2439
      }
 
2440
 
 
2441
      for (ii = 0; ii < 1000; ii++)                                        //  normalize the factors so that
 
2442
      {                                                                    //    they sum to 1.0
 
2443
         sumrf = 0;
 
2444
         for (imx = 0; imx < cimNF; imx++)
 
2445
            sumrf += HDR_respfac[imx][ii];
 
2446
         if (! sumrf) continue;
 
2447
         for (imx = 0; imx < cimNF; imx++)
 
2448
            HDR_respfac[imx][ii] = HDR_respfac[imx][ii] / sumrf;
 
2449
      }
 
2450
 
 
2451
      mutex_lock(&Fpixmap_lock);                                           //  stop window updates
 
2452
 
 
2453
      for (ii = 0; ii < Nwt; ii++)                                         //  start worker threads      v.10.7
 
2454
         start_wthread(HDR_combine_wthread,&wtnx[ii]);
 
2455
      wait_wthreads();                                                     //  wait for completion
 
2456
   
 
2457
      mutex_unlock(&Fpixmap_lock);
 
2458
      mwpaint2();                                                          //  update window
 
2459
   }
 
2460
 
 
2461
   return 0;                                                               //  not executed
 
2462
}
 
2463
 
 
2464
 
 
2465
void * HDR_combine_wthread(void *arg)                                      //  working thread
 
2466
{
 
2467
   int         index = *((int *) (arg));
 
2468
   int         imx, ww, hh, ii, px3, py3, vstat;
 
2469
   double      sintf[10], costf[10], xoff, yoff;
 
2470
   double      px, py, red, green, blue, bright, factor;
 
2471
   uint16      vpix[3], *pix3;
 
2472
 
 
2473
   for (imx = 0; imx < cimNF; imx++)                                       //  pre-calculate trig functions
 
2474
   {
 
2475
      sintf[imx] = sin(cimOffs[imx].tf);
 
2476
      costf[imx] = cos(cimOffs[imx].tf);
 
2477
   }
 
2478
 
 
2479
   ww = E3pxm16->ww;
 
2480
   hh = E3pxm16->hh;
 
2481
 
 
2482
   for (py3 = index; py3 < hh; py3 += Nwt)                                 //  step through all output pixels
 
2483
   for (px3 = 0; px3 < ww; px3++)
 
2484
   {
 
2485
      ii = py3 * ww + px3;
 
2486
      bright = HDRbright[ii];                                              //  mean brightness, 0.0 to 1.0
 
2487
      ii = 1000 * bright;
 
2488
      
 
2489
      red = green = blue = 0;
 
2490
      
 
2491
      for (imx = 0; imx < cimNF; imx++)                                    //  loop input images
 
2492
      {
 
2493
         factor = HDR_respfac[imx][ii];                                    //  image contribution to this pixel
 
2494
         if (! factor) continue;                                           //  none
 
2495
         
 
2496
         xoff = cimOffs[imx].xf;
 
2497
         yoff = cimOffs[imx].yf;
 
2498
 
 
2499
         px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);       //  input virtual pixel mapping to
 
2500
         py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);       //    this output pixel
 
2501
 
 
2502
         vstat = vpixel(cimPXMw[imx],px,py,vpix);                          //  get input pixel
 
2503
         if (! vstat) continue;
 
2504
 
 
2505
         red += factor * vpix[0];                                          //  accumulate brightness contribution
 
2506
         green += factor * vpix[1];
 
2507
         blue += factor * vpix[2];
 
2508
      }
 
2509
         
 
2510
      pix3 = PXMpix(E3pxm16,px3,py3);                                      //  output pixel
 
2511
 
 
2512
      pix3[0] = red;                                                       //  = sum of input pixel contributions
 
2513
      pix3[1] = green;
 
2514
      pix3[2] = blue;
 
2515
   }
 
2516
   
 
2517
   exit_wthread();
 
2518
   return 0;                                                               //  not executed
 
2519
}
 
2520
 
 
2521
 
 
2522
/**************************************************************************
 
2523
 
 
2524
   Make an HDF (high depth of field) image from several images of the same
 
2525
   subject with different focus settings. Combine the images and allow the
 
2526
   user to "paint" the output composite image using the mouse and choosing 
 
2527
   the sharpest input image for each area of the output image. The result 
 
2528
   is an image with a depth of field that exceeds the camera capability.
 
2529
   
 
2530
   The images are aligned at the center, but small differences in camera 
 
2531
   position (hand-held photos) will cause parallax errors that prevent 
 
2532
   perfect alignment of the images. Also, the images with nearer focus
 
2533
   will be slightly larger than those with farther focus. These problems
 
2534
   can be compensated by dragging and warping the images using the mouse.                 v.10.7 
 
2535
 
 
2536
**************************************************************************/
 
2537
 
 
2538
int      HDFstat;                                                          //  1 = OK, 0 = failed or canceled
 
2539
double   HDFinitAlignSize = 160;                                           //  initial align image size
 
2540
double   HDFimageIncrease = 1.6;                                           //  image size increase per align cycle
 
2541
double   HDFsampSize = 6000;                                               //  pixel sample size
 
2542
 
 
2543
double   HDFinitSearchRange = 8.0;                                         //  initial search range, +/- pixels
 
2544
double   HDFinitSearchStep = 1.0;                                          //  initial search step, pixels
 
2545
double   HDFinitWarpRange = 4.0;                                           //  initial corner warp range
 
2546
double   HDFinitWarpStep = 1.0;                                            //  initial corner warp step
 
2547
double   HDFsearchRange = 2.0;                                             //  normal search range
 
2548
double   HDFsearchStep = 1.0;                                              //  normal search step
 
2549
double   HDFwarpRange = 1.0;                                               //  normal corner warp range     v.11.03
 
2550
double   HDFwarpStep = 0.67;                                               //  normal corner warp step
 
2551
 
 
2552
void * HDF_align_thread(void *);
 
2553
void   HDF_tweak();
 
2554
void   HDF_mousefunc();
 
2555
void * HDF_combine_thread(void *);
 
2556
 
 
2557
editfunc    EFhdf;                                                         //  edit function data
 
2558
 
 
2559
 
 
2560
//  menu function
 
2561
 
 
2562
void m_HDF(GtkWidget *, cchar *)                                           //  v.10.7
 
2563
{
 
2564
   char        **flist;
 
2565
   int         imx, err, ww, hh;
 
2566
   double      diffw, diffh;
 
2567
   
 
2568
   zfuncs::F1_help_topic = "HDF";                                          //  help topic
 
2569
 
 
2570
   if (mod_keep()) return;                                                 //  warn unsaved changes
 
2571
   if (! menulock(1)) return;                                              //  test menu lock            v.11.07
 
2572
   menulock(0);
 
2573
 
 
2574
   for (imx = 0; imx < 10; imx++)
 
2575
   {                                                                       //  clear all file and PXM data
 
2576
      cimFile[imx] = 0;
 
2577
      cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0;
 
2578
   }
 
2579
 
 
2580
   cimNF = 0;   
 
2581
 
 
2582
   flist = zgetfileN(ZTX("Select 2 to 9 files"),"openN",curr_file);        //  select images to combine
 
2583
   if (! flist) return;
 
2584
 
 
2585
   for (imx = 0; flist[imx]; imx++);                                       //  count selected files
 
2586
   if (imx < 2 || imx > 9) {
 
2587
      zmessageACK(mWin,ZTX("Select 2 to 9 files"));
 
2588
      goto cleanup;
 
2589
   }
 
2590
 
 
2591
   cimNF = imx;                                                            //  file count
 
2592
   for (imx = 0; imx < cimNF; imx++)
 
2593
      cimFile[imx] = strdupz(flist[imx],0,"HDF");                          //  set up file list
 
2594
   
 
2595
   if (! cim_load_files()) goto cleanup;                                   //  load and check all files
 
2596
 
 
2597
   ww = cimPXMf[0]->ww;
 
2598
   hh = cimPXMf[0]->hh;
 
2599
   
 
2600
   for (imx = 1; imx < cimNF; imx++)                                       //  check image compatibility
 
2601
   {
 
2602
      diffw = abs(ww - cimPXMf[imx]->ww);
 
2603
      diffw = diffw / ww;
 
2604
      diffh = abs(hh - cimPXMf[imx]->hh);
 
2605
      diffh = diffh / hh;
 
2606
 
 
2607
      if (diffw > 0.02 || diffh > 0.02) {
 
2608
         zmessageACK(mWin,ZTX("Images are not all the same size"));
 
2609
         goto cleanup;
 
2610
      }
 
2611
   }
 
2612
 
 
2613
   free_resources();                                                       //  ready to commit
 
2614
 
 
2615
   err = f_open(cimFile[0],0);                                             //  curr_file = 1st file in list
 
2616
   if (err) goto cleanup;
 
2617
 
 
2618
   EFhdf.funcname = "HDF";
 
2619
   if (! edit_setup(EFhdf)) goto cleanup;                                  //  setup edit (will lock)
 
2620
 
 
2621
   start_thread(HDF_align_thread,0);                                       //  align each pair of images
 
2622
   wrapup_thread(0);                                                       //  wait for completion
 
2623
   if (HDFstat != 1) goto cancel;
 
2624
 
 
2625
   HDF_tweak();                                                            //  combine images based on user inputs
 
2626
   if (HDFstat != 1) goto cancel;
 
2627
 
 
2628
   CEF->Fmod = 1;                                                          //  done
 
2629
   edit_done(EFhdf);
 
2630
   goto cleanup;
 
2631
 
 
2632
cancel:
 
2633
   edit_cancel(EFhdf);
 
2634
 
 
2635
cleanup:
 
2636
 
 
2637
   if (flist) {
 
2638
      for (imx = 0; flist[imx]; imx++)                                     //  free file list
 
2639
         zfree(flist[imx]);
 
2640
      zfree(flist);
 
2641
   }
 
2642
 
 
2643
   for (imx = 0; imx < cimNF; imx++) {                                     //  free cim file and PXM data
 
2644
      if (cimFile[imx]) zfree(cimFile[imx]);
 
2645
      if (cimPXMf[imx]) PXM_free(cimPXMf[imx]);
 
2646
      if (cimPXMs[imx]) PXM_free(cimPXMs[imx]);
 
2647
      if (cimPXMw[imx]) PXM_free(cimPXMw[imx]);
 
2648
   }
 
2649
   
 
2650
   *SB_text = 0;
 
2651
   return;
 
2652
}
 
2653
 
 
2654
 
 
2655
//  HDF align each pair of input images, output combined image to E3pxm16
 
2656
//  cimPXMf[*]  original image
 
2657
//  cimPXMs[*]  scaled and color adjusted for pixel comparisons
 
2658
//  cimPXMw[*]  warped for display
 
2659
 
 
2660
void * HDF_align_thread(void *)                                            //  v.10.7
 
2661
{
 
2662
   int         imx, im1, im2, ww, hh, ii, nn;
 
2663
   double      R, maxtf, mintf, midtf;
 
2664
   double      xoff, yoff, toff, dxoff, dyoff;
 
2665
   cimoffs     offsets[10];                                                //  x/y/t offsets after alignment
 
2666
   
 
2667
   Fzoom = 0;                                                              //  fit to window if big
 
2668
   Fblowup = 1;                                                            //  scale up to window if small
 
2669
   Ffuncbusy++;                                                            //  v.11.01
 
2670
   cimShrink = 0;                                                          //  no warp shrinkage (pano)
 
2671
   cimPano = cimPanoV = 0;                                                 //  no pano mode
 
2672
 
 
2673
   for (imx = 0; imx < cimNF; imx++)                                       //  bugfix     v.10.8
 
2674
      memset(&offsets[imx],0,sizeof(cimoffs));
 
2675
   
 
2676
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  loop each pair of images
 
2677
   {
 
2678
      im2 = im1 + 1;
 
2679
 
 
2680
      memset(&cimOffs[im1],0,sizeof(cimoffs));                             //  initial image offsets = 0
 
2681
      memset(&cimOffs[im2],0,sizeof(cimoffs));
 
2682
      
 
2683
      ww = cimPXMf[im1]->ww;                                               //  image dimensions
 
2684
      hh = cimPXMf[im1]->hh;
 
2685
 
 
2686
      nn = ww;                                                             //  use larger of ww, hh
 
2687
      if (hh > ww) nn = hh;
 
2688
      cimScale = HDFinitAlignSize / nn;                                    //  initial align image size
 
2689
      if (cimScale > 1.0) cimScale = 1.0;
 
2690
 
 
2691
      cimBlend = 0;                                                        //  no blend width (use all)
 
2692
      cim_get_overlap(im1,im2,cimPXMf);                                    //  get overlap area
 
2693
      cim_match_colors(im1,im2,cimPXMf);                                   //  get color matching factors
 
2694
 
 
2695
      cimSearchRange = HDFinitSearchRange;                                 //  initial align search range
 
2696
      cimSearchStep = HDFinitSearchStep;                                   //  initial align search step
 
2697
      cimWarpRange = HDFinitWarpRange;                                     //  initial align corner warp range
 
2698
      cimWarpStep = HDFinitWarpStep;                                       //  initial align corner warp step
 
2699
      cimSampSize = HDFsampSize;                                           //  pixel sample size for align/compare
 
2700
      cimNsearch = 0;                                                      //  reset align search counter
 
2701
 
 
2702
      while (true)                                                         //  loop, increasing image size
 
2703
      {
 
2704
         cim_scale_image(im1,cimPXMs);                                     //  scale images to cimScale
 
2705
         cim_scale_image(im2,cimPXMs);
 
2706
 
 
2707
         cim_adjust_colors(cimPXMs[im1],1);                                //  apply color adjustments
 
2708
         cim_adjust_colors(cimPXMs[im2],2);
 
2709
 
 
2710
         cim_warp_image(im1);                                              //  warp images for show
 
2711
         cim_warp_image(im2);
 
2712
 
 
2713
         cimShowIm1 = im1;                                                 //  show these two images
 
2714
         cimShowIm2 = im2;                                                 //    with 50/50 blend
 
2715
         cimShowAll = 0;
 
2716
         cim_show_images(1,0);                                             //  (y offset can change)
 
2717
 
 
2718
         cim_get_overlap(im1,im2,cimPXMs);                                 //  get overlap area             v.11.04
 
2719
         cim_get_redpix(im1);                                              //  get high-contrast pixels
 
2720
 
 
2721
         cim_align_image(im1,im2);                                         //  align im2 to im1
 
2722
         
 
2723
         zfree(cimRedpix);                                                 //  clear red pixels
 
2724
         cimRedpix = 0;
 
2725
 
 
2726
         if (cimScale == 1.0) break;                                       //  done
 
2727
 
 
2728
         R = HDFimageIncrease;                                             //  next larger image size
 
2729
         cimScale = cimScale * R;
 
2730
         if (cimScale > 0.85) {                                            //  if close to end, jump to end
 
2731
            R = R / cimScale;
 
2732
            cimScale = 1.0;
 
2733
         }
 
2734
 
 
2735
         cimOffs[im1].xf *= R;                                             //  scale offsets for larger image
 
2736
         cimOffs[im1].yf *= R;
 
2737
         cimOffs[im2].xf *= R;
 
2738
         cimOffs[im2].yf *= R;
 
2739
 
 
2740
         for (ii = 0; ii < 4; ii++) {
 
2741
            cimOffs[im1].wx[ii] *= R;
 
2742
            cimOffs[im1].wy[ii] *= R;
 
2743
            cimOffs[im2].wx[ii] *= R;
 
2744
            cimOffs[im2].wy[ii] *= R;
 
2745
         }
 
2746
 
 
2747
         cimSearchRange = HDFsearchRange;                                  //  align search range
 
2748
         cimSearchStep = HDFsearchStep;                                    //  align search step size
 
2749
         cimWarpRange = HDFwarpRange;                                      //  align corner warp range
 
2750
         cimWarpStep = HDFwarpStep;                                        //  align corner warp step size
 
2751
      }
 
2752
      
 
2753
      offsets[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf;                 //  save im2 offsets from im1
 
2754
      offsets[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf;
 
2755
      offsets[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf;
 
2756
 
 
2757
      for (ii = 0; ii < 4; ii++) {
 
2758
         offsets[im2].wx[ii] = cimOffs[im2].wx[ii] - cimOffs[im1].wx[ii];
 
2759
         offsets[im2].wy[ii] = cimOffs[im2].wy[ii] - cimOffs[im1].wy[ii];
 
2760
      }
 
2761
   }
 
2762
 
 
2763
   for (imx = 0; imx < cimNF; imx++)                                       //  offsets[*] >> cimOffs[*]
 
2764
      cimOffs[imx] = offsets[imx];
 
2765
   
 
2766
   cimOffs[0].xf = cimOffs[0].yf = cimOffs[0].tf = 0;                      //  image 0 at (0,0,0)
 
2767
 
 
2768
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  absolute offsets for image 1 to last
 
2769
   {
 
2770
      im2 = im1 + 1;
 
2771
      cimOffs[im2].xf += cimOffs[im1].xf;                                  //  x/y/t offsets are additive
 
2772
      cimOffs[im2].yf += cimOffs[im1].yf;
 
2773
      cimOffs[im2].tf += cimOffs[im1].tf;
 
2774
 
 
2775
      for (ii = 0; ii < 4; ii++) {                                         //  corner warps are additive
 
2776
         cimOffs[im2].wx[ii] += cimOffs[im1].wx[ii];
 
2777
         cimOffs[im2].wy[ii] += cimOffs[im1].wy[ii];
 
2778
      }
 
2779
   }
 
2780
 
 
2781
   for (imx = 1; imx < cimNF; imx++)                                       //  re-warp to absolute       v.10.8
 
2782
      cim_warp_image(imx);
 
2783
 
 
2784
   toff = cimOffs[0].tf;                                                   //  balance +/- thetas
 
2785
   maxtf = mintf = toff;
 
2786
   for (imx = 1; imx < cimNF; imx++) {
 
2787
      toff = cimOffs[imx].tf;
 
2788
      if (toff > maxtf) maxtf = toff;
 
2789
      if (toff < mintf) mintf = toff;
 
2790
   }
 
2791
   midtf = 0.5 * (maxtf + mintf);
 
2792
   
 
2793
   for (imx = 0; imx < cimNF; imx++)
 
2794
      cimOffs[imx].tf -= midtf;
 
2795
 
 
2796
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  adjust x/y offsets for images after im1
 
2797
   for (im2 = im1+1; im2 < cimNF; im2++)                                   //    due to im1 theta offset
 
2798
   {
 
2799
      toff = cimOffs[im1].tf;
 
2800
      xoff = cimOffs[im2].xf - cimOffs[im1].xf;
 
2801
      yoff = cimOffs[im2].yf - cimOffs[im1].yf;
 
2802
      dxoff = yoff * sin(toff);
 
2803
      dyoff = xoff * sin(toff);
 
2804
      cimOffs[im2].xf -= dxoff;
 
2805
      cimOffs[im2].yf += dyoff;
 
2806
   }
 
2807
   
 
2808
   for (imx = 0; imx < cimNF; imx++)                                       //  use final warped images as basis
 
2809
   {                                                                       //    for manual align adjustments
 
2810
      PXM_free(cimPXMs[imx]);                                              //  bugfix     v.11.04
 
2811
      cimPXMs[imx] = PXM_copy(cimPXMw[imx]);
 
2812
   }
 
2813
 
 
2814
   Fzoom = Fblowup = 0;
 
2815
   Ffuncbusy--;
 
2816
   HDFstat = 1;
 
2817
   thread_exit();
 
2818
   return 0;                                                               //  not executed
 
2819
}
 
2820
 
 
2821
 
 
2822
//  paint and warp output image
 
2823
 
 
2824
zdialog  *HDFzd = 0;                                                       //  paint dialog
 
2825
int      HDFmode;                                                          //  mode: paint or warp
 
2826
int      HDFimage;                                                         //  current image (0 based)
 
2827
int      HDFradius;                                                        //  paint mode radius
 
2828
char     *HDFpixmap = 0;                                                   //  map input image per output pixel
 
2829
float    *HDFwarpx[10], *HDFwarpy[10];                                     //  warp memory, pixel displacements
 
2830
 
 
2831
 
 
2832
void HDF_tweak()                                                           //  v.10.7
 
2833
{
 
2834
   char     imageN[8] = "imageN", labN[4] = "0";
 
2835
   int      cc, imx, ww, hh;
 
2836
 
 
2837
   int HDF_tweak_dialog_event(zdialog *zd, cchar *event);
 
2838
 
 
2839
   //    image   (o) 1  (o) 2  (o) 3  ...
 
2840
   //    (o) paint  radius [___]
 
2841
   //    (o) warp [__]
 
2842
   //    [x] my mouse
 
2843
 
 
2844
   HDFzd = zdialog_new(ZTX("Paint and Warp Image"),mWin,Bdone,Bcancel,null);
 
2845
 
 
2846
   zdialog_add_widget(HDFzd,"hbox","hbim","dialog",0,"space=3");
 
2847
   zdialog_add_widget(HDFzd,"label","labim","hbim",ZTX("image"),"space=5");
 
2848
   zdialog_add_widget(HDFzd,"hbox","hbpw","dialog",0,"space=3");
 
2849
   zdialog_add_widget(HDFzd,"vbox","vbpw1","hbpw",0,"homog|space=5");
 
2850
   zdialog_add_widget(HDFzd,"vbox","vbpw2","hbpw",0,"homog|space=5");
 
2851
   zdialog_add_widget(HDFzd,"radio","paint","vbpw1",ZTX("paint"));
 
2852
   zdialog_add_widget(HDFzd,"radio","warp","vbpw1",ZTX("warp"));
 
2853
   zdialog_add_widget(HDFzd,"hbox","hbp","vbpw2");
 
2854
   zdialog_add_widget(HDFzd,"label","labpr","hbp",Bradius,"space=5");
 
2855
   zdialog_add_widget(HDFzd,"spin","radius","hbp","1|400|1|100");
 
2856
   zdialog_add_widget(HDFzd,"label","space","vbpw2");
 
2857
   zdialog_add_widget(HDFzd,"hbox","hbsr","dialog");
 
2858
   zdialog_add_widget(HDFzd,"check","mymouse","hbsr",BmyMouse,"space=5");
 
2859
 
 
2860
   for (imx = 0; imx < cimNF; imx++) {                                     //  add radio button for each image
 
2861
      imageN[5] = '1' + imx;
 
2862
      labN[0] = '1' + imx;
 
2863
      zdialog_add_widget(HDFzd,"radio",imageN,"hbim",labN);
 
2864
   }
 
2865
   
 
2866
   zdialog_stuff(HDFzd,"paint",1);                                         //  paint button on
 
2867
   zdialog_stuff(HDFzd,"warp",0);                                          //  warp button off
 
2868
   zdialog_stuff(HDFzd,"image1",1);                                        //  initial image = 1st
 
2869
 
 
2870
   HDFmode = 0;                                                            //  start in paint mode
 
2871
   HDFimage = 0;                                                           //  initial image
 
2872
   HDFradius = 100;                                                        //  paint radius
 
2873
 
 
2874
   takeMouse(HDFzd,HDF_mousefunc,0);                                       //  connect mouse function          v.10.12
 
2875
   
 
2876
   cc = E3ww * E3hh;                                                       //  allocate pixel map
 
2877
   HDFpixmap = zmalloc(cc,"HDF");
 
2878
   memset(HDFpixmap,cimNF,cc);                                             //  initial state, blend all images
 
2879
 
 
2880
   for (imx = 0; imx < cimNF; imx++) {                                     //  allocate warp memory
 
2881
      ww = cimPXMw[imx]->ww;
 
2882
      hh = cimPXMw[imx]->hh;
 
2883
      HDFwarpx[imx] = (float *) zmalloc(ww * hh * sizeof(float),"HDF");
 
2884
      HDFwarpy[imx] = (float *) zmalloc(ww * hh * sizeof(float),"HDF");
 
2885
   }
 
2886
   
 
2887
   start_thread(HDF_combine_thread,0);                                     //  start working thread
 
2888
   signal_thread();
 
2889
 
 
2890
   zdialog_resize(HDFzd,250,0);                                            //  stretch a bit                   v.11.07
 
2891
   zdialog_run(HDFzd,HDF_tweak_dialog_event,"-10/20");                     //  run dialog, parallel            v.11.07
 
2892
   zdialog_wait(HDFzd);                                                    //  wait for completion
 
2893
 
 
2894
   return;
 
2895
}
 
2896
 
 
2897
 
 
2898
//  dialog event and completion callback function
 
2899
 
 
2900
int HDF_tweak_dialog_event(zdialog *zd, cchar *event)                      //  v.10.7
 
2901
{
 
2902
   int      imx, nn, mymouse;
 
2903
   
 
2904
   if (zd->zstat)                                                          //  dialog finish
 
2905
   {
 
2906
      freeMouse();                                                         //  disconnect mouse function    v.10.12
 
2907
      signal_thread();
 
2908
      wrapup_thread(8);
 
2909
      if (zd->zstat == 1) HDFstat = 1;
 
2910
      else HDFstat = 0;
 
2911
      if (HDFstat == 1) cim_trim();                                        //  cut-off edges       v.10.9
 
2912
      zdialog_free(HDFzd);
 
2913
      HDFmode = 0;
 
2914
      zfree(HDFpixmap);                                                    //  free pixel map
 
2915
      for (imx = 0; imx < cimNF; imx++) {
 
2916
         zfree(HDFwarpx[imx]);                                             //  free warp memory
 
2917
         zfree(HDFwarpy[imx]);
 
2918
      }
 
2919
   }
 
2920
   
 
2921
   if (strEqu(event,"paint")) {                                            //  set paint mode
 
2922
      zdialog_fetch(zd,"paint",nn);
 
2923
      if (! nn) return 1;
 
2924
      HDFmode = 0;
 
2925
      gdk_window_set_cursor(drWin->window,0);                              //  no drag cursor            v.11.03
 
2926
   }
 
2927
   
 
2928
   if (strEqu(event,"warp")) {                                             //  set warp mode
 
2929
      zdialog_fetch(zd,"warp",nn);
 
2930
      if (! nn) return 1;
 
2931
      HDFmode = 1;
 
2932
      paint_toparc(2);                                                     //  stop brush outline
 
2933
      zdialog_fetch(zd,"mymouse",mymouse);
 
2934
      if (mymouse) 
 
2935
         gdk_window_set_cursor(drWin->window,dragcursor);                  //  set drag cursor           v.11.03
 
2936
   }
 
2937
   
 
2938
   if (strnEqu(event,"image",5)) {                                         //  image radio button
 
2939
      nn = event[5] - '0';                                                 //  1 to cimNF
 
2940
      if (nn > 0 && nn <= cimNF) 
 
2941
         HDFimage = nn - 1;                                                //  0 to cimNF-1
 
2942
      signal_thread();
 
2943
   }
 
2944
 
 
2945
   if (strEqu(event,"radius"))                                             //  change paint radius
 
2946
      zdialog_fetch(zd,"radius",HDFradius);
 
2947
 
 
2948
   if (strEqu(event,"mymouse")) {                                          //  toggle mouse capture      v.10.12
 
2949
      zdialog_fetch(zd,"mymouse",mymouse);
 
2950
      if (mymouse) {
 
2951
         takeMouse(zd,HDF_mousefunc,0);                                    //  connect mouse function
 
2952
         if (HDFmode == 1) 
 
2953
            gdk_window_set_cursor(drWin->window,dragcursor);               //  warp mode, drag cursor    v.11.03
 
2954
         signal_thread();
 
2955
      }
 
2956
      else freeMouse();                                                    //  disconnect mouse
 
2957
   }
 
2958
 
 
2959
   return 1;
 
2960
}
 
2961
 
 
2962
 
 
2963
//  HDF dialog mouse function
 
2964
//  paint:  during drag, selected image >> HDFpixmap (within paint radius) >> E3
 
2965
//  warp:   for selected image, cimPXMs >> warp >> cimPXMw >> E3
 
2966
 
 
2967
void HDF_mousefunc()                                                       //  v.10.7
 
2968
{
 
2969
   uint16      vpix1[3], *pix2, *pix3;
 
2970
   int         imx, radius, radius2, vstat1;
 
2971
   int         mx, my, dx, dy, px3, py3;
 
2972
   char        imageN[8] = "imageN";
 
2973
   double      px1, py1;
 
2974
   double      xoff, yoff, sintf[10], costf[10];
 
2975
   int         ii, px, py, ww, hh;
 
2976
   double      mag, dispx, dispy, d1, d2;
 
2977
   PXM         *pxm1, *pxm2;
 
2978
   
 
2979
   if (HDFmode == 0) goto paint;
 
2980
   if (HDFmode == 1) goto warp;
 
2981
   return;
 
2982
 
 
2983
paint:
 
2984
 
 
2985
   radius = HDFradius;                                                     //  paintbrush radius
 
2986
   radius2 = radius * radius;
 
2987
 
 
2988
   toparcx = Mxposn - radius;                                              //  paintbrush outline circle
 
2989
   toparcy = Myposn - radius;
 
2990
   toparcw = toparch = 2 * radius;
 
2991
   Ftoparc = 1;
 
2992
   paint_toparc(3);
 
2993
 
 
2994
   if (LMclick || RMclick) {                                               //  mouse click
 
2995
      LMclick = RMclick = 0;
 
2996
      return;                                                              //  ignore     v.10.8
 
2997
   }
 
2998
 
 
2999
   else if (Mxdrag || Mydrag) {                                            //  drag in progress
 
3000
      mx = Mxdrag;
 
3001
      my = Mydrag;
 
3002
   }
 
3003
   
 
3004
   else return;
 
3005
 
 
3006
   if (mx < 0 || mx > E3ww-1 || my < 0 || my > E3hh-1)                     //  mouse outside image area
 
3007
      return;
 
3008
 
 
3009
   for (imx = 0; imx < cimNF; imx++)                                       //  pre-calculate trig funcs
 
3010
   {
 
3011
      sintf[imx] = sin(cimOffs[imx].tf);
 
3012
      costf[imx] = cos(cimOffs[imx].tf);
 
3013
   }
 
3014
 
 
3015
   for (dy = -radius; dy <= radius; dy++)                                  //  loop pixels around mouse
 
3016
   for (dx = -radius; dx <= radius; dx++)
 
3017
   {
 
3018
      if (dx*dx + dy*dy > radius2) continue;                               //  outside radius
 
3019
 
 
3020
      px3 = mx + dx;                                                       //  output pixel
 
3021
      py3 = my + dy;
 
3022
      if (px3 < 0 || px3 > E3ww-1) continue;                               //  outside image
 
3023
      if (py3 < 0 || py3 > E3hh-1) continue;
 
3024
 
 
3025
      pix3 = PXMpix(E3pxm16,px3,py3);                                      //  output pixel
 
3026
 
 
3027
      imx = py3 * E3ww + px3;                                              //  update pixmap to selected image
 
3028
      HDFpixmap[imx] = HDFimage;
 
3029
      
 
3030
      imx = HDFimage;
 
3031
      xoff = cimOffs[imx].xf;
 
3032
      yoff = cimOffs[imx].yf;
 
3033
      px1 = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);         //  input virtual pixel
 
3034
      py1 = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);
 
3035
      vstat1 = vpixel(cimPXMw[imx],px1,py1,vpix1);
 
3036
      if (vstat1) {
 
3037
         pix3[0] = vpix1[0];
 
3038
         pix3[1] = vpix1[1];
 
3039
         pix3[2] = vpix1[2];
 
3040
      }
 
3041
      else pix3[0] = pix3[1] = pix3[2] = 0;
 
3042
   }
 
3043
 
 
3044
   mx = mx - radius - 1;                                                   //  update window    v.10.12
 
3045
   my = my - radius - 1;
 
3046
   ww = 2 * radius + 3;
 
3047
   paint_toparc(2);
 
3048
   mwpaint3(mx,my,ww,ww);
 
3049
   Ftoparc = 1;
 
3050
   paint_toparc(3);
 
3051
 
 
3052
   return;
 
3053
 
 
3054
warp:
 
3055
 
 
3056
   if (LMclick || RMclick) {                                               //  mouse click
 
3057
      LMclick = RMclick = 0;                                               //  ignore     v.10.8
 
3058
      return;
 
3059
   }
 
3060
 
 
3061
   else if (Mxdrag || Mydrag) {                                            //  drag in progress
 
3062
      mx = Mxdrag;
 
3063
      my = Mydrag;
 
3064
   }
 
3065
   
 
3066
   else return;
 
3067
 
 
3068
   if (mx < 0 || mx > E3ww-1 || my < 0 || my > E3hh-1)                     //  mouse outside image area
 
3069
      return;
 
3070
 
 
3071
   imx = my * E3ww + mx;                                                   //  if pixel has been painted, 
 
3072
   imx = HDFpixmap[imx];                                                   //    select corresp. image to warp
 
3073
   if (imx == cimNF) return;                                               //  else no action      v.10.8
 
3074
   
 
3075
   if (imx != HDFimage) {
 
3076
      HDFimage = imx;                                                      //  update selected image and
 
3077
      imageN[5] = '1' + imx;                                               //    dialog radio button
 
3078
      zdialog_stuff(HDFzd,imageN,1);
 
3079
   }
 
3080
 
 
3081
   pxm1 = cimPXMs[imx];                                                    //  input image
 
3082
   pxm2 = cimPXMw[imx];                                                    //  output image
 
3083
   ww = pxm2->ww;
 
3084
   hh = pxm2->hh;
 
3085
 
 
3086
   mx = Mxdown;                                                            //  drag origin, image coordinates
 
3087
   my = Mydown;
 
3088
   dx = Mxdrag - Mxdown;                                                   //  drag increment
 
3089
   dy = Mydrag - Mydown;
 
3090
   Mxdown = Mxdrag;                                                        //  next drag origin
 
3091
   Mydown = Mydrag;
 
3092
 
 
3093
   d1 = ww * ww + hh * hh;
 
3094
   
 
3095
   for (py = 0; py < hh; py++)                                             //  process all output pixels
 
3096
   for (px = 0; px < ww; px++)
 
3097
   {
 
3098
      d2 = (px-mx)*(px-mx) + (py-my)*(py-my);
 
3099
      mag = (1.0 - d2 / d1);
 
3100
      mag = mag * mag * mag * mag;
 
3101
      mag = mag * mag * mag * mag;
 
3102
      mag = mag * mag * mag * mag;
 
3103
 
 
3104
      dispx = -dx * mag;                                                   //  displacement = drag * mag
 
3105
      dispy = -dy * mag;
 
3106
      
 
3107
      ii = py * ww + px;
 
3108
      HDFwarpx[imx][ii] += dispx;                                          //  add this drag to prior sum
 
3109
      HDFwarpy[imx][ii] += dispy;
 
3110
 
 
3111
      dispx = HDFwarpx[imx][ii];
 
3112
      dispy = HDFwarpy[imx][ii];
 
3113
 
 
3114
      vstat1 = vpixel(pxm1,px+dispx,py+dispy,vpix1);                       //  input virtual pixel
 
3115
      pix2 = PXMpix(pxm2,px,py);                                           //  output pixel
 
3116
      if (vstat1) {
 
3117
         pix2[0] = vpix1[0];
 
3118
         pix2[1] = vpix1[1];
 
3119
         pix2[2] = vpix1[2];
 
3120
      }
 
3121
      else pix2[0] = pix2[1] = pix2[2] = 0;
 
3122
   }
 
3123
 
 
3124
   signal_thread();                                                        //  combine images >> E3 >> main window
 
3125
   return;
 
3126
}
 
3127
 
 
3128
 
 
3129
//  Combine images in E3pxm16 (not reallocated). Update main window.
 
3130
 
 
3131
void * HDF_combine_thread(void *)                                          //  v.10.7
 
3132
{
 
3133
   void * HDF_combine_wthread(void *);
 
3134
   
 
3135
   while (true)
 
3136
   {
 
3137
      thread_idle_loop();                                                  //  wait for work or exit request
 
3138
 
 
3139
      mutex_lock(&Fpixmap_lock);                                           //  stop window updates
 
3140
 
 
3141
      for (int ii = 0; ii < Nwt; ii++)                                     //  start worker threads   v.10.7
 
3142
         start_wthread(HDF_combine_wthread,&wtnx[ii]);
 
3143
      wait_wthreads();                                                     //  wait for completion
 
3144
 
 
3145
      mutex_unlock(&Fpixmap_lock);                                         //  update window
 
3146
      mwpaint2();
 
3147
   }
 
3148
 
 
3149
   return 0;                                                               //  not executed
 
3150
}
 
3151
 
 
3152
 
 
3153
void * HDF_combine_wthread(void *arg)                                      //  worker thread
 
3154
{
 
3155
   int         index = *((int *) (arg));                                   //  no more paint and warp modes  v.10.8
 
3156
   int         px3, py3, vstat1;
 
3157
   int         imx, red, green, blue;
 
3158
   double      px, py;
 
3159
   double      xoff, yoff, sintf[10], costf[10];
 
3160
   uint16      vpix1[3], *pix3;
 
3161
 
 
3162
   for (imx = 0; imx < cimNF; imx++)                                       //  pre-calculate trig funcs
 
3163
   {
 
3164
      sintf[imx] = sin(cimOffs[imx].tf);
 
3165
      costf[imx] = cos(cimOffs[imx].tf);
 
3166
   }
 
3167
 
 
3168
   for (py3 = index+1; py3 < E3hh-1; py3 += Nwt)                           //  step through output pixels
 
3169
   for (px3 = 1; px3 < E3ww-1; px3++)
 
3170
   {
 
3171
      pix3 = PXMpix(E3pxm16,px3,py3);
 
3172
      
 
3173
      imx = py3 * E3ww + px3;
 
3174
      imx = HDFpixmap[imx];
 
3175
 
 
3176
      if (imx < cimNF)                                                     //  specific image maps to pixel
 
3177
      {
 
3178
         xoff = cimOffs[imx].xf;
 
3179
         yoff = cimOffs[imx].yf;
 
3180
         px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);
 
3181
         py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);
 
3182
         vstat1 = vpixel(cimPXMw[imx],px,py,vpix1);                        //  corresp. input vpixel
 
3183
         if (vstat1) {
 
3184
            pix3[0] = vpix1[0];
 
3185
            pix3[1] = vpix1[1];
 
3186
            pix3[2] = vpix1[2];
 
3187
         }
 
3188
         else pix3[0] = pix3[1] = pix3[2] = 0;
 
3189
      }
 
3190
      
 
3191
      else                                                                 //  use blend of all images
 
3192
      {
 
3193
         red = green = blue = 0;
 
3194
 
 
3195
         for (imx = 0; imx < cimNF; imx++)
 
3196
         {
 
3197
            xoff = cimOffs[imx].xf;
 
3198
            yoff = cimOffs[imx].yf;
 
3199
            px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);
 
3200
            py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);
 
3201
            vstat1 = vpixel(cimPXMw[imx],px,py,vpix1);
 
3202
            if (vstat1) {
 
3203
               red += vpix1[0];
 
3204
               green += vpix1[1];
 
3205
               blue += vpix1[2];
 
3206
            }
 
3207
         }
 
3208
         
 
3209
         pix3[0] = red / cimNF;
 
3210
         pix3[1] = green / cimNF;
 
3211
         pix3[2] = blue / cimNF;
 
3212
      }
 
3213
   }
 
3214
 
 
3215
   exit_wthread();
 
3216
   return 0;                                                               //  not executed, avoid gcc warning
 
3217
}
 
3218
 
 
3219
 
 
3220
/**************************************************************************
 
3221
 
 
3222
   Stack/Paint function
 
3223
   Combine multiple images of one subject taken at different times from
 
3224
   (almost) the same camera position. Align the images and allow the user 
 
3225
   to choose which input image to use for each area of the output image, 
 
3226
   by "painting" with the mouse. Use this to remove tourists and cars that
 
3227
   move in and out of a scene being photographed.
 
3228
   
 
3229
**************************************************************************/
 
3230
 
 
3231
int      STPstat;                                                          //  1 = OK, 0 = failed or canceled
 
3232
double   STPinitAlignSize = 160;                                           //  initial align image size
 
3233
double   STPimageIncrease = 1.6;                                           //  image size increase per align cycle
 
3234
double   STPsampSize = 10000;                                              //  pixel sample size    v.11.03
 
3235
 
 
3236
double   STPinitSearchRange = 5.0;                                         //  initial search range, +/- pixels
 
3237
double   STPinitSearchStep = 1.0;                                          //  initial search step, pixels
 
3238
double   STPinitWarpRange = 2.0;                                           //  initial corner warp range
 
3239
double   STPinitWarpStep = 1.0;                                            //  initial corner warp step
 
3240
double   STPsearchRange = 2.0;                                             //  normal search range
 
3241
double   STPsearchStep = 1.0;                                              //  normal search step
 
3242
double   STPwarpRange = 1.0;                                               //  normal corner warp range
 
3243
double   STPwarpStep = 0.67;                                               //  normal corner warp step
 
3244
 
 
3245
void * STP_align_thread(void *);
 
3246
void   STP_tweak();
 
3247
void   STP_mousefunc();
 
3248
void * STP_combine_thread(void *);
 
3249
 
 
3250
editfunc    EFstp;                                                         //  edit function data
 
3251
 
 
3252
 
 
3253
//  menu function
 
3254
 
 
3255
void m_STP(GtkWidget *, cchar *)                                           //  v.11.02
 
3256
{
 
3257
   char        **flist;
 
3258
   int         imx, err, ww, hh;
 
3259
   double      diffw, diffh;
 
3260
   
 
3261
   zfuncs::F1_help_topic = "stack_paint";                                  //  help topic
 
3262
 
 
3263
   if (mod_keep()) return;                                                 //  warn unsaved changes
 
3264
   if (! menulock(1)) return;                                              //  test menu lock            v.11.07
 
3265
   menulock(0);
 
3266
 
 
3267
   for (imx = 0; imx < 10; imx++)
 
3268
   {                                                                       //  clear all file and PXM data
 
3269
      cimFile[imx] = 0;
 
3270
      cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0;
 
3271
   }
 
3272
 
 
3273
   cimNF = 0;   
 
3274
 
 
3275
   flist = zgetfileN(ZTX("Select 2 to 9 files"),"openN",curr_file);        //  select images to combine
 
3276
   if (! flist) return;
 
3277
 
 
3278
   for (imx = 0; flist[imx]; imx++);                                       //  count selected files
 
3279
   if (imx < 2 || imx > 9) {
 
3280
      zmessageACK(mWin,ZTX("Select 2 to 9 files"));
 
3281
      goto cleanup;
 
3282
   }
 
3283
 
 
3284
   cimNF = imx;                                                            //  file count
 
3285
   for (imx = 0; imx < cimNF; imx++)
 
3286
      cimFile[imx] = strdupz(flist[imx],0,"STP");                          //  set up file list
 
3287
   
 
3288
   if (! cim_load_files()) goto cleanup;                                   //  load and check all files
 
3289
 
 
3290
   ww = cimPXMf[0]->ww;
 
3291
   hh = cimPXMf[0]->hh;
 
3292
   
 
3293
   for (imx = 1; imx < cimNF; imx++)                                       //  check image compatibility
 
3294
   {
 
3295
      diffw = abs(ww - cimPXMf[imx]->ww);
 
3296
      diffw = diffw / ww;
 
3297
      diffh = abs(hh - cimPXMf[imx]->hh);
 
3298
      diffh = diffh / hh;
 
3299
 
 
3300
      if (diffw > 0.02 || diffh > 0.02) {
 
3301
         zmessageACK(mWin,ZTX("Images are not all the same size"));
 
3302
         goto cleanup;
 
3303
      }
 
3304
   }
 
3305
 
 
3306
   free_resources();                                                       //  ready to commit
 
3307
 
 
3308
   err = f_open(cimFile[0],0);                                             //  curr_file = 1st file in list
 
3309
   if (err) goto cleanup;
 
3310
 
 
3311
   EFstp.funcname = "stack-paint";
 
3312
   if (! edit_setup(EFstp)) goto cleanup;                                  //  setup edit (will lock)
 
3313
 
 
3314
   start_thread(STP_align_thread,0);                                       //  align each pair of images
 
3315
   wrapup_thread(0);                                                       //  wait for completion
 
3316
   if (STPstat != 1) goto cancel;
 
3317
 
 
3318
   STP_tweak();                                                            //  combine images based on user inputs
 
3319
   if (STPstat != 1) goto cancel;
 
3320
 
 
3321
   CEF->Fmod = 1;                                                          //  done
 
3322
   edit_done(EFstp);
 
3323
   goto cleanup;
 
3324
 
 
3325
cancel:
 
3326
   edit_cancel(EFstp);
 
3327
 
 
3328
cleanup:
 
3329
 
 
3330
   if (flist) {
 
3331
      for (imx = 0; flist[imx]; imx++)                                     //  free file list
 
3332
         zfree(flist[imx]);
 
3333
      zfree(flist);
 
3334
   }
 
3335
 
 
3336
   for (imx = 0; imx < cimNF; imx++) {                                     //  free cim file and PXM data
 
3337
      if (cimFile[imx]) zfree(cimFile[imx]);
 
3338
      if (cimPXMf[imx]) PXM_free(cimPXMf[imx]);
 
3339
      if (cimPXMs[imx]) PXM_free(cimPXMs[imx]);
 
3340
      if (cimPXMw[imx]) PXM_free(cimPXMw[imx]);
 
3341
   }
 
3342
   
 
3343
   *SB_text = 0;
 
3344
   return;
 
3345
}
 
3346
 
 
3347
 
 
3348
//  align each pair of input images, output combined image to E3pxm16
 
3349
//  cimPXMf[*]  original image
 
3350
//  cimPXMs[*]  scaled and color adjusted for pixel comparisons
 
3351
//  cimPXMw[*]  warped for display
 
3352
 
 
3353
void * STP_align_thread(void *)                                            //  v.11.02
 
3354
{
 
3355
   int         imx, im1, im2, ww, hh, ii, nn;
 
3356
   double      R, maxtf, mintf, midtf;
 
3357
   double      xoff, yoff, toff, dxoff, dyoff;
 
3358
   cimoffs     offsets[10];                                                //  x/y/t offsets after alignment
 
3359
   
 
3360
   Fzoom = 0;                                                              //  fit to window if big
 
3361
   Fblowup = 1;                                                            //  scale up to window if small
 
3362
   Ffuncbusy++;
 
3363
   cimShrink = 0;                                                          //  no warp shrinkage (pano)
 
3364
   cimPano = cimPanoV = 0;                                                 //  no pano mode
 
3365
 
 
3366
   for (imx = 0; imx < cimNF; imx++)
 
3367
      memset(&offsets[imx],0,sizeof(cimoffs));
 
3368
   
 
3369
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  loop each pair of images
 
3370
   {
 
3371
      im2 = im1 + 1;
 
3372
 
 
3373
      memset(&cimOffs[im1],0,sizeof(cimoffs));                             //  initial image offsets = 0
 
3374
      memset(&cimOffs[im2],0,sizeof(cimoffs));
 
3375
      
 
3376
      ww = cimPXMf[im1]->ww;                                               //  image dimensions
 
3377
      hh = cimPXMf[im1]->hh;
 
3378
 
 
3379
      nn = ww;                                                             //  use larger of ww, hh
 
3380
      if (hh > ww) nn = hh;
 
3381
      cimScale = STPinitAlignSize / nn;                                    //  initial align image size
 
3382
      if (cimScale > 1.0) cimScale = 1.0;
 
3383
 
 
3384
      cimBlend = 0;                                                        //  no blend width (use all)
 
3385
      cim_get_overlap(im1,im2,cimPXMf);                                    //  get overlap area
 
3386
      cim_match_colors(im1,im2,cimPXMf);                                   //  get color matching factors
 
3387
 
 
3388
      cimSearchRange = STPinitSearchRange;                                 //  initial align search range
 
3389
      cimSearchStep = STPinitSearchStep;                                   //  initial align search step
 
3390
      cimWarpRange = STPinitWarpRange;                                     //  initial align corner warp range
 
3391
      cimWarpStep = STPinitWarpStep;                                       //  initial align corner warp step
 
3392
      cimSampSize = STPsampSize;                                           //  pixel sample size for align/compare
 
3393
      cimNsearch = 0;                                                      //  reset align search counter
 
3394
 
 
3395
      while (true)                                                         //  loop, increasing image size
 
3396
      {
 
3397
         cim_scale_image(im1,cimPXMs);                                     //  scale images to cimScale
 
3398
         cim_scale_image(im2,cimPXMs);
 
3399
 
 
3400
         cim_adjust_colors(cimPXMs[im1],1);                                //  apply color adjustments
 
3401
         cim_adjust_colors(cimPXMs[im2],2);
 
3402
 
 
3403
         cim_warp_image(im1);                                              //  warp images for show
 
3404
         cim_warp_image(im2);
 
3405
 
 
3406
         cimShowIm1 = im1;                                                 //  show these two images
 
3407
         cimShowIm2 = im2;                                                 //    with 50/50 blend
 
3408
         cimShowAll = 0;
 
3409
         cim_show_images(1,0);                                             //  (y offset can change)
 
3410
 
 
3411
         cim_get_overlap(im1,im2,cimPXMs);                                 //  get overlap area             v.11.04
 
3412
         cim_get_redpix(im1);                                              //  get high-contrast pixels
 
3413
 
 
3414
         cim_align_image(im1,im2);                                         //  align im2 to im1
 
3415
         
 
3416
         zfree(cimRedpix);                                                 //  clear red pixels
 
3417
         cimRedpix = 0;
 
3418
 
 
3419
         if (cimScale == 1.0) break;                                       //  done
 
3420
 
 
3421
         R = STPimageIncrease;                                             //  next larger image size
 
3422
         cimScale = cimScale * R;
 
3423
         if (cimScale > 0.85) {                                            //  if close to end, jump to end
 
3424
            R = R / cimScale;
 
3425
            cimScale = 1.0;
 
3426
         }
 
3427
 
 
3428
         cimOffs[im1].xf *= R;                                             //  scale offsets for larger image
 
3429
         cimOffs[im1].yf *= R;
 
3430
         cimOffs[im2].xf *= R;
 
3431
         cimOffs[im2].yf *= R;
 
3432
 
 
3433
         for (ii = 0; ii < 4; ii++) {
 
3434
            cimOffs[im1].wx[ii] *= R;
 
3435
            cimOffs[im1].wy[ii] *= R;
 
3436
            cimOffs[im2].wx[ii] *= R;
 
3437
            cimOffs[im2].wy[ii] *= R;
 
3438
         }
 
3439
 
 
3440
         cimSearchRange = STPsearchRange;                                  //  align search range
 
3441
         cimSearchStep = STPsearchStep;                                    //  align search step size
 
3442
         cimWarpRange = STPwarpRange;                                      //  align corner warp range
 
3443
         cimWarpStep = STPwarpStep;                                        //  align corner warp step size
 
3444
      }
 
3445
 
 
3446
      offsets[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf;                 //  save im2 offsets from im1
 
3447
      offsets[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf;
 
3448
      offsets[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf;
 
3449
 
 
3450
      for (ii = 0; ii < 4; ii++) {
 
3451
         offsets[im2].wx[ii] = cimOffs[im2].wx[ii] - cimOffs[im1].wx[ii];
 
3452
         offsets[im2].wy[ii] = cimOffs[im2].wy[ii] - cimOffs[im1].wy[ii];
 
3453
      }
 
3454
   }
 
3455
 
 
3456
   for (imx = 0; imx < cimNF; imx++)                                       //  offsets[*] >> cimOffs[*]
 
3457
      cimOffs[imx] = offsets[imx];
 
3458
   
 
3459
   cimOffs[0].xf = cimOffs[0].yf = cimOffs[0].tf = 0;                      //  image 0 at (0,0,0)
 
3460
 
 
3461
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  absolute offsets for image 1 to last
 
3462
   {
 
3463
      im2 = im1 + 1;
 
3464
      cimOffs[im2].xf += cimOffs[im1].xf;                                  //  x/y/t offsets are additive
 
3465
      cimOffs[im2].yf += cimOffs[im1].yf;
 
3466
      cimOffs[im2].tf += cimOffs[im1].tf;
 
3467
 
 
3468
      for (ii = 0; ii < 4; ii++) {                                         //  corner warps are additive
 
3469
         cimOffs[im2].wx[ii] += cimOffs[im1].wx[ii];
 
3470
         cimOffs[im2].wy[ii] += cimOffs[im1].wy[ii];
 
3471
      }
 
3472
   }
 
3473
 
 
3474
   for (imx = 1; imx < cimNF; imx++)                                       //  re-warp to absolute
 
3475
      cim_warp_image(imx);
 
3476
 
 
3477
   toff = cimOffs[0].tf;                                                   //  balance +/- thetas
 
3478
   maxtf = mintf = toff;
 
3479
   for (imx = 1; imx < cimNF; imx++) {
 
3480
      toff = cimOffs[imx].tf;
 
3481
      if (toff > maxtf) maxtf = toff;
 
3482
      if (toff < mintf) mintf = toff;
 
3483
   }
 
3484
   midtf = 0.5 * (maxtf + mintf);
 
3485
   
 
3486
   for (imx = 0; imx < cimNF; imx++)
 
3487
      cimOffs[imx].tf -= midtf;
 
3488
 
 
3489
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  adjust x/y offsets for images after im1
 
3490
   for (im2 = im1+1; im2 < cimNF; im2++)                                   //    due to im1 theta offset
 
3491
   {
 
3492
      toff = cimOffs[im1].tf;
 
3493
      xoff = cimOffs[im2].xf - cimOffs[im1].xf;
 
3494
      yoff = cimOffs[im2].yf - cimOffs[im1].yf;
 
3495
      dxoff = yoff * sin(toff);
 
3496
      dyoff = xoff * sin(toff);
 
3497
      cimOffs[im2].xf -= dxoff;
 
3498
      cimOffs[im2].yf += dyoff;
 
3499
   }
 
3500
 
 
3501
   Fzoom = Fblowup = 0;
 
3502
   Ffuncbusy--;
 
3503
   STPstat = 1;
 
3504
   thread_exit();
 
3505
   return 0;                                                               //  not executed
 
3506
}
 
3507
 
 
3508
 
 
3509
//  paint output image
 
3510
 
 
3511
zdialog  *STPzd = 0;                                                       //  paint dialog
 
3512
int      STPimage;                                                         //  current image (0 based)
 
3513
int      STPradius;                                                        //  paint mode radius
 
3514
char     *STPpixmap = 0;                                                   //  map input image per output pixel
 
3515
 
 
3516
 
 
3517
void STP_tweak()                                                           //  v.11.02
 
3518
{
 
3519
   char     imageN[8] = "imageN", labN[4] = "0";
 
3520
   int      cc, imx;
 
3521
 
 
3522
   int STP_tweak_dialog_event(zdialog *zd, cchar *event);
 
3523
 
 
3524
   //    image   (o) 1  (o) 2  (o) 3  ...
 
3525
   //    [x] my mouse    radius [___]
 
3526
 
 
3527
   STPzd = zdialog_new(ZTX("Select and Paint Image"),mWin,Bdone,Bcancel,null);
 
3528
   zdialog_add_widget(STPzd,"hbox","hbim","dialog",0,"space=3");
 
3529
   zdialog_add_widget(STPzd,"label","labim","hbim",ZTX("image"),"space=5");
 
3530
   zdialog_add_widget(STPzd,"hbox","hbmr","dialog",0,"space=3");
 
3531
   zdialog_add_widget(STPzd,"check","mymouse","hbmr",BmyMouse,"space=5");
 
3532
   zdialog_add_widget(STPzd,"label","labr","hbmr",Bradius,"space=5");
 
3533
   zdialog_add_widget(STPzd,"spin","radius","hbmr","1|400|1|100");
 
3534
 
 
3535
   for (imx = 0; imx < cimNF; imx++) {                                     //  add radio button for each image
 
3536
      imageN[5] = '1' + imx;
 
3537
      labN[0] = '1' + imx;
 
3538
      zdialog_add_widget(STPzd,"radio",imageN,"hbim",labN);
 
3539
   }
 
3540
   
 
3541
   zdialog_stuff(STPzd,"image1",1);                                        //  initial image = 1st
 
3542
 
 
3543
   STPimage = 0;                                                           //  initial image
 
3544
   STPradius = 100;                                                        //  paint radius
 
3545
 
 
3546
   takeMouse(STPzd,STP_mousefunc,0);                                       //  connect mouse function
 
3547
   
 
3548
   cc = E3ww * E3hh;                                                       //  allocate pixel map
 
3549
   STPpixmap = zmalloc(cc,"STP");
 
3550
   memset(STPpixmap,cimNF,cc);                                             //  initial state, blend all images
 
3551
 
 
3552
   start_thread(STP_combine_thread,0);                                     //  start working thread
 
3553
   signal_thread();
 
3554
 
 
3555
   zdialog_run(STPzd,STP_tweak_dialog_event,"-10/20");                     //  run dialog, parallel            v.11.07
 
3556
   zdialog_wait(STPzd);                                                    //  wait for completion
 
3557
 
 
3558
   return;
 
3559
}
 
3560
 
 
3561
 
 
3562
//  dialog event and completion callback function
 
3563
 
 
3564
int STP_tweak_dialog_event(zdialog *zd, cchar *event)                      //  v.11.02
 
3565
{
 
3566
   int      nn, mymouse;
 
3567
   
 
3568
   if (zd->zstat)                                                          //  dialog finish
 
3569
   {
 
3570
      freeMouse();                                                         //  disconnect mouse function
 
3571
      signal_thread();
 
3572
      wrapup_thread(8);
 
3573
      if (zd->zstat == 1) STPstat = 1;
 
3574
      else STPstat = 0;
 
3575
      if (STPstat == 1) cim_trim();                                        //  cut-off edges
 
3576
      zdialog_free(STPzd);
 
3577
      zfree(STPpixmap);                                                    //  free pixel map
 
3578
   }
 
3579
   
 
3580
   if (strnEqu(event,"image",5)) {                                         //  image radio button
 
3581
      nn = event[5] - '0';                                                 //  1 to cimNF
 
3582
      if (nn > 0 && nn <= cimNF) 
 
3583
         STPimage = nn - 1;                                                //  0 to cimNF-1
 
3584
      signal_thread();
 
3585
   }
 
3586
 
 
3587
   if (strEqu(event,"radius"))                                             //  change paint radius
 
3588
      zdialog_fetch(zd,"radius",STPradius);
 
3589
 
 
3590
   if (strEqu(event,"mymouse")) {                                          //  toggle mouse capture
 
3591
      zdialog_fetch(zd,"mymouse",mymouse);
 
3592
      if (mymouse) {
 
3593
         takeMouse(zd,STP_mousefunc,0);                                    //  connect mouse function
 
3594
         signal_thread();
 
3595
      }
 
3596
      else freeMouse();                                                    //  disconnect mouse
 
3597
   }
 
3598
 
 
3599
   return 1;
 
3600
}
 
3601
 
 
3602
 
 
3603
//  STP dialog mouse function
 
3604
//  paint:  during drag, selected image >> STPpixmap (within paint radius) >> E3
 
3605
//  warp:   for selected image, cimPXMs >> warp >> cimPXMw >> E3
 
3606
 
 
3607
void STP_mousefunc()                                                       //  v.11.02
 
3608
{
 
3609
   uint16      vpix1[3], *pix3;
 
3610
   int         imx, radius, radius2, vstat1;
 
3611
   int         mx, my, dx, dy, px3, py3, ww;
 
3612
   double      px1, py1;
 
3613
   double      xoff, yoff, sintf[10], costf[10];
 
3614
   
 
3615
   radius = STPradius;                                                     //  paintbrush radius
 
3616
   radius2 = radius * radius;
 
3617
 
 
3618
   toparcx = Mxposn - radius;                                              //  paintbrush outline circle
 
3619
   toparcy = Myposn - radius;
 
3620
   toparcw = toparch = 2 * radius;
 
3621
   Ftoparc = 1;
 
3622
   paint_toparc(3);
 
3623
 
 
3624
   if (LMclick || RMclick) {                                               //  mouse click
 
3625
      LMclick = RMclick = 0;
 
3626
      return;                                                              //  ignore
 
3627
   }
 
3628
 
 
3629
   else if (Mxdrag || Mydrag) {                                            //  drag in progress
 
3630
      mx = Mxdrag;
 
3631
      my = Mydrag;
 
3632
   }
 
3633
   
 
3634
   else return;
 
3635
 
 
3636
   if (mx < 0 || mx > E3ww-1 || my < 0 || my > E3hh-1)                     //  mouse outside image area
 
3637
      return;
 
3638
 
 
3639
   for (imx = 0; imx < cimNF; imx++)                                       //  pre-calculate trig funcs
 
3640
   {
 
3641
      sintf[imx] = sin(cimOffs[imx].tf);
 
3642
      costf[imx] = cos(cimOffs[imx].tf);
 
3643
   }
 
3644
 
 
3645
   for (dy = -radius; dy <= radius; dy++)                                  //  loop pixels around mouse
 
3646
   for (dx = -radius; dx <= radius; dx++)
 
3647
   {
 
3648
      if (dx*dx + dy*dy > radius2) continue;                               //  outside radius
 
3649
 
 
3650
      px3 = mx + dx;                                                       //  output pixel
 
3651
      py3 = my + dy;
 
3652
      if (px3 < 0 || px3 > E3ww-1) continue;                               //  outside image
 
3653
      if (py3 < 0 || py3 > E3hh-1) continue;
 
3654
 
 
3655
      pix3 = PXMpix(E3pxm16,px3,py3);                                      //  output pixel
 
3656
 
 
3657
      imx = py3 * E3ww + px3;                                              //  update pixmap to selected image
 
3658
      STPpixmap[imx] = STPimage;
 
3659
      
 
3660
      imx = STPimage;
 
3661
      xoff = cimOffs[imx].xf;
 
3662
      yoff = cimOffs[imx].yf;
 
3663
      px1 = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);         //  input virtual pixel
 
3664
      py1 = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);
 
3665
      vstat1 = vpixel(cimPXMw[imx],px1,py1,vpix1);
 
3666
      if (vstat1) {
 
3667
         pix3[0] = vpix1[0];
 
3668
         pix3[1] = vpix1[1];
 
3669
         pix3[2] = vpix1[2];
 
3670
      }
 
3671
      else pix3[0] = pix3[1] = pix3[2] = 0;
 
3672
   }
 
3673
 
 
3674
   mx = mx - radius - 1;                                                   //  update window
 
3675
   my = my - radius - 1;
 
3676
   ww = 2 * radius + 3;
 
3677
   paint_toparc(2);
 
3678
   mwpaint3(mx,my,ww,ww);
 
3679
   Ftoparc = 1;
 
3680
   paint_toparc(3);
 
3681
   return;
 
3682
}
 
3683
 
 
3684
 
 
3685
//  Combine images in E3pxm16 (not reallocated). Update main window.
 
3686
 
 
3687
void * STP_combine_thread(void *)                                          //  v.11.02
 
3688
{
 
3689
   void * STP_combine_wthread(void *);
 
3690
   
 
3691
   while (true)
 
3692
   {
 
3693
      thread_idle_loop();                                                  //  wait for work or exit request
 
3694
 
 
3695
      mutex_lock(&Fpixmap_lock);                                           //  stop window updates
 
3696
 
 
3697
      for (int ii = 0; ii < Nwt; ii++)                                     //  start worker threads
 
3698
         start_wthread(STP_combine_wthread,&wtnx[ii]);
 
3699
      wait_wthreads();                                                     //  wait for completion
 
3700
 
 
3701
      mutex_unlock(&Fpixmap_lock);                                         //  update window
 
3702
      mwpaint2();
 
3703
   }
 
3704
 
 
3705
   return 0;                                                               //  not executed
 
3706
}
 
3707
 
 
3708
 
 
3709
void * STP_combine_wthread(void *arg)                                      //  worker thread
 
3710
{
 
3711
   int         index = *((int *) (arg));
 
3712
   int         px3, py3, vstat1;
 
3713
   int         imx, red, green, blue;
 
3714
   double      px, py;
 
3715
   double      xoff, yoff, sintf[10], costf[10];
 
3716
   uint16      vpix1[3], *pix3;
 
3717
 
 
3718
   for (imx = 0; imx < cimNF; imx++)                                       //  pre-calculate trig funcs
 
3719
   {
 
3720
      sintf[imx] = sin(cimOffs[imx].tf);
 
3721
      costf[imx] = cos(cimOffs[imx].tf);
 
3722
   }
 
3723
 
 
3724
   for (py3 = index+1; py3 < E3hh-1; py3 += Nwt)                           //  step through output pixels
 
3725
   for (px3 = 1; px3 < E3ww-1; px3++)
 
3726
   {
 
3727
      pix3 = PXMpix(E3pxm16,px3,py3);
 
3728
      
 
3729
      imx = py3 * E3ww + px3;
 
3730
      imx = STPpixmap[imx];
 
3731
 
 
3732
      if (imx < cimNF)                                                     //  specific image maps to pixel
 
3733
      {
 
3734
         xoff = cimOffs[imx].xf;
 
3735
         yoff = cimOffs[imx].yf;
 
3736
         px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);
 
3737
         py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);
 
3738
         vstat1 = vpixel(cimPXMw[imx],px,py,vpix1);                        //  corresp. input vpixel
 
3739
         if (vstat1) {
 
3740
            pix3[0] = vpix1[0];
 
3741
            pix3[1] = vpix1[1];
 
3742
            pix3[2] = vpix1[2];
 
3743
         }
 
3744
         else pix3[0] = pix3[1] = pix3[2] = 0;
 
3745
      }
 
3746
      
 
3747
      else                                                                 //  use blend of all images
 
3748
      {
 
3749
         red = green = blue = 0;
 
3750
 
 
3751
         for (imx = 0; imx < cimNF; imx++)
 
3752
         {
 
3753
            xoff = cimOffs[imx].xf;
 
3754
            yoff = cimOffs[imx].yf;
 
3755
            px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);
 
3756
            py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);
 
3757
            vstat1 = vpixel(cimPXMw[imx],px,py,vpix1);
 
3758
            if (vstat1) {
 
3759
               red += vpix1[0];
 
3760
               green += vpix1[1];
 
3761
               blue += vpix1[2];
 
3762
            }
 
3763
         }
 
3764
         
 
3765
         pix3[0] = red / cimNF;
 
3766
         pix3[1] = green / cimNF;
 
3767
         pix3[2] = blue / cimNF;
 
3768
      }
 
3769
   }
 
3770
 
 
3771
   exit_wthread();
 
3772
   return 0;                                                               //  not executed, avoid gcc warning
 
3773
}
 
3774
 
 
3775
 
 
3776
/**************************************************************************
 
3777
 
 
3778
   Stack/Noise function
 
3779
   Combine multiple photos of the same subject and average the 
 
3780
   pixels for noise reduction.
 
3781
 
 
3782
**************************************************************************/
 
3783
 
 
3784
double   STN_initAlignSize = 160;                                          //  initial align image size
 
3785
double   STN_imageIncrease = 1.6;                                          //  image size increase per align cycle
 
3786
double   STN_sampSize = 6000;                                              //  pixel sample size
 
3787
 
 
3788
double   STN_initSearchRange = 5.0;                                        //  initial search range, +/- pixels
 
3789
double   STN_initSearchStep = 1.0;                                         //  initial search step, pixels
 
3790
double   STN_initWarpRange = 2.0;                                          //  initial corner warp range
 
3791
double   STN_initWarpStep = 1.0;                                           //  initial corner warp step
 
3792
double   STN_searchRange = 2.0;                                            //  normal search range
 
3793
double   STN_searchStep = 1.0;                                             //  normal search step
 
3794
double   STN_warpRange = 1.0;                                              //  normal corner warp range
 
3795
double   STN_warpStep = 0.67;                                              //  normal corner warp step
 
3796
 
 
3797
int      STN_stat;                                                         //  1 = OK, 0 = failed or canceled
 
3798
int      STN_average = 1, STN_median = 0;                                  //  use average/median of input pixels
 
3799
int      STN_exlow = 0, STN_exhigh = 0;                                    //  exclude low/high pixel
 
3800
 
 
3801
void * STN_align_thread(void *);
 
3802
void   STN_tweak();
 
3803
void * STN_combine_thread(void *);
 
3804
 
 
3805
editfunc    EFstn;                                                         //  edit function data
 
3806
 
 
3807
 
 
3808
//  menu function
 
3809
 
 
3810
void m_STN(GtkWidget *, cchar *)                                           //  new v.10.9
 
3811
{
 
3812
   char        **flist;
 
3813
   int         imx, err, ww, hh;
 
3814
   double      diffw, diffh;
 
3815
   
 
3816
   zfuncs::F1_help_topic = "stack_noise";                                  //  help topic
 
3817
 
 
3818
   if (mod_keep()) return;                                                 //  warn unsaved changes
 
3819
   if (! menulock(1)) return;                                              //  test menu lock            v.11.07
 
3820
   menulock(0);
 
3821
 
 
3822
   for (imx = 0; imx < 10; imx++)
 
3823
   {                                                                       //  clear all file and PXM data
 
3824
      cimFile[imx] = 0;
 
3825
      cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0;
 
3826
   }
 
3827
 
 
3828
   cimNF = 0;   
 
3829
 
 
3830
   flist = zgetfileN(ZTX("Select 2 to 9 files"),"openN",curr_file);        //  select images to combine
 
3831
   if (! flist) return;
 
3832
 
 
3833
   for (imx = 0; flist[imx]; imx++);                                       //  count selected files
 
3834
   if (imx < 2 || imx > 9) {
 
3835
      zmessageACK(mWin,ZTX("Select 2 to 9 files"));
 
3836
      goto cleanup;
 
3837
   }
 
3838
 
 
3839
   cimNF = imx;                                                            //  file count
 
3840
   for (imx = 0; imx < cimNF; imx++)
 
3841
      cimFile[imx] = strdupz(flist[imx],0,"STN");                          //  set up file list
 
3842
   
 
3843
   if (! cim_load_files()) goto cleanup;                                   //  load and check all files
 
3844
 
 
3845
   ww = cimPXMf[0]->ww;
 
3846
   hh = cimPXMf[0]->hh;
 
3847
   
 
3848
   for (imx = 1; imx < cimNF; imx++)                                       //  check image compatibility
 
3849
   {
 
3850
      diffw = abs(ww - cimPXMf[imx]->ww);
 
3851
      diffw = diffw / ww;
 
3852
      diffh = abs(hh - cimPXMf[imx]->hh);
 
3853
      diffh = diffh / hh;
 
3854
 
 
3855
      if (diffw > 0.02 || diffh > 0.02) {
 
3856
         zmessageACK(mWin,ZTX("Images are not all the same size"));
 
3857
         goto cleanup;
 
3858
      }
 
3859
   }
 
3860
 
 
3861
   free_resources();                                                       //  ready to commit
 
3862
 
 
3863
   err = f_open(cimFile[0],0);                                             //  curr_file = 1st file in list
 
3864
   if (err) goto cleanup;
 
3865
 
 
3866
   EFstn.funcname = "stack-noise";
 
3867
   if (! edit_setup(EFstn)) goto cleanup;                                  //  setup edit (will lock)
 
3868
 
 
3869
   start_thread(STN_align_thread,0);                                       //  align each pair of images
 
3870
   wrapup_thread(0);                                                       //  wait for completion
 
3871
   if (STN_stat != 1) goto cancel;
 
3872
 
 
3873
   STN_tweak();                                                            //  combine images based on user inputs
 
3874
   if (STN_stat != 1) goto cancel;
 
3875
 
 
3876
   CEF->Fmod = 1;                                                          //  done
 
3877
   edit_done(EFstn);
 
3878
   goto cleanup;
 
3879
 
 
3880
cancel:
 
3881
   edit_cancel(EFstn);
 
3882
 
 
3883
cleanup:
 
3884
 
 
3885
   if (flist) {
 
3886
      for (imx = 0; flist[imx]; imx++)                                     //  free file list
 
3887
         zfree(flist[imx]);
 
3888
      zfree(flist);
 
3889
   }
 
3890
 
 
3891
   for (imx = 0; imx < cimNF; imx++) {                                     //  free cim file and PXM data
 
3892
      if (cimFile[imx]) zfree(cimFile[imx]);
 
3893
      if (cimPXMf[imx]) PXM_free(cimPXMf[imx]);
 
3894
      if (cimPXMs[imx]) PXM_free(cimPXMs[imx]);
 
3895
      if (cimPXMw[imx]) PXM_free(cimPXMw[imx]);
 
3896
   }
 
3897
   
 
3898
   *SB_text = 0;
 
3899
   return;
 
3900
}
 
3901
 
 
3902
 
 
3903
//  align each image 2nd-last to 1st image
 
3904
//  cimPXMf[*]  original image
 
3905
//  cimPXMs[*]  scaled and color adjusted for pixel comparisons
 
3906
//  cimPXMw[*]  warped for display
 
3907
 
 
3908
void * STN_align_thread(void *)                                            //  v.10.9
 
3909
{
 
3910
   int         imx, im1, im2, ww, hh, ii, nn;
 
3911
   double      R, maxtf, mintf, midtf;
 
3912
   double      xoff, yoff, toff, dxoff, dyoff;
 
3913
   
 
3914
   Fzoom = 0;                                                              //  fit to window if big
 
3915
   Fblowup = 1;                                                            //  scale up to window if small
 
3916
   Ffuncbusy++;                                                            //  v.11.01
 
3917
   cimShrink = 0;                                                          //  no warp shrinkage (pano)
 
3918
   cimPano = cimPanoV = 0;                                                 //  no pano mode
 
3919
 
 
3920
   for (imx = 1; imx < cimNF; imx++)                                       //  loop 2nd to last image
 
3921
   {
 
3922
      im1 = 0;                                                             //  images to align
 
3923
      im2 = imx;
 
3924
 
 
3925
      memset(&cimOffs[im1],0,sizeof(cimoffs));                             //  initial image offsets = 0
 
3926
      memset(&cimOffs[im2],0,sizeof(cimoffs));
 
3927
      
 
3928
      ww = cimPXMf[im1]->ww;                                               //  image dimensions
 
3929
      hh = cimPXMf[im1]->hh;
 
3930
 
 
3931
      nn = ww;                                                             //  use larger of ww, hh
 
3932
      if (hh > ww) nn = hh;
 
3933
      cimScale = STN_initAlignSize / nn;                                   //  initial align image size
 
3934
      if (cimScale > 1.0) cimScale = 1.0;
 
3935
 
 
3936
      cimBlend = 0;                                                        //  no blend width (use all)
 
3937
      cim_get_overlap(im1,im2,cimPXMf);                                    //  get overlap area
 
3938
      cim_match_colors(im1,im2,cimPXMf);                                   //  get color matching factors
 
3939
 
 
3940
      cimSearchRange = STN_initSearchRange;                                //  initial align search range
 
3941
      cimSearchStep = STN_initSearchStep;                                  //  initial align search step
 
3942
      cimWarpRange = STN_initWarpRange;                                    //  initial align corner warp range
 
3943
      cimWarpStep = STN_initWarpStep;                                      //  initial align corner warp step
 
3944
      cimSampSize = STN_sampSize;                                          //  pixel sample size for align/compare
 
3945
      cimNsearch = 0;                                                      //  reset align search counter
 
3946
 
 
3947
      while (true)                                                         //  loop, increasing image size
 
3948
      {
 
3949
         cim_scale_image(im1,cimPXMs);                                     //  scale images to cimScale
 
3950
         cim_scale_image(im2,cimPXMs);
 
3951
 
 
3952
         cim_adjust_colors(cimPXMs[im1],1);                                //  apply color adjustments
 
3953
         cim_adjust_colors(cimPXMs[im2],2);
 
3954
 
 
3955
         cim_warp_image(im1);                                              //  warp images for show
 
3956
         cim_warp_image(im2);
 
3957
 
 
3958
         cimShowIm1 = im1;                                                 //  show these two images
 
3959
         cimShowIm2 = im2;                                                 //    with 50/50 blend
 
3960
         cimShowAll = 0;
 
3961
         cim_show_images(1,0);                                             //  (y offset can change)
 
3962
 
 
3963
         cim_get_overlap(im1,im2,cimPXMs);                                 //  get overlap area             v.11.04
 
3964
         cim_get_redpix(im1);                                              //  get high-contrast pixels
 
3965
 
 
3966
         cim_align_image(im1,im2);                                         //  align im2 to im1
 
3967
         
 
3968
         zfree(cimRedpix);                                                 //  clear red pixels
 
3969
         cimRedpix = 0;
 
3970
 
 
3971
         if (cimScale == 1.0) break;                                       //  done
 
3972
 
 
3973
         R = STN_imageIncrease;                                            //  next larger image size
 
3974
         cimScale = cimScale * R;
 
3975
         if (cimScale > 0.85) {                                            //  if close to end, jump to end
 
3976
            R = R / cimScale;
 
3977
            cimScale = 1.0;
 
3978
         }
 
3979
 
 
3980
         cimOffs[im1].xf *= R;                                             //  scale offsets for larger image
 
3981
         cimOffs[im1].yf *= R;
 
3982
         cimOffs[im2].xf *= R;
 
3983
         cimOffs[im2].yf *= R;
 
3984
 
 
3985
         for (ii = 0; ii < 4; ii++) {
 
3986
            cimOffs[im1].wx[ii] *= R;
 
3987
            cimOffs[im1].wy[ii] *= R;
 
3988
            cimOffs[im2].wx[ii] *= R;
 
3989
            cimOffs[im2].wy[ii] *= R;
 
3990
         }
 
3991
 
 
3992
         cimSearchRange = STN_searchRange;                                 //  align search range
 
3993
         cimSearchStep = STN_searchStep;                                   //  align search step size
 
3994
         cimWarpRange = STN_warpRange;                                     //  align corner warp range
 
3995
         cimWarpStep = STN_warpStep;                                       //  align corner warp step size
 
3996
      }
 
3997
   }
 
3998
 
 
3999
   toff = cimOffs[0].tf;                                                   //  balance +/- thetas
 
4000
   maxtf = mintf = toff;
 
4001
   for (imx = 1; imx < cimNF; imx++) {
 
4002
      toff = cimOffs[imx].tf;
 
4003
      if (toff > maxtf) maxtf = toff;
 
4004
      if (toff < mintf) mintf = toff;
 
4005
   }
 
4006
   midtf = 0.5 * (maxtf + mintf);
 
4007
   
 
4008
   for (imx = 0; imx < cimNF; imx++)
 
4009
      cimOffs[imx].tf -= midtf;
 
4010
 
 
4011
   for (im1 = 0; im1 < cimNF-1; im1++)                                     //  adjust x/y offsets for images after im1
 
4012
   for (im2 = im1+1; im2 < cimNF; im2++)                                   //    due to im1 theta offset
 
4013
   {
 
4014
      toff = cimOffs[im1].tf;
 
4015
      xoff = cimOffs[im2].xf - cimOffs[im1].xf;
 
4016
      yoff = cimOffs[im2].yf - cimOffs[im1].yf;
 
4017
      dxoff = yoff * sin(toff);
 
4018
      dyoff = xoff * sin(toff);
 
4019
      cimOffs[im2].xf -= dxoff;
 
4020
      cimOffs[im2].yf += dyoff;
 
4021
   }
 
4022
 
 
4023
   Fzoom = Fblowup = 0;
 
4024
   Ffuncbusy--;
 
4025
   STN_stat = 1;
 
4026
   thread_exit();
 
4027
   return 0;                                                               //  not executed
 
4028
}
 
4029
 
 
4030
 
 
4031
//  change pixel combination according to user input
 
4032
 
 
4033
void STN_tweak()                                                           //  v.10.9
 
4034
{
 
4035
   zdialog     *zd;
 
4036
 
 
4037
   int STN_tweak_dialog_event(zdialog *zd, cchar *event);
 
4038
 
 
4039
   //    Adjust Pixel Composition
 
4040
   //
 
4041
   //    (o) use average  (o) use median
 
4042
   //    [x] omit lowest value
 
4043
   //    [x] omit highest value
 
4044
 
 
4045
   zd = zdialog_new(ZTX("Adjust Pixel Composition"),mWin,Bdone,Bcancel,null);
 
4046
   zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3");
 
4047
   zdialog_add_widget(zd,"radio","average","hb1","use average","space=3");
 
4048
   zdialog_add_widget(zd,"radio","median","hb1","use median","space=3");
 
4049
   zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3");
 
4050
   zdialog_add_widget(zd,"check","exlow","hb2","omit low pixel","space=3");
 
4051
   zdialog_add_widget(zd,"check","exhigh","hb2","omit high pixel","space=3");
 
4052
   
 
4053
   zdialog_stuff(zd,"average",1);                                          //  default = average
 
4054
   zdialog_stuff(zd,"median",0);
 
4055
   zdialog_stuff(zd,"exlow",0);
 
4056
   zdialog_stuff(zd,"exhigh",0);
 
4057
   
 
4058
   STN_average = 1;
 
4059
   STN_median = 0;
 
4060
   STN_exlow = 0;
 
4061
   STN_exhigh = 0;
 
4062
   
 
4063
   start_thread(STN_combine_thread,0);                                     //  start working thread
 
4064
   signal_thread();
 
4065
 
 
4066
   zdialog_resize(zd,250,0);
 
4067
   zdialog_run(zd,STN_tweak_dialog_event,"-10/20");                        //  run dialog, parallel            v.11.07
 
4068
   zdialog_wait(zd);                                                       //  wait for completion
 
4069
 
 
4070
   return;
 
4071
}
 
4072
 
 
4073
 
 
4074
//  dialog event and completion callback function
 
4075
 
 
4076
int STN_tweak_dialog_event(zdialog *zd, cchar *event)                      //  v.10.9
 
4077
{
 
4078
   if (zd->zstat) {                                                        //  dialog finish
 
4079
      if (zd->zstat == 1) STN_stat = 1;
 
4080
      else STN_stat = 0;
 
4081
      wrapup_thread(8);
 
4082
      zdialog_free(zd);
 
4083
      if (STN_stat == 1) cim_trim();                                       //  trim edges    v.10.9
 
4084
   }
 
4085
   
 
4086
   if (strEqu(event,"average")) {
 
4087
      zdialog_fetch(zd,"average",STN_average);
 
4088
      signal_thread();
 
4089
   }
 
4090
   
 
4091
   if (strEqu(event,"median")) {
 
4092
      zdialog_fetch(zd,"median",STN_median);
 
4093
      signal_thread();
 
4094
   }
 
4095
   
 
4096
   if (strEqu(event,"exlow")) {
 
4097
      zdialog_fetch(zd,"exlow",STN_exlow);
 
4098
      signal_thread();
 
4099
   }
 
4100
   
 
4101
   if (strEqu(event,"exhigh")) {
 
4102
      zdialog_fetch(zd,"exhigh",STN_exhigh);
 
4103
      signal_thread();
 
4104
   }
 
4105
   
 
4106
   return 1;
 
4107
}
 
4108
 
 
4109
 
 
4110
//  compute mean/median mix for each output pixel and update E3 image
 
4111
 
 
4112
void * STN_combine_thread(void *)                                          //  v.10.9
 
4113
{
 
4114
   void * STN_combine_wthread(void *arg);                                  //  worker thread
 
4115
 
 
4116
   while (true)
 
4117
   {
 
4118
      thread_idle_loop();                                                  //  wait for work or exit request
 
4119
 
 
4120
      for (int ii = 0; ii < Nwt; ii++)                                     //  start worker threads
 
4121
         start_wthread(STN_combine_wthread,&wtnx[ii]);
 
4122
      wait_wthreads();                                                     //  wait for completion
 
4123
 
 
4124
      CEF->Fmod = 1;
 
4125
      mwpaint2();                                                          //  update window
 
4126
   }
 
4127
 
 
4128
   return 0;                                                               //  not executed
 
4129
}
 
4130
 
 
4131
 
 
4132
//  worker thread
 
4133
 
 
4134
void * STN_combine_wthread(void *arg)                                      //  v.10.9
 
4135
{
 
4136
   int         index = *((int *) arg);
 
4137
   int         imx, vstat, px3, py3;
 
4138
   int         red, green, blue;
 
4139
   int         ii, ns, ns1, ns2;
 
4140
   int         Rlist[10], Glist[10], Blist[10];
 
4141
   double      px, py;
 
4142
   double      xoff, yoff, sintf[10], costf[10];
 
4143
   uint16      *pix3, vpix[3];
 
4144
 
 
4145
   //    input layers     0      1      2      3      4      5      6      7      8      9     10
 
4146
   int   nsx[11][2] = { {0,0}, {0,0}, {0,1}, {1,1}, {1,2}, {2,2}, {2,3}, {2,4}, {2,5}, {3,5}, {3,6} };
 
4147
   
 
4148
   for (imx = 0; imx < cimNF; imx++)                                       //  pre-calculate trig funcs
 
4149
   {
 
4150
      sintf[imx] = sin(cimOffs[imx].tf);
 
4151
      costf[imx] = cos(cimOffs[imx].tf);
 
4152
   }
 
4153
 
 
4154
   for (py3 = index+1; py3 < E3hh-1; py3 += Nwt)                           //  step through output pixels
 
4155
   for (px3 = 1; px3 < E3ww-1; px3++)
 
4156
   {
 
4157
      for (imx = ns = 0; imx < cimNF; imx++)                               //  get aligned input pixels
 
4158
      {
 
4159
         xoff = cimOffs[imx].xf;
 
4160
         yoff = cimOffs[imx].yf;
 
4161
         px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff);
 
4162
         py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff);
 
4163
 
 
4164
         vstat = vpixel(cimPXMw[imx],px,py,vpix);
 
4165
         if (vstat) {
 
4166
            Rlist[ns] = vpix[0];                                           //  add pixel RGB values to list
 
4167
            Glist[ns] = vpix[1];
 
4168
            Blist[ns] = vpix[2];
 
4169
            ns++;
 
4170
         }
 
4171
      }
 
4172
      
 
4173
      if (! ns) continue;
 
4174
      
 
4175
      if (STN_exlow || STN_exhigh || STN_median) {                         //  RGB values must be sorted
 
4176
         HeapSort(Rlist,ns);
 
4177
         HeapSort(Glist,ns);
 
4178
         HeapSort(Blist,ns);
 
4179
      }
 
4180
 
 
4181
      red = green = blue = 0;
 
4182
 
 
4183
      if (STN_average)                                                     //  average the input pixels
 
4184
      {
 
4185
         ns1 = 0;                                                          //  low and high RGB values
 
4186
         ns2 = ns - 1;
 
4187
         
 
4188
         if (STN_exlow) {                                                  //  exclude low
 
4189
            ns1++;
 
4190
            if (ns1 > ns2) ns1--;
 
4191
         }
 
4192
 
 
4193
         if (STN_exhigh) {                                                 //  exclude high
 
4194
            ns2--;
 
4195
            if (ns1 > ns2) ns2++;
 
4196
         }
 
4197
         
 
4198
         for (ii = ns1; ii <= ns2; ii++)                                   //  sum remaining RGB levels
 
4199
         {
 
4200
            red += Rlist[ii];
 
4201
            green += Glist[ii];
 
4202
            blue += Blist[ii];
 
4203
         }
 
4204
 
 
4205
         ns = ns2 - ns1 + 1;                                               //  sample count
 
4206
 
 
4207
         red = red / ns;                                                   //  output RGB = average
 
4208
         green = green / ns;
 
4209
         blue = blue / ns;
 
4210
      }
 
4211
      
 
4212
      if (STN_median)                                                      //  use median input pixels
 
4213
      {
 
4214
         ns1 = nsx[ns][0];                                                 //  middle group of pixels
 
4215
         ns2 = nsx[ns][1];
 
4216
         
 
4217
         for (ii = ns1; ii <= ns2; ii++)
 
4218
         {
 
4219
            red += Rlist[ii];
 
4220
            green += Glist[ii];
 
4221
            blue += Blist[ii];
 
4222
         }
 
4223
 
 
4224
         ns = ns2 - ns1 + 1;                                               //  sample count
 
4225
 
 
4226
         red = red / ns;                                                   //  output RGB = average
 
4227
         green = green / ns;
 
4228
         blue = blue / ns;
 
4229
      }
 
4230
 
 
4231
      pix3 = PXMpix(E3pxm16,px3,py3);                                      //  output pixel
 
4232
      pix3[0] = red;
 
4233
      pix3[1] = green;
 
4234
      pix3[2] = blue;
 
4235
   }
 
4236
 
 
4237
   exit_wthread();
 
4238
   return 0;                                                               //  not executed
 
4239
}
 
4240
 
 
4241
 
 
4242
/**************************************************************************
 
4243
 
 
4244
    Panorama function: join 2, 3, or 4 images.
 
4245
 
 
4246
***************************************************************************/
 
4247
 
 
4248
int      panStat;                                                          //  1 = OK
 
4249
zdialog  *panozd = 0;                                                      //  pre-align dialog
 
4250
 
 
4251
double   panPreAlignSize = 1000;                                           //  pre-align image size (ww)
 
4252
double   panInitAlignSize = 200;                                           //  initial align image size
 
4253
double   panImageIncrease = 1.6;                                           //  image size increase per align cycle
 
4254
double   panSampSize = 10000;                                              //  pixel sample size
 
4255
 
 
4256
double   panPreAlignBlend = 0.30;                                          //  pre-align blend width * ww
 
4257
double   panInitBlend = 0.20;                                              //  initial blend width during auto-align
 
4258
double   panFinalBlend = 0.08;                                             //  final blend width * ww
 
4259
double   panBlendDecrease = 0.8;                                           //  blend width reduction per align cycle
 
4260
 
 
4261
double   panInitSearchRange = 5.0;                                         //  initial search range, +/- pixels
 
4262
double   panInitSearchStep = 0.7;                                          //  initial search step, pixels
 
4263
double   panInitWarpRange = 4.0;                                           //  initial corner warp range, +/- pixels
 
4264
double   panInitWarpStep = 1.0;                                            //  initial corner warp step, pixels
 
4265
double   panSearchRange = 3.0;                                             //  normal search range, +/- pixels
 
4266
double   panSearchStep = 1.0;                                              //  normal search step, pixels
 
4267
double   panWarpRange = 2.0;                                               //  normal corner warp range, +/- pixels
 
4268
double   panWarpStep = 1.0;                                                //  normal corner warp step, pixels
 
4269
 
 
4270
void  pano_prealign();                                                     //  manual pre-align
 
4271
void  pano_align();                                                        //  auto fine-align
 
4272
void  pano_tweak();                                                        //  user color tweak
 
4273
 
 
4274
editfunc    EFpano;                                                        //  edit function data
 
4275
 
 
4276
 
 
4277
//  menu function
 
4278
 
 
4279
void m_pano(GtkWidget *, cchar *)                                          //  v.10.7
 
4280
{
 
4281
   int      imx, err;
 
4282
   char     **flist = 0;
 
4283
 
 
4284
   zfuncs::F1_help_topic = "panorama";                                     //  help topic
 
4285
 
 
4286
   if (mod_keep()) return;                                                 //  warn unsaved changes
 
4287
   if (! menulock(1)) return;                                              //  test menu lock            v.11.07
 
4288
   menulock(0);
 
4289
 
 
4290
   for (imx = 0; imx < 10; imx++)
 
4291
   {                                                                       //  clear all file and PXM data
 
4292
      cimFile[imx] = 0;
 
4293
      cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0;
 
4294
   }
 
4295
   cimNF = 0;   
 
4296
 
 
4297
   flist = zgetfileN(ZTX("Select 2 to 4 files"),"openN",curr_file);        //  select images to combine
 
4298
   if (! flist) return;
 
4299
 
 
4300
   for (imx = 0; flist[imx]; imx++);                                       //  count selected files
 
4301
   if (imx < 2 || imx > 4) {
 
4302
      zmessageACK(mWin,ZTX("Select 2 to 4 files"));
 
4303
      goto cleanup;
 
4304
   }
 
4305
 
 
4306
   cimNF = imx;                                                            //  file count
 
4307
   for (imx = 0; imx < cimNF; imx++)
 
4308
      cimFile[imx] = strdupz(flist[imx],0,"pano");                         //  set up file list
 
4309
   
 
4310
   if (! cim_load_files()) goto cleanup;                                   //  load and check all files
 
4311
 
 
4312
   free_resources();                                                       //  ready to commit
 
4313
 
 
4314
   err = f_open(cimFile[0],0);                                             //  curr_file = 1st file in list
 
4315
   if (err) goto cleanup;
 
4316
 
 
4317
   EFpano.funcname = "pano";
 
4318
   if (! edit_setup(EFpano)) goto cleanup;                                 //  setup edit (will lock)
 
4319
 
 
4320
   cimShowAll = 1;                                                         //  for cim_show_images(), show all    v.10.9
 
4321
   cimShrink = 0;                                                          //  no warp shrinkage                  v.11.04
 
4322
   cimPano = 1;                                                            //  horizontal pano mode               v.11.04
 
4323
   cimPanoV = 0;
 
4324
   
 
4325
   pano_prealign();                                                        //  manual pre-alignment
 
4326
   if (panStat != 1) goto cancel;
 
4327
 
 
4328
   pano_align();                                                           //  auto full alignment
 
4329
   if (panStat != 1) goto cancel;
 
4330
 
 
4331
   pano_tweak();                                                           //  manual color adjustment
 
4332
   if (panStat != 1) goto cancel;
 
4333
 
 
4334
   CEF->Fmod = 1;                                                          //  done
 
4335
   edit_done(EFpano);
 
4336
   goto cleanup;
 
4337
 
 
4338
cancel:                                                                    //  failed or canceled
 
4339
   edit_cancel(EFpano);
 
4340
 
 
4341
cleanup:
 
4342
 
 
4343
   if (flist) {
 
4344
      for (imx = 0; flist[imx]; imx++)                                     //  free file list
 
4345
         zfree(flist[imx]);
 
4346
      zfree(flist);
 
4347
   }
 
4348
 
 
4349
   for (imx = 0; imx < cimNF; imx++) {                                     //  free cim file and PXM data
 
4350
      if (cimFile[imx]) zfree(cimFile[imx]);
 
4351
      if (cimPXMf[imx]) PXM_free(cimPXMf[imx]);
 
4352
      if (cimPXMs[imx]) PXM_free(cimPXMs[imx]);
 
4353
      if (cimPXMw[imx]) PXM_free(cimPXMw[imx]);
 
4354
   }
 
4355
   
 
4356
   *SB_text = 0;
 
4357
   return;
 
4358
}
 
4359
 
 
4360
 
 
4361
//  perform manual pre-align of all images
 
4362
//  returns alignment data in cimOffs[*]
 
4363
//  lens_mm and lens_bow may also be altered
 
4364
 
 
4365
void pano_prealign()                                                       //  v.10.7
 
4366
{
 
4367
   int    pano_prealign_event(zdialog *zd, cchar *event);                  //  dialog event function
 
4368
   void * pano_prealign_thread(void *);                                    //  working thread
 
4369
 
 
4370
   int         imx, ww, err = 0;
 
4371
   cchar       *exifkey = { exif_focal_length_key };
 
4372
   char        lensname[40], **pp = 0;
 
4373
 
 
4374
   cchar  *align_mess = ZTX("Drag images into rough alignment.\n"
 
4375
                            "To rotate, drag from lower edge.");
 
4376
   cchar  *search_mess = ZTX("Search for lens mm and bow");
 
4377
 
 
4378
   pp = info_get(curr_file,&exifkey,1);                                    //  get lens mm from EXIF if available
 
4379
   if (pp && *pp) {
 
4380
      err = convSD(*pp, lens_mm, 20, 1000);                                //  leave lens_bow unchanged (no source)
 
4381
      strcpy(lensname,"(EXIF)");                                           //  lens name = EXIF
 
4382
   }
 
4383
 
 
4384
   if (! pp || ! *pp || err) {                                             //  not available
 
4385
      lens_mm = lens4_mm[curr_lens];                                       //  get curr. lens mm, bow, name
 
4386
      lens_bow = lens4_bow[curr_lens];
 
4387
      *lensname = 0;
 
4388
      strncatv(lensname,40,"(",lens4_name[curr_lens],")",null);
 
4389
   }
 
4390
   
 
4391
   for (imx = 0; imx < 10; imx++)                                          //  set all alignment offsets = 0
 
4392
      memset(&cimOffs[imx],0,sizeof(cimoffs));
 
4393
      
 
4394
   for (imx = ww = 0; imx < cimNF; imx++)                                  //  sum image widths
 
4395
      ww += cimPXMf[imx]->ww;
 
4396
   
 
4397
   cimScale = 1.4 * panPreAlignSize / ww;                                  //  set alignment image scale
 
4398
   if (cimScale > 1.0) cimScale = 1.0;                                     //  (* 0.7 after overlaps)
 
4399
 
 
4400
   for (imx = 0; imx < cimNF; imx++)                                       //  scale images > cimPXMs[*]
 
4401
      cim_scale_image(imx,cimPXMs);
 
4402
 
 
4403
   for (imx = 0; imx < cimNF; imx++) {                                     //  curve images, cimPXMs[*] replaced
 
4404
      cim_curve_image(imx);
 
4405
      cimPXMw[imx] = PXM_copy(cimPXMs[imx]);                               //  copy to cimPXMw[*] for display
 
4406
   }
 
4407
   
 
4408
   cimOffs[0].xf = cimOffs[0].yf = 0;                                      //  first image at (0,0)
 
4409
 
 
4410
   for (imx = 1; imx < cimNF; imx++)                                       //  position images with 30% overlap
 
4411
   {                                                                       //    in horizontal row
 
4412
      cimOffs[imx].xf = cimOffs[imx-1].xf + 0.7 * cimPXMw[imx-1]->ww;
 
4413
      cimOffs[imx].yf = cimOffs[imx-1].yf;
 
4414
   }
 
4415
   
 
4416
   Fzoom = 0;                                                              //  scale image to fit window
 
4417
   Fblowup = 1;                                                            //  magnify small image to window size
 
4418
 
 
4419
   cimBlend = panPreAlignBlend * cimPXMw[1]->ww;                           //  overlap in align window
 
4420
   cim_show_images(1,0);                                                   //  combine and show images in main window
 
4421
 
 
4422
   panozd = zdialog_new(ZTX("Pre-align Images"),mWin,Bproceed,Bcancel,null);     //  start pre-align dialog
 
4423
   zdialog_add_widget(panozd,"label","lab1","dialog",align_mess,"space=5");
 
4424
   zdialog_add_widget(panozd,"hbox","hb1","dialog",0,"space=2");
 
4425
   zdialog_add_widget(panozd,"spin","spmm","hb1","22|200|0.1|35","space=5");     //  [ 35  ]    lens mm  (source)
 
4426
   zdialog_add_widget(panozd,"label","labmm","hb1",ZTX("lens mm"));              //  [ 0.3 ]    lens bow
 
4427
   zdialog_add_widget(panozd,"label","lablens","hb1","","space=5");              //  [resize]   resize window
 
4428
   zdialog_add_widget(panozd,"hbox","hb2","dialog",0,"space=2");                 //  [search]   search lens mm and bow
 
4429
   zdialog_add_widget(panozd,"spin","spbow","hb2","-9|9|0.01|0","space=5");
 
4430
   zdialog_add_widget(panozd,"label","labbow","hb2",ZTX("lens bow"));
 
4431
   zdialog_add_widget(panozd,"hbox","hb3","dialog",0,"space=2");
 
4432
   zdialog_add_widget(panozd,"button","resize","hb3",ZTX("Resize"),"space=5");
 
4433
   zdialog_add_widget(panozd,"label","labsiz","hb3",ZTX("resize window"),"space=5");
 
4434
   zdialog_add_widget(panozd,"hbox","hb4","dialog",0,"space=2");
 
4435
   zdialog_add_widget(panozd,"button","search","hb4",Bsearch,"space=5");
 
4436
   zdialog_add_widget(panozd,"label","labsearch","hb4",search_mess,"space=5");
 
4437
 
 
4438
   zdialog_stuff(panozd,"spmm",lens_mm);                                   //  stuff lens data
 
4439
   zdialog_stuff(panozd,"spbow",lens_bow);
 
4440
   zdialog_stuff(panozd,"lablens",lensname);                               //  show source of lens data
 
4441
 
 
4442
   panStat = -1;                                                           //  busy status
 
4443
   gdk_window_set_cursor(drWin->window,dragcursor);                        //  set drag cursor              v.11.03
 
4444
   zdialog_run(panozd,pano_prealign_event,"-10/20");                       //  start dialog                 v.11.07
 
4445
   start_thread(pano_prealign_thread,0);                                   //  start working thread
 
4446
   zdialog_wait(panozd);                                                   //  wait for dialog completion
 
4447
   gdk_window_set_cursor(drWin->window,0);                                 //  restore normal cursor        v.11.03
 
4448
   Fzoom = Fblowup = 0;
 
4449
   return;
 
4450
}
 
4451
 
 
4452
 
 
4453
//  pre-align dialog event function
 
4454
 
 
4455
int pano_prealign_event(zdialog *zd, cchar *event)                         //  v.10.7
 
4456
{
 
4457
   int      imx;
 
4458
   double   overlap;
 
4459
 
 
4460
   if (strstr("spmm spbow",event)) {
 
4461
      zdialog_fetch(zd,"spmm",lens_mm);                                    //  get revised lens data
 
4462
      zdialog_fetch(zd,"spbow",lens_bow);
 
4463
   }
 
4464
   
 
4465
   if (strEqu(event,"resize"))                                             //  allocate new E3 image
 
4466
      cim_show_images(1,0);
 
4467
 
 
4468
   if (strEqu(event,"search")) {                                           //  search for optimal lens parms
 
4469
      if (cimNF != 2)
 
4470
         zmessageACK(mWin,ZTX("use two images only"));
 
4471
      else  panStat = 2;                                                   //  tell thread to search
 
4472
      return 0;
 
4473
   }
 
4474
 
 
4475
   if (zd->zstat)                                                          //  dialog complete
 
4476
   {
 
4477
      if (zd->zstat == 1)                                                  //  proceed
 
4478
         panStat = 1;
 
4479
      else                                                                 //  cancel or other
 
4480
         panStat = 0;
 
4481
 
 
4482
      zdialog_free(panozd);                                                //  kill dialog
 
4483
      wrapup_thread(0);                                                    //  wait for thread
 
4484
      
 
4485
      if (! panStat) return 0;                                             //  canceled
 
4486
 
 
4487
      for (imx = 0; imx < cimNF-1; imx++)                                  //  check for enough overlap
 
4488
      {
 
4489
         overlap = cim_get_overlap(imx,imx+1,cimPXMs);                     //  v.11.04
 
4490
         if (overlap < panFinalBlend) {                                    //  v.11.09
 
4491
            zmessageACK(mWin,ZTX("Too little overlap, cannot align"));
 
4492
            panStat = 0;
 
4493
            return 0;
 
4494
         }
 
4495
      }
 
4496
   }
 
4497
 
 
4498
   return 0;
 
4499
}
 
4500
 
 
4501
 
 
4502
//  pre-align working thread
 
4503
//  convert mouse and KB events into image movements                       //  overhauled   v.11.02
 
4504
 
 
4505
void * pano_prealign_thread(void *)
 
4506
{
 
4507
   void   pano_autolens();
 
4508
 
 
4509
   cimoffs     offstemp;
 
4510
   PXM         *pxmtemp;
 
4511
   char        *ftemp;
 
4512
   int         im1, im2, imm, imx;
 
4513
   int         mx0, my0, mx, my;                                           //  mouse drag origin, position
 
4514
   int         xoff, yoff, lox, hix;
 
4515
   int         sepx, minsep;
 
4516
   int         ww, hh, rotate, midx;
 
4517
   double      lens_mm0, lens_bow0;
 
4518
   double      dx, dy, t1, t2, dt;
 
4519
      
 
4520
   imm = ww = hh = rotate = xoff = yoff = 0;                               //  stop compiler warnings
 
4521
 
 
4522
   lens_mm0 = lens_mm;                                                     //  to detect changes
 
4523
   lens_bow0 = lens_bow;
 
4524
 
 
4525
   mx0 = my0 = 0;                                                          //  no drag in progress
 
4526
   Mcapture = KBcapture = 1;                                               //  capture mouse drag and KB keys
 
4527
 
 
4528
   cimBlend = 0;                                                           //  full blend during pre-align
 
4529
 
 
4530
   while (true)                                                            //  loop and align until done
 
4531
   {
 
4532
      zsleep(0.05);                                                        //  logic simplified
 
4533
      
 
4534
      if (panStat == 2) {                                                  //  dialog search button
 
4535
         panStat = -1;                                                     //  back to busy status
 
4536
         pano_autolens();
 
4537
      }
 
4538
      
 
4539
      if (panStat != -1) break;                                            //  quit signal from dialog
 
4540
      
 
4541
      if (lens_mm != lens_mm0 || lens_bow != lens_bow0) {                  //  change in lens parameters
 
4542
         lens_mm0 = lens_mm;
 
4543
         lens_bow0 = lens_bow;
 
4544
 
 
4545
         for (imx = 0; imx < cimNF; imx++) {                               //  re-curve images
 
4546
            cim_scale_image(imx,cimPXMs);
 
4547
            cim_curve_image(imx);
 
4548
            PXM_free(cimPXMw[imx]);
 
4549
            cimPXMw[imx] = PXM_copy(cimPXMs[imx]);
 
4550
         }
 
4551
 
 
4552
         cim_show_images(1,0);                                             //  combine and show images
 
4553
         continue;
 
4554
      }
 
4555
      
 
4556
      if (KBkey) {                                                         //  KB input
 
4557
         if (KBkey == GDK_Left)  cimOffs[imm].xf -= 0.5;                   //  tweak alignment offsets
 
4558
         if (KBkey == GDK_Right) cimOffs[imm].xf += 0.5;
 
4559
         if (KBkey == GDK_Up)    cimOffs[imm].yf -= 0.5;
 
4560
         if (KBkey == GDK_Down)  cimOffs[imm].yf += 0.5;
 
4561
         if (KBkey == GDK_r)     cimOffs[imm].tf += 0.0005;
 
4562
         if (KBkey == GDK_l)     cimOffs[imm].tf -= 0.0005;
 
4563
         KBkey = 0;
 
4564
 
 
4565
         cim_show_images(0,0);                                             //  combine and show images
 
4566
         continue;
 
4567
      }
 
4568
 
 
4569
      if (! Mxdrag && ! Mydrag)                                            //  no drag underway
 
4570
         mx0 = my0 = 0;                                                    //  reset drag origin
 
4571
 
 
4572
      if (Mxdrag || Mydrag)                                                //  mouse drag underway
 
4573
      {
 
4574
         mx = Mxdrag;                                                      //  mouse position in image
 
4575
         my = Mydrag;
 
4576
 
 
4577
         if (! mx0 && ! my0)                                               //  new drag
 
4578
         {
 
4579
            mx0 = mx;                                                      //  set drag origin
 
4580
            my0 = my;
 
4581
            minsep = 9999;
 
4582
            
 
4583
            for (imx = 0; imx < cimNF; imx++)                              //  find image with midpoint
 
4584
            {                                                              //    closest to mouse x
 
4585
               lox = cimOffs[imx].xf;
 
4586
               hix = lox + cimPXMw[imx]->ww;
 
4587
               midx = (lox + hix) / 2;
 
4588
               sepx = abs(midx - mx0);
 
4589
               if (sepx < minsep) {
 
4590
                  minsep = sepx;
 
4591
                  imm = imx;                                               //  image to drag or rotate
 
4592
               }
 
4593
            }
 
4594
            
 
4595
            xoff = cimOffs[imm].xf;
 
4596
            yoff = cimOffs[imm].yf;
 
4597
            ww = cimPXMw[imm]->ww;
 
4598
            hh = cimPXMw[imm]->hh;
 
4599
 
 
4600
            rotate = 0;                                                    //  if drag at bottom edge,
 
4601
            if (my0 > yoff + 0.85 * hh) rotate = 1;                        //    set rotate flag         v.11.04
 
4602
         }
 
4603
         
 
4604
         if (mx != mx0 || my != my0)                                       //  drag is progressing
 
4605
         {
 
4606
            dx = mx - mx0;                                                 //  mouse movement
 
4607
            dy = my - my0;
 
4608
            
 
4609
            if (rotate && my0 > yoff && my > yoff)                         //  rotation
 
4610
            {
 
4611
               if (imm > 0) {
 
4612
                  lox = cimOffs[imm].xf;                                   //  if there is an image to the left,
 
4613
                  hix = cimOffs[imm-1].xf + cimPXMw[imm-1]->ww;            //    midx = midpoint of overlap
 
4614
                  midx = (lox + hix) / 2;
 
4615
               }
 
4616
               else midx = 0;                                              //  this is the leftmost image
 
4617
 
 
4618
               t1 = atan(1.0 * (mx0-xoff) / (my0-yoff));
 
4619
               t2 = atan(1.0 * (mx-xoff) / (my-yoff));
 
4620
               dt = t1 - t2;                                               //  angle change
 
4621
               dx = dt * (hh/2 + yoff);                                    //  pivot = middle of overlap on left
 
4622
               dy = -dt * (midx-xoff);
 
4623
            }
 
4624
 
 
4625
            else  dt = 0;                                                  //  x/y drag
 
4626
 
 
4627
            cimOffs[imm].xf += dx;                                         //  update image
 
4628
            cimOffs[imm].yf += dy;
 
4629
            cimOffs[imm].tf += dt;
 
4630
            xoff = cimOffs[imm].xf;                                        //  v.11.04
 
4631
            yoff = cimOffs[imm].yf;
 
4632
 
 
4633
            cim_show_images(0,0);                                          //  show combined images
 
4634
 
 
4635
            mx0 = mx;                                                      //  next drag origin = current mouse
 
4636
            my0 = my;
 
4637
         }
 
4638
      }
 
4639
 
 
4640
      for (im1 = 0; im1 < cimNF-1; im1++)                                  //  track image order changes
 
4641
      {
 
4642
         im2 = im1 + 1;
 
4643
         if (cimOffs[im2].xf < cimOffs[im1].xf) 
 
4644
         {
 
4645
            ftemp = cimFile[im2];                                          //  switch filespecs
 
4646
            cimFile[im2] = cimFile[im1];
 
4647
            cimFile[im1] = ftemp;
 
4648
            pxmtemp = cimPXMf[im2];                                        //  switch images
 
4649
            cimPXMf[im2] = cimPXMf[im1];
 
4650
            cimPXMf[im1] = pxmtemp;
 
4651
            pxmtemp = cimPXMs[im2];                                        //  scaled images
 
4652
            cimPXMs[im2] = cimPXMs[im1];
 
4653
            cimPXMs[im1] = pxmtemp;
 
4654
            pxmtemp = cimPXMw[im2];                                        //  warped images
 
4655
            cimPXMw[im2] = cimPXMw[im1];
 
4656
            cimPXMw[im1] = pxmtemp;
 
4657
            offstemp = cimOffs[im2];                                       //  offsets
 
4658
            cimOffs[im2] = cimOffs[im1];
 
4659
            cimOffs[im1] = offstemp;
 
4660
            if (imm == im1) imm = im2;                                     //  current drag image
 
4661
            else if (imm == im2) imm = im1;
 
4662
            break;
 
4663
         }
 
4664
      }
 
4665
   }
 
4666
   
 
4667
   KBcapture = Mcapture = 0;
 
4668
   thread_exit();
 
4669
   return 0;                                                               //  not executed, stop g++ warning
 
4670
}
 
4671
 
 
4672
 
 
4673
//  optimize lens parameters
 
4674
//  inputs and outputs:
 
4675
//     pre-aligned images cimPXMw[0] and [1]
 
4676
//     offsets in cimOffs[0] and [1]
 
4677
//     lens_mm, lens_bow
 
4678
 
 
4679
void pano_autolens()                                                       //  v.10.7
 
4680
{
 
4681
   double      mm_range, bow_range, xf_range, yf_range, tf_range;
 
4682
   double      squeeze, xf_rfinal, rnum, matchB, matchlev;
 
4683
   double      overlap, lens_mmB, lens_bowB;
 
4684
   int         imx, randcount = 0;
 
4685
   cimoffs     offsetsB;
 
4686
 
 
4687
   overlap = cim_get_overlap(0,1,cimPXMs);                                 //  v.11.04
 
4688
   if (overlap < 0.1) {
 
4689
      threadmessage = ZTX("Too little overlap, cannot align");
 
4690
      return;
 
4691
   }
 
4692
 
 
4693
   Ffuncbusy++;                                                            //  v.11.01
 
4694
 
 
4695
   cimSampSize = 2000;                                                     //  v.11.03
 
4696
   cimNsearch = 0;
 
4697
 
 
4698
   mm_range = 0.1 * lens_mm;                                               //  set initial search ranges    v.11.03
 
4699
   bow_range = 0.3 * lens_bow;
 
4700
   if (bow_range < 0.5) bow_range = 0.5;
 
4701
   xf_range = 7;
 
4702
   yf_range = 7;
 
4703
   tf_range = 0.01;
 
4704
   xf_rfinal = 0.3;                                                        //  final xf range - when to quit
 
4705
 
 
4706
   cim_match_colors(0,1,cimPXMw);                                          //  adjust colors for image matching
 
4707
   cim_adjust_colors(cimPXMs[0],1);
 
4708
   cim_adjust_colors(cimPXMw[0],1);
 
4709
   cim_adjust_colors(cimPXMs[1],2);
 
4710
   cim_adjust_colors(cimPXMw[1],2);
 
4711
 
 
4712
   lens_mmB = lens_mm;                                                     //  starting point
 
4713
   lens_bowB = lens_bow;
 
4714
   offsetsB = cimOffs[1];
 
4715
   cimSearchRange = 7;
 
4716
 
 
4717
   matchB = 0;
 
4718
 
 
4719
   while (true)
 
4720
   {
 
4721
      srand48(time(0) + randcount++);
 
4722
      lens_mm = lens_mmB + mm_range * (drand48() - 0.5);                   //  new random lens factors
 
4723
      lens_bow = lens_bowB + bow_range * (drand48() - 0.5);                //     within search range
 
4724
      
 
4725
      for (imx = 0; imx <= 1; imx++) {                                     //  re-curve images
 
4726
         cim_scale_image(imx,cimPXMs);
 
4727
         cim_curve_image(imx);
 
4728
         PXM_free(cimPXMw[imx]);
 
4729
         cimPXMw[imx] = PXM_copy(cimPXMs[imx]);
 
4730
      }
 
4731
 
 
4732
      cim_get_redpix(0);                                                   //  get high-contrast pixels     v.11.03
 
4733
      cim_show_images(0,0);                                                //  combine and show images
 
4734
 
 
4735
      squeeze = 0.97;                                                      //  search range reduction       v.10.7
 
4736
         
 
4737
      for (int ii = 0; ii < 1000; ii++)                                    //  loop random x/y/t alignments
 
4738
      {                                                                    
 
4739
         rnum = drand48();
 
4740
         if (rnum < 0.33)                                                  //  random change some alignment offset 
 
4741
            cimOffs[1].xf = offsetsB.xf + xf_range * (drand48() - 0.5);
 
4742
         else if (rnum < 0.67)
 
4743
            cimOffs[1].yf = offsetsB.yf + yf_range * (drand48() - 0.5);
 
4744
         else
 
4745
            cimOffs[1].tf = offsetsB.tf + tf_range * (drand48() - 0.5);
 
4746
 
 
4747
         matchlev = cim_match_images(0,1);                                 //  test quality of image alignment
 
4748
 
 
4749
         sprintf(SB_text,"align: %d  match: %.5f  lens: %.1f %.2f",        //  update status bar
 
4750
                           ++cimNsearch, matchB, lens_mmB, lens_bowB);
 
4751
         zmainloop();                                                      //  v.11.11.1
 
4752
 
 
4753
         if (sigdiff(matchlev,matchB,0.00001) > 0) {
 
4754
            matchB = matchlev;                                             //  save new best fit
 
4755
            lens_mmB = lens_mm;                                            //  alignment is better
 
4756
            lens_bowB = lens_bow;
 
4757
            offsetsB = cimOffs[1];
 
4758
            cim_show_images(0,0);
 
4759
            squeeze = 1;                                                   //  keep same search range as long
 
4760
            break;                                                         //    as improvements are found
 
4761
         }
 
4762
 
 
4763
         if (panStat != -1) goto done;                                     //  user kill
 
4764
      }
 
4765
      
 
4766
      if (xf_range < xf_rfinal) goto done;                                 //  finished
 
4767
 
 
4768
      sprintf(SB_text,"align: %d  match: %.5f  lens: %.1f %.2f",           //  update status bar
 
4769
                        cimNsearch, matchB, lens_mmB, lens_bowB);
 
4770
      zmainloop();                                                         //  v.11.11.1
 
4771
 
 
4772
      mm_range = squeeze * mm_range;                                       //  reduce search range if no 
 
4773
      if (mm_range < 0.02 * lens_mmB) mm_range = 0.02 * lens_mmB;          //    improvements were found
 
4774
      bow_range = squeeze * bow_range;
 
4775
      if (bow_range < 0.1 * lens_bowB) bow_range = 0.1 * lens_bowB;
 
4776
      if (bow_range < 0.2) bow_range = 0.2;
 
4777
      xf_range = squeeze * xf_range;
 
4778
      yf_range = squeeze * yf_range;
 
4779
      tf_range = squeeze * tf_range;
 
4780
   }
 
4781
 
 
4782
done:
 
4783
   zfree(cimRedpix);
 
4784
   cimRedpix = 0;
 
4785
 
 
4786
   lens_mm = lens_mmB;                                                     //  save best lens params found
 
4787
   lens_bow = lens_bowB;
 
4788
   if (panStat == -1 && panozd) {                                          //  unless killed 
 
4789
      zdialog_stuff(panozd,"spmm",lens_mm);
 
4790
      zdialog_stuff(panozd,"spbow",lens_bow);
 
4791
   }
 
4792
 
 
4793
   cimSampSize = panSampSize;                                              //  restore
 
4794
   Ffuncbusy--;
 
4795
   cim_show_images(1,0);                                                   //  images are left color-matched
 
4796
   return;
 
4797
}
 
4798
 
 
4799
 
 
4800
//  fine-alignment
 
4801
//  start with very small image size
 
4802
//  search around offset values for best match
 
4803
//  increase image size and loop until full-size
 
4804
 
 
4805
void pano_align()                                                          //  v.10.7
 
4806
{
 
4807
   int         imx, im1, im2, ww;
 
4808
   double      R, dx, dy, dt;
 
4809
   double      overlap;
 
4810
   cimoffs     offsets0;
 
4811
   
 
4812
   Fzoom = 0;                                                              //  scale E3 to fit window
 
4813
   Fblowup = 1;                                                            //  magnify small image to window size
 
4814
   Ffuncbusy++;                                                            //  v.11.01
 
4815
 
 
4816
   for (imx = 0; imx < cimNF; imx++) {
 
4817
      cimOffs[imx].xf = cimOffs[imx].xf / cimScale;                        //  scale x/y offsets for full-size images
 
4818
      cimOffs[imx].yf = cimOffs[imx].yf / cimScale;
 
4819
   }
 
4820
 
 
4821
   cimScale = 1.0;                                                         //  full-size
 
4822
 
 
4823
   for (imx = 0; imx < cimNF; imx++) {
 
4824
      PXM_free(cimPXMs[imx]);
 
4825
      cimPXMs[imx] = PXM_copy(cimPXMf[imx]);                               //  copy full-size images
 
4826
      cim_curve_image(imx);                                                //  curve them
 
4827
   }
 
4828
 
 
4829
   cimBlend = 0.3 * cimPXMs[0]->ww;
 
4830
   cim_get_overlap(0,1,cimPXMs);                                           //  match images 0 & 1 in overlap area
 
4831
   cim_match_colors(0,1,cimPXMs);
 
4832
   cim_adjust_colors(cimPXMf[0],1);                                        //  image 0 << profile 1
 
4833
   cim_adjust_colors(cimPXMf[1],2);                                        //  image 1 << profile 2
 
4834
 
 
4835
   if (cimNF > 2) {
 
4836
      cimBlend = 0.3 * cimPXMs[1]->ww;
 
4837
      cim_get_overlap(1,2,cimPXMs);
 
4838
      cim_match_colors(1,2,cimPXMs);
 
4839
      cim_adjust_colors(cimPXMf[0],1);
 
4840
      cim_adjust_colors(cimPXMf[1],1);
 
4841
      cim_adjust_colors(cimPXMf[2],2);
 
4842
   }
 
4843
 
 
4844
   if (cimNF > 3) {
 
4845
      cimBlend = 0.3 * cimPXMs[2]->ww;
 
4846
      cim_get_overlap(2,3,cimPXMs);
 
4847
      cim_match_colors(2,3,cimPXMs);
 
4848
      cim_adjust_colors(cimPXMf[0],1);
 
4849
      cim_adjust_colors(cimPXMf[1],1);
 
4850
      cim_adjust_colors(cimPXMf[2],1);
 
4851
      cim_adjust_colors(cimPXMf[3],2);
 
4852
   }
 
4853
 
 
4854
   cimScale = panInitAlignSize / cimPXMf[1]->hh;                           //  initial align image scale
 
4855
   if (cimScale > 1.0) cimScale = 1.0;
 
4856
 
 
4857
   for (imx = 0; imx < cimNF; imx++) {                                     //  scale offsets for image scale
 
4858
      cimOffs[imx].xf = cimOffs[imx].xf * cimScale;
 
4859
      cimOffs[imx].yf = cimOffs[imx].yf * cimScale;
 
4860
   }
 
4861
   
 
4862
   cimSearchRange = panInitSearchRange;                                    //  initial align search range
 
4863
   cimSearchStep = panInitSearchStep;                                      //  initial align search step
 
4864
   cimWarpRange = panInitWarpRange;                                        //  initial align corner warp range
 
4865
   cimWarpStep = panInitWarpStep;                                          //  initial align corner warp step
 
4866
   ww = cimPXMf[0]->ww * cimScale;                                         //  initial align image width
 
4867
   cimBlend = ww * panInitBlend;                                           //  initial align blend width
 
4868
   cimSampSize = panSampSize;                                              //  pixel sample size for align/compare
 
4869
   cimNsearch = 0;                                                         //  reset align search counter
 
4870
   
 
4871
   while (true)                                                            //  loop, increasing image size
 
4872
   {
 
4873
      for (imx = 0; imx < cimNF; imx++) {                                  //  prepare images
 
4874
         cim_scale_image(imx,cimPXMs);                                     //  scale to new size
 
4875
         cim_curve_image(imx);                                             //  curve based on lens params
 
4876
         cim_warp_image_pano(imx,1);                                       //  apply corner warps
 
4877
      }
 
4878
 
 
4879
      cim_show_images(1,0);                                                //  show with 50/50 blend in overlaps
 
4880
 
 
4881
      for (im1 = 0; im1 < cimNF-1; im1++)                                  //  fine-align each image with left neighbor
 
4882
      {
 
4883
         im2 = im1 + 1;
 
4884
 
 
4885
         offsets0 = cimOffs[im2];                                          //  save initial alignment offsets
 
4886
         overlap = cim_get_overlap(im1,im2,cimPXMs);                       //  get overlap area          v.11.04
 
4887
         if (overlap < panFinalBlend-2) {
 
4888
            zmessageACK(mWin,ZTX("Too little overlap, cannot align"));     //  v.11.03
 
4889
            goto fail;
 
4890
         }
 
4891
         cim_get_redpix(im1);                                              //  get high-contrast pixels
 
4892
         
 
4893
         cim_align_image(im1,im2);                                         //  search for best offsets and warps
 
4894
 
 
4895
         zfree(cimRedpix);                                                 //  clear red pixels
 
4896
         cimRedpix = 0;
 
4897
 
 
4898
         dx = cimOffs[im2].xf - offsets0.xf;                               //  changes from initial offsets
 
4899
         dy = cimOffs[im2].yf - offsets0.yf;
 
4900
         dt = cimOffs[im2].tf - offsets0.tf;
 
4901
         
 
4902
         for (imx = im2+1; imx < cimNF; imx++)                             //  propagate to following images
 
4903
         {
 
4904
            cimOffs[imx].xf += dx;
 
4905
            cimOffs[imx].yf += dy;
 
4906
            cimOffs[imx].tf += dt;
 
4907
            ww = cimOffs[imx].xf - cimOffs[im2].xf;
 
4908
            cimOffs[imx].yf += ww * dt;
 
4909
         }
 
4910
      }
 
4911
 
 
4912
      if (cimScale == 1.0) goto success;                                   //  done
 
4913
 
 
4914
      R = panImageIncrease;                                                //  next larger image size
 
4915
      cimScale = cimScale * R;
 
4916
      if (cimScale > 0.85) {                                               //  if close to end, jump to end
 
4917
         R = R / cimScale;
 
4918
         cimScale = 1.0;
 
4919
      }
 
4920
 
 
4921
      for (imx = 0; imx < cimNF; imx++)                                    //  scale offsets for new size
 
4922
      {
 
4923
         cimOffs[imx].xf *= R;
 
4924
         cimOffs[imx].yf *= R;
 
4925
 
 
4926
         for (int ii = 0; ii < 4; ii++) {
 
4927
            cimOffs[imx].wx[ii] *= R;
 
4928
            cimOffs[imx].wy[ii] *= R;
 
4929
         }
 
4930
      }
 
4931
 
 
4932
      cimSearchRange = panSearchRange;                                     //  align search range
 
4933
      cimSearchStep = panSearchStep;                                       //  align search step size
 
4934
      cimWarpRange = panWarpRange;                                         //  align corner warp range
 
4935
      cimWarpStep = panWarpStep;                                           //  align corner warp step size
 
4936
 
 
4937
      cimBlend = cimBlend * panBlendDecrease * R;                          //  blend width, reduced
 
4938
      ww = cimPXMf[0]->ww * cimScale;
 
4939
      if (cimBlend < panFinalBlend * ww) 
 
4940
         cimBlend = panFinalBlend * ww;                                    //  stay above minimum
 
4941
   }
 
4942
 
 
4943
success: 
 
4944
   panStat = 1;
 
4945
   goto align_done;
 
4946
fail:
 
4947
   panStat = 0;
 
4948
align_done:
 
4949
   cimBlend = 1;                                                           //  tiny blend (increase in tweak if wanted)
 
4950
   Fzoom = Fblowup = 0;
 
4951
   Ffuncbusy--;
 
4952
   cim_show_images(0,0);
 
4953
   return;
 
4954
}
 
4955
 
 
4956
 
 
4957
//  get user inputs for RGB changes and blend width, update cimPXMw[*]
 
4958
 
 
4959
void pano_tweak()                                                          //  v.10.7
 
4960
{
 
4961
   int    pano_tweak_event(zdialog *zd, cchar *event);                     //  dialog event function
 
4962
   
 
4963
   cchar    *tweaktitle = ZTX("Match Brightness and Color");
 
4964
   char     imageN[8] = "imageN";
 
4965
   int      imx;
 
4966
   
 
4967
   cimBlend = 1;                                                           //  init. blend width
 
4968
   
 
4969
   panozd = zdialog_new(tweaktitle,mWin,Bdone,Bcancel,null);
 
4970
 
 
4971
   zdialog_add_widget(panozd,"hbox","hbim","dialog",0,"space=5");
 
4972
   zdialog_add_widget(panozd,"label","labim","hbim",ZTX("image"),"space=5");      //  image  (o)  (o)  (o)  (o)
 
4973
   zdialog_add_widget(panozd,"hbox","hbc1","dialog",0,"homog");                   //
 
4974
   zdialog_add_widget(panozd,"label","labred","hbc1",Bred);                       //     red     green    blue
 
4975
   zdialog_add_widget(panozd,"label","labgreen","hbc1",Bgreen);                   //   [_____]  [_____]  [_____]
 
4976
   zdialog_add_widget(panozd,"label","labblue","hbc1",Bblue);                     //
 
4977
   zdialog_add_widget(panozd,"hbox","hbc2","dialog",0,"homog");                   //   brightness [___]  [apply]
 
4978
   zdialog_add_widget(panozd,"spin","red","hbc2","50|200|0.1|100","space=5");     //
 
4979
   zdialog_add_widget(panozd,"spin","green","hbc2","50|200|0.1|100","space=5");   //   --------------------------
 
4980
   zdialog_add_widget(panozd,"spin","blue","hbc2","50|200|0.1|100","space=5");    //
 
4981
   zdialog_add_widget(panozd,"hbox","hbbri","dialog",0,"space=5");                //   [auto color]  [file color]
 
4982
   zdialog_add_widget(panozd,"label","labbr","hbbri",Bbrightness,"space=5");      //
 
4983
   zdialog_add_widget(panozd,"spin","bright","hbbri","50|200|0.1|100");           //   --------------------------
 
4984
   zdialog_add_widget(panozd,"button","brapp","hbbri",Bapply,"space=10");         //
 
4985
   zdialog_add_widget(panozd,"hsep","hsep","dialog",0,"space=5");                 //   blend width [___] [apply]   
 
4986
   zdialog_add_widget(panozd,"hbox","hbc3","dialog",0,"space=5");                 //
 
4987
   zdialog_add_widget(panozd,"button","auto","hbc3",ZTX("auto color"),"space=5"); //          [done]  [cancel]
 
4988
   zdialog_add_widget(panozd,"button","file","hbc3",ZTX("file color"),"space=5");
 
4989
   zdialog_add_widget(panozd,"hsep","hsep","dialog",0,"space=5");
 
4990
   zdialog_add_widget(panozd,"hbox","hbblen","dialog",0);
 
4991
   zdialog_add_widget(panozd,"label","labbl","hbblen",Bblendwidth,"space=5");
 
4992
   zdialog_add_widget(panozd,"spin","blend","hbblen","1|300|1|1");
 
4993
   zdialog_add_widget(panozd,"button","blapp","hbblen",Bapply,"space=15");
 
4994
   
 
4995
   for (imx = 0; imx < cimNF; imx++) {                                     //  add radio button per image
 
4996
      imageN[5] = '0' + imx;
 
4997
      zdialog_add_widget(panozd,"radio",imageN,"hbim",0,"space=5");
 
4998
   }
 
4999
   
 
5000
   zdialog_stuff(panozd,"image0",1);                                       //  pre-select 1st image
 
5001
   zdialog_resize(panozd,300,0);
 
5002
 
 
5003
   panStat = -1;                                                           //  busy status
 
5004
   zdialog_run(panozd,pano_tweak_event,"-10/20");                          //  run dialog, parallel            v.11.07
 
5005
   zdialog_wait(panozd);                                                   //  wait for dialog completion
 
5006
   return;
 
5007
}
 
5008
 
 
5009
 
 
5010
//  dialog event function
 
5011
 
 
5012
int pano_tweak_event(zdialog *zd, cchar *event)                            //  v.10.7
 
5013
{
 
5014
   char        imageN[8] = "imageN";
 
5015
   double      red, green, blue, bright, bright2;
 
5016
   double      red1, green1, blue1;
 
5017
   int         nn, im0, imx, im1, im2, ww, hh, px, py;
 
5018
   uint16      *pixel;
 
5019
   
 
5020
   if (zd->zstat)                                                          //  dialog complete
 
5021
   {
 
5022
      if (zd->zstat == 1) panStat = 1;                                     //  done
 
5023
      if (zd->zstat == 2) panStat = 0;                                     //  cancel
 
5024
      zdialog_free(panozd);                                                //  kill dialog
 
5025
      return 0;
 
5026
   }
 
5027
 
 
5028
   for (im0 = 0; im0 < cimNF; im0++) {                                     //  get which image is selected
 
5029
      imageN[5] = '0' + im0;                                               //    by the radio buttons
 
5030
      zdialog_fetch(zd,imageN,nn);
 
5031
      if (nn) break;
 
5032
   }
 
5033
   if (im0 == cimNF) return 1;
 
5034
 
 
5035
   zdialog_fetch(zd,"red",red);                                            //  get color adjustments
 
5036
   zdialog_fetch(zd,"green",green);
 
5037
   zdialog_fetch(zd,"blue",blue);
 
5038
   zdialog_fetch(zd,"bright",bright);                                      //  brightness adjustment
 
5039
 
 
5040
   bright2 = (red + green + blue) / 3;                                     //  RGB brightness
 
5041
   bright = bright / bright2;                                              //  bright setpoint / RGB brightness
 
5042
   red = red * bright;                                                     //  adjust RGB brightness
 
5043
   green = green * bright;
 
5044
   blue = blue * bright;
 
5045
   
 
5046
   bright = (red + green + blue) / 3;
 
5047
   zdialog_stuff(zd,"red",red);                                            //  force back into consistency
 
5048
   zdialog_stuff(zd,"green",green);
 
5049
   zdialog_stuff(zd,"blue",blue);
 
5050
   zdialog_stuff(zd,"bright",bright);
 
5051
   
 
5052
   if (strEqu(event,"brapp"))                                              //  apply color & brightness changes
 
5053
   {
 
5054
      red = red / 100;                                                     //  normalize 0.5 ... 2.0
 
5055
      green = green / 100;
 
5056
      blue = blue / 100;
 
5057
      
 
5058
      cim_warp_image_pano(im0,0);                                          //  refresh cimPXMw from cimPXMs
 
5059
 
 
5060
      ww = cimPXMw[im0]->ww;
 
5061
      hh = cimPXMw[im0]->hh;
 
5062
      
 
5063
      for (py = 0; py < hh; py++)                                          //  loop all image pixels
 
5064
      for (px = 0; px < ww; px++)
 
5065
      {
 
5066
         pixel = PXMpix(cimPXMw[im0],px,py);
 
5067
         red1 = red * pixel[0];                                            //  apply color factors
 
5068
         green1 = green * pixel[1];
 
5069
         blue1 = blue * pixel[2];
 
5070
         if (! blue1) continue;
 
5071
 
 
5072
         if (red1 > 65535 || green1 > 65535 || blue1 > 65535) {
 
5073
            bright = red1;                                                 //  avoid overflow
 
5074
            if (green1 > bright) bright = green1;
 
5075
            if (blue1 > bright) bright = blue1;
 
5076
            bright = 65535.0 / bright;
 
5077
            red1 = red1 * bright;
 
5078
            green1 = green1 * bright;
 
5079
            blue1 = blue1 * bright;
 
5080
         }
 
5081
         
 
5082
         if (blue1 < 1) blue1 = 1;                                         //  avoid 0       v.10.7
 
5083
 
 
5084
         pixel[0] = red1;
 
5085
         pixel[1] = green1;
 
5086
         pixel[2] = blue1;
 
5087
      }
 
5088
      
 
5089
      cimBlend = 1;
 
5090
      zdialog_stuff(zd,"blend",cimBlend);                                  //  v.11.04
 
5091
      cim_show_images(0,0);                                                //  combine and show with 50/50 blend
 
5092
   }
 
5093
   
 
5094
   if (strEqu(event,"auto"))                                               //  auto match color of selected image
 
5095
   {
 
5096
      for (im1 = im0; im1 < cimNF-1; im1++)                                //  from selected image to last image
 
5097
      {
 
5098
         im2 = im1 + 1;
 
5099
         cimBlend = 0.3 * cimPXMw[im2]->ww;
 
5100
         cim_get_overlap(im1,im2,cimPXMw);                                 //  match images in overlap area
 
5101
         cim_match_colors(im1,im2,cimPXMw);
 
5102
         cim_adjust_colors(cimPXMw[im1],1);                                //  image im1 << profile 1
 
5103
         cim_adjust_colors(cimPXMw[im2],2);                                //  image im2 << profile 2
 
5104
         for (imx = im1-1; imx >= im0; imx--)
 
5105
            cim_adjust_colors(cimPXMw[imx],1);
 
5106
         cimBlend = 1;
 
5107
         zdialog_stuff(zd,"blend",cimBlend);                               //  v.11.04
 
5108
         cim_show_images(0,0);
 
5109
      }
 
5110
 
 
5111
      for (im1 = im0-1; im1 >= 0; im1--)                                   //  from selected image to 1st image
 
5112
      {
 
5113
         im2 = im1 + 1;
 
5114
         cimBlend = 0.3 * cimPXMw[im2]->ww;
 
5115
         cim_get_overlap(im1,im2,cimPXMw);                                 //  match images in overlap area
 
5116
         cim_match_colors(im1,im2,cimPXMw);
 
5117
         cim_adjust_colors(cimPXMw[im1],1);                                //  image im1 << profile 1
 
5118
         cim_adjust_colors(cimPXMw[im2],2);                                //  image im2 << profile 2
 
5119
         for (imx = im2+1; imx < cimNF; imx++)
 
5120
            cim_adjust_colors(cimPXMw[imx],2);
 
5121
         cimBlend = 1;
 
5122
         zdialog_stuff(zd,"blend",cimBlend);                               //  v.11.04
 
5123
         cim_show_images(0,0);
 
5124
      }
 
5125
   }
 
5126
   
 
5127
   if (strEqu(event,"file"))                                               //  use original file colors
 
5128
   {
 
5129
      if (! cim_load_files()) return 1;
 
5130
 
 
5131
      for (imx = 0; imx < cimNF; imx++) {
 
5132
         PXM_free(cimPXMs[imx]);
 
5133
         cimPXMs[imx] = PXM_copy(cimPXMf[imx]);
 
5134
         cim_curve_image(imx);                                             //  curve and warp
 
5135
         cim_warp_image_pano(imx,0);
 
5136
      }
 
5137
 
 
5138
      cimBlend = 1;
 
5139
      zdialog_stuff(zd,"blend",cimBlend);                                  //  v.11.04
 
5140
      cim_show_images(0,0);
 
5141
   }
 
5142
   
 
5143
   if (strEqu(event,"blapp"))                                              //  apply new blend width
 
5144
   {
 
5145
      zdialog_fetch(zd,"blend",cimBlend);                                  //  can be zero
 
5146
      cim_show_images(0,1);                                                //  show with gradual blend
 
5147
   }
 
5148
 
 
5149
   return 1;
 
5150
}
 
5151
 
 
5152
 
 
5153
/**************************************************************************
 
5154
 
 
5155
    Vertical Panorama function: join 2, 3, or 4 images.
 
5156
 
 
5157
***************************************************************************/
 
5158
 
 
5159
void  vpano_prealign();                                                    //  manual pre-align
 
5160
void  vpano_align();                                                       //  auto fine-align
 
5161
void  vpano_tweak();                                                       //  user color tweak
 
5162
 
 
5163
editfunc    EFvpano;                                                       //  edit function data
 
5164
 
 
5165
 
 
5166
//  menu function
 
5167
 
 
5168
void m_vpano(GtkWidget *, cchar *)                                         //  v.11.04
 
5169
{
 
5170
   int      imx, err;
 
5171
   char     **flist = 0;
 
5172
 
 
5173
   zfuncs::F1_help_topic = "panorama";                                     //  help topic
 
5174
 
 
5175
   if (mod_keep()) return;                                                 //  warn unsaved changes
 
5176
   if (! menulock(1)) return;                                              //  test menu lock            v.11.07
 
5177
   menulock(0);
 
5178
 
 
5179
   for (imx = 0; imx < 10; imx++)
 
5180
   {                                                                       //  clear all file and PXM data
 
5181
      cimFile[imx] = 0;
 
5182
      cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0;
 
5183
   }
 
5184
   cimNF = 0;   
 
5185
 
 
5186
   flist = zgetfileN(ZTX("Select 2 to 4 files"),"openN",curr_file);        //  select images to combine
 
5187
   if (! flist) return;
 
5188
 
 
5189
   for (imx = 0; flist[imx]; imx++);                                       //  count selected files
 
5190
   if (imx < 2 || imx > 4) {
 
5191
      zmessageACK(mWin,ZTX("Select 2 to 4 files"));
 
5192
      goto cleanup;
 
5193
   }
 
5194
 
 
5195
   cimNF = imx;                                                            //  file count
 
5196
   for (imx = 0; imx < cimNF; imx++)
 
5197
      cimFile[imx] = strdupz(flist[imx],0,"pano");                         //  set up file list
 
5198
   
 
5199
   if (! cim_load_files()) goto cleanup;                                   //  load and check all files
 
5200
 
 
5201
   free_resources();                                                       //  ready to commit
 
5202
 
 
5203
   err = f_open(cimFile[0],0);                                             //  curr_file = 1st file in list
 
5204
   if (err) goto cleanup;
 
5205
 
 
5206
   EFvpano.funcname = "vpano";
 
5207
   if (! edit_setup(EFvpano)) goto cleanup;                                //  setup edit (will lock)
 
5208
 
 
5209
   cimShowAll = 1;                                                         //  for cim_show_images(), show all
 
5210
   cimShrink = 0;                                                          //  no warp shrinkage                  v.11.04
 
5211
   cimPano = 0;                                                            //  vertical pano mode                 v.11.04
 
5212
   cimPanoV = 1;
 
5213
 
 
5214
   vpano_prealign();                                                       //  manual pre-alignment
 
5215
   if (panStat != 1) goto cancel;
 
5216
 
 
5217
   vpano_align();                                                          //  auto full alignment
 
5218
   if (panStat != 1) goto cancel;
 
5219
 
 
5220
   vpano_tweak();                                                          //  manual color adjustment
 
5221
   if (panStat != 1) goto cancel;
 
5222
 
 
5223
   CEF->Fmod = 1;                                                          //  done
 
5224
   edit_done(EFvpano);
 
5225
   goto cleanup;
 
5226
 
 
5227
cancel:                                                                    //  failed or canceled
 
5228
   edit_cancel(EFvpano);
 
5229
 
 
5230
cleanup:
 
5231
 
 
5232
   if (flist) {
 
5233
      for (imx = 0; flist[imx]; imx++)                                     //  free file list
 
5234
         zfree(flist[imx]);
 
5235
      zfree(flist);
 
5236
   }
 
5237
 
 
5238
   for (imx = 0; imx < cimNF; imx++) {                                     //  free cim file and PXM data
 
5239
      if (cimFile[imx]) zfree(cimFile[imx]);
 
5240
      if (cimPXMf[imx]) PXM_free(cimPXMf[imx]);
 
5241
      if (cimPXMs[imx]) PXM_free(cimPXMs[imx]);
 
5242
      if (cimPXMw[imx]) PXM_free(cimPXMw[imx]);
 
5243
   }
 
5244
   
 
5245
   *SB_text = 0;
 
5246
   return;
 
5247
}
 
5248
 
 
5249
 
 
5250
//  perform manual pre-align of all images
 
5251
//  returns alignment data in cimOffs[*]
 
5252
//  lens_mm and lens_bow may also be altered
 
5253
 
 
5254
void vpano_prealign()
 
5255
{
 
5256
   int    vpano_prealign_event(zdialog *zd, cchar *event);                 //  dialog event function
 
5257
   void * vpano_prealign_thread(void *);                                   //  working thread
 
5258
 
 
5259
   int         imx, hh, err = 0;
 
5260
   cchar       *exifkey = { exif_focal_length_key };
 
5261
   char        lensname[40], **pp = 0;
 
5262
 
 
5263
   cchar  *align_mess = ZTX("Drag images into rough alignment.\n"
 
5264
                            "To rotate, drag from right edge.");
 
5265
 
 
5266
   pp = info_get(curr_file,&exifkey,1);                                    //  get lens mm from EXIF if available
 
5267
   if (pp && *pp) {
 
5268
      err = convSD(*pp, lens_mm, 20, 1000);                                //  leave lens_bow unchanged (no source)
 
5269
      strcpy(lensname,"(EXIF)");                                           //  lens name = EXIF
 
5270
   }
 
5271
 
 
5272
   if (! pp || ! *pp || err) {                                             //  not available
 
5273
      lens_mm = lens4_mm[curr_lens];                                       //  get curr. lens mm, bow, name
 
5274
      lens_bow = lens4_bow[curr_lens];
 
5275
      *lensname = 0;
 
5276
      strncatv(lensname,40,"(",lens4_name[curr_lens],")",null);
 
5277
   }
 
5278
   
 
5279
   for (imx = 0; imx < 10; imx++)                                          //  set all alignment offsets = 0
 
5280
      memset(&cimOffs[imx],0,sizeof(cimoffs));
 
5281
      
 
5282
   for (imx = hh = 0; imx < cimNF; imx++)                                  //  sum image heights
 
5283
      hh += cimPXMf[imx]->hh;
 
5284
   
 
5285
   cimScale = 1.4 * panPreAlignSize / hh;                                  //  set alignment image scale
 
5286
   if (cimScale > 1.0) cimScale = 1.0;                                     //  (* 0.7 after overlaps)
 
5287
 
 
5288
   for (imx = 0; imx < cimNF; imx++)                                       //  scale images > cimPXMs[*]
 
5289
      cim_scale_image(imx,cimPXMs);
 
5290
 
 
5291
   for (imx = 0; imx < cimNF; imx++) {                                     //  curve images, cimPXMs[*] replaced
 
5292
      cim_curve_Vimage(imx);
 
5293
      cimPXMw[imx] = PXM_copy(cimPXMs[imx]);                               //  copy to cimPXMw[*] for display
 
5294
   }
 
5295
   
 
5296
   cimOffs[0].xf = cimOffs[0].yf = 0;                                      //  first image at (0,0)
 
5297
 
 
5298
   for (imx = 1; imx < cimNF; imx++)                                       //  position images with 30% overlap
 
5299
   {                                                                       //    in vertical row
 
5300
      cimOffs[imx].yf = cimOffs[imx-1].yf + 0.7 * cimPXMw[imx-1]->hh;
 
5301
      cimOffs[imx].xf = cimOffs[imx-1].xf;
 
5302
   }
 
5303
   
 
5304
   Fzoom = 0;                                                              //  scale image to fit window
 
5305
   Fblowup = 1;                                                            //  magnify small image to window size
 
5306
 
 
5307
   cimBlend = panPreAlignBlend * cimPXMw[1]->hh;                           //  overlap in align window
 
5308
   cim_show_Vimages(1,0);                                                  //  combine and show images in main window
 
5309
 
 
5310
   panozd = zdialog_new(ZTX("Pre-align Images"),mWin,Bproceed,Bcancel,null);     //  start pre-align dialog
 
5311
   zdialog_add_widget(panozd,"label","lab1","dialog",align_mess,"space=5");
 
5312
   zdialog_add_widget(panozd,"hbox","hb1","dialog",0,"space=2");
 
5313
   zdialog_add_widget(panozd,"spin","spmm","hb1","22|200|0.1|35","space=5");     //  [ 35  ]    lens mm  (source)
 
5314
   zdialog_add_widget(panozd,"label","labmm","hb1",ZTX("lens mm"));              //  [ 0.3 ]    lens bow
 
5315
   zdialog_add_widget(panozd,"label","lablens","hb1","","space=5");              //  [resize]   resize window
 
5316
   zdialog_add_widget(panozd,"hbox","hb2","dialog",0,"space=2");
 
5317
   zdialog_add_widget(panozd,"spin","spbow","hb2","-9|9|0.01|0","space=5");
 
5318
   zdialog_add_widget(panozd,"label","labbow","hb2",ZTX("lens bow"));
 
5319
   zdialog_add_widget(panozd,"hbox","hb3","dialog",0,"space=2");
 
5320
   zdialog_add_widget(panozd,"button","resize","hb3",ZTX("Resize"),"space=5");
 
5321
   zdialog_add_widget(panozd,"label","labsiz","hb3",ZTX("resize window"),"space=5");
 
5322
 
 
5323
   zdialog_stuff(panozd,"spmm",lens_mm);                                   //  stuff lens data
 
5324
   zdialog_stuff(panozd,"spbow",lens_bow);
 
5325
   zdialog_stuff(panozd,"lablens",lensname);                               //  show source of lens data
 
5326
 
 
5327
   panStat = -1;                                                           //  busy status
 
5328
   gdk_window_set_cursor(drWin->window,dragcursor);                        //  set drag cursor 
 
5329
   zdialog_run(panozd,vpano_prealign_event,"-10/20");                      //  start dialog              v.11.07
 
5330
   start_thread(vpano_prealign_thread,0);                                  //  start working thread
 
5331
   zdialog_wait(panozd);                                                   //  wait for dialog completion
 
5332
   gdk_window_set_cursor(drWin->window,0);                                 //  restore normal cursor
 
5333
   Fzoom = Fblowup = 0;
 
5334
   return;
 
5335
}
 
5336
 
 
5337
 
 
5338
//  pre-align dialog event function
 
5339
 
 
5340
int vpano_prealign_event(zdialog *zd, cchar *event)
 
5341
{
 
5342
   int      imx;
 
5343
   double   overlap;
 
5344
 
 
5345
   if (strstr("spmm spbow",event)) {
 
5346
      zdialog_fetch(zd,"spmm",lens_mm);                                    //  get revised lens data
 
5347
      zdialog_fetch(zd,"spbow",lens_bow);
 
5348
   }
 
5349
   
 
5350
   if (strEqu(event,"resize"))                                             //  allocate new E3 image
 
5351
      cim_show_Vimages(1,0);
 
5352
 
 
5353
   if (zd->zstat)                                                          //  dialog complete
 
5354
   {
 
5355
      if (zd->zstat == 1)                                                  //  proceed
 
5356
         panStat = 1;
 
5357
      else                                                                 //  cancel or other
 
5358
         panStat = 0;
 
5359
 
 
5360
      zdialog_free(panozd);                                                //  kill dialog
 
5361
      wrapup_thread(0);                                                    //  wait for thread
 
5362
      
 
5363
      if (! panStat) return 0;                                             //  canceled
 
5364
 
 
5365
      for (imx = 0; imx < cimNF-1; imx++)                                  //  check for enough overlap
 
5366
      {
 
5367
         overlap = cim_get_overlap(imx,imx+1,cimPXMs);                     //  v.11.04
 
5368
         if (overlap < panFinalBlend) {                                    //  v.11.09
 
5369
            zmessageACK(mWin,ZTX("Too little overlap, cannot align"));
 
5370
            panStat = 0;
 
5371
            return 0;
 
5372
         }
 
5373
      }
 
5374
   }
 
5375
 
 
5376
   return 0;
 
5377
}
 
5378
 
 
5379
 
 
5380
//  pre-align working thread
 
5381
//  convert mouse and KB events into image movements                       //  overhauled
 
5382
 
 
5383
void * vpano_prealign_thread(void *)
 
5384
{
 
5385
   cimoffs     offstemp;
 
5386
   PXM         *pxmtemp;
 
5387
   char        *ftemp;
 
5388
   int         im1, im2, imm, imx;
 
5389
   int         mx0, my0, mx, my;                                           //  mouse drag origin, position
 
5390
   int         xoff, yoff, loy, hiy;
 
5391
   int         sepy, minsep;
 
5392
   int         ww, hh, rotate, midy;
 
5393
   double      lens_mm0, lens_bow0;
 
5394
   double      dx, dy, t1, t2, dt;
 
5395
      
 
5396
   imm = ww = hh = rotate = xoff = yoff = 0;                               //  stop compiler warnings
 
5397
 
 
5398
   lens_mm0 = lens_mm;                                                     //  to detect changes
 
5399
   lens_bow0 = lens_bow;
 
5400
 
 
5401
   mx0 = my0 = 0;                                                          //  no drag in progress
 
5402
   Mcapture = KBcapture = 1;                                               //  capture mouse drag and KB keys
 
5403
 
 
5404
   cimBlend = 0;                                                           //  full blend during pre-align
 
5405
 
 
5406
   while (true)                                                            //  loop and align until done
 
5407
   {
 
5408
      zsleep(0.05);                                                        //  logic simplified
 
5409
      
 
5410
      if (panStat != -1) break;                                            //  quit signal from dialog
 
5411
      
 
5412
      if (lens_mm != lens_mm0 || lens_bow != lens_bow0) {                  //  change in lens parameters
 
5413
         lens_mm0 = lens_mm;
 
5414
         lens_bow0 = lens_bow;
 
5415
 
 
5416
         for (imx = 0; imx < cimNF; imx++) {                               //  re-curve images
 
5417
            cim_scale_image(imx,cimPXMs);
 
5418
            cim_curve_Vimage(imx);
 
5419
            PXM_free(cimPXMw[imx]);
 
5420
            cimPXMw[imx] = PXM_copy(cimPXMs[imx]);
 
5421
         }
 
5422
 
 
5423
         cim_show_Vimages(1,0);                                            //  combine and show images
 
5424
         continue;
 
5425
      }
 
5426
      
 
5427
      if (KBkey) {                                                         //  KB input
 
5428
         if (KBkey == GDK_Left)  cimOffs[imm].xf -= 0.5;                   //  tweak alignment offsets
 
5429
         if (KBkey == GDK_Right) cimOffs[imm].xf += 0.5;
 
5430
         if (KBkey == GDK_Up)    cimOffs[imm].yf -= 0.5;
 
5431
         if (KBkey == GDK_Down)  cimOffs[imm].yf += 0.5;
 
5432
         if (KBkey == GDK_r)     cimOffs[imm].tf += 0.0005;
 
5433
         if (KBkey == GDK_l)     cimOffs[imm].tf -= 0.0005;
 
5434
         KBkey = 0;
 
5435
 
 
5436
         cim_show_Vimages(0,0);                                            //  combine and show images
 
5437
         continue;
 
5438
      }
 
5439
 
 
5440
      if (! Mxdrag && ! Mydrag)                                            //  no drag underway
 
5441
         mx0 = my0 = 0;                                                    //  reset drag origin
 
5442
 
 
5443
      if (Mxdrag || Mydrag)                                                //  mouse drag underway
 
5444
      {
 
5445
         mx = Mxdrag;                                                      //  mouse position in image
 
5446
         my = Mydrag;
 
5447
 
 
5448
         if (! mx0 && ! my0)                                               //  new drag
 
5449
         {
 
5450
            mx0 = mx;                                                      //  set drag origin
 
5451
            my0 = my;
 
5452
            minsep = 9999;
 
5453
            
 
5454
            for (imx = 0; imx < cimNF; imx++)                              //  find image with midpoint
 
5455
            {                                                              //    closest to mouse y
 
5456
               loy = cimOffs[imx].yf;
 
5457
               hiy = loy + cimPXMw[imx]->hh;
 
5458
               midy = (loy + hiy) / 2;
 
5459
               sepy = abs(midy - my0);
 
5460
               if (sepy < minsep) {
 
5461
                  minsep = sepy;
 
5462
                  imm = imx;                                               //  image to drag or rotate
 
5463
               }
 
5464
            }
 
5465
 
 
5466
            xoff = cimOffs[imm].xf;
 
5467
            yoff = cimOffs[imm].yf;
 
5468
            ww = cimPXMw[imm]->ww;
 
5469
            hh = cimPXMw[imm]->hh;
 
5470
 
 
5471
            rotate = 0;                                                    //  if drag at right edge,
 
5472
            if (mx0 > xoff + 0.85 * ww) rotate = 1;                        //    set rotate flag
 
5473
         }
 
5474
         
 
5475
         if (mx != mx0 || my != my0)                                       //  drag is progressing
 
5476
         {
 
5477
            dx = mx - mx0;                                                 //  mouse movement
 
5478
            dy = my - my0;
 
5479
            
 
5480
            if (rotate && my0 > yoff && my > yoff)                         //  rotation
 
5481
            {
 
5482
               if (imm > 0) {
 
5483
                  loy = cimOffs[imm].yf;                                   //  if there is an image above,
 
5484
                  hiy = cimOffs[imm-1].yf + cimPXMw[imm-1]->hh;            //    midy = midpoint of overlap
 
5485
                  midy = (loy + hiy) / 2;
 
5486
               }
 
5487
               else midy = 0;                                              //  this is the topmist image
 
5488
               
 
5489
               t1 = atan(1.0 * (my0-yoff) / (mx0-xoff));
 
5490
               t2 = atan(1.0 * (my-yoff) / (mx-xoff));
 
5491
               dt = t2 - t1;                                               //  angle change
 
5492
               dy = - dt * ww / 2;                                         //  pivot = middle of overlap above
 
5493
               dx = dt * (midy-yoff);
 
5494
            }
 
5495
 
 
5496
            else  dt = 0;                                                  //  x/y drag
 
5497
 
 
5498
            cimOffs[imm].xf += dx;                                         //  update image
 
5499
            cimOffs[imm].yf += dy;
 
5500
            cimOffs[imm].tf += dt;
 
5501
            xoff = cimOffs[imm].xf;                                        //  v.11.04
 
5502
            yoff = cimOffs[imm].yf;
 
5503
 
 
5504
            cim_show_Vimages(0,0);                                         //  show combined images
 
5505
 
 
5506
            mx0 = mx;                                                      //  next drag origin = current mouse
 
5507
            my0 = my;
 
5508
         }
 
5509
      }
 
5510
 
 
5511
      for (im1 = 0; im1 < cimNF-1; im1++)                                  //  track image order changes
 
5512
      {
 
5513
         im2 = im1 + 1;
 
5514
         if (cimOffs[im2].yf < cimOffs[im1].yf) 
 
5515
         {
 
5516
            ftemp = cimFile[im2];                                          //  switch filespecs
 
5517
            cimFile[im2] = cimFile[im1];
 
5518
            cimFile[im1] = ftemp;
 
5519
            pxmtemp = cimPXMf[im2];                                        //  switch images
 
5520
            cimPXMf[im2] = cimPXMf[im1];
 
5521
            cimPXMf[im1] = pxmtemp;
 
5522
            pxmtemp = cimPXMs[im2];                                        //  scaled images
 
5523
            cimPXMs[im2] = cimPXMs[im1];
 
5524
            cimPXMs[im1] = pxmtemp;
 
5525
            pxmtemp = cimPXMw[im2];                                        //  warped images
 
5526
            cimPXMw[im2] = cimPXMw[im1];
 
5527
            cimPXMw[im1] = pxmtemp;
 
5528
            offstemp = cimOffs[im2];                                       //  offsets
 
5529
            cimOffs[im2] = cimOffs[im1];
 
5530
            cimOffs[im1] = offstemp;
 
5531
            if (imm == im1) imm = im2;                                     //  current drag image
 
5532
            else if (imm == im2) imm = im1;
 
5533
            break;
 
5534
         }
 
5535
      }
 
5536
   }
 
5537
   
 
5538
   KBcapture = Mcapture = 0;
 
5539
   thread_exit();
 
5540
   return 0;                                                               //  not executed, stop g++ warning
 
5541
}
 
5542
 
 
5543
 
 
5544
//  fine-alignment
 
5545
//  start with very small image size
 
5546
//  search around offset values for best match
 
5547
//  increase image size and loop until full-size
 
5548
 
 
5549
void vpano_align()
 
5550
{
 
5551
   int         imx, im1, im2, ww, hh;
 
5552
   double      R, dx, dy, dt;
 
5553
   double      overlap;
 
5554
   cimoffs     offsets0;
 
5555
   
 
5556
   Fzoom = 0;                                                              //  scale E3 to fit window
 
5557
   Fblowup = 1;                                                            //  magnify small image to window size
 
5558
   Ffuncbusy++;
 
5559
 
 
5560
   for (imx = 0; imx < cimNF; imx++) {
 
5561
      cimOffs[imx].xf = cimOffs[imx].xf / cimScale;                        //  scale x/y offsets for full-size images
 
5562
      cimOffs[imx].yf = cimOffs[imx].yf / cimScale;
 
5563
   }
 
5564
 
 
5565
   cimScale = 1.0;                                                         //  full-size
 
5566
 
 
5567
   for (imx = 0; imx < cimNF; imx++) {
 
5568
      PXM_free(cimPXMs[imx]);
 
5569
      cimPXMs[imx] = PXM_copy(cimPXMf[imx]);                               //  copy full-size images
 
5570
      cim_curve_Vimage(imx);                                               //  curve them
 
5571
   }
 
5572
 
 
5573
   cimBlend = 0.3 * cimPXMs[0]->hh;
 
5574
   cim_get_overlap(0,1,cimPXMs);                                           //  match images 0 & 1 in overlap area
 
5575
   cim_match_colors(0,1,cimPXMs);
 
5576
   cim_adjust_colors(cimPXMf[0],1);                                        //  image 0 << profile 1
 
5577
   cim_adjust_colors(cimPXMf[1],2);                                        //  image 1 << profile 2
 
5578
 
 
5579
   if (cimNF > 2) {
 
5580
      cimBlend = 0.3 * cimPXMs[1]->hh;
 
5581
      cim_get_overlap(1,2,cimPXMs);
 
5582
      cim_match_colors(1,2,cimPXMs);
 
5583
      cim_adjust_colors(cimPXMf[0],1);
 
5584
      cim_adjust_colors(cimPXMf[1],1);
 
5585
      cim_adjust_colors(cimPXMf[2],2);
 
5586
   }
 
5587
 
 
5588
   if (cimNF > 3) {
 
5589
      cimBlend = 0.3 * cimPXMs[2]->hh;
 
5590
      cim_get_overlap(2,3,cimPXMs);
 
5591
      cim_match_colors(2,3,cimPXMs);
 
5592
      cim_adjust_colors(cimPXMf[0],1);
 
5593
      cim_adjust_colors(cimPXMf[1],1);
 
5594
      cim_adjust_colors(cimPXMf[2],1);
 
5595
      cim_adjust_colors(cimPXMf[3],2);
 
5596
   }
 
5597
 
 
5598
   cimScale = panInitAlignSize / cimPXMf[1]->hh;                           //  initial align image scale
 
5599
   if (cimScale > 1.0) cimScale = 1.0;
 
5600
 
 
5601
   for (imx = 0; imx < cimNF; imx++) {                                     //  scale offsets for image scale
 
5602
      cimOffs[imx].xf = cimOffs[imx].xf * cimScale;
 
5603
      cimOffs[imx].yf = cimOffs[imx].yf * cimScale;
 
5604
   }
 
5605
   
 
5606
   cimSearchRange = panInitSearchRange;                                    //  initial align search range
 
5607
   cimSearchStep = panInitSearchStep;                                      //  initial align search step
 
5608
   cimWarpRange = panInitWarpRange;                                        //  initial align corner warp range
 
5609
   cimWarpStep = panInitWarpStep;                                          //  initial align corner warp step
 
5610
   hh = cimPXMf[0]->hh * cimScale;                                         //  initial align image width
 
5611
   cimBlend = hh * panInitBlend;                                           //  initial align blend width
 
5612
   cimSampSize = panSampSize;                                              //  pixel sample size for align/compare
 
5613
   cimNsearch = 0;                                                         //  reset align search counter
 
5614
   
 
5615
   while (true)                                                            //  loop, increasing image size
 
5616
   {
 
5617
      for (imx = 0; imx < cimNF; imx++) {                                  //  prepare images
 
5618
         cim_scale_image(imx,cimPXMs);                                     //  scale to new size
 
5619
         cim_curve_Vimage(imx);                                            //  curve based on lens params
 
5620
         cim_warp_image_Vpano(imx,1);                                      //  apply corner warps
 
5621
      }
 
5622
      
 
5623
      cim_show_Vimages(1,0);                                               //  show with 50/50 blend in overlaps
 
5624
 
 
5625
      for (im1 = 0; im1 < cimNF-1; im1++)                                  //  fine-align each image with top neighbor
 
5626
      {
 
5627
         im2 = im1 + 1;
 
5628
 
 
5629
         offsets0 = cimOffs[im2];                                          //  save initial alignment offsets
 
5630
         overlap = cim_get_overlap(im1,im2,cimPXMs);                       //  get overlap area                v.11.04
 
5631
         if (overlap < panFinalBlend-2) {
 
5632
            zmessageACK(mWin,ZTX("Too little overlap, cannot align"));
 
5633
            goto fail;
 
5634
         }
 
5635
 
 
5636
         cim_get_redpix(im1);                                              //  get high-contrast pixels
 
5637
         
 
5638
         cim_align_image(im1,im2);                                         //  search for best offsets and warps
 
5639
         
 
5640
         zfree(cimRedpix);                                                 //  clear red pixels
 
5641
         cimRedpix = 0;
 
5642
 
 
5643
         dx = cimOffs[im2].xf - offsets0.xf;                               //  changes from initial offsets
 
5644
         dy = cimOffs[im2].yf - offsets0.yf;
 
5645
         dt = cimOffs[im2].tf - offsets0.tf;
 
5646
         
 
5647
         for (imx = im2+1; imx < cimNF; imx++)                             //  propagate to following images
 
5648
         {
 
5649
            cimOffs[imx].xf += dx;
 
5650
            cimOffs[imx].yf += dy;
 
5651
            cimOffs[imx].tf += dt;
 
5652
            ww = cimOffs[imx].xf - cimOffs[im2].xf;
 
5653
            cimOffs[imx].yf += ww * dt;
 
5654
         }
 
5655
      }
 
5656
      
 
5657
      if (cimScale == 1.0) goto success;                                   //  done
 
5658
 
 
5659
      R = panImageIncrease;                                                //  next larger image size
 
5660
      cimScale = cimScale * R;
 
5661
      if (cimScale > 0.85) {                                               //  if close to end, jump to end
 
5662
         R = R / cimScale;
 
5663
         cimScale = 1.0;
 
5664
      }
 
5665
 
 
5666
      for (imx = 0; imx < cimNF; imx++)                                    //  scale offsets for new size
 
5667
      {
 
5668
         cimOffs[imx].xf *= R;
 
5669
         cimOffs[imx].yf *= R;
 
5670
 
 
5671
         for (int ii = 0; ii < 4; ii++) {
 
5672
            cimOffs[imx].wx[ii] *= R;
 
5673
            cimOffs[imx].wy[ii] *= R;
 
5674
         }
 
5675
      }
 
5676
 
 
5677
      cimSearchRange = panSearchRange;                                     //  align search range
 
5678
      cimSearchStep = panSearchStep;                                       //  align search step size
 
5679
      cimWarpRange = panWarpRange;                                         //  align corner warp range
 
5680
      cimWarpStep = panWarpStep;                                           //  align corner warp step size
 
5681
 
 
5682
      cimBlend = cimBlend * panBlendDecrease * R;                          //  blend width, reduced
 
5683
      hh = cimPXMf[0]->hh * cimScale;
 
5684
      if (cimBlend < panFinalBlend * hh) 
 
5685
         cimBlend = panFinalBlend * hh;                                    //  stay above minimum
 
5686
   }
 
5687
 
 
5688
success: 
 
5689
   panStat = 1;
 
5690
   goto align_done;
 
5691
fail:
 
5692
   panStat = 0;
 
5693
align_done:
 
5694
   Fzoom = Fblowup = 0;
 
5695
   Ffuncbusy--;
 
5696
   cimBlend = 1;                                                           //  tiny blend (increase in tweak if wanted)
 
5697
   cim_show_Vimages(0,0);
 
5698
   return;
 
5699
}
 
5700
 
 
5701
 
 
5702
//  get user inputs for RGB changes and blend width, update cimPXMw[*]
 
5703
 
 
5704
void vpano_tweak()
 
5705
{
 
5706
   int    vpano_tweak_event(zdialog *zd, cchar *event);                    //  dialog event function
 
5707
   
 
5708
   cchar    *tweaktitle = ZTX("Match Brightness and Color");
 
5709
   char     imageN[8] = "imageN";
 
5710
   int      imx;
 
5711
   
 
5712
   cimBlend = 1;                                                           //  init. blend width
 
5713
   
 
5714
   panozd = zdialog_new(tweaktitle,mWin,Bdone,Bcancel,null);
 
5715
 
 
5716
   zdialog_add_widget(panozd,"hbox","hbim","dialog",0,"space=5");
 
5717
   zdialog_add_widget(panozd,"label","labim","hbim",ZTX("image"),"space=5");      //  image  (o)  (o)  (o)  (o)
 
5718
   zdialog_add_widget(panozd,"hbox","hbc1","dialog",0,"homog");                   //
 
5719
   zdialog_add_widget(panozd,"label","labred","hbc1",Bred);                       //     red     green    blue
 
5720
   zdialog_add_widget(panozd,"label","labgreen","hbc1",Bgreen);                   //   [_____]  [_____]  [_____]
 
5721
   zdialog_add_widget(panozd,"label","labblue","hbc1",Bblue);                     //
 
5722
   zdialog_add_widget(panozd,"hbox","hbc2","dialog",0,"homog");                   //   brightness [___]  [apply]
 
5723
   zdialog_add_widget(panozd,"spin","red","hbc2","50|200|0.1|100","space=5");     //
 
5724
   zdialog_add_widget(panozd,"spin","green","hbc2","50|200|0.1|100","space=5");   //   --------------------------
 
5725
   zdialog_add_widget(panozd,"spin","blue","hbc2","50|200|0.1|100","space=5");    //
 
5726
   zdialog_add_widget(panozd,"hbox","hbbri","dialog",0,"space=5");                //   [auto color]  [file color]
 
5727
   zdialog_add_widget(panozd,"label","labbr","hbbri",Bbrightness,"space=5");      //
 
5728
   zdialog_add_widget(panozd,"spin","bright","hbbri","50|200|0.1|100");           //   --------------------------
 
5729
   zdialog_add_widget(panozd,"button","brapp","hbbri",Bapply,"space=10");         //
 
5730
   zdialog_add_widget(panozd,"hsep","hsep","dialog",0,"space=5");                 //   blend width [___] [apply]   
 
5731
   zdialog_add_widget(panozd,"hbox","hbc3","dialog",0,"space=5");                 //
 
5732
   zdialog_add_widget(panozd,"button","auto","hbc3",ZTX("auto color"),"space=5"); //          [done]  [cancel]
 
5733
   zdialog_add_widget(panozd,"button","file","hbc3",ZTX("file color"),"space=5");
 
5734
   zdialog_add_widget(panozd,"hsep","hsep","dialog",0,"space=5");
 
5735
   zdialog_add_widget(panozd,"hbox","hbblen","dialog",0);
 
5736
   zdialog_add_widget(panozd,"label","labbl","hbblen",Bblendwidth,"space=5");
 
5737
   zdialog_add_widget(panozd,"spin","blend","hbblen","1|300|1|1");
 
5738
   zdialog_add_widget(panozd,"button","blapp","hbblen",Bapply,"space=15");
 
5739
   
 
5740
   for (imx = 0; imx < cimNF; imx++) {                                     //  add radio button per image
 
5741
      imageN[5] = '0' + imx;
 
5742
      zdialog_add_widget(panozd,"radio",imageN,"hbim",0,"space=5");
 
5743
   }
 
5744
   
 
5745
   zdialog_stuff(panozd,"image0",1);                                       //  pre-select 1st image
 
5746
   zdialog_resize(panozd,300,0);
 
5747
 
 
5748
   panStat = -1;                                                           //  busy status
 
5749
   zdialog_run(panozd,vpano_tweak_event,"-10/20");                         //  run dialog, parallel            v.11.07
 
5750
   zdialog_wait(panozd);                                                   //  wait for dialog completion
 
5751
   return;
 
5752
}
 
5753
 
 
5754
 
 
5755
//  dialog event function
 
5756
 
 
5757
int vpano_tweak_event(zdialog *zd, cchar *event)
 
5758
{
 
5759
   char        imageN[8] = "imageN";
 
5760
   double      red, green, blue, bright, bright2;
 
5761
   double      red1, green1, blue1;
 
5762
   int         nn, im0, imx, im1, im2, ww, hh, px, py;
 
5763
   uint16      *pixel;
 
5764
   
 
5765
   if (zd->zstat)                                                          //  dialog complete
 
5766
   {
 
5767
      if (zd->zstat == 1) panStat = 1;                                     //  done
 
5768
      if (zd->zstat == 2) panStat = 0;                                     //  cancel
 
5769
      zdialog_free(panozd);                                                //  kill dialog
 
5770
      return 0;
 
5771
   }
 
5772
 
 
5773
   for (im0 = 0; im0 < cimNF; im0++) {                                     //  get which image is selected
 
5774
      imageN[5] = '0' + im0;                                               //    by the radio buttons
 
5775
      zdialog_fetch(zd,imageN,nn);
 
5776
      if (nn) break;
 
5777
   }
 
5778
   if (im0 == cimNF) return 1;
 
5779
 
 
5780
   zdialog_fetch(zd,"red",red);                                            //  get color adjustments
 
5781
   zdialog_fetch(zd,"green",green);
 
5782
   zdialog_fetch(zd,"blue",blue);
 
5783
   zdialog_fetch(zd,"bright",bright);                                      //  brightness adjustment
 
5784
 
 
5785
   bright2 = (red + green + blue) / 3;                                     //  RGB brightness
 
5786
   bright = bright / bright2;                                              //  bright setpoint / RGB brightness
 
5787
   red = red * bright;                                                     //  adjust RGB brightness
 
5788
   green = green * bright;
 
5789
   blue = blue * bright;
 
5790
   
 
5791
   bright = (red + green + blue) / 3;
 
5792
   zdialog_stuff(zd,"red",red);                                            //  force back into consistency
 
5793
   zdialog_stuff(zd,"green",green);
 
5794
   zdialog_stuff(zd,"blue",blue);
 
5795
   zdialog_stuff(zd,"bright",bright);
 
5796
   
 
5797
   if (strEqu(event,"brapp"))                                              //  apply color & brightness changes
 
5798
   {
 
5799
      red = red / 100;                                                     //  normalize 0.5 ... 2.0
 
5800
      green = green / 100;
 
5801
      blue = blue / 100;
 
5802
      
 
5803
      cim_warp_image_Vpano(im0,0);                                         //  refresh cimPXMw from cimPXMs
 
5804
 
 
5805
      ww = cimPXMw[im0]->ww;
 
5806
      hh = cimPXMw[im0]->hh;
 
5807
      
 
5808
      for (py = 0; py < hh; py++)                                          //  loop all image pixels
 
5809
      for (px = 0; px < ww; px++)
 
5810
      {
 
5811
         pixel = PXMpix(cimPXMw[im0],px,py);
 
5812
         red1 = red * pixel[0];                                            //  apply color factors
 
5813
         green1 = green * pixel[1];
 
5814
         blue1 = blue * pixel[2];
 
5815
         if (! blue1) continue;
 
5816
 
 
5817
         if (red1 > 65535 || green1 > 65535 || blue1 > 65535) {
 
5818
            bright = red1;                                                 //  avoid overflow
 
5819
            if (green1 > bright) bright = green1;
 
5820
            if (blue1 > bright) bright = blue1;
 
5821
            bright = 65535.0 / bright;
 
5822
            red1 = red1 * bright;
 
5823
            green1 = green1 * bright;
 
5824
            blue1 = blue1 * bright;
 
5825
         }
 
5826
         
 
5827
         if (blue1 < 1) blue1 = 1;                                         //  avoid 0
 
5828
 
 
5829
         pixel[0] = red1;
 
5830
         pixel[1] = green1;
 
5831
         pixel[2] = blue1;
 
5832
      }
 
5833
      
 
5834
      cimBlend = 1;
 
5835
      zdialog_stuff(zd,"blend",cimBlend);
 
5836
      cim_show_Vimages(0,0);                                               //  combine and show with 50/50 blend
 
5837
   }
 
5838
 
 
5839
   if (strEqu(event,"auto"))                                               //  auto match color of selected image
 
5840
   {
 
5841
      for (im1 = im0; im1 < cimNF-1; im1++)                                //  from selected image to last image
 
5842
      {
 
5843
         im2 = im1 + 1;
 
5844
         cimBlend = 0.3 * cimPXMw[im2]->hh;
 
5845
         cim_get_overlap(im1,im2,cimPXMw);                                 //  match images in overlap area
 
5846
         cim_match_colors(im1,im2,cimPXMw);
 
5847
         cim_adjust_colors(cimPXMw[im1],1);                                //  image im1 << profile 1
 
5848
         cim_adjust_colors(cimPXMw[im2],2);                                //  image im2 << profile 2
 
5849
         for (imx = im1-1; imx >= im0; imx--)
 
5850
            cim_adjust_colors(cimPXMw[imx],1);
 
5851
         cimBlend = 1;
 
5852
         zdialog_stuff(zd,"blend",cimBlend);
 
5853
         cim_show_Vimages(0,0);
 
5854
      }
 
5855
 
 
5856
      for (im1 = im0-1; im1 >= 0; im1--)                                   //  from selected image to 1st image
 
5857
      {
 
5858
         im2 = im1 + 1;
 
5859
         cimBlend = 0.3 * cimPXMw[im2]->hh;
 
5860
         cim_get_overlap(im1,im2,cimPXMw);                                 //  match images in overlap area
 
5861
         cim_match_colors(im1,im2,cimPXMw);
 
5862
         cim_adjust_colors(cimPXMw[im1],1);                                //  image im1 << profile 1
 
5863
         cim_adjust_colors(cimPXMw[im2],2);                                //  image im2 << profile 2
 
5864
         for (imx = im2+1; imx < cimNF; imx++)
 
5865
            cim_adjust_colors(cimPXMw[imx],2);
 
5866
         cimBlend = 1;
 
5867
         zdialog_stuff(zd,"blend",cimBlend);
 
5868
         cim_show_Vimages(0,0);
 
5869
      }
 
5870
   }
 
5871
   
 
5872
   if (strEqu(event,"file"))                                               //  use original file colors
 
5873
   {
 
5874
      if (! cim_load_files()) return 1;
 
5875
 
 
5876
      for (imx = 0; imx < cimNF; imx++) {
 
5877
         PXM_free(cimPXMs[imx]);
 
5878
         cimPXMs[imx] = PXM_copy(cimPXMf[imx]);
 
5879
         cim_curve_Vimage(imx);                                            //  curve and warp
 
5880
         cim_warp_image_Vpano(imx,0);
 
5881
      }
 
5882
 
 
5883
      cimBlend = 1;
 
5884
      zdialog_stuff(zd,"blend",cimBlend);
 
5885
      cim_show_Vimages(0,0);
 
5886
   }
 
5887
   
 
5888
   if (strEqu(event,"blapp"))                                              //  apply new blend width
 
5889
   {
 
5890
      zdialog_fetch(zd,"blend",cimBlend);                                  //  can be zero
 
5891
      cim_show_Vimages(0,1);                                               //  show with gradual blend
 
5892
   }
 
5893
 
 
5894
   return 1;
 
5895
}
 
5896
 
 
5897