1 // This is core/vil1/examples/vil1_scale.cxx
2 // Example: scaling.
3 
4 #include <iostream>
5 #include <cmath>
6 #include <cstdlib>
7 #ifdef _MSC_VER
8 #  include "vcl_msvc_warnings.h"
9 #endif
10 
11 #include "vil1/vil1_new.h"
12 #include "vil1/vil1_load.h"
13 #include "vil1/vil1_rgb.h"
14 
15 void
16 vil1_scale(const vil1_image & in, int newxsize, int newysize, const vil1_image & out);
17 
18 int
main(int argc,char ** argv)19 main(int argc, char ** argv)
20 {
21   if (argc != 6)
22   {
23     std::cerr << "usage: vil1_scale w h in out format\n";
24     return -1;
25   }
26   int w = std::stoi(argv[1]);
27   int h = std::stoi(argv[2]);
28   char const * input_filename = argv[3];
29   char const * output_filename = argv[4];
30   char const * output_format = argv[5];
31 
32   vil1_image in = vil1_load(input_filename);
33   if (!in)
34     return -1;
35 
36   vil1_image out = vil1_new(output_filename, w, h, in, output_format);
37 
38   vil1_scale(in, w, h, out);
39   return 0;
40 }
41 
42 
43 constexpr int SCALE = 4096;
44 constexpr int HALFSCALE = 2048;
45 
46 struct pnmscale
47 {
48   int rows, cols, format, newformat, newrows, newcols, newpixels;
49   int argn, specxscale, specyscale, specxsize, specysize, specxysize;
50   float xscale, yscale;
51   long sxscale, syscale;
52 
pnmscalepnmscale53   pnmscale() { specxscale = specyscale = specxsize = specysize = specxysize = newpixels = 0; }
54 
55   void
pm_errorpnmscale56   pm_error(char const * msg)
57   {
58     std::cerr << "vil1_scale: ERROR: " << msg << std::endl;
59     std::abort();
60   }
61 
62   void
set_xscalepnmscale63   set_xscale(double xscale)
64   {
65     if (specxscale)
66       pm_error("already specified an x scale");
67     if (specxsize)
68       pm_error("only one of -xsize/-width and -xscale may be specified");
69     if (xscale <= 0.0)
70       pm_error("x scale must be greater than 0");
71     specxscale = 1;
72     this->xscale = float(xscale);
73   }
74 
75   void
set_yscalepnmscale76   set_yscale(double yscale)
77   {
78     if (specyscale)
79       pm_error("already specified a y scale");
80     if (specysize)
81       pm_error("only one of -ysize/-height and -yscale may be specified");
82     if (yscale <= 0.0)
83       pm_error("y scale must be greater than 0");
84     specyscale = 1;
85     this->yscale = float(yscale);
86   }
87 
88   void
set_widthpnmscale89   set_width(int newcols)
90   {
91     set_xsize(newcols);
92   }
93   void
set_xsizepnmscale94   set_xsize(int newcols)
95   {
96     if (specxsize)
97       pm_error("already specified a width");
98     if (specxscale)
99       pm_error("only one of -xscale and -xsize/-width may be specified");
100     if (newcols <= 0)
101       pm_error("new width must be greater than 0");
102     specxsize = 1;
103     this->newcols = newcols;
104   }
105 
106   void
set_heightpnmscale107   set_height(int newrows)
108   {
109     set_ysize(newrows);
110   }
111   void
set_ysizepnmscale112   set_ysize(int newrows)
113   {
114     if (specysize)
115       pm_error("already specified a height");
116     if (specyscale)
117       pm_error("only one of -yscale and -ysize/-height may be specified");
118 
119     if (newrows <= 0)
120       pm_error("new height must be greater than 0");
121     specysize = 1;
122     this->newrows = newrows;
123   }
124 
125   void
set_xysizepnmscale126   set_xysize(int newcols, int newrows)
127   {
128     if (specxsize || specysize || specxscale || specyscale || newpixels)
129       pm_error("can't use -xysize with any other specifiers");
130 
131     if (newcols <= 0 || newrows <= 0)
132       pm_error("new width and height must be greater than 0");
133     specxsize = 1;
134     specysize = 1;
135     specxysize = 1;
136     this->newcols = newcols;
137     this->newrows = newrows;
138   }
139 
140   void
set_pixelspnmscale141   set_pixels(int newpixels)
142   {
143     if (specxsize || specysize || specxscale || specyscale)
144       pm_error("can't use -pixels with any other specifiers");
145     if (newpixels <= 0)
146       pm_error("number of pixels must be greater than 0");
147     this->newpixels = newpixels;
148   }
149 
150   void
set_scalepnmscale151   set_scale(double scale)
152   {
153     yscale = xscale = float(scale);
154     specxscale = specyscale = 1;
155     if (scale <= 0.0)
156       pm_error("scale must be greater than 0");
157   }
158 
159   int current_inrow;
160   vil1_image in;
161   int current_outrow;
162   vil1_image out;
163 
164   void
initpnmscale165   init(const vil1_image & in, const vil1_image & out)
166   {
167     this->in = in;
168     this->out = out;
169     current_inrow = 0;
170     current_outrow = 0;
171 
172     cols = in.width();
173     rows = in.height();
174 
175     /* Compute all sizes and scales. */
176     if (newpixels)
177     {
178       if (rows * cols <= newpixels)
179       {
180         newrows = rows;
181         newcols = cols;
182         xscale = yscale = 1.0;
183       }
184       else
185       {
186         xscale = yscale = std::sqrt((float)newpixels / (float)cols / (float)rows);
187         specxscale = specyscale = 1;
188       }
189     }
190 
191     if (specxysize)
192     {
193       if ((float)newcols / (float)newrows > (float)cols / (float)rows)
194         specxsize = 0;
195       else
196         specysize = 0;
197     }
198 
199     if (specxsize)
200       xscale = (float)newcols / (float)cols;
201     else if (specxscale)
202       newcols = int(cols * xscale + 0.999);
203 
204     if (specysize)
205       yscale = (float)newrows / (float)rows;
206     else if (specyscale)
207       newrows = int(rows * yscale + 0.999);
208     else
209     {
210       if (specxsize)
211       {
212         yscale = xscale;
213         newrows = int(rows * yscale + 0.999);
214       }
215       else
216       {
217         yscale = 1.0;
218         newrows = rows;
219       }
220     }
221 
222     if (!(specxsize || specxscale))
223     {
224       if (specysize)
225       {
226         xscale = yscale;
227         newcols = int(cols * xscale + 0.999);
228       }
229       else
230       {
231         xscale = 1.0;
232         newcols = cols;
233       }
234     }
235 
236     sxscale = int(xscale * SCALE);
237     syscale = int(yscale * SCALE);
238   }
239 };
240 
241 template <class T, class longT>
242 struct pnmscaleT : public pnmscale
243 {
244   void
245   go();
246   void
pnm_readpnmrowpnmscaleT247   pnm_readpnmrow(T * buf, int cols)
248   {
249     in.get_section(buf, 0, current_inrow++, cols, 1);
250   }
251   void
pnm_writepnmrowpnmscaleT252   pnm_writepnmrow(T * tempxelrow, int newcols)
253   {
254     out.put_section(tempxelrow, 0, current_outrow++, newcols, 1);
255   }
256 };
257 
258 template <class T, class longT>
259 void
go()260 pnmscaleT<T, longT>::go()
261 {
262   T * xelrow;
263   T * tempxelrow;
264   T * newxelrow;
265   T * xP;
266   T * nxP;
267   int row, col, needtoreadrow;
268   long fracrowtofill, fracrowleft;
269 
270   xelrow = new T[cols];
271   if (newrows == rows) /* shortcut Y scaling if possible */
272     tempxelrow = xelrow;
273   else
274     tempxelrow = new T[cols];
275 
276   auto * gs = new longT[cols];
277   int rowsread = 0;
278   fracrowleft = syscale;
279   needtoreadrow = 1;
280   for (col = 0; col < cols; ++col)
281     gs[col] = longT(HALFSCALE);
282   fracrowtofill = SCALE;
283 
284   newxelrow = new T[newcols];
285 
286   for (row = 0; row < newrows; ++row)
287   {
288     // First scale Y from xelrow into tempxelrow.
289     if (newrows == rows) // shortcut Y scaling if possible
290     {
291       pnm_readpnmrow(xelrow, cols);
292     }
293     else
294     {
295       while (fracrowleft < fracrowtofill)
296       {
297         if (needtoreadrow)
298           if (rowsread < rows)
299           {
300             pnm_readpnmrow(xelrow, cols);
301             ++rowsread;
302             // needtoreadrow = 0;
303           }
304         for (col = 0, xP = xelrow; col < cols; ++col, ++xP)
305           gs[col] += longT(fracrowleft * (*xP));
306         fracrowtofill -= fracrowleft;
307         fracrowleft = syscale;
308         needtoreadrow = 1;
309       }
310       // Now fracrowleft is >= fracrowtofill, so we can produce a row.
311       if (needtoreadrow)
312         if (rowsread < rows)
313         {
314           pnm_readpnmrow(xelrow, cols);
315           ++rowsread;
316           needtoreadrow = 0;
317         }
318       for (col = 0, xP = xelrow, nxP = tempxelrow; col < cols; ++col, ++xP, ++nxP)
319       {
320         longT g = gs[col] + fracrowtofill * *xP;
321         g /= SCALE;
322 
323         *nxP = (T)g;
324         gs[col] = longT(HALFSCALE);
325       }
326       fracrowleft -= fracrowtofill;
327       if (fracrowleft == 0)
328       {
329         fracrowleft = syscale;
330         needtoreadrow = 1;
331       }
332       fracrowtofill = SCALE;
333     }
334 
335     /* Now scale X from tempxelrow into newxelrow and write it out. */
336     if (newcols == cols) /* shortcut X scaling if possible */
337       pnm_writepnmrow(tempxelrow, newcols);
338     else
339     {
340       longT g;
341       long fraccoltofill, fraccolleft;
342       int needcol;
343 
344       nxP = newxelrow;
345       fraccoltofill = SCALE;
346       g = longT(HALFSCALE);
347       needcol = 0;
348       for (col = 0, xP = tempxelrow; col < cols; ++col, ++xP)
349       {
350         fraccolleft = sxscale;
351         while (fraccolleft >= fraccoltofill)
352         {
353           if (needcol)
354           {
355             ++nxP;
356             g = longT(HALFSCALE);
357           }
358           g += fraccoltofill * *xP;
359           g /= SCALE;
360           *nxP = (T)g;
361 
362           fraccolleft -= fraccoltofill;
363           fraccoltofill = SCALE;
364           needcol = 1;
365         }
366         if (fraccolleft > 0)
367         {
368           if (needcol)
369           {
370             ++nxP;
371             g = longT(HALFSCALE);
372             needcol = 0;
373           }
374           g += fraccolleft * *xP;
375           fraccoltofill -= fraccolleft;
376         }
377       }
378       if (fraccoltofill > 0)
379       {
380         --xP;
381         g += fraccoltofill * *xP;
382       }
383       if (!needcol)
384       {
385         g /= SCALE;
386         *nxP = (T)g;
387       }
388       pnm_writepnmrow(newxelrow, newcols);
389     }
390   }
391 }
392 
393 template struct pnmscaleT<unsigned char, long>;
394 
395 template struct pnmscaleT<vil1_rgb<unsigned char>, vil1_rgb<long>>;
396 
397 void
vil1_scale(const vil1_image & in,int newxsize,int newysize,const vil1_image & out)398 vil1_scale(const vil1_image & in, int newxsize, int newysize, const vil1_image & out)
399 {
400   pnmscaleT<vil1_rgb<unsigned char>, vil1_rgb<long>> p;
401   p.set_xsize(newxsize);
402   p.set_ysize(newysize);
403   p.init(in, out);
404   p.go();
405 }
406