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