Mon, 03 Aug 2009 14:09:20 +0100
added P-touch decoder source
1 /*
2 #
3 # File : gmic.cpp
4 # ( C++ source file )
5 #
6 # Description : GREYC's Magic Image Converter (library and executable)
7 # ( http://gmic.sourceforge.net )
8 # This file is a part of the CImg Library project.
9 # ( http://cimg.sourceforge.net )
10 #
11 # Copyright : David Tschumperle
12 # ( http://www.greyc.ensicaen.fr/~dtschump/ )
13 #
14 # License : CeCILL v2.0
15 # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
16 #
17 # This software is governed by the CeCILL license under French law and
18 # abiding by the rules of distribution of free software. You can use,
19 # modify and/ or redistribute the software under the terms of the CeCILL
20 # license as circulated by CEA, CNRS and INRIA at the following URL
21 # "http://www.cecill.info".
22 #
23 # As a counterpart to the access to the source code and rights to copy,
24 # modify and redistribute granted by the license, users are provided only
25 # with a limited warranty and the software's author, the holder of the
26 # economic rights, and the successive licensors have only limited
27 # liability.
28 #
29 # In this respect, the user's attention is drawn to the risks associated
30 # with loading, using, modifying and/or developing or reproducing the
31 # software by the user in light of its specific status of free software,
32 # that may mean that it is complicated to manipulate, and that also
33 # therefore means that it is reserved for developers and experienced
34 # professionals having in-depth computer knowledge. Users are therefore
35 # encouraged to load and test the software's suitability as regards their
36 # requirements in conditions enabling the security of their systems and/or
37 # data to be ensured and, more generally, to use and operate it in the
38 # same conditions as regards security.
39 #
40 # The fact that you are presently reading this means that you have had
41 # knowledge of the CeCILL license and that you accept its terms.
42 #
43 */
45 // Add specific G'MIC methods to the CImg<T> class.
46 //-------------------------------------------------
47 #ifdef cimg_plugin
49 template<typename t>
50 CImg<T> get_replace(const CImg<t>& img) const {
51 return +img;
52 }
54 template<typename t>
55 CImg<T>& replace(CImg<t>& img) {
56 return img.transfer_to(*this);
57 }
59 CImg<T>& gmic_set(const double value, const int x, const int y, const int z, const int v) {
60 (*this).atXYZV(x,y,z,v,0) = (T)value;
61 return *this;
62 }
64 CImg<T> get_gmic_set(const double value, const int x, const int y, const int z, const int v) const {
65 return (+*this).gmic_set(value,x,y,z,v);
66 }
68 CImg<T> get_draw_point(const int x, const int y, const int z,
69 const CImg<T>& col, const float opacity) const {
70 return (+*this).draw_point(x,y,z,col,opacity);
71 }
73 CImg<T> get_draw_line(const int x0, const int y0, const int x1, const int y1,
74 const CImg<T>& col, const float opacity) const {
75 return (+*this).draw_line(x0,y0,x1,y1,col,opacity);
76 }
78 template<typename t>
79 CImg<T> get_draw_polygon(const CImg<t>& pts, const CImg<T>& col, const float opacity) const {
80 return (+*this).draw_polygon(pts,col,opacity);
81 }
83 CImg<T> get_draw_ellipse(const int x, const int y, const float r0, const float r1,
84 const float ru, const float rv, const CImg<T>& col, const float opacity) const {
85 return (+*this).draw_ellipse(x,y,r0,r1,ru,rv,col,opacity);
86 }
88 CImg<T> get_draw_text(const int x, const int y, const char *const text, const T *const col,
89 const int bg, const float opacity,const int siz) const {
90 return (+*this).draw_text(x,y,text,col,bg,opacity,siz);
91 }
93 CImg<T> get_draw_image(const int x, const int y, const int z,
94 const CImg<T>& sprite, const CImg<T>& mask, const float opacity) const {
95 return (+*this).draw_image(x,y,z,sprite,mask,opacity);
96 }
98 CImg<T> get_draw_image(const int x, const int y, const int z,
99 const CImg<T>& sprite, const float opacity) const {
100 return (+*this).draw_image(x,y,z,sprite,opacity);
101 }
103 CImg<T> get_draw_plasma(const float alpha, const float beta, const float opacity) const {
104 return (+*this).draw_plasma(alpha,beta,opacity);
105 }
107 CImg<T> get_draw_mandelbrot(const CImg<T>& color_palette, const float opacity,
108 const double z0r, const double z0i, const double z1r, const double z1i,
109 const unsigned int itermax, const bool normalized_iteration,
110 const bool julia_set, const double paramr, const double parami) const {
111 return (+*this).draw_mandelbrot(color_palette,opacity,z0r,z0i,z1r,z1i,itermax,
112 normalized_iteration,julia_set,paramr,parami);
113 }
115 CImg<T> get_draw_fill(const int x, const int y, const int z,
116 const CImg<T>& col, const float opacity, const float tolerance) const {
117 return (+*this).draw_fill(x,y,z,col,opacity,tolerance);
118 }
120 bool is_CImg3d() const {
121 const bool is_header = (width==1 && height>=8 && depth==1 && dim==1 &&
122 (*this)[0]=='C'+0.5f && (*this)[1]=='I'+0.5f &&
123 (*this)[2]=='m'+0.5f && (*this)[3]=='g'+0.5f &&
124 (*this)[4]=='3'+0.5f && (*this)[5]=='d'+0.5f);
125 if (!is_header) return false;
126 const int
127 nbv = (int)(*this)[6],
128 nbp = (int)(*this)[7];
129 if (nbv<=0 || nbp<=0) return false;
130 const T *ptrs = ptr() + 8 + 3*nbv, *const ptre = end();
131 if (ptrs>=ptre) return false;
132 for (int i = 0; i<nbp && ptrs<ptre; ++i) {
133 const int N = (int)*(ptrs++);
134 if (N<=0 || N>=8) return false;
135 ptrs+=N;
136 }
137 ptrs+=4*nbp;
138 if (ptrs>ptre) return false;
139 return true;
140 }
142 template<typename tp, typename tf, typename tc, typename to>
143 CImg<T> get_draw_object3d(const float x0, const float y0, const float z0,
144 const CImg<tp>& points, const CImgList<tf>& primitives,
145 const CImgList<tc>& colors, const CImg<to>& opacities,
146 const unsigned int render_type, const bool double_sided,
147 const float focale, const float lightx, const float lighty,
148 const float lightz, const float specular_light, const float specular_shine,
149 float *const zbuffer) const {
150 return (+*this).draw_object3d(x0,y0,z0,points,primitives,colors,opacities,render_type,double_sided,focale,
151 lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
152 }
154 template<typename tp, typename tc, typename to>
155 CImg<T>& object3dtoCImg3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImg<to>& opacities) {
156 if (is_empty() || !primitives) { primitives.assign(); colors.assign(); opacities.assign(); return *this; }
157 const unsigned int primitives_size = primitives.size;
158 CImgList<floatT> res;
159 res.insert(CImg<floatT>("CImg3d",1,6,1,1,false)+=0.5f);
160 res.insert(CImg<floatT>::vector((float)width,(float)primitives.size));
161 res.insert(1); resize(-100,3,1,1,0).transpose().unroll('y').transfer_to(res.last());
162 cimglist_for(primitives,p) {
163 res.insert(CImg<floatT>::vector((float)primitives[p].size())).insert(primitives[p]).last().unroll('y');
164 primitives[p].assign();
165 }
166 primitives.assign();
167 const unsigned int defined_colors = colors.size;
168 cimglist_for(colors,c) { res.insert(colors[c]).last().resize(1,3,1,1,-1); colors[c].assign(); }
169 colors.assign();
170 if (defined_colors<primitives_size) res.insert(1).last().assign(1,3*(primitives_size-defined_colors),1,1,200);
171 const unsigned int defined_opacities = opacities.size();
172 res.insert(opacities).last().unroll('y');
173 opacities.assign();
174 if (defined_opacities<primitives.size) res.insert(1).last().assign(1,primitives_size-defined_opacities,1,1,1);
175 return res.get_append('y').transfer_to(*this);
176 }
178 template<typename tp, typename tc, typename to>
179 CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImg<to>& opacities) {
180 const T *ptrs = ptr() + 6;
181 const unsigned int
182 nbv = (unsigned int)*(ptrs++),
183 nbp = (unsigned int)*(ptrs++);
184 CImg<T> points(nbv,3);
185 primitives.assign(nbp);
186 colors.assign(nbp,1,3,1,1);
187 opacities.assign(nbp);
188 cimg_forX(points,x) { points(x,0) = (T)*(ptrs++); points(x,1) = (T)*(ptrs++); points(x,2) = (T)*(ptrs++); }
189 cimglist_for(primitives,p) {
190 const unsigned int N = (unsigned int)*(ptrs++);
191 primitives[p].assign(ptrs,1,N,1,1,false);
192 ptrs+=N;
193 }
194 cimglist_for(colors,c) { colors(c,0) = (tc)*(ptrs++); colors(c,1) = (tc)*(ptrs++); colors(c,2) = (tc)*(ptrs++); }
195 opacities.assign(ptrs,1,nbp,1,1,false);
196 return assign(points);
197 }
199 CImg<T> get_appendCImg3d(const CImg<T>& img) const {
200 CImg<T> res(1,img.size() + size() - 8);
201 const T *ptrs = ptr() + 6, *ptrs0 = img.ptr() + 6;
202 T *ptrd = res.ptr();
203 *(ptrd++) = (T)('C' + 0.5f); *(ptrd++) = (T)('I' + 0.5f);
204 *(ptrd++) = (T)('m' + 0.5f); *(ptrd++) = (T)('g' + 0.5f);
205 *(ptrd++) = (T)('3' + 0.5f); *(ptrd++) = (T)('d' + 0.5f);
206 const unsigned int
207 nbv = (unsigned int)*(ptrs++),
208 nbv0 = (unsigned int)*(ptrs0++),
209 nbp = (unsigned int)*(ptrs++),
210 nbp0 = (unsigned int)*(ptrs0++);
211 *(ptrd++) = (T)(nbv + nbv0);
212 *(ptrd++) = (T)(nbp + nbp0);
213 std::memcpy(ptrd,ptrs,sizeof(T)*nbv*3);
214 ptrd+=3*nbv; ptrs+=3*nbv;
215 std::memcpy(ptrd,ptrs0,sizeof(T)*nbv0*3);
216 ptrd+=3*nbv0; ptrs0+=3*nbv0;
217 for (unsigned int i = 0; i<nbp; ++i) {
218 const unsigned int N = (unsigned int)*(ptrs++);
219 *(ptrd++) = (T)N;
220 std::memcpy(ptrd,ptrs,sizeof(T)*N);
221 ptrd+=N; ptrs+=N;
222 }
223 for (unsigned int i = 0; i<nbp0; ++i) {
224 const unsigned int N = (unsigned int)*(ptrs0++);
225 *(ptrd++) = (T)N;
226 for (unsigned int j = 0; j<N; ++j) *(ptrd++) = (T)(*(ptrs0++) + nbv);
227 }
228 std::memcpy(ptrd,ptrs,sizeof(T)*nbp*3);
229 ptrd+=3*nbp; ptrs+=3*nbp;
230 std::memcpy(ptrd,ptrs0,sizeof(T)*nbp0*3);
231 ptrd+=3*nbp0; ptrs0+=3*nbp0;
232 std::memcpy(ptrd,ptrs,sizeof(T)*nbp);
233 ptrd+=nbp;
234 std::memcpy(ptrd,ptrs0,sizeof(T)*nbp0);
235 return res;
236 }
238 CImg<T>& appendCImg3d(const CImg<T>& img) {
239 return get_appendCImg3d(img).transfer_to(*this);
240 }
242 CImg<T>& centerCImg3d() {
243 const unsigned int nbv = (unsigned int)(*this)[6];
244 const T *ptrs = ptr() + 8;
245 float xm = cimg::type<float>::max(), ym = xm, zm = xm, xM = cimg::type<float>::min(), yM = xM, zM = xM;
246 for (unsigned int i = 0; i<nbv; ++i) {
247 const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
248 if (x<xm) xm = x; if (x>xM) xM = x;
249 if (y<ym) ym = y; if (y>yM) yM = y;
250 if (z<zm) zm = z; if (z>zM) zM = z;
251 }
252 const float xc = (xm + xM)/2, yc = (ym + yM)/2, zc = (zm + zM)/2;
253 T *ptrd = ptr() + 8;
254 for (unsigned int i = 0; i<nbv; ++i) { *(ptrd++)-=(T)xc; *(ptrd++)-=(T)yc; *(ptrd++)-=(T)zc; }
255 return *this;
256 }
258 CImg<T> get_centerCImg3d() const {
259 return (+*this).centerCImg3d();
260 }
262 CImg<T>& normalizeCImg3d() {
263 const unsigned int nbv = (unsigned int)(*this)[6];
264 const T *ptrs = ptr() + 8;
265 float xm = cimg::type<float>::max(), ym = xm, zm = xm, xM = cimg::type<float>::min(), yM = xM, zM = xM;
266 for (unsigned int i = 0; i<nbv; ++i) {
267 const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
268 if (x<xm) xm = x; if (x>xM) xM = x;
269 if (y<ym) ym = y; if (y>yM) yM = y;
270 if (z<zm) zm = z; if (z>zM) zM = z;
271 }
272 const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
273 if (delta>0) {
274 T *ptrd = ptr() + 8;
275 for (unsigned int i = 0; i<3*nbv; ++i) *(ptrd++)/=(T)delta;
276 }
277 return *this;
278 }
280 CImg<T> get_normalizeCImg3d() const {
281 return (+*this).normalizeCImg3d();
282 }
284 template<typename t>
285 CImg<T>& rotateCImg3d(const CImg<t>& rot) {
286 const unsigned int nbv = (unsigned int)(*this)[6];
287 const T *ptrs = ptr() + 8;
288 const float
289 a = (float)rot(0,0), b = (float)rot(1,0), c = (float)rot(2,0),
290 d = (float)rot(0,1), e = (float)rot(1,1), f = (float)rot(2,1),
291 g = (float)rot(0,2), h = (float)rot(1,2), i = (float)rot(2,2);
292 T *ptrd = ptr() + 8;
293 for (unsigned int j = 0; j<nbv; ++j) {
294 const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
295 *(ptrd++) = (T)(a*x + b*y + c*z);
296 *(ptrd++) = (T)(d*x + e*y + f*z);
297 *(ptrd++) = (T)(g*x + h*y + i*z);
298 }
299 return *this;
300 }
302 template<typename t>
303 CImg<T> get_rotateCImg3d(const CImg<t>& rot) const {
304 return (+*this).rotateCImg3d(rot);
305 }
307 CImg<T>& translateCImg3d(const float tx, const float ty, const float tz) {
308 const unsigned int nbv = (unsigned int)(*this)[6];
309 T *ptrd = ptr() + 8;
310 for (unsigned int j = 0; j<nbv; ++j) { *(ptrd++) += (T)tx; *(ptrd++) += (T)ty; *(ptrd++) += (T)tz; }
311 return *this;
312 }
314 CImg<T> get_translateCImg3d(const float tx, const float ty, const float tz) const {
315 return (+*this).translateCImg3d(tx,ty,tz);
316 }
318 CImg<T>& coloropacityCImg3d(const float R, const float G, const float B, const float opacity, const bool set_RGB, const bool set_opacity) {
319 T *ptrd = ptr() + 6;
320 const unsigned int
321 nbv = (unsigned int)*(ptrd++),
322 nbp = (unsigned int)*(ptrd++);
323 ptrd+=3*nbv;
324 for (unsigned int i = 0; i<nbp; ++i) { const unsigned int N = (unsigned int)*(ptrd++); ptrd+=N; }
325 if (set_RGB) for (unsigned int c = 0; c<nbp; ++c) { *(ptrd++) = (T)R; *(ptrd++) = (T)G; *(ptrd++) = (T)B; } else ptrd+=3*nbp;
326 if (set_opacity) for (unsigned int o = 0; o<nbp; ++o) *(ptrd++) = (T)opacity;
327 return *this;
328 }
330 CImg<T> get_coloropacityCImg3d(const float R, const float G, const float B, const float opacity, const bool set_RGB, const bool set_opacity) const {
331 return (+*this).coloropacityCImg3d(R,G,B,opacity,set_RGB,set_opacity);
332 }
334 #else // eq. to #ifndef cimg_plugin
336 #define cimg_debug 1
337 #ifndef cimg_gmic_cpp
338 #define cimg_gmic_cpp "examples/gmic.cpp"
339 #define cimg_cimg_h "../CImg.h"
340 #endif
341 #define cimg_stdout stdout
342 #define cimg_plugin cimg_gmic_cpp
343 #include cimg_cimg_h
344 #include "gmic.h"
345 using namespace cimg_library;
347 // The lines below are necessary when using a non-standard compiler such as visualcpp6.
348 #ifdef cimg_use_visualcpp6
349 #define std
350 #endif
351 #ifdef min
352 #undef min
353 #undef max
354 #endif
356 #if !defined(gmic_main) || !defined(gmic_separate_compilation)
358 // Define some useful macros.
359 //---------------------------
361 // Code for validity checking of indices.
362 #define gmic_inds indices2string(indices,true)
363 #define gmic_check_indice(ind,funcname) { \
364 const int indo = (int)ind; \
365 if (ind<0) ind+=images.size; \
366 if (ind<0 || ind>=(int)images.size) { \
367 if (images.size) error(funcname " : Invalid indice '[%d]' (valid indice range is -%u...%u).",gmic_inds,indo,images.size,images.size-1); \
368 else error(funcname " : Invalid indice '[%d]' (image list is empty).",gmic_inds,indo); \
369 } \
370 }
372 // Code for having 'get' or 'non-get' versions of G'MIC commands.
373 #define gmic_apply(instance,function) { \
374 if (get_version) { \
375 unsigned int posi = 0; \
376 if (images.contains(instance,posi)) filenames.insert(filenames[posi]); \
377 else filenames.insert(CImg<char>("(gmic)",7,1,1,1,false)); \
378 CImg<T> res = instance.get_##function; \
379 images.insert(1); res.transfer_to(images.last()); \
380 } else instance.function; \
381 }
383 // Code for simple commands that has no parameters and act on images.
384 #define gmic_simple_item(option,function,description) \
385 if (!cimg::strcmp(option,item0)) { \
386 print(description,gmic_inds); cimg_foroff(indices,l) gmic_apply(images[indices[l]],function()); \
387 continue; \
388 }
390 // Code for the type cast command.
391 #define gmic_cast(pixel_type,st_type) \
392 if (!cimg::strcmp(#pixel_type,argument)) { \
393 print("Set pixel type to '%s'.",#pixel_type); ++position; \
394 if (!cimg::strcmp(st_type,cimg::type<T>::string())) continue; \
395 CImgList<pixel_type> casted_images; \
396 while (images) { casted_images.insert(images[0]); images.remove(0); } \
397 return parse_##pixel_type(casted_images); \
398 }
400 // Code for G'MIC arithmetic commands.
401 #define gmic_arithmetic_item(option1,option2,\
402 function1,description1,arg1_1,arg1_2,value_type1, \
403 function2,description2_1,description2_2,arg2_1,arg2_2,description3) \
404 if (!cimg::strcmp(option1,item0) || !cimg::strcmp(option2,item0)) { \
405 double value = 0; char inds[4096] = { 0 }, sep = 0, end = 0; \
406 if (std::sscanf(argument,"%lf%c",&value,&end)==1) { \
407 print(description1 ".",arg1_1,arg1_2); \
408 cimg_foroff(indices,l) \
409 if (get_version) { \
410 images.insert(images[indices[l]]); images.last().function1((value_type1)value); \
411 filenames.insert(filenames[indices[l]]); } \
412 else images[indices[l]].function1((value_type1)value); \
413 ++position; \
414 } else if (std::sscanf(argument,"[%4095[0-9.eE%+-]%c%c",inds,&sep,&end)==2 && sep==']') { \
415 const CImg<unsigned int> ind = indices2cimg(inds,images.size,option1); \
416 if (ind.size()!=1) error(description2_1 " : Argument '[%s]' should contain one indice.",gmic_inds,inds); \
417 print(description2_2 ".",arg2_1,arg2_2); \
418 const CImg<T> img0 = images[ind[0]]; \
419 cimg_foroff(indices,l) \
420 if (get_version) { \
421 images.insert(images[indices[l]]); images.last().function2(img0); \
422 filenames.insert(filenames[indices[l]]); } \
423 else images[indices[l]].function2(img0); \
424 ++position; \
425 } else { \
426 print(description3 ".",gmic_inds); \
427 if (images && indices) { \
428 for (unsigned int siz = indices.size(), ind0 = indices[0], off = 0, l = 1; l<siz; ++l) { \
429 const unsigned int ind = indices[l] - off; \
430 images[ind0].function2(images[ind]); \
431 images.remove(ind); filenames.remove(ind); \
432 ++off; \
433 }}} continue; \
434 }
436 // Constructors.
437 //--------------
438 #if defined(gmic_float) || !defined(gmic_separate_compilation)
440 #include "gmic_def.h"
442 gmic_exception::gmic_exception() {
443 message[0] = '\0';
444 }
446 gmic_exception::gmic_exception(const char *format, ...) {
447 std::va_list ap;
448 va_start(ap,format);
449 std::vsprintf(message,format,ap);
450 va_end(ap);
451 }
453 gmic_exception::gmic_exception(const char *format, std::va_list ap) {
454 std::vsprintf(message,format,ap);
455 }
457 gmic::gmic() {
458 assign(0);
459 }
461 // Set default values of G'MIC parameters and macros.
462 //----------------------------------------------------
463 gmic& gmic::assign(const unsigned int size, const char *const custom_macros, const bool add_macros_start) {
464 filenames.assign(size,CImg<char>("(gmic)",7,1,1,1,false));
465 position = 0;
466 verbosity_level = 0;
467 is_released = true;
468 is_debug = false;
469 is_begin = true;
470 background3d[0] = 120;
471 background3d[1] = 120;
472 background3d[2] = 140;
473 render3d = 4;
474 renderd3d = -1;
475 is_oriented3d = false;
476 focale3d = 500;
477 light3d_x = 0;
478 light3d_y = 0;
479 light3d_z = -5000;
480 specular_light3d = 0.15f;
481 specular_shine3d = 0.8f;
482 is_fullpath = false;
483 add_macros(data_def,sizeof(data_def)-1,true);
484 add_macros(custom_macros,cimg::strlen(custom_macros)-1,add_macros_start);
485 return *this;
486 }
488 // Error procedure.
489 //-----------------
490 const gmic& gmic::error(const char *format, ...) const {
491 va_list ap;
492 va_start(ap,format);
493 char message[1024] = { 0 };
494 std::vsprintf(message,format,ap);
495 va_end(ap);
496 if (verbosity_level>=0) {
497 std::fprintf(cimg_stdout,"\n<gmic-#%u> ** Error ** %s",filenames.size,message);
498 std::fprintf(cimg_stdout,"\n<gmic-#%u> Abort G'MIC instance.\n",filenames.size);
499 std::fflush(cimg_stdout);
500 }
501 throw gmic_exception(message);
502 return *this;
503 }
505 // Warning procedure.
506 //-------------------
507 const gmic& gmic::warning(const char *format, ...) const {
508 va_list ap;
509 va_start(ap,format);
510 if (verbosity_level>=0) {
511 std::fprintf(cimg_stdout,"\n<gmic-#%u> ** Warning ** ",filenames.size);
512 std::vfprintf(cimg_stdout,format,ap);
513 std::fflush(cimg_stdout);
514 }
515 va_end(ap);
516 return *this;
517 }
519 // Print debug messages.
520 //----------------------
521 const gmic& gmic::debug(const char *format, ...) const {
522 const char t_normal[] = { 0x1b,'[','0',';','0',';','0','m','\0' };
523 const char t_red[] = { 0x1b,'[','4',';','3','1',';','5','9','m','\0' };
524 const char t_bold[] = { 0x1b,'[','1','m','\0' };
525 if (is_debug) {
526 va_list ap;
527 va_start(ap,format);
528 std::fprintf(cimg_stdout,"\n%s%s<gmic-debug-#%u>%s ",t_bold,t_red,filenames.size,t_normal);
529 std::vfprintf(cimg_stdout,format,ap);
530 va_end(ap);
531 std::fflush(cimg_stdout);
532 }
533 return *this;
534 }
536 // Print status messages.
537 //-----------------------
538 const gmic& gmic::print(const char *format, ...) const {
539 va_list ap;
540 va_start(ap,format);
541 if (verbosity_level>=0) {
542 std::fprintf(cimg_stdout,"\n<gmic-#%u> ",filenames.size);
543 std::vfprintf(cimg_stdout,format,ap);
544 std::fflush(cimg_stdout);
545 }
546 va_end(ap);
547 return *this;
548 }
550 // Add macros from a char* buffer.
551 //---------------------------------
552 gmic& gmic::add_macros(const char *const data_macros, const unsigned int data_size, const bool add_macros_at_start) {
553 if (!data_macros || !data_size) return *this;
554 char mac[4096] = { 0 }, com[256*1024] = { 0 }, line[256*1024] = { 0 }, sep = 0;
555 const char *data = data_macros, *const data_end = data_macros + data_size;
556 while (data<data_end) {
557 if (*data=='\n') ++data;
558 else {
559 if (std::sscanf(data,"%262143[^\n]",line)>0) data += cimg::strlen(line) + 1;
560 if (line[0]!='#') { // Useful line (not a comment)
561 mac[0] = com[0] = 0;
562 if (std::sscanf(line,"%4095[^: ] %c %262143[^\n]",mac,&sep,com)>=2 && sep==':' &&
563 std::sscanf(mac,"%4095s",line)==1) { // Macro definition.
564 macros.insert(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),add_macros_at_start?0:macros.size);
565 commands.insert(CImg<char>(com,cimg::strlen(com)+1,1,1,1,false),add_macros_at_start?0:commands.size);
566 } else { // Possible continuation of a previous macro definition.
567 if (!macros) error("Fatal error : Invalid G'MIC macros data.");
568 CImg<char> &last = commands[add_macros_at_start?0:commands.size-1];
569 last[last.size()-1] = ' ';
570 last.append(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),'x');
571 }
572 }
573 }
574 }
575 return *this;
576 }
578 // Add macros from a macro file.
579 //------------------------------
580 gmic& gmic::add_macros(std::FILE *const file, const bool add_macros_at_start) {
581 if (!file) return *this;
582 char mac[4096] = { 0 }, com[256*1024] = { 0 }, line[256*1024] = { 0 }, sep = 0;
583 int err = 0;
584 while ((err=std::fscanf(file,"%262143[^\n] ",line)>=0)) {
585 if (err) { // Non empty-line
586 mac[0] = com[0] = 0;
587 if (line[0]!='#') { // Useful line (not a comment).
588 if (std::sscanf(line,"%4095[^: ] %c %262143[^\n]",mac,&sep,com)>=2 && sep==':' &&
589 std::sscanf(mac,"%4095s",line)==1) { // Macro definition.
590 macros.insert(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),add_macros_at_start?0:macros.size);
591 commands.insert(CImg<char>(com,cimg::strlen(com)+1,1,1,1,false),add_macros_at_start?0:commands.size);
592 } else { // Possible continuation of a previous macro definition.
593 if (!macros) error("Fatal error : Invalid G'MIC macros data.");
594 CImg<char> &last = commands[add_macros_at_start?0:commands.size-1];
595 last[last.size()-1] = ' ';
596 last.append(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),'x');
597 }
598 }
599 }
600 }
601 return *this;
602 }
604 // Return indices of the images from a string.
605 //--------------------------------------------
606 CImg<unsigned int> gmic::indices2cimg(const char *const string, const unsigned int indice_max,
607 const char *const command) const {
608 if (!cimg::strlen(string)) return CImg<unsigned int>();
609 CImgList<unsigned int> inds;
610 const char *it = string;
611 for (bool stopflag = false; !stopflag; ) {
612 char sep = 0, end = 0, item0[4096] = { 0 }, item1[4096] = { 0 };
613 float ind0 = 0, ind1 = 0, step = 1;
614 if (std::sscanf(it,"%4095[^,]%c",item0,&end)!=2) stopflag = true;
615 else it += 1 + cimg::strlen(item0);
616 const int err = std::sscanf(item0,"%4095[^:]%c%f%c",item1,&sep,&step,&end);
617 if (err!=1 && err!=3) error("Command '%s' : Invalid indice(s) '[%s]'.",command,string);
618 if (std::sscanf(item1,"%f%%-%f%c%c",&ind0,&ind1,&sep,&end)==3 && sep=='%') {
619 ind0 = (float)cimg::round(ind0*indice_max/100,1);
620 ind1 = (float)cimg::round(ind1*indice_max/100,1);
621 } else if (std::sscanf(item1,"%f%%-%f%c",&ind0,&ind1,&end)==2)
622 ind0 = (float)cimg::round(ind0*indice_max/100,1);
623 else if (std::sscanf(item1,"%f-%f%c%c",&ind0,&ind1,&sep,&end)==3 && sep=='%')
624 ind1 = (float)cimg::round(ind1*indice_max/100,1);
625 else if (std::sscanf(item1,"%f-%f%c",&ind0,&ind1,&end)==2) { }
626 else if (std::sscanf(item1,"%f%c%c",&ind0,&sep,&end)==2 && sep=='%')
627 ind1 = (ind0 = (float)cimg::round(ind0*indice_max/100,1));
628 else if (std::sscanf(item1,"%f%c",&ind0,&end)==1)
629 ind1 = ind0;
630 else error("Command '%s' : Invalid indice(s) '[%s]'.",command,string);
631 if (ind0<0) ind0+=indice_max;
632 if (ind1<0) ind1+=indice_max;
633 if (ind0<0 || ind0>=indice_max || ind1<0 || ind1>=indice_max || step<=0) {
634 if (indice_max) error("Command '%s' : Invalid indice(s) '[%s]' (valid indice range is -%u...%u).",
635 command,string,indice_max,indice_max-1);
636 else error("Command '%s' : Invalid indice(s) '[%s]' (image list is empty).",
637 command,string);
638 }
639 if (ind0>ind1) cimg::swap(ind0,ind1);
640 const unsigned int
641 iind0 = (unsigned int)ind0,
642 _ind1 = (unsigned int)ind1,
643 iind1 = (unsigned int)(_ind1 - cimg::mod((float)_ind1,step));
644 if (iind0==iind1) inds.insert(CImg<unsigned int>::vector(iind0));
645 else inds.insert(CImg<unsigned int>::sequence((unsigned int)(1+(iind1-iind0)/step),
646 (unsigned int)iind0,
647 (unsigned int)iind1).get_split('y'));
648 }
649 inds = inds.get_append('y').sort().get_split('y');
650 cimglist_for(inds,l) if (l!=inds.size-1 && inds(l,0)==inds(l+1,0)) inds.remove(l--);
651 if (is_debug) {
652 debug("Indices : ");
653 inds.get_append('y').print(); // List indices if debug mode is activated.
654 }
655 return inds.get_append('y').sort();
656 }
658 // Return stringified version of indices or filenames.
659 //----------------------------------------------------
660 char* gmic::indices2string(const CImg<unsigned int>& indices, const bool display_indices) const {
661 static char res0[4096] = { 0 }, res1[4096] = { 0 };
662 const unsigned int siz = indices.size();
663 if (display_indices) {
664 switch (siz) {
665 case 0: std::sprintf(res0," []"); break;
666 case 1: std::sprintf(res0," [%u]",indices[0]); break;
667 case 2: std::sprintf(res0,"s [%u,%u]",indices[0],indices[1]); break;
668 case 3: std::sprintf(res0,"s [%u,%u,%u]",indices[0],indices[1],indices[2]); break;
669 case 4: std::sprintf(res0,"s [%u,%u,%u,%u]",indices[0],indices[1],indices[2],indices[3]); break;
670 default: std::sprintf(res0,"s [%u,...,%u]",indices[0],indices[siz-1]);
671 }
672 return res0;
673 }
674 switch (siz) {
675 case 0: std::sprintf(res1," "); break;
676 case 1: std::sprintf(res1,"%s",filenames[indices[0]].ptr()); break;
677 case 2: std::sprintf(res1,"%s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr()); break;
678 case 3: std::sprintf(res1,"%s, %s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr(),
679 filenames[indices[2]].ptr()); break;
680 case 4: std::sprintf(res1,"%s, %s, %s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr(),
681 filenames[indices[2]].ptr(), filenames[indices[3]].ptr()); break;
682 default: std::sprintf(res1,"%s, ..., %s",filenames[indices[0]].ptr(),filenames[indices[siz-1]].ptr());
683 }
684 return res1;
685 }
686 #endif // #if defined(gmic_float) || !defined(gmic_separate_compilation)
688 // Template constructors.
689 //-----------------------
690 template<typename T>
691 gmic::gmic(const int argc, const char *const *const argv, CImgList<T>& images, const char *custom_macros, const bool add_macros_at_start) {
692 assign(images.size,custom_macros,add_macros_at_start);
693 for (int pos = 1; pos<argc; ++pos)
694 command_line.insert(CImg<char>(argv[pos],cimg::strlen(argv[pos])+1,1,1,1,false));
695 is_released = false;
696 parse(images);
697 }
699 template<typename T>
700 gmic::gmic(const char *const command, CImgList<T>& images, const char *custom_macros, const bool add_macros_at_start) {
701 assign(images.size,custom_macros,add_macros_at_start);
702 char item[4096] = { 0 };
703 for (const char *ncommand = command; *ncommand; ) {
704 if (std::sscanf(ncommand,"%[^ ]",item)==1) {
705 const int l = cimg::strlen(item);
706 command_line.insert(CImg<char>(item,l+1,1,1,1,false));
707 ncommand += l;
708 while (*ncommand==' ') ++ncommand;
709 } else break;
710 }
711 is_released = true;
712 parse(images);
713 }
715 // Display specified image(s).
716 //-----------------------------
717 template<typename T>
718 bool gmic::display_images(const CImgList<T>& images, const CImg<unsigned int>& indices,
719 const bool verbose) const {
720 if (!images || !indices) { print("Display image []."); return false; }
721 CImgList<unsigned int> inds = indices.get_unroll('x').get_split('x');
722 CImgList<T> visu;
723 unsigned int max_height = 0;
724 cimglist_for(inds,l) {
725 const CImg<T>& img = images[inds(l,0)];
726 if (img.height>max_height && !img.is_CImg3d()) max_height = img.height;
727 }
728 cimglist_for(inds,l) {
729 const unsigned int ind = inds(l,0);
730 const CImg<T> &img = images[ind];
731 if (img) {
732 if (!max_height || img.height<max_height) visu.insert(img,~0U,true);
733 else visu.insert(img.get_lines(0,max_height-1));
734 } else if (verbose) { warning("Display image : Image [%d] is empty.",ind); inds.remove(l--); }
735 }
736 const CImg<unsigned int> nindices = inds.get_append('x');
737 const char *const fnames = indices2string(nindices,false);
738 print("Display image%s = '%s'.\n\n",gmic_inds,fnames);
739 if (visu.size) {
740 if (visu.size!=1) visu.display(fnames,verbosity_level>=0,'x','p');
741 else {
742 const CImg<T> &img = visu[0];
743 char title[4096] = { 0 };
744 std::sprintf(title,"%s (%dx%dx%dx%d)",fnames,img.dimx(),img.dimy(),img.dimz(),img.dimv());
745 img.display(title,verbosity_level>=0);
746 }
747 }
748 return true;
749 }
751 // Display plots of specified image(s).
752 //--------------------------------------
753 template<typename T>
754 bool gmic::display_plots(const CImgList<T>& images, const CImg<unsigned int>& indices,
755 const unsigned int plot_type, const unsigned int vertex_type,
756 const double xmin, const double xmax,
757 const double ymin, const double ymax,
758 const bool verbose) const {
759 if (!images || !indices) { print("Plot image []."); return false; }
760 CImgDisplay disp(cimg_fitscreen(640,480,1),0,0);
761 cimg_foroff(indices,l) {
762 const unsigned int ind = indices[l];
763 const CImg<T>& img = images[ind];
764 if (img) {
765 print("Plot image%s = '%s'.\n",gmic_inds,indices2string(indices,false));
766 if (verbosity_level>=0) { std::fputc('\n',cimg_stdout); img.print(filenames[ind].ptr()); }
767 char title[4096] = { 0 };
768 std::sprintf(title,"%s (%dx%dx%dx%d)",
769 filenames[ind].ptr(),img.dimx(),img.dimy(),img.dimz(),img.dimv());
770 img.display_graph(disp.set_title(title),plot_type,vertex_type,0,xmin,xmax,0,ymin,ymax);
771 } else if (verbose) warning("Plot image : Image [%d] is empty.",ind);
772 }
773 return true;
774 }
776 // Display specified 3D object(s).
777 //--------------------------------
778 template<typename T>
779 bool gmic::display_objects3d(const CImgList<T>& images, const CImg<unsigned int>& indices,
780 const bool verbose) const {
781 if (!indices) { print("Display 3D object []."); return false; }
782 CImg<unsigned char> background;
783 bool exist3d = false;
784 CImgDisplay disp;
785 cimg_foroff(indices,l) {
786 const unsigned int ind = indices[l];
787 const CImg<T> &img = images[ind];
788 if (!img.is_CImg3d()) {
789 if (verbose) warning("Display 3D object : Image [%d] is not a 3D object.",ind);
790 } else {
791 exist3d = true;
792 if (!background || !disp) {
793 background.assign(cimg_fitscreen(640,480,1),1,3);
794 cimg_forV(background,k) background.get_shared_channel(k).fill(background3d[k]);
795 disp.assign(background);
796 }
797 CImgList<unsigned int> primitives3d;
798 CImgList<unsigned char> colors3d;
799 CImg<float> opacities3d;
800 CImg<float> points3d(img);
801 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
802 print("Display 3D object [%u] = '%s' (%d points, %u primitives).",
803 ind,filenames[ind].ptr(),points3d.dimx(),primitives3d.size);
804 disp.set_title("%s (%d points, %u primitives)",
805 filenames[ind].ptr(),points3d.dimx(),primitives3d.size);
806 background.display_object3d(disp,points3d,primitives3d,colors3d,opacities3d,
807 true,render3d,renderd3d,!is_oriented3d,focale3d,specular_light3d,specular_shine3d);
808 if (disp.is_closed) break;
809 }
810 }
811 return exist3d;
812 }
814 // Substitute '@' expressions.
815 //----------------------------
816 template<typename T>
817 CImg<char> gmic::substitute_arobace(const char *const argument, const CImgList<T>& images) const {
818 if (!argument) return CImg<char>();
819 CImgList<char> _largument;
820 char range[4096] = { 0 };
821 for (const char *nargument = argument; *nargument; ) {
822 if (*nargument=='@') {
823 char argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
824 int ind = 0, bcond = 0; *range = 0; char sepx = 0, sepy = 0, sepz = 0, sepv = 0, sep = 0, end = 0;
825 float x = 0, y = 0, z = 0, v = 0, m = 0, M = 1;
826 if (nargument[1]=='#' ||
827 (std::sscanf(nargument,"@{#%c",&sep)==1 && sep=='}')) {
828 std::sprintf(range,"%u",images.size);
829 _largument.insert(CImg<char>(range,cimg::strlen(range),1,1,1,true));
830 if (sep=='}') nargument+=4; else nargument+=2;
831 } else if (std::sscanf(nargument,"@%d",&ind)==1 ||
832 (std::sscanf(nargument,"@{%d%c",&ind,&sep)==2 && sep=='}') ||
833 (std::sscanf(nargument,"@{%d%*c%4095[^}]%c",&ind,range,&sep)==3 && sep=='}')) {
834 int nind = ind;
835 if (nind<0) nind+=images.size;
836 if (nind<0 || nind>=(int)images.size) {
837 if (images.size) error("Invalid indice '%d' in '@argument' (valid indice range is -%u...%u).",
838 ind,images.size,images.size-1);
839 else error("Invalid indice '%d' in '@argument' (image list is empty).",ind);
840 }
841 const unsigned int sizrange = cimg::strlen(range);
842 const CImg<T>& img = images[nind];
843 CImg<T> values;
844 if (sizrange) {
845 const CImg<unsigned int> iinds = indices2cimg(range,img.size(),"parsing");
846 values.assign(iinds.size());
847 cimg_foroff(iinds,p) values[p] = img[iinds(p)];
848 } else values = img.get_shared();
849 const CImg<char> vs = values.value_string();
850 const unsigned int vsl = vs.size();
851 if (vsl>1) _largument.insert(CImg<char>(vs.ptr(),vsl-1,1,1,1,true));
852 nargument+= 1 + (sep?2:0) + (sizrange?1:0) + sizrange + std::sprintf(range,"%d",ind);
853 } else if (((std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%c",&ind,argx,&sep)==3 && sep==')') ||
854 (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind,argx,argy,&sep)==4 && sep==')') ||
855 (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
856 &ind,argx,argy,argz,&sep)==5 && sep==')') ||
857 (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
858 &ind,argx,argy,argz,argv,&sep)==6 && sep==')') ||
859 (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
860 &ind,argx,argy,argz,argv,&bcond,&sep)==7 && sep==')')) &&
861 (!*argx ||
862 (std::sscanf(argx,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
863 std::sscanf(argx,"%f%c",&x,&end)==1) &&
864 (!*argy ||
865 (std::sscanf(argy,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
866 std::sscanf(argy,"%f%c",&y,&end)==1) &&
867 (!*argz ||
868 (std::sscanf(argz,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
869 std::sscanf(argz,"%f%c",&z,&end)==1) &&
870 (!*argv ||
871 (std::sscanf(argv,"%f%c%c",&v,&sepv,&end)==2 && sepv=='%') ||
872 std::sscanf(argv,"%f%c",&v,&end)==1)) {
873 int nind = ind;
874 if (nind<0) nind+=images.size;
875 if (nind<0 || nind>=(int)images.size) {
876 if (images.size) error("Invalid indice '%d' in '@argument' (valid indice range is -%u...%u).",
877 ind,images.size,images.size-1);
878 else error("Invalid indice '%d' in '@argument' (image list is empty).",ind);
879 }
880 const CImg<T>& img = images[nind];
881 const int
882 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
883 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
884 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1),
885 nv = (int)cimg::round(sepv=='%'?v*(img.dimv()-1)/100:v,1);
886 std::sprintf(range,"%g",bcond?(double)img.atXYZV(nx,ny,nz,nv):(double)img.atXYZV(nx,ny,nz,nv,0));
887 _largument.insert(CImg<char>(range,cimg::strlen(range)));
888 while (*nargument!=')') ++nargument; ++nargument;
889 } else if ((std::sscanf(nargument,"@%c",&sep)==1 && sep=='?') ||
890 (std::sscanf(nargument,"@{?%c",&sep)==1 && sep=='}') ||
891 (std::sscanf(nargument,"@[?%c",&sep)==1 && sep==']') ||
892 (std::sscanf(nargument,"@{?%*c%f%c",&M,&sep)==2 && sep=='}') ||
893 (std::sscanf(nargument,"@[?%*c%f%c",&M,&sep)==2 && sep==']') ||
894 (std::sscanf(nargument,"@{?%*c%f%*c%f%c",&m,&M,&sep)==3 && sep=='}') ||
895 (std::sscanf(nargument,"@[?%*c%f%*c%f%c",&m,&M,&sep)==3 && sep==']')) {
896 const double r = m + (M-m)*cimg::rand();
897 if (sep==']') std::sprintf(range,"%d",(int)cimg::round(r,1)); else std::sprintf(range,"%g",r);
898 _largument.insert(CImg<char>(range,cimg::strlen(range),1,1,1,true));
899 if (sep=='?') nargument+=2; else { while (*nargument!=sep) ++nargument; ++nargument; }
900 } else _largument.insert(CImg<char>(nargument++,1,1,1,1,true));
901 } else {
902 std::sscanf(nargument,"%4095[^@]",range);
903 const int l = cimg::strlen(range);
904 _largument.insert(CImg<char>(range,l,1,1,1,true));
905 nargument+=l;
906 }
907 }
908 _largument.insert(CImg<char>::vector(0));
909 return _largument.get_append('x');
910 }
912 // Main parsing procedure.
913 //------------------------
914 template<typename T>
915 gmic& gmic::parse(CImgList<T> &images) {
916 const unsigned int command_line_maxsize = 65535;
917 const int no_ind = (int)(~0U>>1);
918 cimg::exception_mode() = 0;
920 // Begin command line parsing.
921 while (position<command_line.size && command_line.size<command_line_maxsize) {
922 const char
923 *const orig_item = command_line[position].ptr(),
924 *const orig_argument = position+1<command_line.size?command_line[position+1].ptr():"";
926 // Substitute '@' expressions in 'orig_item' and 'orig_argument' if necessary.
927 CImg<char> _item, _argument, _argument_text;
928 if (*orig_item=='-' || *orig_item=='[' || *orig_item=='(') {
929 if (std::strchr(orig_item,'@')) {
930 _item = substitute_arobace(orig_item,images);
931 if (_item.size()>4095) error("Buffer overflow when substituting item '%s'.",orig_item);
932 }
933 if (*orig_item=='-' &&
934 (*orig_argument!='-' ||
935 (*orig_argument=='-' && (orig_argument[1]=='.' || orig_argument[1]=='@' ||
936 (orig_argument[1]>='0' && orig_argument[1]<='9'))))
937 && std::strchr(orig_argument,'@')) {
938 _argument = substitute_arobace(orig_argument,images);
939 if (_argument.size()>4095) error("Buffer overflow when substituting argument '%s'.",orig_argument);
940 }
941 }
942 const char
943 *item = _item?_item.ptr():orig_item,
944 *argument = _argument?_argument.ptr():orig_argument;
945 const char *argument_text = argument;
946 if (cimg::strlen(argument)>=64) {
947 _argument_text.assign(64,1,1,1,0);
948 std::memcpy(_argument_text.ptr(),argument,60*sizeof(char));
949 _argument_text[60] = _argument_text[61] = _argument_text[62] = '.'; _argument_text[63] = 0;
950 argument_text = _argument_text.ptr();
951 }
953 // Get current item/command from the command line.
954 char item0[4096] = { 0 }, item1[4096] = { 0 };
955 bool get_version = false;
956 CImg<unsigned int> indices;
957 if (item[0]=='-' && item[1] && item[1]!='.') {
958 char sep0 = 0, sep1 = 0, end = 0;
959 if (item[1]=='-' && item[2] && item[2]!='[' && item[2]!='3' && item[3]!='d') { ++item; get_version = true; }
960 const int err = std::sscanf(item,"%4095[^[]%c%4095[0-9.eE%,:+-]%c%c",item0,&sep0,item1,&sep1,&end);
961 if (err==1) indices = CImg<unsigned int>::sequence(images.size,0,images.size-1);
962 else if (err==4 && sep1==']')
963 indices = indices2cimg(item1,(!strcmp(item0,"-i")||!strcmp(item0,"-input"))?images.size+1:images.size,item0);
964 else { std::strcpy(item0,item); item1[0] = 0; }
965 }
966 ++position;
968 // Check for verbosity commands.
969 if (*item=='-') {
970 if (!cimg::strcmp("-verbose+",item) || !cimg::strcmp("-v+",item)) ++verbosity_level;
971 else if (!cimg::strcmp("-verbose-",item) || !cimg::strcmp("-v-",item)) --verbosity_level;
972 }
974 if (is_begin) { print("Start G'MIC instance."); is_begin = false; }
975 debug("Item : '%s', Argument : '%s'.",item,argument);
977 // Begin command interpretation.
978 try {
979 if (*item=='-') {
981 //----------------
982 // Global options
983 //----------------
985 // Verbosity (actually, just continue to next command since verbosity has been already processed above).
986 if (!cimg::strcmp("-verbose+",item) || !cimg::strcmp("-v+",item) ||
987 !cimg::strcmp("-verbose-",item) || !cimg::strcmp("-v-",item)) continue;
989 // Load macro file.
990 if (!cimg::strcmp("-macros",item) || !cimg::strcmp("-m",item)) {
991 print("Load macro file '%s'",cimg::basename(argument));
992 std::FILE *const file = cimg::fopen(argument,"r");
993 if (file) {
994 const unsigned int siz = macros.size;
995 add_macros(file,true);
996 cimg::fclose(file);
997 if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u macros added).",macros.size-siz);
998 }
999 else error("Load macro file '%s' : File not found.",argument);
1000 ++position; continue;
1001 }
1003 // Switch 'is_debug' flag.
1004 if (!cimg::strcmp("-debug",item)) {
1005 is_debug = !is_debug;
1006 continue;
1007 }
1009 // Switch 'is_fullpath' flag.
1010 if (!cimg::strcmp("-fullpath",item)) {
1011 is_fullpath = !is_fullpath;
1012 continue;
1013 }
1015 //----------------------
1016 // Arithmetic operators
1017 //----------------------
1019 // Common arithmetic operators.
1020 gmic_arithmetic_item("-add","-+",
1021 operator+=,"Add %g to image%s",value,gmic_inds,T,
1022 operator+=,"Add to image%s",
1023 "Add image [%d] to image%s",ind[0],gmic_inds,
1024 "Add image%s together");
1026 gmic_arithmetic_item("-sub","--",
1027 operator-=,"Substract %g to image%s",value,gmic_inds,T,
1028 operator-=,"Substract to image%s",
1029 "Substract image [%d] to image%s",ind[0],gmic_inds,
1030 "Substract image%s together");
1032 gmic_arithmetic_item("-mul","-*",
1033 operator*=,"Multiply image%s by %g",gmic_inds,value,double,
1034 mul,"Multiply image%s",
1035 "Multiply image%s by image [%d]",gmic_inds,ind[0],
1036 "Multiply image%s together");
1038 gmic_arithmetic_item("-div","-/",
1039 operator/=,"Divide image%s by %g",gmic_inds,value,double,
1040 div,"Divide image%s",
1041 "Divide image%s by image [%d]",gmic_inds,ind[0],
1042 "Divide image%s together");
1044 gmic_arithmetic_item("-pow","-^",
1045 pow,"Compute image%s to the power of %g",gmic_inds,value,double,
1046 pow,"Compute power of image%s",
1047 "Compute image%s to the power of image [%d]",gmic_inds,ind[0],
1048 "Compute the power of image%s together");
1050 gmic_arithmetic_item("-min","-min",
1051 min,"Compute pointwise minimum between image%s and %g",gmic_inds,value,T,
1052 min,"Compute pointwise minimum with image%s",
1053 "Compute pointwise minimum between image%s and image [%d]",gmic_inds,ind[0],
1054 "Compute pointwise minimum between image%s together");
1056 gmic_arithmetic_item("-max","-max",
1057 max,"Compute pointwise maximum between image%s and %g",gmic_inds,value,T,
1058 max,"Compute pointwise maximum with image%s",
1059 "Compute pointwise maximum between image%s and image [%d]",gmic_inds,ind[0],
1060 "Compute pointwise maximum between image%s together");
1062 gmic_arithmetic_item("-mod","-%",
1063 operator%=,"Compute pointwise modulo between image%s and %g.",gmic_inds,value,T,
1064 operator%=,"Compute pointwise modulo with image%s",
1065 "Compute pointwise modulo between image%s and image [%d]",gmic_inds,ind[0],
1066 "Compute pointwise modulo between image%s together");
1068 gmic_arithmetic_item("-and","-and",
1069 operator&=,"Compute bitwise AND between image%s and %g.",gmic_inds,value,unsigned int,
1070 operator&=,"Compute bitwise AND with image%s",
1071 "Compute bitwise AND between image%s and image [%d]",gmic_inds,ind[0],
1072 "Compute bitwise AND between image%s together");
1074 gmic_arithmetic_item("-or","-or",
1075 operator|=,"Compute bitwise OR between image%s and %g.",gmic_inds,value,unsigned int,
1076 operator|=,"Compute bitwise OR with image%s",
1077 "Compute bitwise OR between image%s and image [%d]",gmic_inds,ind[0],
1078 "Compute bitwise OR between image%s together");
1080 gmic_arithmetic_item("-xor","-xor",
1081 operator^=,"Compute bitwise XOR between image%s and %g.",gmic_inds,value,unsigned int,
1082 operator^=,"Compute bitwise XOR with image%s",
1083 "Compute bitwise XOR between image%s and image [%d]",gmic_inds,ind[0],
1084 "Compute bitwise XOR between image%s together");
1086 // Other arithmetic operators.
1087 gmic_simple_item("-cos",cos,"Compute cosine of image%s.");
1088 gmic_simple_item("-sin",sin,"Compute sine of image%s.");
1089 gmic_simple_item("-tan",tan,"Compute tangent of image%s.");
1090 gmic_simple_item("-acos",acos,"Compute arccosine of image%s.");
1091 gmic_simple_item("-asin",asin,"Compute arcsine of image%s.");
1092 gmic_simple_item("-atan",atan,"Compute arctangent of image%s.");
1093 gmic_simple_item("-abs",abs,"Compute absolute value of image%s.");
1094 gmic_simple_item("-sqr",sqr,"Compute square function of image%s.");
1095 gmic_simple_item("-sqrt",sqrt,"Compute square root of image%s.");
1096 gmic_simple_item("-exp",exp,"Compute exponential of image%s.");
1097 gmic_simple_item("-log",log,"Compute logarithm of image%s.");
1098 gmic_simple_item("-log10",log10,"Compute logarithm_10 of image%s.");
1100 //---------------------------------------
1101 // Pointwise operations on pixel values
1102 //---------------------------------------
1104 // Type cast.
1105 if (!cimg::strcmp("-type",item) || !cimg::strcmp("-t",item)) {
1106 typedef unsigned char uchar;
1107 typedef unsigned short ushort;
1108 typedef unsigned int uint;
1109 #ifndef gmic_minimal
1110 gmic_cast(bool,"bool");
1111 gmic_cast(uchar,"unsigned char");
1112 gmic_cast(char,"char");
1113 gmic_cast(ushort,"unsigned short");
1114 gmic_cast(short,"short");
1115 gmic_cast(uint,"unsigned int");
1116 gmic_cast(int,"int");
1117 gmic_cast(double,"double");
1118 #endif
1119 gmic_cast(float,"float");
1120 error("Set pixel type : Invalid argument '%s' "
1121 "(should be '{bool,uchar,char,ushort,short,uint,int,float,double}').",
1122 argument_text);
1123 }
1125 // Set scalar value.
1126 if (!cimg::strcmp("-set",item0) || !cimg::strcmp("-=",item0)) {
1127 double value = 0; float x = 0, y = 0, z = 0, v = 0; char sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0;
1128 char argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
1129 if ((std::sscanf(argument,"%lf%c",&value,&end)==1 ||
1130 std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%c",&value,argx,&end)==2 ||
1131 std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&value,argx,argy,&end)==3 ||
1132 std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&value,argx,argy,argz,&end)==4 ||
1133 std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&value,argx,argy,argz,argv,&end)==5) &&
1134 (!*argx ||
1135 (std::sscanf(argx,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
1136 std::sscanf(argx,"%f%c",&x,&end)==1) &&
1137 (!*argy ||
1138 (std::sscanf(argy,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
1139 std::sscanf(argy,"%f%c",&y,&end)==1) &&
1140 (!*argz ||
1141 (std::sscanf(argz,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
1142 std::sscanf(argz,"%f%c",&z,&end)==1) &&
1143 (!*argv ||
1144 (std::sscanf(argv,"%f%c%c",&v,&sepv,&end)==2 && sepv=='%') ||
1145 std::sscanf(argv,"%f%c",&v,&end)==1)) {
1146 print("Set scalar value %g at position (%g%s,%g%s,%g%s,%g%s) in image%s",
1147 value,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",v,sepv=='%'?"%":"",gmic_inds);
1148 cimg_foroff(indices,l) {
1149 CImg<T> &img = images[indices[l]];
1150 const int
1151 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
1152 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
1153 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1),
1154 nv = (int)cimg::round(sepv=='%'?v*(img.dimv()-1)/100:v,1);
1155 gmic_apply(images[indices[l]],gmic_set(value,nx,ny,nz,nv));
1156 }
1157 } else error("Set scalar value in image%s : Invalid argument '%s' "
1158 "(should be 'value,x[,y[,z[,v]]]').",
1159 argument_text);
1160 ++position; continue;
1161 }
1163 // Invert endianness.
1164 gmic_simple_item("-endian",invert_endianness,"Invert endianness of image%s.");
1166 // Fill.
1167 if (!cimg::strcmp("-fill",item0) || !cimg::strcmp("-f",item0)) {
1168 char sep = 0, end = 0; int ind0 = no_ind;
1169 if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
1170 gmic_check_indice(ind0,"Fill image%s");
1171 print("Fill image%s with values of image [%d].",gmic_inds,ind0);
1172 const CImg<T> values = images[ind0];
1173 cimg_foroff(indices,l) gmic_apply(images[indices[l]],fill(values));
1174 } else {
1175 print("Fill image%s with values '%s'.",gmic_inds,argument_text);
1176 cimg_foroff(indices,l) gmic_apply(images[indices[l]],fill(argument,true));
1177 }
1178 ++position; continue;
1179 }
1181 // Threshold.
1182 if (!cimg::strcmp("-threshold",item0)) {
1183 char sep = 0, end = 0; int soft = 0; double value = 0;
1184 if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
1185 (std::sscanf(argument,"%lf%c%c",&value,&sep,&end)==2 && sep=='%') ||
1186 std::sscanf(argument,"%lf%*c%d%c",&value,&soft,&end)==2 ||
1187 (std::sscanf(argument,"%lf%c%*c%d%c",&value,&sep,&soft,&end)==3 && sep=='%')) {
1188 print("Threshold image%s with %g%s (%s threshold).",gmic_inds,value,sep=='%'?"%":"",soft?"soft":"hard");
1189 cimg_foroff(indices,l) {
1190 CImg<T> &img = images[indices[l]];
1191 double vmin = 0, vmax = 0, nvalue = value;
1192 if (sep=='%') { vmin = img.minmax(vmax); nvalue = vmin + (vmax - vmin)*value/100; }
1193 gmic_apply(img,threshold((T)nvalue,soft?true:false));
1194 }
1195 ++position;
1196 } else {
1197 print("Threshold image%s : Interactive mode.",gmic_inds);
1198 CImgDisplay disp;
1199 char title[4096] = { 0 };
1200 cimg_foroff(indices,l) {
1201 CImg<T>
1202 &img = images[indices[l]],
1203 visu = img.depth>1?img.get_projections2d(img.dimx()/2,img.dimy()/2,img.dimz()/2).
1204 channels(0,cimg::min(3,img.dimv())-1):img.get_channels(0,cimg::min(3,img.dimv()-1));
1205 if (disp) disp.resize(cimg_fitscreen(visu.dimx(),visu.dimy(),1),false);
1206 else disp.assign(cimg_fitscreen(visu.dimx(),visu.dimy(),1),0,1);
1207 double
1208 vmin = 0, vmax = (double)img.maxmin(vmin),
1209 distmax = std::sqrt(cimg::sqr(disp.dimx()-1.0) + cimg::sqr(disp.dimy()-1.0)),
1210 amount = 50;
1211 bool stopflag = false, obutt = false;
1212 int omx = -1, omy = -1;
1213 CImg<T> res;
1214 for (disp.show().button = disp.key = 0; !stopflag; ) {
1215 const unsigned int key = disp.key;
1216 if (!res) {
1217 std::sprintf(title,"%s : Interactive threshold %.3g%%",filenames[indices[l]].ptr(),amount);
1218 disp.display(res=visu.get_threshold((T)(vmin + amount*(vmax-vmin)/100))).
1219 set_title(title).wait();
1220 }
1221 const int mx = disp.mouse_x, my = disp.mouse_y;
1222 if (disp.button && mx>=0 && my>=0) {
1223 if (omx==mx && omy==my && !obutt) break;
1224 omx = mx; omy = my; obutt = true;
1225 const double dist = std::sqrt((double)cimg::sqr(mx) + cimg::sqr(my));
1226 amount = dist*100/distmax;
1227 res.assign();
1228 } else if (!disp.button) obutt = false;
1229 if (disp.is_closed || (key && key!=cimg::keyCTRLLEFT)) stopflag = true;
1230 if (key==cimg::keyD && disp.is_keyCTRLLEFT &&
1231 (disp.resize(cimg_fitscreen(3*disp.width/2,3*disp.height/2,1),stopflag=false).key=0)==0)
1232 disp.is_resized = true;
1233 if (key==cimg::keyC && disp.is_keyCTRLLEFT &&
1234 (disp.resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),stopflag=false).key=0)==0)
1235 disp.is_resized = true;
1236 if (disp.is_resized) {
1237 disp.resize(false).display(res);
1238 distmax = std::sqrt(cimg::sqr(disp.dimx()-1.0) + cimg::sqr(disp.dimy()-1.0));
1239 }
1240 }
1241 gmic_apply(img,threshold((T)(vmin + amount*(vmax-vmin)/100)));
1242 }
1243 }
1244 continue;
1245 }
1247 // Cut.
1248 if (!cimg::strcmp("-cut",item0)) {
1249 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
1250 double value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
1251 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
1252 ((std::sscanf(arg0,"[%d%c%c",&ind0,&sep0,&end)==2 && sep0==']') ||
1253 (std::sscanf(arg0,"%lf%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1254 std::sscanf(arg0,"%lf%c",&value0,&end)==1) &&
1255 ((std::sscanf(arg1,"[%d%c%c",&ind1,&sep1,&end)==2 && sep1==']') ||
1256 (std::sscanf(arg1,"%lf%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
1257 std::sscanf(arg1,"%lf%c",&value1,&end)==1)) {
1258 if (ind0!=no_ind) { gmic_check_indice(ind0,"Cut image%s"); value0 = images[ind0].min(); sep0 = 0; }
1259 if (ind1!=no_ind) { gmic_check_indice(ind1,"Cut image%s"); value1 = images[ind1].max(); sep1 = 0; }
1260 print("Cut image%s in [%g%s,%g%s].",gmic_inds,value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"");
1261 cimg_foroff(indices,l) {
1262 CImg<T> &img = images[indices[l]];
1263 double vmin = 0, vmax = 0, nvalue0 = value0, nvalue1 = value1;
1264 if (sep0=='%') { vmin = img.minmax(vmax); nvalue0 = vmin + (vmax - vmin)*value0/100; }
1265 if (sep1=='%') { vmin = img.minmax(vmax); nvalue1 = vmin + (vmax - vmin)*value1/100; }
1266 gmic_apply(img,cut((T)nvalue0,(T)nvalue1));
1267 }
1268 ++position;
1269 } else if (std::sscanf(argument,"[%d%c%c",&(ind0=no_ind),&sep0,&end)==2) {
1270 if (ind0!=no_ind) gmic_check_indice(ind0,"Cut image%s");
1271 value0 = images[ind0].minmax(value1);
1272 print("Cut image%s in [%g,%g].",gmic_inds,value0,value1);
1273 cimg_foroff(indices,l) gmic_apply(images[indices[l]],cut((T)value0,(T)value1));
1274 ++position;
1275 } else {
1276 print("Cut image%s : Interactive mode.",gmic_inds);
1277 CImgDisplay disp;
1278 char title[4096] = { 0 };
1279 cimg_foroff(indices,l) {
1280 CImg<T>
1281 &img = images[indices[l]],
1282 visu = img.depth>1?img.get_projections2d(img.dimx()/2,img.dimy()/2,img.dimz()/2).
1283 channels(0,cimg::min(3,img.dimv())-1):img.get_channels(0,cimg::min(3,img.dimv()-1));
1284 if (disp) disp.resize(cimg_fitscreen(visu.dimx(),visu.dimy(),1),false);
1285 else disp.assign(cimg_fitscreen(visu.dimx(),visu.dimy(),1),0,1);
1286 double vmin = 0, vmax = (double)img.maxmin(vmin), amount0 = 0, amount1 = 100;
1287 bool stopflag = false, obutt = false;
1288 int omx = -1, omy = -1;
1289 CImg<T> res;
1290 for (disp.show().button = disp.key = 0; !stopflag; ) {
1291 const unsigned int key = disp.key;
1292 if (!res) {
1293 std::sprintf(title,"%s : Interactive cut [%.3g%%,%.3g%%]",
1294 filenames[indices[l]].ptr(),amount0,amount1);
1295 disp.display(res = visu.get_cut((T)(vmin + amount0*(vmax-vmin)/100),
1296 (T)(vmin + amount1*(vmax-vmin)/100))).
1297 set_title(title).wait();
1298 }
1299 const int mx = disp.mouse_x, my = disp.mouse_y;
1300 if (disp.button && mx>=0 && my>=0) {
1301 if (omx==mx && omy==my && !obutt) break;
1302 omx = mx; omy = my; obutt = true;
1303 amount0 = mx*100/disp.dimx(); amount1 = my*100/disp.dimy();
1304 res.assign();
1305 } else if (!disp.button) obutt = false;
1306 if (disp.is_closed || (key && key!=cimg::keyCTRLLEFT)) stopflag = true;
1307 if (key==cimg::keyD && disp.is_keyCTRLLEFT &&
1308 (disp.resize(cimg_fitscreen(3*disp.width/2,3*disp.height/2,1),stopflag=false).key=0)==0)
1309 disp.is_resized = true;
1310 if (key==cimg::keyC && disp.is_keyCTRLLEFT &&
1311 (disp.resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),stopflag=false).key=0)==0)
1312 disp.is_resized = true;
1313 if (disp.is_resized) disp.resize(false).display(res);
1314 }
1315 gmic_apply(img,cut((T)(vmin + amount0*(vmax-vmin)/100),(T)(vmin + amount1*(vmax-vmin)/100)));
1316 }
1317 }
1318 continue;
1319 }
1321 // Normalize.
1322 if (!cimg::strcmp("-normalize",item0) || !cimg::strcmp("-n",item0)) {
1323 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
1324 double value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
1325 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
1326 ((std::sscanf(arg0,"[%d%c%c",&ind0,&sep0,&end)==2 && sep0==']') ||
1327 (std::sscanf(arg0,"%lf%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1328 std::sscanf(arg0,"%lf%c",&value0,&end)==1) &&
1329 ((std::sscanf(arg1,"[%d%c%c",&ind1,&sep1,&end)==2 && sep1==']') ||
1330 (std::sscanf(arg1,"%lf%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
1331 std::sscanf(arg1,"%lf%c",&value1,&end)==1)) {
1332 if (ind0!=no_ind) { gmic_check_indice(ind0,"Normalize image%s"); value0 = images[ind0].min(); sep0 = 0; }
1333 if (ind1!=no_ind) { gmic_check_indice(ind1,"Normalize image%s"); value1 = images[ind1].max(); sep1 = 0; }
1334 print("Normalize image%s in [%g%s,%g%s].",gmic_inds,value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"");
1335 cimg_foroff(indices,l) {
1336 CImg<T> &img = images[indices[l]];
1337 double vmin = 0, vmax = 0, nvalue0 = value0, nvalue1 = value1;
1338 if (sep0=='%') { vmin = img.minmax(vmax); nvalue0 = vmin + (vmax - vmin)*value0/100; }
1339 if (sep1=='%') { vmin = img.minmax(vmax); nvalue1 = vmin + (vmax - vmin)*value1/100; }
1340 gmic_apply(img,normalize((T)nvalue0,(T)nvalue1));
1341 }
1342 } else if (std::sscanf(argument,"[%d%c%c",&(ind0=no_ind),&sep0,&end)==2) {
1343 if (ind0!=no_ind) gmic_check_indice(ind0,"Normalize image%s");
1344 value0 = images[ind0].minmax(value1);
1345 print("Normalize image%s in [%g,%g].",gmic_inds,value0,value1);
1346 cimg_foroff(indices,l) gmic_apply(images[indices[l]],normalize((T)value0,(T)value1));
1347 } else error("Normalize image%s : Invalid argument '%s' "
1348 "(should be '{value1[%%],[indice]},{value2[%%],[indice]}').",gmic_inds,argument_text);
1349 ++position; continue;
1350 }
1352 // Round.
1353 if (!cimg::strcmp("-round",item0)) {
1354 char end = 0; double value = 0; int rtype = 0;
1355 if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
1356 std::sscanf(argument,"%lf%*c%d%c",&value,&rtype,&end)==2) {
1357 print("Round image%s with value %g (%s rounding).",
1358 gmic_inds,value,rtype<0?"backward":rtype>0?"forward":"nearest");
1359 cimg_foroff(indices,l) gmic_apply(images[indices[l]],round((float)value,rtype));
1360 } else error("Round image%s : Invalid argument '%s' "
1361 "(should be 'round_value[,round_type]').",gmic_inds,argument_text);
1362 ++position; continue;
1363 }
1365 // Equalize histogram.
1366 if (!cimg::strcmp("-equalize",item0)) {
1367 float nb = 256; char sep = 0, end = 0;
1368 if (std::sscanf(argument,"%f%c",&nb,&end)==1 ||
1369 (std::sscanf(argument,"%f%c%c",&nb,&sep,&end)==2 && sep=='%')) {
1370 if (nb<=0) error("Equalize image%s : Invalid cluster number %g.",gmic_inds,nb);
1371 print("Equalize image%s with %g%s clusters.",gmic_inds,nb,sep=='%'?"%":"");
1372 cimg_foroff(indices,l) {
1373 CImg<T>& img = images[indices[l]];
1374 unsigned int N = (unsigned int)nb;
1375 if (sep=='%') { double m, M = img.maxmin(m); N = (unsigned int)cimg::round((M-m)*nb/100,1); }
1376 gmic_apply(img,equalize((int)nb));
1377 }
1378 } else error("Equalize image%s : Invalid argument '%s' "
1379 "(should be 'nb_clusters[%%]').",gmic_inds,argument_text);
1380 ++position; continue;
1381 }
1383 // Quantize.
1384 if (!cimg::strcmp("-quantize",item0)) {
1385 int nb = 0; char end = 0;
1386 if (std::sscanf(argument,"%d%c",&nb,&end)==1) {
1387 print("Quantize image%s with %d levels.",gmic_inds,nb);
1388 cimg_foroff(indices,l) gmic_apply(images[indices[l]],quantize(nb));
1389 } else error("Quantize image%s : Invalid argument '%s' "
1390 "(should be 'nb_levels').",gmic_inds,argument_text);
1391 ++position; continue;
1392 }
1394 // Add noise.
1395 if (!cimg::strcmp("-noise",item0)) {
1396 float sigma = 0; char sep = 0, end = 0; int ntype = 0;
1397 if (std::sscanf(argument,"%f%c",&sigma,&end)==1 ||
1398 (std::sscanf(argument,"%f%c%c",&sigma,&sep,&end)==2 && sep=='%') ||
1399 std::sscanf(argument,"%f%*c%d%c",&sigma,&ntype,&end)==2 ||
1400 (std::sscanf(argument,"%f%c%*c%d%c",&sigma,&sep,&ntype,&end)==3 && sep=='%')) {
1401 const char *st_type = ntype==0?"gaussian":ntype==1?"uniform":ntype==2?"salt&pepper":"poisson";
1402 if (sep=='%') sigma = -sigma;
1403 print("Add %s noise with standard deviation %g%s to image%s.",
1404 st_type,cimg::abs(sigma),sigma<0?"%":"",gmic_inds);
1405 cimg_foroff(indices,l) gmic_apply(images[indices[l]],noise(sigma,ntype));
1406 } else error("Add noise to image%s : Invalid argument '%s' "
1407 "(should be 'std[%%][,noise_type]').",gmic_inds,argument_text);
1408 ++position; continue;
1409 }
1411 // Rand.
1412 if (!cimg::strcmp("-rand",item0)) {
1413 double value0 = 0, value1 = 0; char end = 0;
1414 if (std::sscanf(argument,"%lf%*c%lf%c",&value0,&value1,&end)==2) {
1415 print("Fill image%s with random values in [%g,%g].",gmic_inds,value0,value1);
1416 cimg_foroff(indices,l) gmic_apply(images[indices[l]],rand((T)value0,(T)value1));
1417 } else error("Fill image%s with random values : Invalid argument '%s' "
1418 "(should be 'valmin,valmax').",gmic_inds,argument_text);
1419 ++position; continue;
1420 }
1422 // Compute pointwise norms and orientations.
1423 gmic_simple_item("-norm",pointwise_norm,"Compute vector norm.");
1424 gmic_simple_item("-orientation",pointwise_orientation,"Compute vector orientation.");
1426 //------------------------
1427 // Color base conversions
1428 //------------------------
1429 gmic_simple_item("-rgb2hsv",RGBtoHSV,"Convert image%s from RGB to HSV colorbases.");
1430 gmic_simple_item("-rgb2hsl",RGBtoHSL,"Convert image%s from RGB to HSL colorbases.");
1431 gmic_simple_item("-rgb2hsi",RGBtoHSI,"Convert image%s from RGB to HSI colorbases.");
1432 gmic_simple_item("-rgb2yuv",RGBtoYUV,"Convert image%s from RGB to YUV colorbases.");
1433 gmic_simple_item("-rgb2ycbcr",RGBtoYCbCr,"Convert image%s from RGB to YCbCr colorbases.");
1434 gmic_simple_item("-rgb2xyz",RGBtoXYZ,"Convert image%s from RGB to XYZ colorbases.");
1435 gmic_simple_item("-rgb2lab",RGBtoLab,"Convert image%s from RGB to Lab colorbases.");
1436 gmic_simple_item("-rgb2cmy",RGBtoCMY,"Convert image%s from RGB to CMY colorbases.");
1437 gmic_simple_item("-rgb2cmyk",RGBtoCMYK,"Convert image%s from RGB to CMYK colorbases.");
1438 gmic_simple_item("-cmyk2rgb",CMYKtoRGB,"Convert image%s from CMYK to RGB colorbases.");
1439 gmic_simple_item("-cmy2rgb",CMYtoRGB,"Convert image%s from CMY to RGB colorbases.");
1440 gmic_simple_item("-lab2rgb",LabtoRGB,"Convert image%s from Lab to RGB colorbases.");
1441 gmic_simple_item("-xyz2rgb",XYZtoRGB,"Convert image%s from XYZ to RGB colorbases.");
1442 gmic_simple_item("-ycbcr2rgb",YCbCrtoRGB,"Convert image%s from YCbCr to RGB colorbases.");
1443 gmic_simple_item("-yuv2rgb",YUVtoRGB,"Convert image%s from YUV to RGB colorbases.");
1444 gmic_simple_item("-hsi2rgb",HSItoRGB,"Convert image%s from HSI to RGB colorbases.");
1445 gmic_simple_item("-hsl2rgb",HSLtoRGB,"Convert image%s from HSL to RGB colorbases.");
1446 gmic_simple_item("-hsv2rgb",HSVtoRGB,"Convert image%s from HSV to RGB colorbases.");
1448 // Apply LUT.
1449 if (!cimg::strcmp("-lut2rgb",item0)) {
1450 int nb = 0, ind0 = 0; char sep = 0, end = 0;
1451 if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
1452 gmic_check_indice(ind0,"Map LUT on image%s");
1453 print("Map LUT [%d] on image%s.",ind0,gmic_inds);
1454 const CImg<T> palette = images[ind0];
1455 cimg_foroff(indices,l) gmic_apply(images[indices[l]],pointwise_norm().LUTtoRGB(palette));
1456 } else if (std::sscanf(argument,"%d%c",&nb,&end)==1) {
1457 if (nb<0 || nb>2) error("Map LUT on image%s : Invalid LUT number %d.",gmic_inds,nb);
1458 print("Map %s LUT on image%s.",nb==0?"default":nb==1?"rainbow":"cluster",gmic_inds);
1459 cimg_foroff(indices,l) gmic_apply(images[indices[l]],pointwise_norm().
1460 LUTtoRGB(nb==0?CImg<T>::default_LUT8():nb==1?CImg<T>::rainbow_LUT8():CImg<T>::contrast_LUT8()));
1461 } else error("Map LUT on image%s : Invalid argument '%s' "
1462 "(should be '[indice]' or '{0,1,2}').",gmic_inds,argument_text);
1463 ++position; continue;
1464 }
1466 // Convert to LUT.
1467 if (!cimg::strcmp("-rgb2lut",item0)) {
1468 int nb = 0, ind0 = 0, dithering = 0; char sep = 0, end = 0;
1469 if (std::sscanf(argument,"[%d%c%*c%d",&ind0,&sep,&dithering)>=2 && sep==']') {
1470 gmic_check_indice(ind0,"Index image%s with LUT");
1471 print("Index image%s with LUT [%d] %s dithering.",gmic_inds,ind0,dithering?"with":"without");
1472 const CImg<T> palette = images[ind0];
1473 cimg_foroff(indices,l) gmic_apply(images[indices[l]],RGBtoLUT(palette,dithering,true));
1474 } else if (std::sscanf(argument,"%d%*c%d%c",&nb,&dithering,&end)==2 ||
1475 std::sscanf(argument,"%d%c",&nb,&end)==1) {
1476 if (nb<0 || nb>2) error("Index image%s with LUT : Invalid LUT number %d.",gmic_inds,nb);
1477 print("Index image%s with %s LUT.",gmic_inds,nb==0?"default":nb==1?"rainbow":"cluster");
1478 cimg_foroff(indices,l) gmic_apply(images[indices[l]],
1479 RGBtoLUT(nb==0?CImg<T>::default_LUT8():nb==1?CImg<T>::rainbow_LUT8():CImg<T>::contrast_LUT8(),
1480 dithering,true));
1481 } else error("Index image%s with LUT : Invalid argument '%s' "
1482 "(should be '[indice][,dithering]', or '{0,1,2}[,dithering]').",gmic_inds,argument_text);
1483 ++position; continue;
1484 }
1486 //-----------------------
1487 // Geometric manipulation
1488 //-----------------------
1490 // Resize.
1491 if (!cimg::strcmp("-resize",item0) || !cimg::strcmp("-r",item0)) {
1492 char
1493 sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0,
1494 argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
1495 float valx = 0, valy = 0, valz = 0, valv = 0;
1496 int interpolation = 1, borders = -1, center = 0, indx = no_ind, indy = no_ind, indz = no_ind, indv = no_ind;
1497 if ((std::sscanf(argument,"[%d%c%c",&indx,&sepx,&end)==2 ||
1498 std::sscanf(argument,"[%d%c%*c%d%c",&indx,&sepx,&interpolation,&end)==3 ||
1499 std::sscanf(argument,"[%d%c%*c%d%*c%d%c",&indx,&sepx,&interpolation,&borders,&end)==4 ||
1500 std::sscanf(argument,"[%d%c%*c%d%*c%d%*c%d%c",&indx,&sepx,&interpolation,&borders,¢er,&end)==5)
1501 && sepx==']') {
1502 gmic_check_indice(indx,"Resize image%s");
1503 const int
1504 ivalx = images[indx].dimx(),
1505 ivaly = images[indx].dimy(),
1506 ivalz = images[indx].dimz(),
1507 ivalv = images[indx].dimv();
1508 print("Resize image%s to %dx%dx%dx%d with %s interpolation.",
1509 gmic_inds,ivalx,ivaly,ivalz,ivalv,
1510 interpolation==0?"no":interpolation==1?"nearest neighbor":
1511 interpolation==2?"moving average":interpolation==3?"linear":
1512 interpolation==4?"grid":"cubic");
1513 cimg_foroff(indices,l) gmic_apply(images[indices[l]],resize(ivalx,ivaly,ivalz,ivalv,interpolation,borders,center?true:false));
1514 ++position;
1515 } else if (((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%d%*c%d%*c%d%c",
1516 argx,argy,argz,argv,&(interpolation=1),&(borders=-1),&(center=0),&end)==7 ||
1517 std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%d%*c%d%c",
1518 argx,argy,argz,argv,&interpolation,&borders,&end)==6 ||
1519 std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%d%c",
1520 argx,argy,argz,argv,&interpolation,&end)==5 ||
1521 std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
1522 argx,argy,argz,argv,&end)==4) &&
1523 ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
1524 (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
1525 std::sscanf(argx,"%f%c",&valx,&end)==1) &&
1526 ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
1527 (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
1528 std::sscanf(argy,"%f%c",&valy,&end)==1) &&
1529 ((std::sscanf(argz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']') ||
1530 (std::sscanf(argz,"%f%c%c",&valz,&sepz,&end)==2 && sepz=='%') ||
1531 std::sscanf(argz,"%f%c",&valz,&end)==1) &&
1532 ((std::sscanf(argv,"[%d%c%c",&indv,&sepv,&end)==2 && sepv==']') ||
1533 (std::sscanf(argv,"%f%c%c",&valv,&sepv,&end)==2 && sepv=='%') ||
1534 std::sscanf(argv,"%f%c",&valv,&end)==1)) ||
1535 ((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
1536 argx,argy,argz,&end)==3) &&
1537 ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
1538 (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
1539 std::sscanf(argx,"%f%c",&valx,&end)==1) &&
1540 ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
1541 (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
1542 std::sscanf(argy,"%f%c",&valy,&end)==1) &&
1543 ((std::sscanf(argz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']') ||
1544 (std::sscanf(argz,"%f%c%c",&valz,&sepz,&end)==2 && sepz=='%') ||
1545 std::sscanf(argz,"%f%c",&valz,&end)==1)) ||
1546 ((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
1547 argx,argy,&end)==2) &&
1548 ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
1549 (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
1550 std::sscanf(argx,"%f%c",&valx,&end)==1) &&
1551 ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
1552 (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
1553 std::sscanf(argy,"%f%c",&valy,&end)==1)) ||
1554 ((std::sscanf(argument,"%4095[][0-9.eE%+-]%c",
1555 argx,&end)==1) &&
1556 ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
1557 (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
1558 std::sscanf(argx,"%f%c",&valx,&end)==1))) {
1559 if (indx!=no_ind) { gmic_check_indice(indx,"Resize image%s"); valx = (float)images[indx].dimx(); sepx = 0; }
1560 if (indy!=no_ind) { gmic_check_indice(indy,"Resize image%s"); valy = (float)images[indy].dimy(); sepy = 0; }
1561 if (indz!=no_ind) { gmic_check_indice(indz,"Resize image%s"); valz = (float)images[indz].dimz(); sepz = 0; }
1562 if (indv!=no_ind) { gmic_check_indice(indv,"Resize image%s"); valv = (float)images[indv].dimv(); sepv = 0; }
1563 if (!valx) { valx = 100; sepx = '%'; }
1564 if (!valy) { valy = 100; sepy = '%'; }
1565 if (!valz) { valz = 100; sepz = '%'; }
1566 if (!valv) { valv = 100; sepv = '%'; }
1567 print("Resize image%s to %g%s%g%s%g%s%g%s with %s interpolation.",
1568 gmic_inds,valx,sepx=='%'?"%x":"x",valy,sepy=='%'?"%x":"x",valz,
1569 sepz=='%'?"%x":"x",valv,sepv=='%'?"% ":" ",
1570 interpolation==0?"no":interpolation==1?"nearest neighbor":
1571 interpolation==2?"moving average":interpolation==3?"linear":
1572 interpolation==4?"grid":"cubic");
1574 cimg_foroff(indices,l) {
1575 CImg<T>& img = images[indices[l]];
1576 const int
1577 ivalx0 = (int)cimg::round(sepx=='%'?valx*img.dimx()/100:valx,1),
1578 ivaly0 = (int)cimg::round(sepy=='%'?valy*img.dimy()/100:valy,1),
1579 ivalz0 = (int)cimg::round(sepz=='%'?valz*img.dimz()/100:valz,1),
1580 ivalv0 = (int)cimg::round(sepv=='%'?valv*img.dimv()/100:valv,1),
1581 ivalx = ivalx0?ivalx0:1,
1582 ivaly = ivaly0?ivaly0:1,
1583 ivalz = ivalz0?ivalz0:1,
1584 ivalv = ivalv0?ivalv0:1;
1585 gmic_apply(img,resize(ivalx,ivaly,ivalz,ivalv,interpolation,borders,center?true:false));
1586 }
1587 ++position;
1588 } else {
1589 print("Resize image%s : Interactive mode.",gmic_inds);
1590 char title[4096] = { 0 };
1591 cimg_foroff(indices,l) {
1592 CImg<T>& img = images[indices[l]];
1593 CImgDisplay disp(img,0,1);
1594 std::sprintf(title,"%s : Interactive resize",filenames[indices[l]].ptr());
1595 disp.set_title(title);
1596 img.get_select(0,disp);
1597 print("Resize image [%d] to %dx%d.",indices[l],disp.dimx(),disp.dimy());
1598 gmic_apply(img,resize(disp));
1599 }
1600 }
1601 continue;
1602 }
1604 // Resize2x. and Resize3x.
1605 gmic_simple_item("-resize2x",resize_doubleXY,"Resize image%s using Scale2x algorithm.");
1606 gmic_simple_item("-resize3x",resize_doubleXY,"Resize image%s using Scale3x algorithm.");
1608 // Crop.
1609 if (!cimg::strcmp("-crop",item0) || !cimg::strcmp("-c",item0)) {
1610 char st0[4096] = { 0 }, st1[4096] = { 0 }, st2[4096] = { 0 }, st3[4096] = { 0 };
1611 char st4[4096] = { 0 }, st5[4096] = { 0 }, st6[4096] = { 0 }, st7[4096] = { 0 };
1612 char sep0 = 0, sep1 = 0, sep2 = 0, sep3 = 0, sep4 = 0, sep5 = 0, sep6 = 0, sep7 = 0, end = 0;
1613 float a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0, a7 = 0; int borders = 0;
1614 if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
1615 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
1616 st0,st1,st2,st3,st4,st5,st6,st7,&borders,&end)==9 ||
1617 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
1618 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
1619 st0,st1,st2,st3,st4,st5,st6,st7,&end)==8) &&
1620 (std::sscanf(st0,"%f%c",&a0,&end)==1 || (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
1621 (std::sscanf(st1,"%f%c",&a1,&end)==1 || (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
1622 (std::sscanf(st2,"%f%c",&a2,&end)==1 || (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
1623 (std::sscanf(st3,"%f%c",&a3,&end)==1 || (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%')) &&
1624 (std::sscanf(st4,"%f%c",&a4,&end)==1 || (std::sscanf(st4,"%f%c%c",&a4,&sep4,&end)==2 && sep4=='%')) &&
1625 (std::sscanf(st5,"%f%c",&a5,&end)==1 || (std::sscanf(st5,"%f%c%c",&a5,&sep5,&end)==2 && sep5=='%')) &&
1626 (std::sscanf(st6,"%f%c",&a6,&end)==1 || (std::sscanf(st6,"%f%c%c",&a6,&sep6,&end)==2 && sep6=='%')) &&
1627 (std::sscanf(st7,"%f%c",&a7,&end)==1 || (std::sscanf(st7,"%f%c%c",&a7,&sep7,&end)==2 && sep7=='%'))) {
1628 print("Crop image%s with (%g%s%g%s%g%s%g%s x (%g%s%g%s%g%s%g%s.",gmic_inds,
1629 a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%,":",",
1630 a2,sep2=='%'?"%,":",",a3,sep3=='%'?"%)":")",
1631 a4,sep4=='%'?"%,":",",a5,sep5=='%'?"%,":",",
1632 a6,sep6=='%'?"%,":",",a7,sep7=='%'?"%)":")");
1633 cimg_foroff(indices,l) {
1634 CImg<T> &img = images[indices[l]];
1635 const int
1636 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
1637 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
1638 z0 = (int)cimg::round(sep2=='%'?a2*img.dimz()/100:a2,1),
1639 v0 = (int)cimg::round(sep3=='%'?a3*img.dimv()/100:a3,1),
1640 x1 = (int)cimg::round(sep4=='%'?a4*img.dimx()/100:a4,1),
1641 y1 = (int)cimg::round(sep5=='%'?a5*img.dimy()/100:a5,1),
1642 z1 = (int)cimg::round(sep6=='%'?a6*img.dimz()/100:a6,1),
1643 v1 = (int)cimg::round(sep7=='%'?a7*img.dimv()/100:a7,1);
1644 gmic_apply(img,crop(x0,y0,z0,v0,x1,y1,z1,v1,borders?true:false));
1645 }
1646 ++position;
1647 } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
1648 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
1649 st0,st1,st2,st3,st4,st5,&borders,&end)==7 ||
1650 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
1651 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
1652 st0,st1,st2,st3,st4,st5,&end)==6) &&
1653 (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
1654 (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
1655 (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
1656 (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
1657 (std::sscanf(st2,"%f%c",&a2,&end)==1 ||
1658 (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
1659 (std::sscanf(st3,"%f%c",&a3,&end)==1 ||
1660 (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%')) &&
1661 (std::sscanf(st4,"%f%c",&a4,&end)==1 ||
1662 (std::sscanf(st4,"%f%c%c",&a4,&sep4,&end)==2 && sep4=='%')) &&
1663 (std::sscanf(st5,"%f%c",&a5,&end)==1 ||
1664 (std::sscanf(st5,"%f%c%c",&a5,&sep5,&end)==2 && sep5=='%'))) {
1665 print("Crop image%s with (%g%s%g%s%g%s x (%g%s%g%s%g%s.",gmic_inds,
1666 a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%,":",",a2,sep2=='%'?"%)":")",
1667 a3,sep3=='%'?"%,":",",a4,sep4=='%'?"%,":",",a5,sep5=='%'?"%)":")");
1668 cimg_foroff(indices,l) {
1669 CImg<T> &img = images[indices[l]];
1670 const int
1671 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
1672 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
1673 z0 = (int)cimg::round(sep2=='%'?a2*img.dimz()/100:a2,1),
1674 x1 = (int)cimg::round(sep3=='%'?a3*img.dimx()/100:a3,1),
1675 y1 = (int)cimg::round(sep4=='%'?a4*img.dimy()/100:a4,1),
1676 z1 = (int)cimg::round(sep5=='%'?a5*img.dimz()/100:a5,1);
1677 gmic_apply(img,crop(x0,y0,z0,x1,y1,z1,borders?true:false));
1678 }
1679 ++position;
1680 } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
1681 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
1682 st0,st1,st2,st3,&borders,&end)==5 ||
1683 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
1684 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
1685 st0,st1,st2,st3,&end)==4) &&
1686 (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
1687 (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
1688 (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
1689 (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
1690 (std::sscanf(st2,"%f%c",&a2,&end)==1 ||
1691 (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
1692 (std::sscanf(st3,"%f%c",&a3,&end)==1 ||
1693 (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%'))) {
1694 print("Crop image%s with (%g%s%g%s x (%g%s%g%s.",gmic_inds,
1695 a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%)":")",
1696 a2,sep2=='%'?"%,":",",a3,sep3=='%'?"%)":")");
1697 cimg_foroff(indices,l) {
1698 CImg<T> &img = images[indices[l]];
1699 const int
1700 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
1701 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
1702 x1 = (int)cimg::round(sep2=='%'?a2*img.dimx()/100:a2,1),
1703 y1 = (int)cimg::round(sep3=='%'?a3*img.dimy()/100:a3,1);
1704 gmic_apply(img,crop(x0,y0,x1,y1,borders?true:false));
1705 }
1706 ++position;
1707 } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",st0,st1,&borders,&end)==3 ||
1708 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",st0,st1,&end)==2) &&
1709 (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
1710 (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
1711 (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
1712 (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%'))) {
1713 print("Crop image%s with (%g%s x (%g%s.",gmic_inds,
1714 a0,sep0=='%'?"%)":")",a1,sep1=='%'?"%)":")");
1715 cimg_foroff(indices,l) {
1716 CImg<T> &img = images[indices[l]];
1717 const int
1718 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
1719 x1 = (int)cimg::round(sep1=='%'?a1*img.dimx()/100:a1,1);
1720 gmic_apply(img,crop(x0,x1,borders?true:false));
1721 }
1722 ++position;
1723 } else {
1724 print("Crop image%s : Interactive mode.",gmic_inds);
1725 char title[4096] = { 0 };
1726 cimg_foroff(indices,l) {
1727 CImg<T>& img = images[indices[l]];
1728 CImgDisplay disp(cimg_fitscreen(img.dimx(),img.dimy(),1),0,1);
1729 std::sprintf(title,"%s : Interactive crop",filenames[indices[l]].ptr());
1730 disp.set_title(title);
1731 const CImg<int> s = img.get_select(disp,2);
1732 print("Crop image [%d] with (%d,%d,%d) x (%d,%d,%d).",
1733 indices[l],s[0],s[1],s[2],s[3],s[4],s[5]);
1734 gmic_apply(img,crop(s[0],s[1],s[2],s[3],s[4],s[5]));
1735 }
1736 }
1737 continue;
1738 }
1740 // Autocrop.
1741 if (!cimg::strcmp("-autocrop",item0)) {
1742 print("Autocrop image%s with color '%s'.",gmic_inds,argument_text);
1743 cimg_foroff(indices,l) {
1744 CImg<T>& img = images[indices[l]];
1745 const CImg<T> col = CImg<T>(img.dimv()).fill(argument,true);
1746 gmic_apply(img,autocrop(col));
1747 }
1748 ++position; continue;
1749 }
1751 // Select channels.
1752 if (!cimg::strcmp("-channels",item0)) {
1753 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
1754 float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
1755 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
1756 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
1757 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1758 std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
1759 ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
1760 (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
1761 std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
1762 if (ind0!=no_ind) { gmic_check_indice(ind0,"Keep channels of image%s"); value0 = images[ind0].dimv()-1.0f; sep0 = 0; }
1763 if (ind1!=no_ind) { gmic_check_indice(ind1,"Keep channels of image%s"); value1 = images[ind1].dimv()-1.0f; sep1 = 0; }
1764 print("Keep channels %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
1765 cimg_foroff(indices,l) {
1766 CImg<T> &img = images[indices[l]];
1767 const int
1768 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,1),
1769 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimv()-1)/100:value1,1);
1770 gmic_apply(img,channels(nvalue0,nvalue1));
1771 }
1772 } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
1773 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
1774 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1775 std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
1776 if (ind0!=no_ind) { gmic_check_indice(ind0,"Keep channel of image%s"); value0 = images[ind0].dimv()-1.0f; sep0 = 0; }
1777 print("Keep channel %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
1778 cimg_foroff(indices,l) {
1779 CImg<T> &img = images[indices[l]];
1780 const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,1);
1781 gmic_apply(img,channel(nvalue0));
1782 }
1783 } else error("Keep channels of image%s : Invalid argument '%s' "
1784 "(should be 'channel0[%%][,channel1[%%]]').",gmic_inds,argument_text);
1785 ++position; continue;
1786 }
1788 // Select slices.
1789 if (!cimg::strcmp("-slices",item0)) {
1790 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
1791 float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
1792 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
1793 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
1794 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1795 std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
1796 ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
1797 (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
1798 std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
1799 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select slices of image%s"); value0 = images[ind0].dimz()-1.0f; sep0 = 0; }
1800 if (ind1!=no_ind) { gmic_check_indice(ind1,"Select slices of image%s"); value1 = images[ind1].dimz()-1.0f; sep1 = 0; }
1801 print("Select slices %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
1802 cimg_foroff(indices,l) {
1803 CImg<T> &img = images[indices[l]];
1804 const int
1805 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,0),
1806 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimv()-1)/100:value1,0);
1807 gmic_apply(img,slices(nvalue0,nvalue1));
1808 }
1809 } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
1810 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
1811 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1812 std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
1813 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select slice of image%s"); value0 = images[ind0].dimz()-1.0f; sep0 = 0; }
1814 print("Select slice %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
1815 cimg_foroff(indices,l) {
1816 CImg<T> &img = images[indices[l]];
1817 const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimz()-1)/100:value0,1);
1818 gmic_apply(img,slice(nvalue0));
1819 }
1820 } else error("Select slices of image%s : Invalid argument '%s' "
1821 "(should be 'slice0[%%][,slice1[%%]]').",gmic_inds,argument_text);
1822 ++position; continue;
1823 }
1825 // Select lines.
1826 if (!cimg::strcmp("-lines",item0) || !cimg::strcmp("-l",item0)) {
1827 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
1828 float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
1829 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
1830 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
1831 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1832 std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
1833 ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
1834 (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
1835 std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
1836 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select lines of image%s"); value0 = images[ind0].dimy()-1.0f; sep0 = 0; }
1837 if (ind1!=no_ind) { gmic_check_indice(ind1,"Select lines of image%s"); value1 = images[ind1].dimy()-1.0f; sep1 = 0; }
1838 print("Select lines %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
1839 cimg_foroff(indices,l) {
1840 CImg<T> &img = images[indices[l]];
1841 const int
1842 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimy()-1)/100:value0,1),
1843 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimy()-1)/100:value1,1);
1844 gmic_apply(img,lines(nvalue0,nvalue1));
1845 }
1846 } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
1847 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
1848 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1849 std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
1850 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select lines of image%s"); value0 = images[ind0].dimy()-1.0f; sep0 = 0; }
1851 print("Select lines %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
1852 cimg_foroff(indices,l) {
1853 CImg<T> &img = images[indices[l]];
1854 const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimy()-1)/100:value0,1);
1855 gmic_apply(img,line(nvalue0));
1856 }
1857 } else error("Select lines of image%s : Invalid argument '%s' "
1858 "(should be 'line0[%%][,line1[%%]]').",gmic_inds,argument_text);
1859 ++position; continue;
1860 }
1862 // Columns.
1863 if (!cimg::strcmp("-columns",item0)) {
1864 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
1865 float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
1866 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
1867 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
1868 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1869 std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
1870 ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
1871 (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
1872 std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
1873 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select columns of image%s"); value0 = images[ind0].dimx()-1.0f; sep0 = 0; }
1874 if (ind1!=no_ind) { gmic_check_indice(ind1,"Select columns of image%s"); value1 = images[ind1].dimx()-1.0f; sep1 = 0; }
1875 print("Select columns %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
1876 cimg_foroff(indices,l) {
1877 CImg<T> &img = images[indices[l]];
1878 const int
1879 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimx()-1)/100:value0,1),
1880 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimx()-1)/100:value1,1);
1881 gmic_apply(img,lines(nvalue0,nvalue1));
1882 }
1883 } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
1884 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
1885 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
1886 std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
1887 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select columns of image%s"); value0 = images[ind0].dimx()-1.0f; sep0 = 0; }
1888 print("Select columns %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
1889 cimg_foroff(indices,l) {
1890 CImg<T> &img = images[indices[l]];
1891 const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimx()-1)/100:value0,1);
1892 gmic_apply(img,line(nvalue0));
1893 }
1894 } else error("Select columns of image%s : Invalid argument '%s' "
1895 "(should be 'column0[%%][,column1[%%]]').",gmic_inds,argument_text);
1896 ++position; continue;
1897 }
1899 // Rotate.
1900 if (!cimg::strcmp("-rotate",item0)) {
1901 float angle = 0; int borders = 0, interpolation = 1; char end = 0;
1902 if (std::sscanf(argument,"%f%c",&angle,&end)==1 ||
1903 std::sscanf(argument,"%f%*c%d%c",&angle,&borders,&end)==2 ||
1904 std::sscanf(argument,"%f%*c%d%*c%d%c",&angle,&borders,&interpolation,&end)==3) {
1905 print("Rotate image%s with an angle of %g deg and %s interpolation.",
1906 gmic_inds,angle,interpolation?"linear":"nearest-neighbor");
1907 if (borders>=0) { cimg_foroff(indices,l) gmic_apply(images[indices[l]],rotate(angle,borders,interpolation)); }
1908 else cimg_foroff(indices,l) {
1909 CImg<T> &img = images[indices[l]];
1910 gmic_apply(img,rotate(angle,img.dimx()/2.0f,img.dimy()/2.0f,1,-1-borders,interpolation));
1911 }
1912 } else error("Rotate image%s : Invalid argument '%s' "
1913 "(should be 'angle[,border_conditions[,interpolation]]').",gmic_inds,argument_text);
1914 ++position;
1915 continue;
1916 }
1918 // Mirror.
1919 if (!cimg::strcmp("-mirror",item0)) {
1920 const char axis = cimg::uncase(*argument);
1921 if (cimg::strlen(argument)==1) {
1922 print("Mirror image%s along the %c-axis.",gmic_inds,axis);
1923 cimg_foroff(indices,l) gmic_apply(images[indices[l]],mirror(axis));
1924 } else error("Mirror image%s : Invalid argument '%s' "
1925 "(should be '{x,y,z,v}').",gmic_inds,argument_text);
1926 ++position; continue;
1927 }
1929 // Translate.
1930 if (!cimg::strcmp("-translate",item0)) {
1931 char stx[4096] = { 0 }, sty[4096] = { 0 }, stz[4096] = { 0 }, stv[4096] = { 0 };
1932 char sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0;
1933 float dx = 0, dy = 0, dz = 0, dv = 0; int borders = 0;
1934 if (((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
1935 stx,sty,stz,stv,&borders,&end)==5 ||
1936 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
1937 stx,sty,stz,stv,&end)==4) &&
1938 (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
1939 (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%')) &&
1940 (std::sscanf(stz,"%f%c",&dz,&end)==1 || (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%')) &&
1941 (std::sscanf(stv,"%f%c",&dv,&end)==1 || (std::sscanf(stv,"%f%c%c",&dv,&sepv,&end)==2 && sepv=='%'))) ||
1942 (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",stx,sty,stz,&end)==3 &&
1943 (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
1944 (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%')) &&
1945 (std::sscanf(stz,"%f%c",&dz,&end)==1 || (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%'))) ||
1946 (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",stx,sty,&end)==2 &&
1947 (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
1948 (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%'))) ||
1949 (std::sscanf(argument,"%4095[0-9.eE%+-]%c",stx,&end)==1 &&
1950 (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')))) {
1951 print("Translate image%s with vector (%g%s,%g%s,%g%s,%g%s).",
1952 gmic_inds,dx,sepx=='%'?"%":"",dy,sepy=='%'?"%":"",dz,sepz=='%'?"%":"",dv,sepv=='%'?"%":"");
1953 cimg_foroff(indices,l) {
1954 CImg<T> &img = images[indices[l]];
1955 const int
1956 ndx = (int)cimg::round(sepx=='%'?dx*img.dimx()/100:dx,1),
1957 ndy = (int)cimg::round(sepy=='%'?dy*img.dimy()/100:dy,1),
1958 ndz = (int)cimg::round(sepz=='%'?dz*img.dimz()/100:dz,1),
1959 ndv = (int)cimg::round(sepv=='%'?dv*img.dimv()/100:dv,1);
1960 gmic_apply(images[indices[l]],translate(ndx,ndy,ndz,ndv,borders));
1961 }
1962 } else error("Translate image%s : Invalid argument '%s' "
1963 "(should be 'tx[%%][,ty[%%][,tz[%%][,tv[%%][,border_conditions]]]]').",gmic_inds,argument_text);
1964 ++position; continue;
1965 }
1967 // Transpose.
1968 gmic_simple_item("-transpose",transpose,"Transpose image%s.");
1970 // Invert.
1971 gmic_simple_item("-invert",invert,"Compute matrix inversion of image%s.");
1973 // Permute axes.
1974 if (!cimg::strcmp("-permute",item0)) {
1975 print("Permute axes of image%s with permutation '%s'.",gmic_inds,argument_text);
1976 cimg_foroff(indices,l) gmic_apply(images[indices[l]],permute_axes(argument));
1977 ++position; continue;
1978 }
1980 // Unroll.
1981 if (!cimg::strcmp("-unroll",item0)) {
1982 const char axis = cimg::uncase(*argument);
1983 if (cimg::strlen(argument)==1 && (axis=='x' || axis=='y' || axis=='z' || axis=='v')) {
1984 print("Unroll image%s along the %c-axis.",gmic_inds,axis);
1985 cimg_foroff(indices,l) gmic_apply(images[indices[l]],unroll(axis));
1986 } else error("Unroll image%s : Invalid argument '%s' "
1987 "(should be '{x,y,z,v}').",gmic_inds,argument_text);
1988 ++position; continue;
1989 }
1991 // Split image(s).
1992 if (!cimg::strcmp("-split",item0) || !cimg::strcmp("-s",item0)) {
1993 char axis = cimg::uncase(*argument), foo = 0, end = 0; int nb = 0, keep_value = 0; double value = 0;
1994 if ((std::sscanf(argument,"%c%c",&foo,&end)==1 ||
1995 std::sscanf(argument,"%c%*c%d%c",&foo,&nb,&end)==2) &
1996 (axis=='x' || axis=='y' || axis=='z' || axis=='v')) {
1997 if (nb<0) error("Split image%s along the %c-axis in %d part : Invalid number of parts.",
1998 gmic_inds,axis,nb);
1999 if (nb>0) print("Split image%s along the %c-axis in %d parts.",gmic_inds,axis,nb);
2000 else print("Split image%s along the %c-axis.",gmic_inds,axis);
2001 unsigned int off = 0;
2002 cimg_foroff(indices,l) {
2003 const unsigned int ind = indices[l] + off;
2004 const CImg<T>& img = images[ind];
2005 const CImg<char> filename = filenames[ind];
2006 const CImgList<T> split = img.get_split(axis,nb);
2007 if (get_version) {
2008 images.insert(split);
2009 filenames.insert(split.size,filename);
2010 } else {
2011 images.remove(ind); images.insert(split,ind);
2012 filenames.remove(ind); filenames.insert(split.size,filename,ind);
2013 off+=split.size-1;
2014 }
2015 }
2016 } else if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
2017 std::sscanf(argument,"%lf%*c%d%c",&value,&keep_value,&end)==2) {
2018 print("Split image%s according to value %g.",gmic_inds,value);
2019 unsigned int off = 0;
2020 cimg_foroff(indices,l) {
2021 const unsigned int ind = indices[l] + off;
2022 CImg<T>& img = images[ind];
2023 const CImg<char> filename = filenames[ind];
2024 const CImgList<T> split = img.get_split((T)value,keep_value,false);
2025 if (get_version) {
2026 images.insert(split);
2027 filenames.insert(split.size,filename);
2028 } else {
2029 images.remove(ind); images.insert(split,ind);
2030 filenames.remove(ind); filenames.insert(split.size,filename,ind);
2031 off+=split.size-1;
2032 }
2033 }
2034 } else error("Split image%s : Invalid argument '%s' "
2035 "(should be 'axis[,nb_parts]' where 'axis' can be '{x,y,z,v}').",gmic_inds,argument_text);
2036 ++position; continue;
2037 }
2039 // Append image(s).
2040 if (!cimg::strcmp("-append",item0) || !cimg::strcmp("-a",item0)) {
2041 char axis = 0, align='p', end = 0;
2042 if ((std::sscanf(argument,"%c%c",&axis,&end)==1 ||
2043 std::sscanf(argument,"%c%*c%c%c",&axis,&align,&end)==2)) {
2044 axis = cimg::uncase(axis);
2045 print("Append image%s along the %c-axis with %s alignment.",
2046 gmic_inds,axis,align=='p'?"left":align=='c'?"center":"right");
2047 CImgList<T> subimages; cimg_foroff(indices,l) subimages.insert(images[indices[l]],~0U,true);
2048 if (get_version) {
2049 images.insert(subimages.get_append(axis,align));
2050 filenames.insert(filenames[indices[0]]);
2051 } else {
2052 images.insert(subimages.get_append(axis,align),indices[0]);
2053 filenames.insert(filenames[indices[0]],indices[0]);
2054 int off = 1;
2055 cimg_foroff(indices,l) {
2056 const int ind = indices[l] + off;
2057 images.remove(ind); filenames.remove(ind);
2058 --off;
2059 }
2060 }
2061 } else error("Append image%s : Invalid argument '%s' "
2062 "(should be 'axis[,alignement]' where 'axis' can be '{x,y,z,v}' "
2063 "and alignement '{p,c,n}').",gmic_inds,argument_text);
2064 ++position; continue;
2065 }
2067 // Warp image(s).
2068 if (!cimg::strcmp("-warp",item0)) {
2069 int ind0 = no_ind, interpolation = 1, relative = 0, nb = 1, borders = 1; char end = 0, sep = 0;
2070 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']')||
2071 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&relative,&end)==2 ||
2072 std::sscanf(argument,"[%d]%*c%d%*c%d%c",&ind0,&relative,&interpolation,&end)==3 ||
2073 std::sscanf(argument,"[%d]%*c%d%*c%d%*c%d%c",&ind0,&relative,&interpolation,&borders,&end)==4 ||
2074 std::sscanf(argument,"[%d]%*c%d%*c%d%*c%d%*c%d%c",&ind0,&relative,&interpolation,&borders,&nb,&end)==5) {
2075 gmic_check_indice(ind0,"Warp image%s");
2076 if (nb!=1) print("Warp image%s with %s field [%u] and %d frames.",
2077 gmic_inds,relative?"relative":"absolute",ind0,nb);
2078 else print("Warp image%s with %s field [%u].",gmic_inds,relative?"relative":"absolute",ind0);
2079 if (nb>=1) {
2080 const CImg<T> warp = images[ind0];
2081 unsigned int off = 0;
2082 cimg_foroff(indices,l) {
2083 const unsigned int ind = indices[l] + off;
2084 CImg<T> &img = images[ind];
2085 CImgList<T> frames(nb);
2086 cimglist_for(frames,t) {
2087 const CImg<T> nwarp = warp.get_resize(img.dimx(),img.dimy(),img.dimz(),warp.dimv(),3)*=(t+1.0f)/nb;
2088 frames[t] = img.get_warp(nwarp,relative?true:false,interpolation?true:false,borders);
2089 }
2090 if (get_version) {
2091 images.insert(frames);
2092 filenames.insert(nb-1,filenames[ind]);
2093 } else {
2094 images.remove(ind); images.insert(frames,ind);
2095 filenames.insert(nb-1,filenames[ind],ind);
2096 off+=nb-1;
2097 }
2098 }
2099 }
2100 } else error("Warp image%s : Invalid argument '%s' "
2101 "(should be '[indice][,relative[,interpolation[,border_conditions[,nb_frames]]]]').",
2102 gmic_inds,argument_text);
2103 ++position; continue;
2104 }
2106 //-----------------------
2107 // Image filtering
2108 //-----------------------
2110 // Gaussian blur.
2111 if (!cimg::strcmp("-blur",item0)) {
2112 float sigma = -1; int borders = 1; char end = 0;
2113 if ((std::sscanf(argument,"%f%c",&sigma,&end)==1 ||
2114 std::sscanf(argument,"%f%*c%d%c",&sigma,&borders,&end)==2)
2115 && sigma>=0) {
2116 print("Blur image%s with standard deviation %g.",gmic_inds,sigma);
2117 cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur(sigma,borders?true:false));
2118 } else error("Blur image%s : Invalid argument '%s' "
2119 "(should be 'stdev[,border_conditions]', with stdev>=0).",gmic_inds,argument_text);
2120 ++position; continue;
2121 }
2123 // Bilateral filter.
2124 if (!cimg::strcmp("-bilateral",item0)) {
2125 float sigmas = 0, sigmar = 0; char end = 0;
2126 if (std::sscanf(argument,"%f%*c%f%c",&sigmas,&sigmar,&end)==2) {
2127 print("Apply bilateral filter on image%s with standart deviations %g and %g.",
2128 gmic_inds,sigmas,sigmar);
2129 cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_bilateral(sigmas,sigmar));
2130 } else error("Apply bilateral filter on image%s : Invalid argument '%s' "
2131 "(should be 'stdevs,stdevr').",gmic_inds,argument_text);
2132 ++position; continue;
2133 }
2135 // Smooth.
2136 if (!cimg::strcmp("-smooth",item0)) {
2137 float amplitude = 0, sharpness = 0.7f, anisotropy = 0.3f, alpha = 0.6f, sigma = 1.1f, dl =0.8f, da = 30.0f, gauss_prec = 2.0f;
2138 unsigned int interpolation_type = 0, fast_approx = 1;
2139 char end = 0;
2140 if (std::sscanf(argument,"%f%c",&litude,&end)==1 ||
2141 std::sscanf(argument,"%f%*c%f%c",&litude,&sharpness,&end)==2 ||
2142 std::sscanf(argument,"%f%*c%f%*c%f%c",&litude,&sharpness,&anisotropy,&end)==3 ||
2143 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&litude,&sharpness,&anisotropy,&alpha,&end)==4 ||
2144 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%c",&litude,&sharpness,&anisotropy,&alpha,&sigma,&end)==5 ||
2145 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",&litude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&end)==6 ||
2146 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",&litude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&end)==7 ||
2147 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",
2148 &litude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&end)==8 ||
2149 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%u%c",
2150 &litude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&interpolation_type,&end)==9 ||
2151 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%u%*c%u%c",
2152 &litude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&interpolation_type,&fast_approx,&end)==10) {
2153 print("Smooth image%s anisotropically with "
2154 "amplitude %g, sharpness %g, anisotropy %g, alpha %g and sigma %g.",
2155 gmic_inds,amplitude,sharpness,anisotropy,alpha,sigma);
2156 cimg_foroff(indices,l)
2157 gmic_apply(images[indices[l]],blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,
2158 dl,da,gauss_prec,interpolation_type,fast_approx?true:false));
2159 } else error("Smooth image%s anisotropically : Invalid argument '%s' "
2160 "(should be 'amplitude[,sharpness[,anisotropy[,alpha[,sigma[,dl[,da[,prec[,interp[,fast]]]]]]]]]').",
2161 gmic_inds,argument_text);
2162 ++position; continue;
2163 }
2165 // Patch averaging.
2166 if (!cimg::strcmp("-denoise",item0)) {
2167 float sigmas = 10, sigmar = 10; int psize = 5, rsize = 6; char end = 0;
2168 if (std::sscanf(argument,"%f%c",&sigmas,&end)==1 ||
2169 std::sscanf(argument,"%f%*c%f%c",&sigmas,&sigmar,&end)==2 ||
2170 std::sscanf(argument,"%f%*c%f%*c%d%c",&sigmas,&sigmar,&psize,&end)==3 ||
2171 std::sscanf(argument,"%f%*c%f%*c%d%*c%d%c",&sigmas,&sigmar,&psize,&rsize,&end)==4) {
2172 if (sigmas<0 || sigmar<0 || psize<0 || rsize<0)
2173 error("Denoise image%s with %dx%d patches, standard deviations %lg,%g and lookup size %d : "
2174 "Invalid parameters.",gmic_inds,psize,psize,sigmas,sigmar,rsize);
2175 print("Denoise image%s with %dx%d patches, standard deviations %lg,%g and lookup size %d.",
2176 gmic_inds,psize,psize,sigmas,sigmar,rsize);
2177 cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_patch(psize,sigmas,sigmar,rsize));
2178 } else error("Denoise image%s : Invalid argument '%s' "
2179 "(should be 'stdev_s[,stdev_p[,patch_size[,lookup_size]]]').",
2180 gmic_inds,argument_text);
2181 ++position; continue;
2182 }
2184 // Median filter.
2185 if (!cimg::strcmp("-median",item0)) {
2186 int siz = 3; char end = 0;
2187 if (std::sscanf(argument,"%d%c",&siz,&end)==1) {
2188 if (siz<=0) error("Apply median filter on image%s : Invalid size %d.",gmic_inds,siz);
2189 print("Apply median filter of size %d on image%s.",siz,gmic_inds);
2190 cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_median(siz));
2191 } else error("Apply median filter on image%s : Invalid argument '%s' "
2192 "(should be 'size').",gmic_inds,argument_text);
2193 ++position; continue;
2194 }
2196 // Sharpen.
2197 if (!cimg::strcmp("-sharpen",item0)) {
2198 float amplitude = 0, edge = 1, alpha = 0, sigma = 0; int sharpen_type = 0; char end = 0;
2199 if (std::sscanf(argument,"%f%c",&litude,&end)==1 ||
2200 std::sscanf(argument,"%f%*c%d%c",&litude,&sharpen_type,&end)==2 ||
2201 std::sscanf(argument,"%f%*c%d%*c%f%c",&litude,&sharpen_type,&edge,&end)==3 ||
2202 std::sscanf(argument,"%f%*c%d%*c%f%*c%f%c",&litude,&sharpen_type,&edge,&alpha,&end)==4 ||
2203 std::sscanf(argument,"%f%*c%d%*c%f%*c%f%*c%f%c",&litude,&sharpen_type,&edge,&alpha,&sigma,&end)==5) {
2204 if (sharpen_type)
2205 print("Sharpen image%s with shock filters and amplitude %g, edge %g, alpha %g and sigma %g.",
2206 gmic_inds,amplitude,edge,alpha,sigma);
2207 else
2208 print("Sharpen image%s with inverse diffusion and amplitude %g.",gmic_inds,amplitude);
2209 cimg_foroff(indices,l) gmic_apply(images[indices[l]],sharpen(amplitude,sharpen_type?true:false,edge,alpha,sigma));
2210 } else error("Sharpen image%s : Invalid argument '%s' "
2211 "(should be 'amplitude[,sharpen_type[,edge[,alpha[,sigma]]]]', "
2212 "where 'sharpen_type' can be '{0=inverse diffusion, 1=shock filters}').",
2213 gmic_inds,argument_text);
2214 ++position; continue;
2215 }
2217 // Convolve.
2218 if (!cimg::strcmp("-convolve",item0)) {
2219 int ind0 = no_ind, borders = 1; char sep = 0, end = 0;
2220 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
2221 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
2222 gmic_check_indice(ind0,"Convolve image%s");
2223 print("Convolve image%s with mask [%d].",gmic_inds,ind0);
2224 const CImg<T> mask = images[ind0];
2225 cimg_foroff(indices,l) gmic_apply(images[indices[l]],convolve(mask,borders));
2226 } else error("Convolve image%s : Invalid argument '%s' "
2227 "(should be '[indice][,border_conditions]').",gmic_inds,argument_text);
2228 ++position; continue;
2229 }
2231 // Correlate.
2232 if (!cimg::strcmp("-correlate",item0)) {
2233 int ind0 = no_ind, borders = 1; char sep = 0, end = 0;
2234 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
2235 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
2236 gmic_check_indice(ind0,"Correlate image%s");
2237 print("Correlate image%s with mask [%d].",gmic_inds,ind0);
2238 const CImg<T> mask = images[ind0];
2239 cimg_foroff(indices,l) gmic_apply(images[indices[l]],correlate(mask,borders));
2240 } else error("Correlate image%s : Invalid argument '%s' "
2241 "(should be '[indice][,border_conditions]').",gmic_inds,argument_text);
2242 ++position; continue;
2243 }
2245 // Erode.
2246 if (!cimg::strcmp("-erode",item0)) {
2247 int siz = 3, ind0 = no_ind, borders = 1; char sep = 0, end = 0;
2248 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
2249 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
2250 gmic_check_indice(ind0,"Erode image%s");
2251 print("Erode image%s with mask [%d].",gmic_inds,ind0);
2252 const CImg<T> mask = images[ind0];
2253 cimg_foroff(indices,l) gmic_apply(images[indices[l]],erode(mask,borders));
2254 } else if (std::sscanf(argument,"%d%c",&siz,&end)==1 ||
2255 std::sscanf(argument,"%d%*c%d%c",&siz,&borders,&end)==2) {
2256 if (siz<=0) error("Erode image%s : Invalid size %d.",gmic_inds,siz);
2257 print("Erode image%s with size %d.",gmic_inds,siz);
2258 cimg_foroff(indices,l) gmic_apply(images[indices[l]],erode(siz,borders));
2259 } else error("Erode image%s : Invalid argument '%s' "
2260 "(should be '[indice]' or 'size').",gmic_inds,argument_text);
2261 ++position; continue;
2262 }
2264 // Dilate.
2265 if (!cimg::strcmp("-dilate",item0)) {
2266 int siz = 3, ind0 = no_ind, borders = 1; char sep = 0, end = 0;
2267 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
2268 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
2269 gmic_check_indice(ind0,"Dilate image%s");
2270 print("Dilate image%s with mask [%d].",gmic_inds,ind0);
2271 const CImg<T> mask = images[ind0];
2272 cimg_foroff(indices,l) gmic_apply(images[indices[l]],dilate(mask,borders));
2273 } else if (std::sscanf(argument,"%d%c",&siz,&end)==1 ||
2274 std::sscanf(argument,"%d%*c%d%c",&siz,&borders,&end)==2) {
2275 if (siz<=0) error("Dilate image%s : Invalid size %d.",gmic_inds,siz);
2276 print("Dilate image%s with size %d.",gmic_inds,siz);
2277 cimg_foroff(indices,l) gmic_apply(images[indices[l]],dilate(siz,borders));
2278 } else error("Dilate image%s : Invalid argument '%s' "
2279 "(should be '[indice]' or 'size').",gmic_inds,argument_text);
2280 ++position; continue;
2281 }
2283 // Compute gradient.
2284 if (!cimg::strcmp("-gradient",item0)) {
2285 char axes[4096] = { 0 }, *naxes = 0, end = 0; int scheme = 3;
2286 print("Compute gradient of image%s.",gmic_inds);
2287 if (std::sscanf(argument,"%4095[xyz]%c",axes,&end)==1 ||
2288 std::sscanf(argument,"%4095[xyz]%*c%d%c",axes,&scheme,&end)==2) { naxes = axes; ++position; }
2289 unsigned int off = 0;
2290 cimg_foroff(indices,l) {
2291 const unsigned int ind = indices[l] + off;
2292 CImg<T>& img = images[ind];
2293 const CImg<char> filename = filenames[ind];
2294 const CImgList<T> gradient = img.get_gradient(naxes,scheme);
2295 if (get_version) {
2296 images.insert(gradient);
2297 filenames.insert(gradient.size,filename);
2298 } else {
2299 images.remove(ind); images.insert(gradient,ind);
2300 filenames.remove(ind); filenames.insert(gradient.size,filename,ind);
2301 off+=gradient.size-1;
2302 }
2303 }
2304 continue;
2305 }
2307 // Compute Hessian.
2308 if (!cimg::strcmp("-hessian",item0)) {
2309 char axes[4096] = { 0 }, *naxes = 0, end = 0;
2310 print("Compute Hessian of image%s.",gmic_inds);
2311 if (std::sscanf(argument,"%4095[xyz]%c",axes,&end)==1) { naxes = axes; ++position; }
2312 unsigned int off = 0;
2313 cimg_foroff(indices,l) {
2314 const unsigned int ind = indices[l] + off;
2315 CImg<T>& img = images[ind];
2316 const CImg<char> filename = filenames[ind];
2317 const CImgList<T> hessian = img.get_hessian(naxes);
2318 if (get_version) {
2319 images.insert(hessian);
2320 filenames.insert(hessian.size,filename);
2321 } else {
2322 images.remove(ind); images.insert(hessian,ind);
2323 filenames.remove(ind); filenames.insert(hessian.size,filename,ind);
2324 off+=hessian.size-1;
2325 }
2326 }
2327 continue;
2328 }
2330 // Compute direct or inverse FFT.
2331 const bool inv_fft = !cimg::strcmp("-ifft",item0);
2332 if (!cimg::strcmp("-fft",item0) || inv_fft) {
2333 print("Compute %sFourier Transform of complex data",inv_fft?"inverse ":"");
2334 cimg_foroff(indices,l) {
2335 const unsigned int ind0 = indices[l], ind1 = l+1<_maxl?indices[l+1]:~0U;
2336 if (ind1!=~0U) {
2337 if (verbosity_level>=0) std::fprintf(cimg_stdout," ([%u],[%u])%c",ind0,ind1,l==_maxl-1?'.':',');
2338 CImgList<T> fft(images[ind0],images[ind1],!get_version);
2339 fft.FFT(inv_fft);
2340 if (get_version) {
2341 images.insert(2);
2342 fft[0].transfer_to(images[images.size-2]);
2343 fft[1].transfer_to(images[images.size-1]);
2344 filenames.insert(filenames[ind0]);
2345 filenames.insert(filenames[ind1]);
2346 } else {
2347 fft[0].transfer_to(images[ind0]);
2348 fft[1].transfer_to(images[ind1]);
2349 }
2350 ++l;
2351 } else {
2352 if (verbosity_level>=0) std::fprintf(cimg_stdout," ([%u],0)",ind0);
2353 CImgList<T> fft(images[ind0],!get_version);
2354 fft.insert(fft[0],~0U,false);
2355 fft[1].fill(0);
2356 fft.FFT(inv_fft);
2357 if (get_version) {
2358 images.insert(2);
2359 fft[0].transfer_to(images[images.size-2]);
2360 fft[1].transfer_to(images[images.size-1]);
2361 filenames.insert(2,filenames[ind0]);
2362 } else {
2363 fft[0].transfer_to(images[ind0]);
2364 images.insert(fft[1],1+ind0);
2365 filenames.insert(filenames[ind0],1+ind0);
2366 }
2367 }
2368 }
2369 continue;
2370 }
2372 //-----------------------------
2373 // Image creation and drawing
2374 //-----------------------------
2376 // Dimensions.
2377 if (!cimg::strcmp("-dimensions",item0)) {
2378 print("Get dimensions of image%s.",gmic_inds);
2379 cimg_foroff(indices,l) {
2380 CImg<T>& img = images[indices[l]];
2381 CImg<int> dims = CImg<int>::vector(img.dimx(),img.dimy(),img.dimz(),img.dimv());
2382 gmic_apply(img,replace(dims));
2383 }
2384 continue;
2385 }
2387 // Stats.
2388 if (!cimg::strcmp("-stats",item0)) {
2389 print("Get statistics of image%s.",gmic_inds);
2390 cimg_foroff(indices,l) gmic_apply(images[indices[l]],stats());
2391 continue;
2392 }
2394 // Histogram.
2395 if (!cimg::strcmp("-histogram",item0)) {
2396 int nb_levels = 256; char sep = 0, end = 0;
2397 if (std::sscanf(argument,"%d%c",&nb_levels,&end)==1 ||
2398 (std::sscanf(argument,"%d%c%c",&nb_levels,&sep,&end)==2 && sep=='%')) {
2399 print("Compute histogram of image%s using %d%s levels.",gmic_inds,nb_levels,sep=='%'?"%":"");
2400 cimg_foroff(indices,l) {
2401 CImg<T> &img = images[indices[l]];
2402 int nnb_levels = nb_levels;
2403 if (sep=='%') { double m, M = img.maxmin(m); nnb_levels = (int)cimg::round(nb_levels*(1+M-m)/100,1); }
2404 gmic_apply(images[indices[l]],histogram(nnb_levels));
2405 }
2406 } else error("Compute histogram of image%s : Invalid argument '%s' "
2407 "(should be 'nb_levels[%%]').",gmic_inds,argument_text);
2408 ++position; continue;
2409 }
2411 // Distance function.
2412 if (!cimg::strcmp("-distance",item0)) {
2413 double value = 0; char sep = 0, end = 0;
2414 if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
2415 (std::sscanf(argument,"%lf%c%c",&value,&sep,&end)==2 && sep=='%')) {
2416 print("Compute distance map of image%s to isovalue %g%s.",gmic_inds,value,sep=='%'?"%":"");
2417 cimg_foroff(indices,l) {
2418 CImg<T> &img = images[indices[l]];
2419 double isovalue = value;
2420 if (sep=='%') { double m, M = img.maxmin(m); isovalue = m + value*(M - m)/100; }
2421 gmic_apply(img,distance((T)isovalue));
2422 }
2423 } else error("Compute distance function of image%s : Invalid argument '%s' "
2424 "(should be 'value[%%]').",gmic_inds,argument_text);
2425 ++position; continue;
2426 }
2428 // Apply Hamilton-Jacobi PDE to compute distance to 0.
2429 if (!cimg::strcmp("-hamilton",item0)) {
2430 int nb_iter = 0; float band_size = 0; char end = 0;
2431 if (std::sscanf(argument,"%d%c",&nb_iter,&end)==1 ||
2432 std::sscanf(argument,"%d%*c%f%c",&nb_iter,&band_size,&end)==2) {
2433 print("Apply %d iterations of Hamilton-Jacobi PDE on image%s.",nb_iter,gmic_inds);
2434 cimg_foroff(indices,l) gmic_apply(images[indices[l]],distance_hamilton((unsigned int)nb_iter,band_size));
2435 } else error("Apply %d iterations of Hamilton-Jacobi PDE on image%s : Invalid argument '%s' "
2436 "(should be 'nb_iter[,band_size]', with band_size>0).",nb_iter,gmic_inds,argument_text);
2437 ++position; continue;
2438 }
2440 // Label regions.
2441 gmic_simple_item("-label",label_regions,"Label regions on image%s.");
2443 // Displacement field.
2444 if (!cimg::strcmp("-displacement",item0)) {
2445 float smooth = 0.1f, precision = 0.1f; int ind0 = no_ind, nbscales = 0, itermax = 1000; char sep = 0, end = 0;
2446 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
2447 std::sscanf(argument,"[%d]%*c%f%c",&ind0,&smooth,&end)==2 ||
2448 std::sscanf(argument,"[%d]%*c%f%*c%f%c",&ind0,&smooth,&precision,&end)==3 ||
2449 std::sscanf(argument,"[%d]%*c%f%*c%f%*c%d%c",&ind0,&smooth,&precision,&nbscales,&end)==4 ||
2450 std::sscanf(argument,"[%d]%*c%f%*c%f%*c%d%*c%d%c",&ind0,&smooth,&precision,&nbscales,&itermax,&end)==5) {
2451 gmic_check_indice(ind0,"Compute displacement field of image%s");
2452 print("Compute displacement field of image%s with target [%u] and smoothness %g.",
2453 gmic_inds,ind0,smooth);
2454 const CImg<T> target = images[ind0];
2455 cimg_foroff(indices,l) gmic_apply(images[indices[l]],displacement_field(target,smooth,precision,nbscales,itermax));
2456 } else error("Compute displacement field of image%s : Invalid argument '%s' "
2457 "(should be '[indice][,smoothness[,precision[,nbscales[,itermax]]]]').",gmic_inds,argument_text);
2458 ++position; continue;
2459 }
2461 // Sort.
2462 gmic_simple_item("-sort",sort,"Sort values in image%s.");
2464 // PSNR.
2465 if (!cimg::strcmp("-psnr",item0)) {
2466 double valmax = 255; char end = 0;
2467 if (std::sscanf(argument,"%lf%c",&valmax,&end)==1) ++position;
2468 if (images.size) {
2469 const unsigned int siz = indices.size();
2470 print("Compute a %ux%u matrix [%u] of PSNR values (max. pixel value is %g).",siz,siz,images.size,valmax);
2471 CImg<T> res(siz,siz,1,1,(T)-1);
2472 cimg_forXY(res,x,y) if (x>y) res(x,y) = res(y,x) = (T)images[indices[x]].PSNR(images[indices[y]],(float)valmax);
2473 images.insert(res);
2474 filenames.insert(CImg<char>("PSNR",5,1,1,1,false));
2475 } else error("Compute PSNR : image list is empty.");
2476 continue;
2477 }
2479 // Draw point.
2480 if (!cimg::strcmp("-point",item0)) {
2481 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, color[4096] = { 0 };
2482 char sepx0 = 0, sepy0 = 0, sepz0 = 0, end = 0;
2483 float x0 = 0, y0 = 0, z0 = 0, opacity = 1;
2484 if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c%4095[0-9.eE,+-]",
2485 arg0,arg1,arg2,&opacity,color)>=2 &&
2486 ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
2487 std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
2488 ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
2489 std::sscanf(arg1,"%f%c",&y0,&end)==1) &&
2490 ((std::sscanf(arg2,"%f%c%c",&z0,&sepz0,&end)==2 && sepz0=='%') ||
2491 std::sscanf(arg2,"%f%c",&z0,&end)==1 || !arg2[0])) {
2492 print("Draw point (%g%s,%g%s,%g%s) with color '%s' and opacity %g on image%s.",
2493 x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",z0,sepz0=='%'?"%":"",
2494 color[0]?color:"default",opacity,gmic_inds);
2495 cimg_foroff(indices,l) {
2496 CImg<T> &img = images[indices[l]];
2497 CImg<T> col(img.dimv(),1,1,1,0);
2498 col.fill(color,true);
2499 const int
2500 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
2501 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1),
2502 nz0 = (int)cimg::round(sepz0=='%'?z0*(img.dimz()-1)/100:z0,1);
2503 gmic_apply(img,draw_point(nx0,ny0,nz0,col,opacity));
2504 }
2505 } else error("Draw point on image%s : Invalid argument '%s' "
2506 "(should be 'x[%%],y[%%][,z[%%][,opacity[,color]]])",gmic_inds,argument_text);
2507 ++position; continue;
2508 }
2510 // Draw line.
2511 if (!cimg::strcmp("-line",item0)) {
2512 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, arg3[4096] = { 0 }, color[4096] = { 0 };
2513 char sepx0 = 0, sepy0 = 0, sepx1 = 0, sepy1 = 0, end = 0;
2514 float x0 = 0, y0 = 0, x1 = 0, y1 = 0, opacity = 1;
2515 if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]"
2516 "%*c%f%*c%4095[0-9.eE,+-]",
2517 arg0,arg1,arg2,arg3,&opacity,color)>=4 &&
2518 ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
2519 std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
2520 ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
2521 std::sscanf(arg1,"%f%c",&y0,&end)==1) &&
2522 ((std::sscanf(arg2,"%f%c%c",&x1,&sepx1,&end)==2 && sepx1=='%') ||
2523 std::sscanf(arg2,"%f%c",&x1,&end)==1) &&
2524 ((std::sscanf(arg3,"%f%c%c",&y1,&sepy1,&end)==2 && sepy1=='%') ||
2525 std::sscanf(arg3,"%f%c",&y1,&end)==1)) {
2526 print("Draw line (%g%s,%g%s) - (%g%s,%g%s) with color '%s' and opacity %g on image%s.",
2527 x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",x1,sepx1=='%'?"%":"",y1,sepy1=='%'?"%":"",
2528 color[0]?color:"default",opacity,gmic_inds);
2529 cimg_foroff(indices,l) {
2530 CImg<T> &img = images[indices[l]];
2531 CImg<T> col(img.dimv(),1,1,1,0);
2532 col.fill(color,true);
2533 const int
2534 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
2535 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1),
2536 nx1 = (int)cimg::round(sepx1=='%'?x1*(img.dimx()-1)/100:x1,1),
2537 ny1 = (int)cimg::round(sepy1=='%'?y1*(img.dimy()-1)/100:y1,1);
2538 gmic_apply(img,draw_line(nx0,ny0,nx1,ny1,col,opacity));
2539 }
2540 } else error("Draw line on image%s : Invalid argument '%s' "
2541 "(should be 'x0[%%],y0[%%],x1[%%],y1[%%][,opacity[,color]]')",gmic_inds,argument_text);
2542 ++position; continue;
2543 }
2545 // Draw polygon.
2546 if (!cimg::strcmp("-polygon",item0)) {
2547 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, tmp[4096] = { 0 }, sepx0 = 0, sepy0 = 0, end = 0;
2548 int N = 0; float x0 = 0, y0 = 0, opacity = 1;
2549 if (std::sscanf(argument,"%d%c",&N,&end)==2 && N>2) {
2550 const char
2551 *nargument = argument + std::sprintf(tmp,"%d",N) + 1,
2552 *const eargument = argument + cimg::strlen(argument);
2553 CImg<float> coords0(N,2,1,1,0);
2554 CImg<bool> percents(N,2,1,1,0);
2555 for (int n = 0; n<N; ++n) if (nargument<eargument) {
2556 if (std::sscanf(nargument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]",arg0,arg1)==2 &&
2557 ((std::sscanf(arg0,"%f%c%c",&x0,&(sepx0=0),&end)==2 && sepx0=='%') ||
2558 std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
2559 ((std::sscanf(arg1,"%f%c%c",&y0,&(sepy0=0),&end)==2 && sepy0=='%') ||
2560 std::sscanf(arg1,"%f%c",&y0,&end)==1)) {
2561 coords0(n,0) = x0; percents(n,0) = (sepx0=='%');
2562 coords0(n,1) = y0; percents(n,1) = (sepy0=='%');
2563 nargument+=cimg::strlen(arg0) + cimg::strlen(arg1) + 2;
2564 } else error("Draw polygon on image%s : Invalid or incomplete argument '%s' "
2565 "(should be 'N,x0[%%],y0[%%],x1[%%],y1[%%],..,xN[%%],yN[%%][,opacity[,color]]' with N>=3)",
2566 gmic_inds,argument_text);
2567 } else error("Draw polygon on image%s : Incomplete argument '%s' "
2568 "(%d xy-coordinates should be defined)",
2569 gmic_inds,argument_text,N);
2570 if (nargument<eargument && std::sscanf(nargument,"%4095[0-9.eE+-]",arg0)==1 &&
2571 std::sscanf(arg0,"%f",&opacity)==1) nargument+=cimg::strlen(arg0)+1;
2572 const char *const color = nargument<eargument?nargument:&(end=0);
2573 print("Draw %d-vertices polygon with color '%s' and opacity %g on image%s.",
2574 N,color[0]?color:"default",opacity,gmic_inds);
2575 cimg_foroff(indices,l) {
2576 CImg<T> &img = images[indices[l]];
2577 CImg<int> coords(coords0);
2578 cimg_forX(coords,p) {
2579 if (percents(p,0)) coords(p,0) = (int)cimg::round(coords0(p,0)*(img.dimx()-1)/100,1);
2580 if (percents(p,1)) coords(p,1) = (int)cimg::round(coords0(p,1)*(img.dimy()-1)/100,1);
2581 }
2582 CImg<T> col(img.dimv(),1,1,1,0);
2583 col.fill(color,true);
2584 gmic_apply(img,draw_polygon(coords,col,opacity));
2585 }
2586 } else error("Draw polygon on image%s : Invalid argument '%s' "
2587 "(should be 'N,x0[%%],y0[%%],x1[%%],y1[%%],..,xN[%%],yN[%%][,opacity[,color]]' with N>=3)",
2588 gmic_inds,argument_text);
2589 ++position; continue;
2590 }
2592 // Draw ellipse.
2593 if (!cimg::strcmp("-ellipse",item0)) {
2594 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, color[4096] = { 0 };
2595 char sepx0 = 0, sepy0 = 0, end = 0;
2596 float x0 = 0, y0 = 0, r0 = 0, r1 = 0, ru = 1, rv = 0, opacity = 1;
2597 if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c%f%*c%f%*c%f%*c%f%*c%4095[0-9.eE,+-]",
2598 arg0,arg1,&r0,&r1,&ru,&rv,&opacity,color)>=4 &&
2599 ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
2600 std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
2601 ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
2602 std::sscanf(arg1,"%f%c",&y0,&end)==1)) {
2603 print("Draw ellipse centered at (%g%s,%g%s) with radii (%g,%g), orientation (%g,%g), color '%s' "
2604 "and opacity %g on image%s.",
2605 x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",
2606 r0,r1,ru,rv,color[0]?color:"default",opacity,gmic_inds);
2607 cimg_foroff(indices,l) {
2608 CImg<T> &img = images[indices[l]];
2609 CImg<T> col(img.dimv(),1,1,1,0);
2610 col.fill(color,true);
2611 const int
2612 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
2613 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1);
2614 gmic_apply(img,draw_ellipse(nx0,ny0,r0,r1,ru,rv,col,opacity));
2615 }
2616 } else error("Draw ellipse on image%s : Invalid argument '%s' "
2617 "(should be 'x[%%],y[%%],r,R[,u,v[,opacity[,color]]])",
2618 gmic_inds,argument_text);
2619 ++position; continue;
2620 }
2622 // Draw text.
2623 if (!cimg::strcmp("-text",item0)) {
2624 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, color[4096] = { 0 }, text[4096] = { 0 };
2625 char sepx0 = 0, sepy0 = 0, end = 0;
2626 float x0 = 0, y0 = 0, opacity = 1; int siz = 11;
2627 if (std::sscanf(argument,"%4095[^,],%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%*c%f%*c%4095[0-9.eE,+-]",
2628 text,arg0,arg1,&siz,&opacity,color)>=1 &&
2629 ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
2630 std::sscanf(arg0,"%f%c",&x0,&end)==1 || !arg0[0]) &&
2631 ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
2632 std::sscanf(arg1,"%f%c",&y0,&end)==1 || !arg1[0])) {
2633 cimg::strclean(text); cimg::strescape(text);
2634 print("Draw text \"%s\" at position (%g%s,%g%s) with font size %d, color '%s' "
2635 "and opacity %f on image%s.",
2636 text,x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",siz,color[0]?color:"default",opacity,gmic_inds);
2637 cimg_foroff(indices,l) {
2638 CImg<T> &img = images[indices[l]];
2639 CImg<T> col(img.dimv(),1,1,1,0);
2640 col.fill(color,true);
2641 const int
2642 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
2643 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1);
2644 gmic_apply(img,draw_text(nx0,ny0,text,col.ptr(),0,opacity,siz));
2645 }
2646 } else error("Draw text on image%s : Invalid argument '%s' "
2647 "(should be 'text[,x[%%],y[%%][,size[,opacity[,color]]]]').",
2648 gmic_inds,argument_text);
2649 ++position; continue;
2650 }
2652 // Draw image.
2653 if (!cimg::strcmp("-image",item0)) {
2654 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, sep = 0, sepx = 0, sepy = 0, sepz = 0, end = 0;
2655 int ind0 = no_ind, indm0 = no_ind; float x = 0, y = 0, z = 0, opacity = 1;
2656 if (((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==1 && sep==']') ||
2657 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,&end)==2 ||
2658 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,&end)==3 ||
2659 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,arg2,&end)==4 ||
2660 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%c",&ind0,arg0,arg1,arg2,&opacity,&end)==5 ||
2661 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c[%d%c%c",
2662 &ind0,arg0,arg1,arg2,&opacity,&indm0,&sep,&end)==7) &&
2663 (!*arg0 ||
2664 std::sscanf(arg0,"%f%c",&x,&end)==1 ||
2665 (std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%')) &&
2666 (!*arg1 ||
2667 std::sscanf(arg1,"%f%c",&y,&end)==1 ||
2668 (std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%')) &&
2669 (!*arg2 ||
2670 std::sscanf(arg2,"%f%c",&z,&end)==1 ||
2671 (std::sscanf(arg2,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%'))) {
2672 gmic_check_indice(ind0,"Draw image on image%s");
2673 const CImg<T> sprite = images[ind0];
2674 CImg<T> mask;
2675 if (indm0!=no_ind) {
2676 gmic_check_indice(indm0,"Draw image on image%s");
2677 mask = images[indm0];
2678 print("Draw image [%d] at (%g%s,%g%s,%g%s), with mask [%d] and opacity %f on image%s.",
2679 ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",indm0,opacity,gmic_inds);
2680 } else print("Draw image [%d] at (%g%s,%g%s,%g%s) with opacity %f on image%s.",
2681 ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",opacity,gmic_inds);
2682 cimg_foroff(indices,l) {
2683 CImg<T> &img = images[indices[l]];
2684 const int
2685 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
2686 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
2687 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1);
2688 if (indm0!=no_ind) { gmic_apply(img,draw_image(nx,ny,nz,sprite,mask,opacity)); }
2689 else { gmic_apply(img,draw_image(nx,ny,nz,sprite,opacity)); }
2690 }
2691 } else error("Draw image on image%s : Invalid argument '%s' "
2692 "(should be '[indice][,x[%%][,y[%%][,z[%%][,opacity[,indice_mask]]]]]').",
2693 gmic_inds,argument_text);
2694 ++position; continue;
2695 }
2697 // Draw 3D object.
2698 if (!cimg::strcmp("-object3d",item0)) {
2699 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, sep = 0, sepx = 0, sepy = 0, end = 0;
2700 float x = 0, y = 0, z = 0, opacity = 1;
2701 int ind0 = no_ind;
2702 if (((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
2703 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,&end)==2 ||
2704 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,&end)==3 ||
2705 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%c",&ind0,arg0,arg1,&z,&end)==4 ||
2706 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c%f%c",&ind0,arg0,arg1,&z,&opacity,&end)==5) &&
2707 (!*arg0 ||
2708 std::sscanf(arg0,"%f%c",&x,&end)==1 ||
2709 (std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%')) &&
2710 (!*arg1 ||
2711 std::sscanf(arg1,"%f%c",&y,&end)==1 ||
2712 (std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%'))) {
2713 gmic_check_indice(ind0,"Draw 3D object on image%s");
2714 if (!images[ind0].is_CImg3d())
2715 error("Draw 3D object on image%s : Image [%d] is not a 3D object.",gmic_inds,ind0);
2716 print("Draw 3D object [%d] at (%g%s,%g%s,%g) on image%s, with opacity %g.",
2717 ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,gmic_inds,opacity);
2718 CImgList<unsigned int> primitives3d;
2719 CImgList<unsigned char> colors3d;
2720 CImg<float> opacities3d, points3d(images[ind0]);
2721 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
2722 opacities3d*=opacity;
2723 cimg_foroff(indices,l) {
2724 CImg<T> &img = images[indices[l]];
2725 const float
2726 nx = (float)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
2727 ny = (float)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1);
2728 gmic_apply(img,draw_object3d(nx,ny,z,points3d,primitives3d,colors3d,opacities3d,
2729 render3d,!is_oriented3d,focale3d,light3d_x,light3d_y,light3d_z,specular_light3d,
2730 specular_shine3d,0));
2731 }
2732 } else error("Draw 3D object on image%s : Invalid argument '%s' "
2733 "(should be '[indice][,x[%%][,y[%%][,z[,opacity[,zoom[,u1,v1,w1,angle1[,...]]]]]]]').",
2734 gmic_inds,argument_text);
2735 ++position; continue;
2736 }
2738 // Draw plasma fractal.
2739 if (!cimg::strcmp("-plasma",item0)) {
2740 float alpha = 1, beta = 1, opacity = 1; char end = 0;
2741 if (std::sscanf(argument,"%f%c",&alpha,&end)==1 ||
2742 std::sscanf(argument,"%f%*c%f%c",&alpha,&beta,&end)==2 ||
2743 std::sscanf(argument,"%f%*c%f%*c%f%c",&alpha,&beta,&opacity,&end)==3) {
2744 print("Draw plasma in image%s with alpha %g, beta %g and opacity %g.",gmic_inds,alpha,beta,opacity);
2745 cimg_foroff(indices,l) gmic_apply(images[indices[l]],draw_plasma(alpha,beta,opacity));
2746 } else error("Draw plasma in image%d : Invalid argument '%s' "
2747 "(should be 'alpha[,beta[,opacity]]').",gmic_inds,argument_text);
2748 ++position; continue;
2749 }
2751 // Draw Mandelbrot/Julia fractal.
2752 if (!cimg::strcmp("-mandelbrot",item0)) {
2753 double z0r = -2, z0i = -2, z1r = 2, z1i = 2, paramr = 0, parami = 0; char end = 0;
2754 float opacity = 1; int itermax = 100, julia = 0;
2755 if (std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%c",&z0r,&z0i,&z1r,&z1i,&end)==4 ||
2756 std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%c",&z0r,&z0i,&z1r,&z1i,&itermax,&end)==5 ||
2757 std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%*c%d%*c%lf%*c%lf%c",
2758 &z0r,&z0i,&z1r,&z1i,&itermax,&julia,¶mr,¶mi,&end)==8 ||
2759 std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%*c%d%*c%lf%*c%lf%*c%f%c",
2760 &z0r,&z0i,&z1r,&z1i,&itermax,&julia,¶mr,¶mi,&opacity,&end)==9) {
2761 print("Draw %s fractal in image%s from complex area (%g,%g)-(%g,%g) with c0 = (%g,%g) (%d iterations).",
2762 julia?"Julia":"Mandelbrot",gmic_inds,z0r,z0i,z1r,z1i,paramr,parami,itermax);
2763 cimg_foroff(indices,l)
2764 gmic_apply(images[indices[l]],draw_mandelbrot(CImg<T>(),opacity,z0r,z0i,z1r,z1i,itermax,true,
2765 julia?true:false,paramr,parami));
2766 } else error("Draw fractal in image%s : Invalid argument '%s' "
2767 "(should be 'z0r,z0i,z1r,z1i[,itermax[,julia,c0r,c0i[,opacity]]]').",gmic_inds,argument_text);
2768 ++position; continue;
2769 }
2771 // Flood fill.
2772 if (!cimg::strcmp("-flood",item0)) {
2773 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, color[4096] = { 0 };
2774 char sepx = 0, sepy = 0, sepz = 0, end = 0;
2775 float x = 0, y = 0, z = 0, tolerance = 0, opacity = 1;
2776 if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%*c%f%*c%4095[0-9.eE,+-]",
2777 arg0,arg1,arg2,&tolerance,&opacity,color)>=1 &&
2778 ((std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
2779 std::sscanf(arg0,"%f%c",&x,&end)==1) &&
2780 ((std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
2781 std::sscanf(arg1,"%f%c",&y,&end)==1 || !arg1[0]) &&
2782 ((std::sscanf(arg2,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
2783 std::sscanf(arg2,"%f%c",&z,&end)==1 || !arg2[0])) {
2784 print("Flood fill image%s from (%g%s,%g%s,%g%s) with tolerance %g, opacity %g and color '%s'.",
2785 gmic_inds,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",tolerance,opacity,color);
2786 cimg_foroff(indices,l) {
2787 CImg<T> &img = images[indices[l]];
2788 CImg<T> col(img.dimv(),1,1,1,0);
2789 col.fill(color,true);
2790 const int
2791 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
2792 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
2793 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1);
2794 gmic_apply(img,draw_fill(nx,ny,nz,col,opacity,tolerance));
2795 }
2796 } else error("Flood fill image%s : Invalid argument '%s' "
2797 "(should be 'x[,y[,z[,tolerance[,opacity[,color]]]]]').",gmic_inds,argument_text);
2798 ++position; continue;
2799 }
2801 //-------------------------
2802 // Image list manipulation
2803 //-------------------------
2805 // Remove specified image(s).
2806 if (!cimg::strcmp("-remove",item0) || !cimg::strcmp("-rm",item0)) {
2807 print("Remove image%s",gmic_inds);
2808 unsigned int off = 0;
2809 cimg_foroff(indices,l) {
2810 const unsigned int ind = indices[l] - off;
2811 images.remove(ind); filenames.remove(ind);
2812 ++off;
2813 }
2814 if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u image%s left).",images.size,images.size==1?"":"s");
2815 continue;
2816 }
2818 // Keep specified image(s).
2819 if (!cimg::strcmp("-keep",item0) || !cimg::strcmp("-k",item0)) {
2820 print("Keep image%s",gmic_inds);
2821 CImgList<T> nimages(indices.size());
2822 cimg_foroff(indices,l) nimages[l].swap(images[indices[l]]);
2823 nimages.transfer_to(images);
2824 if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u image%s left).",images.size,images.size==1?"":"s");
2825 continue;
2826 }
2828 // Move image(s) to specified position.
2829 if (!cimg::strcmp("-move",item0) || !cimg::strcmp("-mv",item0)) {
2830 int ind0 = no_ind; char end = 0;
2831 if (std::sscanf(argument,"%d%c",&ind0,&end)==1) {
2832 if (ind0<0) ind0+=images.size;
2833 if (ind0<0) ind0 = 0;
2834 if (ind0>(int)images.size) ind0 = images.size;
2835 print("Move image%s to position %d.",gmic_inds,ind0);
2836 CImgList<T> nimages;
2837 CImgList<char> nfilenames;
2838 cimg_foroff(indices,l) {
2839 const unsigned int ind = indices[l];
2840 nimages.insert(1); nimages.last().swap(images[ind]);
2841 nfilenames.insert(1); nfilenames.last().swap(filenames[ind]);
2842 }
2843 images.insert(nimages,ind0); filenames.insert(nfilenames,ind0);
2844 { cimglist_for(images,l) if (!images[l]) { images.remove(l); filenames.remove(l--); }}
2845 } else error("Move image%s : Invalid argument '%s' "
2846 "(should be 'position').",gmic_inds,argument_text);
2847 ++position; continue;
2848 }
2850 // Reverse images order.
2851 if (!cimg::strcmp("-reverse",item0)) {
2852 print("Reverse images order.");
2853 CImgList<T> nimages(indices.size());
2854 CImgList<char> nfilenames(indices.size());
2855 cimg_foroff(indices,l) { nimages[l].swap(images[indices[l]]); nfilenames[l].swap(filenames[indices[l]]); }
2856 nimages.reverse(); nfilenames.reverse();
2857 { cimg_foroff(indices,l) { nimages[l].swap(images[indices[l]]); nfilenames[l].swap(filenames[indices[l]]); }}
2858 continue;
2859 }
2861 // Set image name.
2862 if (!cimg::strcmp("-name",item0)) {
2863 cimg_foroff(indices,l) filenames[indices[l]].assign(argument,cimg::strlen(argument)+1,1,1,1,false);
2864 ++position; continue;
2865 }
2867 //-------------------------
2868 // 3D objects manipulation
2869 //-------------------------
2871 // Generate 3D cube.
2872 if (!cimg::strcmp("-cube3d",item)) {
2873 float size = 100; char end = 0;
2874 if (std::sscanf(argument,"%f%c",&size,&end)==1) {
2875 print("Generate 3D cube with size %g.",size);
2876 CImgList<unsigned int> primitives3d;
2877 CImg<float> points3d = CImg<T>::cube3d(primitives3d,size);
2878 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
2879 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
2880 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
2881 images.insert(points3d);
2882 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
2883 } else error("Generate 3D cube : Invalid argument '%s' "
2884 "(should be 'size').",argument_text);
2885 ++position; continue;
2886 }
2888 // Generate 3D cone.
2889 if (!cimg::strcmp("-cone3d",item)) {
2890 float radius = 100, height = 200; char end = 0; unsigned int subdivisions = 24;
2891 if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
2892 std::sscanf(argument,"%f%*c%f%c",&radius,&height,&end)==2 ||
2893 std::sscanf(argument,"%f%*c%f%*c%u%c",&radius,&height,&subdivisions,&end)==3) {
2894 print("Generate 3D cone with radius %g, height %g and %u subdivisions.",radius,height,subdivisions);
2895 CImgList<unsigned int> primitives3d;
2896 CImg<float> points3d = CImg<T>::cone3d(primitives3d,radius,height,subdivisions);
2897 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
2898 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
2899 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
2900 images.insert(points3d);
2901 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
2902 } else error("Generate 3D cone : Invalid argument '%s' "
2903 "(should be 'radius[,height[,subdivisions]]').",argument_text);
2904 ++position; continue;
2905 }
2907 // Generate 3D cylinder.
2908 if (!cimg::strcmp("-cylinder3d",item)) {
2909 float radius = 100, height = 200; char end = 0; unsigned int subdivisions = 24;
2910 if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
2911 std::sscanf(argument,"%f%*c%f%c",&radius,&height,&end)==2 ||
2912 std::sscanf(argument,"%f%*c%f%*c%u%c",&radius,&height,&subdivisions,&end)==3) {
2913 print("Generate 3D cylinder with radius %g, height %g and %u subdivisions.",radius,height,subdivisions);
2914 CImgList<unsigned int> primitives3d;
2915 CImg<float> points3d = CImg<T>::cylinder3d(primitives3d,radius,height,subdivisions);
2916 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
2917 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
2918 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
2919 images.insert(points3d);
2920 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
2921 } else error("Generate 3D cylinder : Invalid argument '%s' "
2922 "(should be 'radius[,height[,subdivisions]]').",argument_text);
2923 ++position; continue;
2924 }
2926 // Generate 3D torus.
2927 if (!cimg::strcmp("-torus3d",item)) {
2928 float radius1 = 100, radius2 = 30; char end = 0; unsigned int subdivisions1 = 24, subdivisions2 = 12;
2929 if (std::sscanf(argument,"%f%*c%f%c",&radius1,&radius2,&end)==2 ||
2930 std::sscanf(argument,"%f%*c%f%*c%u%*c%u%c",&radius1,&radius2,&subdivisions1,&subdivisions2,&end)==4) {
2931 print("Generate 3D torus with radii %g and %g, and subdivisions %u and %u.",radius1,radius2,subdivisions1,subdivisions2);
2932 CImgList<unsigned int> primitives3d;
2933 CImg<float> points3d = CImg<T>::torus3d(primitives3d,radius1,radius2,subdivisions1,subdivisions2);
2934 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
2935 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
2936 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
2937 images.insert(points3d);
2938 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
2939 } else error("Generate 3D torus : Invalid argument '%s' "
2940 "(should be 'radius1,radius2[,subdivisions1,subdivisions2]').",argument_text);
2941 ++position; continue;
2942 }
2944 // Generate 3D plane.
2945 if (!cimg::strcmp("-plane3d",item)) {
2946 float sizex = 100, sizey = 30; char end = 0; unsigned int subdivisionsx = 24, subdivisionsy = 12;
2947 if (std::sscanf(argument,"%f%*c%f%c",&sizex,&sizey,&end)==2 ||
2948 std::sscanf(argument,"%f%*c%f%*c%u%*c%u%c",&sizex,&sizey,&subdivisionsx,&subdivisionsy,&end)==4) {
2949 print("Generate 3D plane with dimensions %g and %g, and subdivisions %u and %u.",sizex,sizey,subdivisionsx,subdivisionsy);
2950 CImgList<unsigned int> primitives3d;
2951 CImg<float> points3d = CImg<T>::plane3d(primitives3d,sizex,sizey,subdivisionsx,subdivisionsy);
2952 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
2953 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
2954 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
2955 images.insert(points3d);
2956 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
2957 } else error("Generate 3D plane : Invalid argument '%s' "
2958 "(should be 'sizex,sizey[,subdivisionsx,subdivisionsy]').",argument_text);
2959 ++position; continue;
2960 }
2962 // Generate 3D sphere.
2963 if (!cimg::strcmp("-sphere3d",item)) {
2964 float radius = 100; char end = 0; unsigned int subdivisions = 3;
2965 if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
2966 std::sscanf(argument,"%f%*c%u%c",&radius,&subdivisions,&end)==2) {
2967 print("Generate 3D sphere with radius %g and %u subdivisions.",radius,subdivisions);
2968 CImgList<unsigned int> primitives3d;
2969 CImg<float> points3d = CImg<T>::sphere3d(primitives3d,radius,subdivisions);
2970 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
2971 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
2972 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
2973 images.insert(points3d);
2974 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
2975 } else error("Generate 3D sphere : Invalid argument '%s' "
2976 "(should be 'radius[,subdivisions]').",argument_text);
2977 ++position; continue;
2978 }
2980 // Build 3D elevation.
2981 if (!cimg::strcmp("-elevation3d",item0)) {
2982 float zfact = 0.2f; char end = 0, sep = 0; int ind0 = no_ind;
2983 if (std::sscanf(argument,"%f%c",&zfact,&end)==1 ||
2984 (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']')) {
2985 CImg<typename CImg<T>::Tfloat> elev;
2986 if (ind0!=no_ind) {
2987 gmic_check_indice(ind0,"Build 3D elevation of image%s");
2988 print("Build 3D elevation of image%s with elevation map [%d].",gmic_inds,ind0);
2989 if (images[ind0].dimv()==1) elev = images[ind0];
2990 else elev = images[ind0].get_pointwise_norm();
2991 } else print("Build 3D elevation of image%s with z-factor %g.",gmic_inds,zfact);
2992 cimg_foroff(indices,l) {
2993 CImg<T>& img = images[indices[l]];
2994 CImgList<unsigned int> primitives3d;
2995 CImgList<unsigned char> colors3d;
2996 CImg<float> opacities3d, points3d;
2997 if (elev) points3d = img.get_elevation3d(primitives3d,colors3d,elev);
2998 else {
2999 if (img.dimv()==1) (elev = img)*=zfact; else (elev = img.get_pointwise_norm())*=zfact;
3000 points3d = img.get_elevation3d(primitives3d,colors3d,elev);
3001 elev.assign();
3002 }
3003 opacities3d.assign(1,primitives3d.size,1,1,1);
3004 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
3005 gmic_apply(img,replace(points3d));
3006 }
3007 } else error("Build 3D elevation : invalid argument '%s' "
3008 "(should be 'z-factor' or '[indice]').",argument_text);
3009 ++position; continue;
3010 }
3012 // Build 3D isovalue.
3013 if (!cimg::strcmp("-isovalue3d",item0)) {
3014 float value = 0; char end = 0;
3015 if (std::sscanf(argument,"%f%c",&value,&end)==1) {
3016 print("Build 3D isovalue %g of image%s.",value,gmic_inds);
3017 cimg_foroff(indices,l) {
3018 const unsigned int ind = indices[l];
3019 CImg<T>& img = images[ind];
3020 CImg<float> points3d;
3021 CImgList<unsigned int> primitives3d;
3022 CImgList<unsigned char> colors3d;
3023 CImg<float> opacities3d;
3024 CImg<unsigned char> palette;
3025 palette.assign(3,img.dim,1,1,220).noise(35,1);
3026 if (img.dim==1) palette(0) = palette(1) = palette(2) = 255;
3027 else {
3028 palette(0,0) = 255; palette(1,0) = 30; palette(2,0) = 30;
3029 palette(0,1) = 30; palette(1,1) = 255; palette(2,1) = 30;
3030 if (img.dim>=3) palette(0,2) = 30; palette(1,2) = 30; palette(2,2) = 255;
3031 }
3032 cimg_forV(img,k) {
3033 CImgList<unsigned int> prims;
3034 const CImg<float> pts = img.get_shared_channel(k).get_isovalue3d(prims,value);
3035 if (pts) {
3036 points3d.append_object3d(primitives3d,pts,prims);
3037 colors3d.insert(prims.size,
3038 CImg<unsigned char>::vector(palette(0,k),palette(1,k),palette(2,k)));
3039 }
3040 }
3041 opacities3d.assign(1,primitives3d.size,1,1,1);
3042 if (!points3d)
3043 warning("Build 3D isovalue of image [%u] : Isovalue %g not found.",ind,value);
3044 else points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
3045 gmic_apply(img,replace(points3d));
3046 }
3047 } else error("Build 3D isovalue of image%s : Invalid argument '%s' "
3048 "(should be 'isovalue').",gmic_inds,argument_text);
3049 ++position; continue;
3050 }
3052 // Center a 3D object.
3053 if (!cimg::strcmp("-center3d",item0) || !cimg::strcmp("-c3d",item0)) {
3054 print("Center 3D object%s.",gmic_inds);
3055 cimg_foroff(indices,l) {
3056 const unsigned int ind = indices[l];
3057 if (!images[ind].is_CImg3d())
3058 error("Center 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
3059 gmic_apply(images[ind],centerCImg3d());
3060 }
3061 continue;
3062 }
3064 // Normalize a 3D object.
3065 if (!cimg::strcmp("-normalize3d",item0) || !cimg::strcmp("-n3d",item0)) {
3066 print("Normalize 3D object%s.",gmic_inds);
3067 cimg_foroff(indices,l) {
3068 const unsigned int ind = indices[l];
3069 if (!images[ind].is_CImg3d())
3070 error("Normalize 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
3071 gmic_apply(images[ind],normalizeCImg3d());
3072 }
3073 continue;
3074 }
3076 // Rotate a 3D object.
3077 if (!cimg::strcmp("-rotate3d",item0) || !cimg::strcmp("-rot3d",item0)) {
3078 float u = 0, v = 0, w = 1, angle = 0; char end = 0;
3079 if (std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&u,&v,&w,&angle,&end)==4) {
3080 print("Rotate 3D object%s around axis (%g,%g,%g) with angle %g.",gmic_inds,u,v,w,angle);
3081 const CImg<float> rot = CImg<float>::rotation_matrix(u,v,w,(float)(angle*cimg::valuePI/180));
3082 cimg_foroff(indices,l) {
3083 const unsigned int ind = indices[l];
3084 if (!images[ind].is_CImg3d())
3085 error("Rotate 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
3086 gmic_apply(images[ind],rotateCImg3d(rot));
3087 }
3088 } else error("Rotate 3D object%s : Invalid argument '%s' "
3089 "(should be 'u,v,w,angle').",gmic_inds,argument_text);
3090 ++position; continue;
3091 }
3093 // Add 3D objects together or translate a 3D object.
3094 if (!cimg::strcmp("-add3d",item0) || !cimg::strcmp("-+3d",item0)) {
3095 float tx = 0, ty = 0, tz = 0; int ind0 = no_ind; char sep = 0, end = 0;
3096 if (std::sscanf(argument,"%f%c",&tx,&end)==1 ||
3097 std::sscanf(argument,"%f%*c%f%c",&tx,&ty,&end)==2 ||
3098 std::sscanf(argument,"%f%*c%f%*c%f%c",&tx,&ty,&tz,&end)==3) {
3099 print("Translate 3D object%s with vector (%g,%g,%g).",gmic_inds,tx,ty,tz);
3100 cimg_foroff(indices,l) {
3101 const unsigned int ind = indices[l];
3102 if (!images[ind].is_CImg3d())
3103 error("Translate 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
3104 gmic_apply(images[ind],translateCImg3d(tx,ty,tz));
3105 }
3106 ++position;
3107 } else if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
3108 gmic_check_indice(ind0,"Merge object with 3D object%s.");
3109 const CImg<T> img0 = images[ind0];
3110 if (!img0.is_CImg3d()) error("Merge object [%d] with 3D object%s : Image [%d] is not a 3D object.",ind0,gmic_inds,ind0);
3111 print("Merge object [%d] with 3D object%s.",ind0,gmic_inds);
3112 cimg_foroff(indices,l) {
3113 const unsigned int ind = indices[l];
3114 const CImg<T> &img = images[ind];
3115 if (!img.is_CImg3d())
3116 error("Merge object [%d] with 3D object%s : Image [%d] is not a 3D object.",ind0,gmic_inds,ind);
3117 gmic_apply(images[ind],appendCImg3d(img0));
3118 }
3119 ++position;
3120 } else {
3121 print("Merge 3D object%s together.",gmic_inds);
3122 if (indices) {
3123 const unsigned int ind0 = indices[0];
3124 if (!images[ind0].is_CImg3d())
3125 error("Merge 3D object%s together : Image [%d] is not a 3D object.",gmic_inds,ind0);
3126 for (unsigned int siz = indices.size(), off = 0, l = 1; l<siz; ++l) {
3127 const unsigned int ind = indices[l] - off;
3128 if (!images[ind].is_CImg3d())
3129 error("Merge 3D object%s together : Image [%d] is not a 3D object.",gmic_inds,ind);
3130 images[ind0].appendCImg3d(images[ind]);
3131 images.remove(ind); filenames.remove(ind);
3132 ++off;
3133 }
3134 }
3135 }
3136 continue;
3137 }
3139 // Translate 3D object by the opposite vector.
3140 if (!cimg::strcmp("-sub3d",item0) || !cimg::strcmp("--3d",item0)) {
3141 float tx = 0, ty = 0, tz = 0; char end = 0;
3142 if (std::sscanf(argument,"%f%c",&tx,&end)==1 ||
3143 std::sscanf(argument,"%f%*c%f%c",&tx,&ty,&end)==2 ||
3144 std::sscanf(argument,"%f%*c%f%*c%f%c",&tx,&ty,&tz,&end)==3) {
3145 print("Translate 3D object%s with vector -(%g,%g,%g).",gmic_inds,tx,ty,tz);
3146 cimg_foroff(indices,l) {
3147 CImg<T>& img = images[indices[l]];
3148 CImgList<unsigned int> primitives3d;
3149 CImgList<unsigned char> colors3d;
3150 CImg<float> opacities3d;
3151 CImg<T> points3d;
3152 if (get_version) points3d.assign(img); else img.transfer_to(points3d);
3153 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
3154 points3d.get_shared_line(0)-=tx;
3155 points3d.get_shared_line(1)-=ty;
3156 points3d.get_shared_line(2)-=tz;
3157 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
3158 if (get_version) {
3159 images.insert(1); points3d.transfer_to(images.last());
3160 filenames.insert(filenames[indices[l]]);
3161 } else points3d.transfer_to(images[indices[l]]);
3162 }
3163 } else error("Translate 3D object%s : Invalid argument '%s' "
3164 "(should be 'tx,ty,tz').",gmic_inds,argument_text);
3165 ++position; continue;
3166 }
3168 // Scale a 3D object.
3169 bool divide = false;
3170 if (!cimg::strcmp("-mul3d",item0) || !cimg::strcmp("-*3d",item0) ||
3171 ((divide=true)==true && (!cimg::strcmp("-div3d",item0) || !cimg::strcmp("-/3d",item0)))) {
3172 float sx = 0, sy = 1, sz = 1; char end = 0;
3173 if ((std::sscanf(argument,"%f%c",&sx,&end)==1 && (sy = sz = sx),1) ||
3174 std::sscanf(argument,"%f%*c%f%c",&sx,&sy,&end)==2 ||
3175 std::sscanf(argument,"%f%*c%f%*c%f%c",&sx,&sy,&sz,&end)==3) {
3176 if (divide) print("Scale 3D object%s with factors (1/%g,1/%g,1/%g).",gmic_inds,sx,sy,sz);
3177 else print("Scale 3D object%s with factors (%g,%g,%g).",gmic_inds,sx,sy,sz);
3178 cimg_foroff(indices,l) {
3179 CImg<T>& img = images[indices[l]];
3180 CImgList<unsigned int> primitives3d;
3181 CImgList<unsigned char> colors3d;
3182 CImg<float> opacities3d;
3183 CImg<T> points3d;
3184 if (get_version) points3d.assign(img); else img.transfer_to(points3d);
3185 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
3186 if (divide) {
3187 points3d.get_shared_line(0)/=sx;
3188 points3d.get_shared_line(1)/=sy;
3189 points3d.get_shared_line(2)/=sz;
3190 } else {
3191 points3d.get_shared_line(0)*=sx;
3192 points3d.get_shared_line(1)*=sy;
3193 points3d.get_shared_line(2)*=sz;
3194 }
3195 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
3196 if (get_version) {
3197 images.insert(1); points3d.transfer_to(images.last());
3198 filenames.insert(filenames[indices[l]]);
3199 } else points3d.transfer_to(images[indices[l]]);
3200 }
3201 } else error("Scale 3D object%s : Invalid argument '%s' "
3202 "(should be 'fact' or 'factx,facty[,factz]').",gmic_inds,argument_text);
3203 ++position; continue;
3204 }
3206 // Set color of 3D object(s).
3207 if (!cimg::strcmp("-color3d",item0) || !cimg::strcmp("-col3d",item0)) {
3208 float R = 200, G = 200, B = 200, opacity = -1; char end = 0;
3209 if (std::sscanf(argument,"%f%*c%f%*c%f%c",&R,&G,&B,&end)==3 ||
3210 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&R,&G,&B,&opacity,&end)==4) {
3211 const bool set_opacity = (opacity>=0);
3212 R = (float)cimg::round(R,1); G = (float)cimg::round(G,1); B = (float)cimg::round(B,1);
3213 if (R<0) R = 0; if (R>255) R = 255;
3214 if (G<0) G = 0; if (G>255) G = 255;
3215 if (B<0) B = 0; if (B>255) B = 255;
3216 if (set_opacity) print("Set colors of 3D object%s to (%g,%g,%g) and opacity to %g.",gmic_inds,R,G,B,opacity);
3217 else print("Set color of 3D object%s to (%g,%g,%g).",gmic_inds,R,G,B);
3218 cimg_foroff(indices,l) {
3219 const unsigned int ind = indices[l];
3220 if (!images[ind].is_CImg3d())
3221 error("Set color of 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
3222 gmic_apply(images[ind],coloropacityCImg3d(R,G,B,opacity,true,set_opacity));
3223 }
3224 } else error("Set color of 3D object%s : Invalid argument '%s' "
3225 "(should be 'R,G,B[,opacity]').",gmic_inds,argument_text);
3226 ++position; continue;
3227 }
3229 // Set opacity of 3D object(s).
3230 if (!cimg::strcmp("-opacity3d",item0) || !cimg::strcmp("-opac3d",item0)) {
3231 float opacity = 1; char end = 0;
3232 if (std::sscanf(argument,"%f%c",&opacity,&end)==1) {
3233 print("Set opacity of 3D object%s to %g.",gmic_inds,opacity);
3234 cimg_foroff(indices,l) {
3235 const unsigned int ind = indices[l];
3236 if (!images[ind].is_CImg3d())
3237 error("Set opacity of 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
3238 gmic_apply(images[ind],coloropacityCImg3d(0,0,0,opacity,false,true));
3239 }
3240 } else error("Set opacity of 3D object%s : Invalid argument '%s' "
3241 "(should be 'opacity').",gmic_inds,argument_text);
3242 ++position; continue;
3243 }
3245 // Invert 3D orientation.
3246 if (!cimg::strcmp("-invert3d",item0) || !cimg::strcmp("-i3d",item0)) {
3247 print("Invert orientation of 3D object%s.",gmic_inds);
3248 cimg_foroff(indices,l) {
3249 CImg<T> &img = images[indices[l]];
3250 CImgList<unsigned int> primitives3d;
3251 CImgList<unsigned char> colors3d;
3252 CImg<float> opacities3d;
3253 CImg<T> points3d;
3254 if (get_version) points3d.assign(img); else img.transfer_to(points3d);
3255 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
3256 if (primitives3d) primitives3d.invert_object3d();
3257 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
3258 if (get_version) {
3259 images.insert(1); points3d.transfer_to(images.last());
3260 filenames.insert(filenames[indices[l]]);
3261 } else points3d.transfer_to(images[indices[l]]);
3262 }
3263 continue;
3264 }
3266 // Split 3D object(s) into 6 vector images {header,N,vertices,primitives,colors,opacities}
3267 if (!cimg::strcmp("-split3d",item0) || !cimg::strcmp("-s3d",item0)) {
3268 print("Split 3D object%s into its different characteristics.",gmic_inds);
3269 unsigned int off = 0;
3270 cimg_foroff(indices,l) {
3271 const unsigned int ind = indices[l] + off;
3272 CImg<T> &img = images[ind];
3273 const CImg<char> filename = filenames[ind];
3274 CImgList<unsigned int> primitives3d;
3275 CImgList<unsigned char> colors3d;
3276 CImg<float> opacities3d;
3277 CImg<T> points3d;
3278 if (get_version) points3d.assign(img); else img.transfer_to(points3d);
3279 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
3280 CImgList<T> split;
3281 split.insert(CImg<T>("CImg3d",1,6,1,1,false)+=0.5f);
3282 split.insert(CImg<T>::vector((T)points3d.dimx(),(T)primitives3d.size));
3283 split.insert(1); points3d.resize(-100,3,1,1,0).transpose().unroll('y').transfer_to(split.last());
3284 points3d.assign();
3285 CImgList<T> _prims;
3286 cimglist_for(primitives3d,p)
3287 _prims.insert(CImg<T>::vector((T)primitives3d[p].size())).insert(primitives3d[p]).last().unroll('y');
3288 primitives3d.assign();
3289 split.insert(_prims.get_append('y')); _prims.assign();
3290 split.insert(colors3d.get_append('x').transpose().unroll('y')); colors3d.assign();
3291 split.insert(1); opacities3d.transfer_to(split.last());
3292 if (get_version) {
3293 images.insert(split);
3294 filenames.insert(split.size,filename);
3295 } else {
3296 images.remove(ind); images.insert(split,ind);
3297 filenames.remove(ind); filenames.insert(split.size,filename,ind);
3298 off+=split.size-1;
3299 }
3300 }
3301 continue;
3302 }
3304 // Set 3D light position.
3305 if (!cimg::strcmp("-light3d",item) || !cimg::strcmp("-l3d",item)) {
3306 float lx = 0, ly = 0, lz = -5000; char end = 0;
3307 if (std::sscanf(argument,"%f%*c%f%*c%f%c",&lx,&ly,&lz,&end)==3) {
3308 print("Set 3D light position at (%g,%g,%g).",lx,ly,lz);
3309 light3d_x = lx;
3310 light3d_y = ly;
3311 light3d_z = lz;
3312 } else error("Set 3D light position : Invalid argument '%s' "
3313 "(should be 'posx,posy,posz').",argument_text);
3314 ++position; continue;
3315 }
3317 // Set 3D focale.
3318 if (!cimg::strcmp("-focale3d",item) || !cimg::strcmp("-f3d",item)) {
3319 float focale = 500; char end = 0;
3320 if (std::sscanf(argument,"%f%c",&focale,&end)==1) {
3321 focale3d = focale;
3322 print("Set 3D focale to %g.",focale);
3323 } else error("Set 3D focale : Invalid argument '%s' "
3324 "(should be 'value').");
3325 ++position; continue;
3326 }
3328 // Set 3D specular light parameters.
3329 if (!cimg::strcmp("-specl3d",item) || !cimg::strcmp("-sl3d",item)) {
3330 float value = 0; char end = 0;
3331 if (std::sscanf(argument,"%f%c",&value,&end)==1) {
3332 specular_light3d = value;
3333 print("Set amount of 3D specular light to %g.",specular_light3d);
3334 }
3335 else error("Set amount of 3D specular light : invalid argument '%s'"
3336 "(should be 'value').",
3337 argument_text);
3338 ++position; continue;
3339 }
3341 if (!cimg::strcmp("-specs3d",item) || !cimg::strcmp("-ss3d",item)) {
3342 float value = 0; char end = 0;
3343 if (std::sscanf(argument,"%f%c",&value,&end)==1) {
3344 specular_shine3d = value;
3345 print("Set shininess of 3D specular light to %g.",specular_shine3d);
3346 }
3347 else error("Set shininess of 3D specular light : invalid argument '%s'"
3348 "(should be 'value').",
3349 argument_text);
3350 ++position; continue;
3351 }
3353 // Switch double-sided mode for 3D rendering.
3354 if (!cimg::strcmp("-orient3d",item) || !cimg::strcmp("-o3d",item)) {
3355 is_oriented3d = !is_oriented3d;
3356 continue;
3357 }
3359 // Set 3D rendering mode.
3360 if (!cimg::strcmp("-render3d",item) || !cimg::strcmp("-r3d",item)) {
3361 unsigned int value = 0; char end = 0;
3362 if (std::sscanf(argument,"%u%c",&value,&end)==1) {
3363 render3d = value;
3364 print("Set static 3D render mode to %s.",
3365 render3d==-1?"bounding-box":
3366 render3d==0?"pointwise":render3d==1?"linear":render3d==2?"flat":
3367 render3d==3?"flat-shaded":render3d==4?"Gouraud-shaded":
3368 render3d==5?"Phong-shaded":"none");
3369 }
3370 else error("Set static 3D render mode : invalid argument '%s'"
3371 "(should be '{0=pointwise, 1=linear, 2=flat, 3=flat shaded, 4=Gouraud shaded, 5=Phong-shaded}').",
3372 argument_text);
3373 ++position; continue;
3374 }
3376 if (!cimg::strcmp("-renderd3d",item) || !cimg::strcmp("-rd3d",item)) {
3377 unsigned int value = 0; char end = 0;
3378 if (std::sscanf(argument,"%u%c",&value,&end)==1) {
3379 renderd3d = value;
3380 print("Set dynamic 3D render mode to %s.",
3381 renderd3d==-1?"bounding-box":
3382 renderd3d==0?"pointwise":renderd3d==1?"linear":renderd3d==2?"flat":
3383 renderd3d==3?"flat-shaded":renderd3d==4?"Gouraud-shaded":
3384 renderd3d==5?"Phong-shaded":"none");
3385 }
3386 else error("Set dynamic 3D render mode : invalid argument '%s'"
3387 "(should be '{0=pointwise, 1=linear, 2=flat, 3=flat shaded, 4=Gouraud shaded, 5=Phong-shaded}').",
3388 argument_text);
3389 ++position; continue;
3390 }
3392 // Set 3D background color.
3393 if (!cimg::strcmp("-background3d",item) || !cimg::strcmp("-b3d",item)) {
3394 int R = 0, G = 0, B = 0; char end = 0;
3395 const int nb = std::sscanf(argument,"%d%*c%d%*c%d%c",&R,&G,&B,&end);
3396 switch (nb) {
3397 case 1 : background3d[0] = background3d[1] = background3d[2] = R; break;
3398 case 2 : background3d[0] = R; background3d[1] = background3d[2] = G; break;
3399 case 3 : background3d[0] = R; background3d[1] = G; background3d[2] = B; break;
3400 default: error("Set 3D background color : Invalid argument '%s'.",argument_text);
3401 }
3402 print("Set 3D background color to (%d,%d,%d).",
3403 (int)background3d[0],(int)background3d[1],(int)background3d[2]);
3404 ++position; continue;
3405 }
3407 //----------------
3408 // Other commands.
3409 //----------------
3411 // No operations : do nothing
3412 if (!cimg::strcmp("-nop",item)) {
3413 continue;
3414 }
3416 // Skip next argument;
3417 if (!cimg::strcmp("-skip",item)) {
3418 ++position;
3419 continue;
3420 }
3422 // Echo.
3423 if (!cimg::strcmp("-echo",item) || !cimg::strcmp("-e",item)) {
3424 const int l = cimg::strlen(argument);
3425 if (l>=2 && argument[0]=='"' && argument[l-1]=='"') {
3426 if (l==2) print(""); else {
3427 CImg<char> nargument(argument+1,l-1,1,1,1,false);
3428 nargument(l-2)=0;
3429 print("%s",nargument.ptr());
3430 }
3431 } else print("%s",argument);
3432 ++position; continue;
3433 }
3435 // Print.
3436 if (!cimg::strcmp("-print",item0) || !cimg::strcmp("-p",item0)) {
3437 if (images.size) {
3438 print("Print image%s.\n\n",gmic_inds);
3439 char title[4096];
3440 if (verbosity_level>=0) cimg_foroff(indices,l) {
3441 const unsigned int ind = indices[l];
3442 std::sprintf(title,"image [%u] = '%s'",ind,filenames[ind].ptr());
3443 images[ind].print(title);
3444 }
3445 is_released = true;
3446 } else print("Print image[].");
3447 continue;
3448 }
3450 // Quit.
3451 if (!cimg::strcmp("-quit",item) || !cimg::strcmp("-q",item)) {
3452 print("Quit.");
3453 is_released = true;
3454 dowhile.assign();
3455 repeatdone.assign();
3456 position = command_line.size;
3457 continue;
3458 }
3460 // Do...while.
3461 if (!cimg::strcmp("-do",item)) {
3462 dowhile.insert(CImg<int>::vector((int)position));
3463 continue;
3464 }
3466 if (!cimg::strcmp("-while",item)) {
3467 double cond = 0; char end = 0;
3468 if (std::sscanf(argument,"%lf%c",&cond,&end)!=1) cond = 0;
3469 if (!dowhile) error("Directive '-while' is not associated with a '-do' command.");
3470 if (cond<=0) dowhile.remove();
3471 else { position = (unsigned int)dowhile.last()(0); continue; }
3472 ++position; continue;
3473 }
3475 // If..else..endif
3476 if (!cimg::strcmp("-if",item)) {
3477 double cond = 0; char end = 0;
3478 if (std::sscanf(argument,"%lf%c",&cond,&end)!=1) cond = 0;
3479 if (cond<=0) {
3480 for (int nbifs = 1; nbifs && position<command_line.size; ++position) {
3481 const char *it = command_line[position].ptr();
3482 if (!cimg::strcmp("-if",it)) ++nbifs;
3483 if (!cimg::strcmp("-endif",it)) --nbifs;
3484 if (!cimg::strcmp("-else",it) && nbifs==1) --nbifs;
3485 }
3486 continue;
3487 }
3488 ++position; continue;
3489 }
3490 if (!cimg::strcmp("-else",item)) {
3491 for (int nbifs = 1; nbifs && position<command_line.size; ++position) {
3492 if (!cimg::strcmp("-if",command_line[position].ptr())) ++nbifs;
3493 if (!cimg::strcmp("-endif",command_line[position].ptr())) --nbifs;
3494 }
3495 continue;
3496 }
3497 if (!cimg::strcmp("-endif",item)) continue;
3499 // Repeat...done
3500 if (!cimg::strcmp("-repeat",item)) {
3501 float fnb = 0; char end = 0;
3502 if (std::sscanf(argument,"%f%c",&fnb,&end)==1) {
3503 const int nb = (int)fnb;
3504 if (nb>0) repeatdone.insert(CImg<int>::vector((int)position+1,nb));
3505 else {
3506 int nbrepeats = 0;
3507 for (nbrepeats = 1; nbrepeats && position<command_line.size; ++position) {
3508 const char *it = command_line[position].ptr();
3509 if (!cimg::strcmp("-repeat",it)) ++nbrepeats;
3510 if (!cimg::strcmp("-done",it)) --nbrepeats;
3511 }
3512 if (nbrepeats && position>=command_line.size)
3513 error("Directive '-done' is missing after a '-repeat' command.");
3514 continue;
3515 }
3516 } else error("Repeat operation : Invalid argument '%s' "
3517 "(should be a number).",argument_text);
3518 ++position; continue;
3519 }
3521 if (!cimg::strcmp("-done",item)) {
3522 if (!repeatdone) error("Directive '-done' is not associated with a '-repeat' command.");
3523 if (--repeatdone.last()(1))
3524 position = (unsigned int)repeatdone.last()(0);
3525 else repeatdone.remove();
3526 continue;
3527 }
3529 // Check argument type
3530 if (!cimg::strcmp("-int",item)) {
3531 char it[4096], end = 0, sep = 0; int value = 0;
3532 if (*argument) for (const char *nargument = argument; *nargument; ) {
3533 const int nb = std::sscanf(nargument,"%4095[^,]%c",it,&sep);
3534 if (nb) {
3535 if (std::sscanf(it,"%d%c",&value,&end)==1) nargument+=cimg::strlen(it) + nb -1;
3536 else error("Argument '%s' is not an integer value.",it);
3537 } else error("Argument '%s' is not an integer value.",argument_text);
3538 }
3539 ++position; continue;
3540 }
3542 if (!cimg::strcmp("-float",item)) {
3543 char it[4096], end = 0, sep = 0; double value = 0;
3544 if (*argument) for (const char *nargument = argument; *nargument; ) {
3545 const int nb = std::sscanf(nargument,"%4095[^,]%c",it,&sep);
3546 if (nb) {
3547 if (std::sscanf(it,"%lf%c",&value,&end)==1) nargument+=cimg::strlen(it) + nb -1;
3548 else error("Argument '%s' is not a float value.",it);
3549 } else error("Argument '%s' is not a float value.",argument_text);
3550 }
3551 ++position; continue;
3552 }
3554 //--------------------------
3555 // Input/Output and Display
3556 //--------------------------
3558 // Display.
3559 if (!cimg::strcmp("-display",item0) || !cimg::strcmp("-d",item0)) {
3560 if (display_images(images,indices,true)) is_released = true;
3561 continue;
3562 }
3564 // Display 3D object.
3565 if (!cimg::strcmp("-display3d",item0) || !cimg::strcmp("-d3d",item0)) {
3566 if (display_objects3d(images,indices,true)) is_released = true;
3567 continue;
3568 }
3570 // Display as a graph plot.
3571 if (!cimg::strcmp("-plot",item0)) {
3572 int plot_type = 1, vertex_type = 1; double ymin = 0, ymax = 0, xmin = 0, xmax = 0; char end = 0;
3573 const int nb = std::sscanf(argument,"%d%*c%d%*c%lf%*c%lf%*c%lf%*c%lf%c",&plot_type,&vertex_type,&xmin,&xmax,&ymin,&ymax,&end);
3574 if (nb==1 || nb==2 || nb==4 || nb==6) ++position;
3575 else { plot_type = 1; vertex_type = 0; ymin = ymax = xmin = xmax = 0; }
3576 is_released |= display_plots(images,indices,plot_type,vertex_type,xmin,xmax,ymin,ymax,true);
3577 continue;
3578 }
3580 // Select image feature.
3581 if (!cimg::strcmp("-select",item0)) {
3582 int select_type = 0; char end = 0;
3583 if (std::sscanf(argument,"%d%c",&select_type,&end)==1) {
3584 cimg_foroff(indices,l) gmic_apply(images[indices[l]],select(filenames[indices[l]].ptr(),select_type));
3585 } else error("Select image%s : Invalid argument '%s' "
3586 "(should be 'select_type').",gmic_inds,argument_text);
3587 ++position; continue;
3588 }
3590 // Output.
3591 if (!cimg::strcmp("-output",item0) || !cimg::strcmp("-o",item0)) {
3592 char filename[4096] = { 0 }; char options[4096] = { 0 };
3593 if (std::sscanf(argument,"%4095[^,],%s",filename,options)!=2) std::strcpy(filename,argument);
3594 const char *const ext = cimg::split_filename(filename);
3595 if (!cimg::strcasecmp("off",ext)) {
3596 char nfilename[4096] = { 0 };
3597 std::strcpy(nfilename,filename);
3598 const unsigned int siz = indices.size();
3599 cimg_foroff(indices,l) {
3600 const unsigned int ind = indices[l];
3601 if (siz!=1) cimg::number_filename(filename,l,6,nfilename);
3602 if (!images[ind].is_CImg3d())
3603 error("Output 3D object [%u] as file '%s' : Image [%u] is not a 3D object.",ind,nfilename,ind);
3604 print("Output 3D object [%u] as file '%s'.",ind,nfilename);
3605 CImgList<unsigned int> primitives3d;
3606 CImgList<unsigned char> colors3d;
3607 CImg<float> opacities3d;
3608 CImg<float> points3d(images[ind]);
3609 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d).save_off(nfilename,primitives3d,colors3d);
3610 }
3611 } else if (!cimg::strcasecmp("jpeg",ext) || !cimg::strcasecmp("jpg",ext)) {
3612 int quality = 100; char end = 0;
3613 if (std::sscanf(options,"%d%c",&quality,&end)!=1) quality = 100;
3614 if (quality<0) quality = 0; else if (quality>100) quality = 100;
3615 CImgList<T> output_images;
3616 cimg_foroff(indices,l) output_images.insert(images[indices[l]],~0U,true);
3617 print("Output image%s as file '%s', with quality %u%%",gmic_inds,filename,quality);
3618 if (!output_images) throw CImgInstanceException("CImgList<%s>::save() : File '%s, instance list (%u,%p) is empty.",
3619 output_images.pixel_type(),filename,
3620 output_images.size,output_images.data);
3621 if (output_images.size==1) output_images[0].save_jpeg(filename,quality);
3622 else {
3623 char nfilename[1024];
3624 cimglist_for(output_images,l) {
3625 cimg::number_filename(filename,l,6,nfilename);
3626 output_images[l].save_jpeg(nfilename,quality);
3627 }
3628 }
3629 } else {
3630 CImgList<T> output_images;
3631 cimg_foroff(indices,l) output_images.insert(images[indices[l]],~0U,true);
3632 print("Output image%s as file '%s'.",gmic_inds,filename);
3633 output_images.save(filename);
3634 }
3635 is_released = true; ++position; continue;
3636 }
3638 // Substitute macros commands if necessary.
3639 if (cimg::strcmp("-i",item0) && cimg::strcmp("-input",item0)) {
3640 bool macro_found = false;
3641 cimglist_for(macros,l) {
3642 const char
3643 *const macro = macros[l].ptr(),
3644 *const command = commands[l].ptr();
3646 if (!cimg::strcmp(item+1,macro) && *command) {
3647 CImgList<char> arguments(256);
3648 unsigned int nb_arguments = 0;
3649 char s_argument[4096] = { 0 }, tmp[4096] = { 0 }, tmp2[4096] = { 0 };
3650 bool has_arguments = false;
3651 macro_found = true;
3652 debug("Found macro '%s', substituting by '%s'.",macro,command);
3654 // Get command-line values of macro arguments.
3655 if (argument)
3656 for (const char *nargument = argument; nb_arguments<256 && *nargument &&
3657 std::sscanf(nargument,"%4095[^,]",s_argument)==1;) {
3658 CImg<char>(s_argument,cimg::strlen(s_argument)+1,1,1,1,false).transfer_to(arguments[nb_arguments++]);
3659 nargument+=cimg::strlen(s_argument);
3660 if (*nargument) ++nargument;
3661 }
3663 // Substitute arguments in macro command expression.
3664 CImg<char> substituted_command;
3665 CImgList<char> lreplacement;
3666 for (const char *ncommand = command; *ncommand;) if (*ncommand=='$') {
3667 char *replace_text = 0, sep = 0;
3668 int ind = 0, ind1 = 0;
3670 // Replace $# and ${#}.
3671 if (ncommand[1]=='#' || (ncommand[1]=='{' && ncommand[2]=='#' && ncommand[3]=='}')) {
3672 std::sprintf(replace_text=s_argument,"%u",nb_arguments);
3673 ncommand+=(ncommand[1]=='#')?2:4;
3674 has_arguments = true;
3676 // Replace $* and ${*}.
3677 } else if (ncommand[1]=='*' || (ncommand[1]=='{' && ncommand[2]=='*' && ncommand[3]=='}')) {
3678 replace_text = &(s_argument[0]=0);
3679 for (unsigned int j = 1; j<=nb_arguments; ++j) {
3680 replace_text+=std::sprintf(replace_text,"%s",arguments[j-1].ptr());
3681 if (j<nb_arguments) *(replace_text++) = ',';
3682 }
3683 replace_text = s_argument;
3684 ncommand+=(ncommand[1]=='*')?2:4;
3685 has_arguments = true;
3687 // Replace ${i*}.
3688 } else if (std::sscanf(ncommand,"${%d*%c",&ind,&sep)==2 &&
3689 ind>0 && ind<256 && sep=='}') {
3690 replace_text = &(s_argument[0]=0);
3691 for (unsigned int j = ind; j<=nb_arguments; ++j) {
3692 replace_text+=std::sprintf(replace_text,"%s",arguments[j-1].ptr());
3693 if (j<nb_arguments) *(replace_text++) = ',';
3694 }
3695 replace_text = s_argument;
3696 ncommand+=std::sprintf(tmp,"${%d*}",ind);
3697 has_arguments = true;
3699 // Replace $i and ${i}.
3700 } else if ((std::sscanf(ncommand,"$%d",&ind)==1 ||
3701 (std::sscanf(ncommand,"${%d%c",&ind,&sep)==2 && sep=='}')) &&
3702 ind>0 && ind<256) {
3703 if (!arguments[ind-1]) {
3704 if (sep=='}') error("Macro '%s' : Argument '$%d' is undefined (in expression '${%d}').",macro,ind,ind);
3705 else error("Macro '%s' : Argument '$%d' is undefined (in expression '$%d').",macro,ind,ind);
3706 }
3707 replace_text = arguments[ind-1].ptr();
3708 ncommand+=std::sprintf(tmp,"$%d",ind) + (sep=='}'?2:0);
3709 has_arguments = true;
3711 // Replace ${i=$#}.
3712 } else if (std::sscanf(ncommand,"${%d=$#%c",&ind,&sep)==2 &&
3713 ind>0 && ind<256 && sep=='}') {
3714 std::sprintf(replace_text=s_argument,"%g",(double)nb_arguments);
3715 CImg<char>(s_argument,cimg::strlen(s_argument)+1,1,1,1,false).transfer_to(arguments[ind-1]);
3716 ncommand+=std::sprintf(tmp,"${%d=$#}",ind);
3717 has_arguments = true;
3719 // Replace ${i=$j}.
3720 } else if (std::sscanf(ncommand,"${%d=$%d%c",&ind,&ind1,&sep)==3 && sep=='}' &&
3721 ind>0 && ind<256 && ind1>0 && ind1<256) {
3722 if (!arguments[ind1-1])
3723 error("Macro '%s' : Argument '$%d' is undefined (in expression '${%d=$%d}').",macro,ind1,ind,ind1);
3724 if (!arguments[ind-1]) arguments[ind-1] = arguments[ind1-1];
3725 replace_text = arguments[ind-1].ptr();
3726 ncommand+=std::sprintf(tmp,"${%d=$%d}",ind,ind1);
3727 has_arguments = true;
3729 // Replace ${i=default}.
3730 } else if (std::sscanf(ncommand,"${%d=%4095[^}]%c",&ind,tmp,&sep)==3 && sep=='}' &&
3731 ind>0 && ind<256) {
3732 if (!arguments[ind-1]) CImg<char>(tmp,cimg::strlen(tmp)+1,1,1,1,false).transfer_to(arguments[ind-1]);
3733 replace_text = arguments[ind-1].ptr();
3734 ncommand+=cimg::strlen(tmp) + 4 + std::sprintf(tmp2,"%d",ind);
3735 has_arguments = true;
3737 // Any other expression starting by '$'.
3738 } else {
3739 replace_text = &(s_argument[0]='$');
3740 if (std::sscanf(ncommand,"%4095[^$]",s_argument+1)!=1) { s_argument[1] = 0; ++ncommand; }
3741 else ncommand+=cimg::strlen(s_argument);
3742 }
3744 const int replace_length = cimg::strlen(replace_text);
3745 if (replace_length) {
3746 lreplacement.insert(1);
3747 CImg<char>(replace_text,replace_length,1,1,1,false).transfer_to(lreplacement.last());
3748 }
3750 } else {
3751 std::sscanf(ncommand,"%4095[^$]",s_argument);
3752 const int replace_length = cimg::strlen(s_argument);
3753 if (replace_length) {
3754 lreplacement.insert(1);
3755 CImg<char>(s_argument,replace_length,1,1,1,false).transfer_to(lreplacement.last());
3756 ncommand+=cimg::strlen(s_argument);
3757 }
3758 }
3759 const CImg<char> zero(1,1,1,1,0);
3760 lreplacement.insert(zero).get_append('x').transfer_to(substituted_command);
3762 // Substitute macro expression in command line.
3763 bool is_dquote = false;
3764 cimg_foroff(substituted_command,k)
3765 if (substituted_command[k]=='"') is_dquote = !is_dquote;
3766 else if (is_dquote && substituted_command[k]==' ') substituted_command[k] = 30;
3767 CImgList<char> command_items = substituted_command.get_split(' ',false,false);
3768 cimglist_for(command_items,k) {
3769 CImg<char> &item = command_items[k];
3770 cimg_foroff(item,l) if (item[l]==30) item[l]=' ';
3771 }
3772 cimglist_for(command_items,k) command_items[k].append(zero,'y');
3773 if (position<command_line.size && has_arguments) command_line.remove(position);
3774 command_line.remove(--position);
3775 command_line.insert(command_items,position);
3776 break;
3777 }
3778 }
3779 if (macro_found) continue;
3780 }
3781 }
3783 // Input.
3784 if (!cimg::strcmp("-i",item0) || !cimg::strcmp("-input",item0)) ++position;
3785 else { if (get_version) --item; argument = item; item1[0] = 0; }
3786 if (!cimg::strlen(item1)) indices.assign(1,1,1,1,images.size);
3787 CImgList<T> input_images;
3788 CImgList<char> input_filenames;
3789 bool obj3d = false;
3790 char st_inds[4096] = { 0 }, stx[4096] = { 0 }, sty[4096] = { 0 }, stz[4096] = { 0 }, stv[4096] = { 0 };
3791 char end = 0, sep = 0, sepx = 0, sepy = 0, sepz = 0, sepv = 0;
3792 int nb = 1, indx = no_ind, indy = no_ind, indz = no_ind, indv = no_ind;
3793 float dx = 0, dy = 1, dz = 1, dv = 1;
3795 if (std::sscanf(argument,"[%4095[0-9%,:-]]%*c%d%c",st_inds,&nb,&end)==2 ||
3796 (std::sscanf(argument,"[%4095[0-9%,:-]%c%c",st_inds,&sep,&end)==2 && sep==']')) {
3798 // nb copies of existing sub-images.
3799 const CImg<unsigned int> indices0 = indices2cimg(st_inds,images.size,"-input");
3800 char st_tmp[4096] = { 0 }; std::strcpy(st_tmp,indices2string(indices0,true));
3801 if (nb<=0) error("Input %d copies of image%s : Invalid argument '%s'.",
3802 nb,st_tmp,argument_text);
3803 if (nb!=1) print("Input %d copies of image%s at position%s",nb,st_tmp,gmic_inds);
3804 else print("Input copy of image%s at position%s",st_tmp,gmic_inds);
3805 for (int i = 0; i<nb; ++i) cimg_foroff(indices0,l) {
3806 input_images.insert(images[indices0[l]]);
3807 input_filenames.insert(filenames[indices0[l]]);
3808 }
3809 } else if (((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%d%c",
3810 stx,sty,stz,stv,&(nb=1),&end)==5 ||
3811 std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
3812 stx,sty,stz,stv,&end)==4) &&
3813 (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
3814 (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
3815 (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
3816 (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
3817 (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
3818 (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']')) &&
3819 (std::sscanf(stz,"%f%c",&dz,&end)==1 ||
3820 (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%') ||
3821 (std::sscanf(stz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']')) &&
3822 (std::sscanf(stv,"%f%c",&dv,&end)==1 ||
3823 (std::sscanf(stv,"%f%c%c",&dv,&sepv,&end)==2 && sepv=='%') ||
3824 (std::sscanf(stv,"[%d%c%c",&indv,&sepv,&end)==2 && sepv==']'))) ||
3825 (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",stx,sty,stz,&end)==3 &&
3826 (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
3827 (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
3828 (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
3829 (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
3830 (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
3831 (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']')) &&
3832 (std::sscanf(stz,"%f%c",&dz,&end)==1 ||
3833 (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%') ||
3834 (std::sscanf(stz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']'))) ||
3835 (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",stx,sty,&end)==2 &&
3836 (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
3837 (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
3838 (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
3839 (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
3840 (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
3841 (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']'))) ||
3842 (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",stx,&end)==1 &&
3843 (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
3844 (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
3845 (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')))) {
3847 // nb new black image.
3848 if (indx!=no_ind) { gmic_check_indice(indx,"Input black image%s"); dx = (float)images[indx].dimx(); sepx = 0; }
3849 if (indy!=no_ind) { gmic_check_indice(indy,"Input black image%s"); dy = (float)images[indy].dimy(); sepy = 0; }
3850 if (indz!=no_ind) { gmic_check_indice(indz,"Input black image%s"); dz = (float)images[indz].dimz(); sepz = 0; }
3851 if (indv!=no_ind) { gmic_check_indice(indv,"Input black image%s"); dv = (float)images[indv].dimv(); sepv = 0; }
3852 if (sepx=='%') { dx = images.size?dx*images.last().dimx()/100:0; if (!(int)dx) ++dx; }
3853 if (sepy=='%') { dy = images.size?dy*images.last().dimy()/100:0; if (!(int)dy) ++dy; }
3854 if (sepz=='%') { dz = images.size?dz*images.last().dimz()/100:0; if (!(int)dz) ++dz; }
3855 if (sepv=='%') { dv = images.size?dv*images.last().dimv()/100:0; if (!(int)dv) ++dv; }
3857 if (nb<=0) error("Input %d black image%s : Invalid number of copies.",nb,gmic_inds);
3858 if (dx<=0 || dy<=0 || dz<=0 || dv<=0)
3859 error("Input %d black image%s : Invalid image dimensions %gx%gx%gx%g.",
3860 nb,gmic_inds,dx,dy,dz,dv);
3861 if (nb!=1) print("Input %d black images at position%s",nb,gmic_inds);
3862 else print("Input black image at position%s",gmic_inds);
3863 CImg<T> empty((int)dx,(int)dy,(int)dz,(int)dv,0);
3864 input_images.insert(nb-1,empty); input_images.insert(1);
3865 input_images.last().swap(empty);
3866 filenames.insert(input_images.size,CImg<char>("(gmic)",7,1,1,1,false));
3867 } else if (std::sscanf(argument,"(%4095[^)])x%d%c",stx,&(nb=1),&end)==2 ||
3868 (std::sscanf(argument,"(%4095[^)]%c%c",stx,&sep,&end)==2 && sep==')')) {
3870 // Insert nb IxJxKxL image(s) with specified values.
3871 if (nb<=0) error("Input %d images : Invalid number of copies.",nb);
3872 unsigned int cx = 0, cy = 0, cz = 0, cv = 0, maxcx = 0, maxcy = 0, maxcz = 0;
3873 const char *nargument = 0;
3874 for (nargument = argument+1; *nargument!=')'; ) {
3875 char s_value[256] = { 0 }, separator = 0; double value = 0;
3876 if (std::sscanf(nargument,"%255[0-9.eE+-]%c",s_value,&separator)==2 && std::sscanf(s_value,"%lf",&value)==1) {
3877 if (cx>maxcx) maxcx = cx;
3878 if (cy>maxcy) maxcy = cy;
3879 if (cz>maxcz) maxcz = cz;
3880 switch (separator) {
3881 case '^' : cx = cy = cz = 0; ++cv; break;
3882 case '/' : cx = cy = 0; ++cz; break;
3883 case ';' : cx = 0; ++cy; break;
3884 default : ++cx;
3885 }
3886 nargument+=cimg::strlen(s_value) + (separator==')'?0:1);
3887 } else break;
3888 }
3889 if (*nargument!=')') error("Input %d images : Invalid input string '%s'.",nb,argument);
3891 CImg<T> img(maxcx+1,maxcy+1,maxcz+1,cv+1,0);
3892 cx = cy = cz = cv = 0;
3893 for (nargument = argument+1; *nargument; ) {
3894 char s_value[256] = { 0 }, separator = 0; double value = 0;
3895 if (std::sscanf(nargument,"%255[0-9.eE+-]%c",s_value,&separator)==2 && std::sscanf(s_value,"%lf",&value)==1) {
3896 img(cx,cy,cz,cv) = (T)value;
3897 switch (separator) {
3898 case '^' : cx = cy = cz = 0; ++cv; break;
3899 case '/' : cx = cy = 0; ++cz; break;
3900 case ';' : cx = 0; ++cy; break;
3901 default : ++cx;
3902 }
3903 nargument+=cimg::strlen(s_value) + (separator==')'?0:1);
3904 } else break;
3905 }
3906 if (nb==1) print("Input image %dx%dx%dx%d",img.dimx(),img.dimy(),img.dimz(),img.dimv());
3907 else print("Input %d images %dx%d",nb,img.dimx(),img.dimy(),img.dimz(),img.dimv());
3908 input_images.insert(nb,img); filenames.insert(nb,CImg<char>("(gmic)",7,1,1,1,false));
3909 } else {
3911 // Insert image as a loaded filename.
3912 char filename[4096] = { 0 }, options[4096] = { 0 }; const char *ext = 0, *basename = 0;
3913 if (argument[0]!='-' || (argument[1] && argument[1]!='.')) {
3914 std::FILE *file = std::fopen(argument,"r");
3915 if (file) { std::fclose(file); std::strcpy(filename,argument); }
3916 else {
3917 std::sscanf(argument,"%4095[^,],%s",filename,options);
3918 if (!(file=std::fopen(filename,"r"))) {
3919 if (filename[0]=='-') error("Input '%s' : Command not found.",filename);
3920 else error("Input '%s' : File not found.",filename);
3921 }
3922 std::fclose(file);
3923 }
3924 } else std::strcpy(filename,argument);
3925 basename = cimg::basename(filename);
3926 ext = cimg::split_filename(filename);
3928 if (!cimg::strcasecmp("off",ext)) { // 3D object file.
3929 print("Input 3D object '%s'",filename);
3930 CImgList<unsigned int> primitives3d;
3931 CImgList<unsigned char> colors3d;
3932 CImg<float> opacities3d, points3d = CImg<float>::get_load_off(filename,primitives3d,colors3d);
3933 opacities3d.assign(1,primitives3d.size,1,1,1);
3934 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
3935 input_images.insert(1); points3d.transfer_to(input_images[0]);
3936 input_filenames.insert(CImg<char>(is_fullpath?filename:basename,
3937 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
3938 obj3d = true;
3939 } else if (!cimg::strcasecmp(ext,"avi") ||
3940 !cimg::strcasecmp(ext,"mov") ||
3941 !cimg::strcasecmp(ext,"asf") ||
3942 !cimg::strcasecmp(ext,"divx") ||
3943 !cimg::strcasecmp(ext,"flv") ||
3944 !cimg::strcasecmp(ext,"mpg") ||
3945 !cimg::strcasecmp(ext,"m1v") ||
3946 !cimg::strcasecmp(ext,"m2v") ||
3947 !cimg::strcasecmp(ext,"m4v") ||
3948 !cimg::strcasecmp(ext,"mjp") ||
3949 !cimg::strcasecmp(ext,"mkv") ||
3950 !cimg::strcasecmp(ext,"mpe") ||
3951 !cimg::strcasecmp(ext,"movie") ||
3952 !cimg::strcasecmp(ext,"ogm") ||
3953 !cimg::strcasecmp(ext,"qt") ||
3954 !cimg::strcasecmp(ext,"rm") ||
3955 !cimg::strcasecmp(ext,"vob") ||
3956 !cimg::strcasecmp(ext,"wmv") ||
3957 !cimg::strcasecmp(ext,"xvid") ||
3958 !cimg::strcasecmp(ext,"mpeg")) {
3959 unsigned int value0 = 0, value1 = 0, step = 1; char sep0 = 0, sep1 = 0, end = 0;
3960 if ((std::sscanf(options,"%u%c%*c%u%c%*c%u%c",&value0,&sep0,&value1,&sep1,&step,&end)==5 && sep0=='%' && sep1=='%') ||
3961 (std::sscanf(options,"%u%c%*c%u%*c%u%c",&value0,&sep0,&value1,&step,&end)==4 && sep0=='%') ||
3962 (std::sscanf(options,"%u%*c%u%c%*c%u%c",&value0,&value1,&sep1,&step,&end)==4 && sep1=='%') ||
3963 (std::sscanf(options,"%u%*c%u%*c%u%c",&value0,&value1,&step,&end)==3) ||
3964 (std::sscanf(options,"%u%c%*c%u%c%c",&value0,&sep0,&value1,&sep1,&end)==4 && sep0=='%' && sep1=='%') ||
3965 (std::sscanf(options,"%u%c%*c%u%c",&value0,&sep0,&value1,&end)==3 && sep0=='%') ||
3966 (std::sscanf(options,"%u%*c%u%c%c",&value0,&value1,&sep1,&end)==3 && sep1=='%') ||
3967 (std::sscanf(options,"%u%*c%u%c",&value0,&value1,&end)==2)) { // Read several frames
3968 print("Input frames %u%s...%u%s with step %u of file '%s'",
3969 value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",step,filename);
3970 if (sep0=='%' || sep1=='%') {
3971 const unsigned int nb_frames = CImg<unsigned int>::get_load_ffmpeg(filename,0,0,0)[0];
3972 if (sep0=='%') value0 = value0*nb_frames/100;
3973 if (sep1=='%') value1 = value1*nb_frames/100;
3974 }
3975 } else if ((std::sscanf(options,"%u%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
3976 (std::sscanf(options,"%u%c",&value0,&end)==1)) { // Read one frame
3977 print("Input frame %u%s of file '%s'",value0,sep0=='%'?"%":"",filename);
3978 if (sep0=='%') {
3979 const unsigned int nb_frames = CImg<unsigned int>::get_load_ffmpeg(filename,0,0,0)[0];
3980 value0 = value0*nb_frames/100;
3981 }
3982 value1 = value0; step = 1;
3983 } else { // Read all frames
3984 print("Input all frames of file '%s'",filename);
3985 value0 = 0; value1 = ~0U; sep0 = sep1 = 0; step = 1;
3986 }
3987 input_images.load_ffmpeg(filename,value0,value1,step);
3988 if (input_images)
3989 input_filenames.insert(input_images.size,CImg<char>(is_fullpath?filename:basename,
3990 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
3991 } else if (!cimg::strcasecmp("raw",ext)) { // Raw file.
3992 int dx = 0, dy = 1, dz = 1, dv = 1;
3993 if (std::sscanf(options,"%d%*c%d%*c%d%*c%d",&dx,&dy,&dz,&dv)>0) {
3994 if (dx<=0 || dy<=0 || dz<=0 || dv<=0)
3995 error("Input raw file '%s' : Invalid specified dimensions %dx%dx%dx%d.",filename,dx,dy,dz,dv);
3996 print("Input raw file '%s'",filename);
3997 input_images.insert(1); input_images[0].load_raw(filename,dx,dy,dz,dv);
3998 input_filenames.insert(CImg<char>(is_fullpath?filename:basename,
3999 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
4000 } else error("Input raw file '%s' : Image dimensions must be specified.",filename);
4001 } else if (!cimg::strcasecmp("yuv",ext)) { // YUV file.
4002 int dx = 0, dy = 0; unsigned int first = 0, last = ~0U, step = 1;
4003 if (std::sscanf(options,"%d%*c%d%*c%u%*c%u%*c%u",&dx,&dy,&first,&last,&step)>0) {
4004 if (dx<=0 || dy<=0)
4005 error("Input yuv file '%s' : Invalid specified dimensions %dx%d.",filename,dx,dy);
4006 print("Input yuv file '%s'",filename);
4007 input_images.load_yuv(filename,dx,dy,first,last,step);
4008 input_filenames.insert(input_images.size,CImg<char>(is_fullpath?filename:basename,
4009 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
4010 } else error("Input yuv file '%s' : Image dimensions must be specified.",filename);
4011 } else { // Other file type.
4012 print("Input file '%s'",filename);
4013 input_images.load(filename);
4014 input_filenames.insert(input_images.size,
4015 CImg<char>(is_fullpath?filename:basename,
4016 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
4017 }
4018 }
4020 if (verbosity_level>=0) {
4021 if (input_images) {
4022 const unsigned int last = input_images.size-1;
4023 if (obj3d)
4024 std::fprintf(cimg_stdout," (%d points, %u primitives, %u colors).",
4025 (unsigned int)input_images(0,6),
4026 (unsigned int)input_images(0,7),
4027 (unsigned int)input_images(0,8));
4028 else if (input_images.size==1)
4029 std::fprintf(cimg_stdout," (1 image %ux%ux%ux%u).",
4030 input_images[0].width,input_images[0].height,input_images[0].depth,
4031 input_images[0].dim);
4032 else std::fprintf(cimg_stdout," (%u images [0] = %ux%ux%ux%u, %s[%u] = %ux%ux%ux%u).",
4033 input_images.size,
4034 input_images[0].width,input_images[0].height,input_images[0].depth,
4035 input_images[0].dim,
4036 last==1?"":"...,",
4037 last,
4038 input_images[last].width,input_images[last].height,input_images[last].depth,
4039 input_images[last].dim);
4040 } else std::fprintf(cimg_stdout," (no available data).");
4041 }
4043 for (unsigned int l = 0, siz = indices.size()-1, off = 0; l<=siz; ++l) {
4044 const unsigned int ind = indices[l] + off;
4045 if (l!=siz) images.insert(input_images,ind);
4046 else {
4047 images.insert(input_images.size,ind);
4048 cimglist_for(input_images,k) images[ind+k].swap(input_images[k]);
4049 }
4050 filenames.insert(input_filenames,ind);
4051 off+=input_images.size;
4052 }
4054 } catch (CImgException &e) {
4055 const char *error_message = e.message;
4056 char tmp[4096] = { 0 }, sep = 0;
4057 if (std::sscanf(error_message,"%4095[^>]>:%c",tmp,&sep)==2 && sep==':') error_message+=cimg::strlen(tmp)+3;
4058 error(error_message);
4059 }
4060 }
4062 // Check if command line has grown too much (possible recursive macro calls).
4063 if (command_line.size>=command_line_maxsize)
4064 error("Command line overflow : There are too much commands specified (possible recursive macro substitution).");
4066 // Check if some loops have not been terminated.
4067 if (dowhile) warning("A '-while' directive is missing somewhere.");
4068 if (repeatdone) warning("A '-done' directive is missing somewhere.");
4070 // Display final result if necessary (not 'released' before).
4071 if (images.size && !is_released) {
4072 if (!display_objects3d(images,CImg<unsigned int>::sequence(images.size,0,images.size-1),false))
4073 display_images(images,CImg<unsigned int>::sequence(images.size,0,images.size-1),true);
4074 }
4076 print("End G'MIC instance.\n");
4077 return *this;
4078 }
4080 // Small hack to separate the compilation of G'MIC in different pixel types.
4081 // (only intended to save computer memory when compiling !)
4082 //--------------------------------------------------------------------------
4083 #ifdef gmic_minimal
4084 gmic& gmic::parse_float(CImgList<float>& images) { return parse(images); }
4085 template gmic::gmic(const int, const char *const *const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
4086 template gmic::gmic(const char* const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
4087 #else
4088 #if defined(gmic_bool) || !defined(gmic_separate_compilation)
4089 gmic& gmic::parse_bool(CImgList<bool>& images) { return parse(images); }
4090 template gmic::gmic(const int, const char *const *const, CImgList<bool>&, const char *const custom_macros, const bool add_macros_at_start);
4091 template gmic::gmic(const char* const, CImgList<bool>&, const char *const custom_macros, const bool add_macros_at_start);
4092 #endif
4093 #if defined(gmic_uchar) || !defined(gmic_separate_compilation)
4094 gmic& gmic::parse_uchar(CImgList<unsigned char>& images) { return parse(images); }
4095 template gmic::gmic(const int, const char *const *const, CImgList<unsigned char>&, const char *const custom_macros, const bool add_macros_at_start);
4096 template gmic::gmic(const char* const, CImgList<unsigned char>&, const char *const custom_macros, const bool add_macros_at_start);
4097 #endif
4098 #if defined(gmic_char) || !defined(gmic_separate_compilation)
4099 gmic& gmic::parse_char(CImgList<char>& images) { return parse(images); }
4100 template gmic::gmic(const int, const char *const *const, CImgList<char>&, const char *const custom_macros, const bool add_macros_at_start);
4101 template gmic::gmic(const char* const, CImgList<char>&, const char *const custom_macros, const bool add_macros_at_start);
4102 #endif
4103 #if defined(gmic_ushort) || !defined(gmic_separate_compilation)
4104 gmic& gmic::parse_ushort(CImgList<unsigned short>& images) { return parse(images); }
4105 template gmic::gmic(const int, const char *const *const, CImgList<unsigned short>&, const char *const custom_macros, const bool add_macros_at_start);
4106 template gmic::gmic(const char* const, CImgList<unsigned short>&, const char *const custom_macros, const bool add_macros_at_start);
4107 #endif
4108 #if defined(gmic_short) || !defined(gmic_separate_compilation)
4109 gmic& gmic::parse_short(CImgList<short>& images) { return parse(images); }
4110 template gmic::gmic(const int, const char *const *const, CImgList<short>&, const char *const custom_macros, const bool add_macros_at_start);
4111 template gmic::gmic(const char* const, CImgList<short>&, const char *const custom_macros, const bool add_macros_at_start);
4112 #endif
4113 #if defined(gmic_uint) || !defined(gmic_separate_compilation)
4114 gmic& gmic::parse_uint(CImgList<unsigned int>& images) { return parse(images); }
4115 template gmic::gmic(const int, const char *const *const, CImgList<unsigned int>&, const char *const custom_macros, const bool add_macros_at_start);
4116 template gmic::gmic(const char* const, CImgList<unsigned int>&, const char *const custom_macros, const bool add_macros_at_start);
4117 #endif
4118 #if defined(gmic_int) || !defined(gmic_separate_compilation)
4119 gmic& gmic::parse_int(CImgList<int>& images) { return parse(images); }
4120 template gmic::gmic(const int, const char *const *const, CImgList<int>&, const char *const custom_macros, const bool add_macros_at_start);
4121 template gmic::gmic(const char* const, CImgList<int>&, const char *const custom_macros, const bool add_macros_at_start);
4122 #endif
4123 #if defined(gmic_float) || !defined(gmic_separate_compilation)
4124 gmic& gmic::parse_float(CImgList<float>& images) { return parse(images); }
4125 template gmic::gmic(const int, const char *const *const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
4126 template gmic::gmic(const char* const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
4127 #endif
4128 #if defined(gmic_double) || !defined(gmic_separate_compilation)
4129 gmic& gmic::parse_double(CImgList<double>& images) { return parse(images); }
4130 template gmic::gmic(const int, const char *const *const, CImgList<double>&, const char *const custom_macros, const bool add_macros_at_start);
4131 template gmic::gmic(const char* const, CImgList<double>&, const char *const custom_macros, const bool add_macros_at_start);
4132 #endif
4133 #endif
4134 #endif
4136 //-----------------------
4137 // Start main procedure.
4138 //-----------------------
4139 #if defined(gmic_main) || (!defined(gmic_separate_compilation) && !defined(gmic_minimal))
4140 extern char data_def[];
4142 int main(int argc, char **argv) {
4144 // Display help if necessary.
4145 //---------------------------
4146 if (argc==1) {
4147 std::fprintf(cimg_stdout,"<gmic> No options or data provided. Try '%s -h' for help.\n",cimg::basename(argv[0]));
4148 std::exit(0);
4149 }
4151 if (cimg_option("-h",false,0) || cimg_option("-help",false,0) || cimg_option("--help",false,0)) {
4152 cimg_usage("GREYC's Magic Image Converter");
4154 char version[1024] = { 0 };
4155 std::sprintf(version," Version %d.%d.%d.%d, Copyright (C) 2008-2009, David Tschumperle (http://gmic.sourceforge.net)",
4156 gmic_version/1000,(gmic_version/100)%10,(gmic_version/10)%10,gmic_version%10);
4157 cimg_help(version);
4159 cimg_help("\n Usage\n"
4160 " -----");
4161 cimg_help(" gmic [file_1] [file_2] .. [file_n] [command_1] .. [command_n] [file_n+1] ...\n");
4162 cimg_help(" G'MIC is an interpreter of image processing macros whose goal is to convert, manipulate and");
4163 cimg_help(" visualize generic 1D/2D/3D multi-spectral image and video files. It follows these simple rules :\n");
4164 cimg_help(" - G'MIC handles a numbered list of images which are all stored in computer memory.");
4165 cimg_help(" - The first image of the list has indice '[0]'.");
4166 cimg_help(" - Negative indices are treated in a cyclic way (i.e. '[-1]' is the last image,");
4167 cimg_help(" '[-2]' the penultimate one, and so on...).");
4168 cimg_help(" - Command line items tell how to add/remove/manipulate/display images of the list.");
4169 cimg_help(" - Items are read and executed in the order they appear on the command line, from the left to the right.");
4170 cimg_help(" - Items can thus appear more than one time on the command line.");
4171 cimg_help(" - An item starting by '-' is a G'MIC instruction.");
4172 cimg_help(" - One instruction may have two equivalent names (regular and short).");
4173 cimg_help(" - A G'MIC instruction may have mandatory or optional arguments.");
4174 cimg_help(" - When multiple arguments are needed, they are separated by commas ','.");
4175 cimg_help(" - Items that are not instructions are considered either as input filenames or input strings.");
4176 cimg_help(" - When an input filename is encountered, the corresponding image data are loaded");
4177 cimg_help(" and added to the end of the image list.");
4178 cimg_help(" (see section 'Filename options' below for more informations on file input/output).");
4179 cimg_help(" - Special filenames '-' or '-.ext' mean 'standard input/output' (optionally. in 'ext' format).");
4180 cimg_help(" - Special input strings can be used to insert new images to the list. They can be :");
4181 cimg_help(" - 'width[%][xheight[%][xdepth[%][xdim[%][xN]]]]' : Insert 'N' black images with specified size.");
4182 cimg_help(" (adding '%' to a dimension means 'percentage of to the same dimension in the last image'),");
4183 cimg_help(" - '[indice]' or '[indice]xN' : Insert 1 or N copies of the existing image [indice].");
4184 cimg_help(" - '(v1,v2,...)' or '(v1,v2,...)xN' : Insert 1 or N copies of the specified IxJxKxL image.");
4185 cimg_help(" Separators inside '(..)' can be ',' (column), ';' (line), '/' (slice) or '^' (channel).");
4186 cimg_help(" - A G'MIC instruction may be restricted to a specific subset of the list, by adding '[subset]' to");
4187 cimg_help(" the instruction name. Several usual expressions are possible for 'subset', for instance : ");
4188 cimg_help(" '-command[0,1,3]' : Apply instruction on images 0,1 and 3.");
4189 cimg_help(" '-command[3-5]' : Apply instruction on images 3 to 5.");
4190 cimg_help(" '-command[50%-100%] : Apply instruction on the second half of the image list.");
4191 cimg_help(" '-command[0,-2,-1]' : Apply instruction on the first and two latest images.");
4192 cimg_help(" '-command[0-9:3]' : Apply instruction on images 0 to 9, with a step of 3 (i.e. images 0,3,6,9).");
4193 cimg_help(" '-command[0,2-4,50%--1]' : Apply instruction on images 0,2,3,4 and the second half of the list.");
4194 cimg_help(" - When no image subset is specified, a G'MIC instruction is applied on all images of the list.");
4195 cimg_help(" - Native (non-macro) instructions starting with '--' instead of '-' do not act 'in-place' but");
4196 cimg_help(" insert their result as a new image at the end of the list.");
4197 cimg_help(" - On the command line, any item of the form '@indice' or '@{indice}' is replaced");
4198 cimg_help(" by the values of the image '[indice]' separated by commas.");
4199 cimg_help(" - Items '@?' (or '@{?}'), '@{?,max}' or '@{?,min,max}' are replaced by a float random value between [0,1], [0,max] or [min,max].");
4200 cimg_help(" - Items '@[?]', '@[?,max]' or '@[?,min,max]' do the same but return integer random values.");
4201 cimg_help(" - Restrictions to a subset of image values can be specified with '@{indice,subset}' (as in '@{2,0-50%}').");
4202 cimg_help(" - Restriction to a particular pixel coordinate can be specified with '@(indice,x[,y[,z[,v[,borders]]]])'.");
4203 cimg_help(" - On the command line, the item '@#' is replaced by the number of images in the list.");
4204 cimg_help(" - Input filenames or commands may result to the generation of 3D objects.");
4205 cimg_help(" - A 3D object viewer, based on software rendering, is included in G'MIC.");
4206 cimg_help(" - A 3D object is stored as a single-column image containing all object data, in the following order :");
4207 cimg_help(" { magic header, vertices, faces, colors, opacities }.");
4208 cimg_help(" - Custom user-defined G'MIC instructions can be defined with the use of a macro file.");
4209 cimg_help(" - A macro file is a simple ASCII text file, each line being of the form");
4210 cimg_help(" 'instruction_name : substitution' or 'substitution (continuation)' or '# comment'.");
4211 cimg_help(" - Each invoked macro instruction is substituted as its defined content on the command line.");
4212 cimg_help(" - A macro file 'gmic_def.raw' is distributed within the G'MIC package.");
4213 cimg_help(" - The macros defined in 'gmic_def.raw' are included by default in G'MIC.");
4214 cimg_help(" - Macros arguments, separated by commas, can be added after the invokation of a macro instruction.");
4215 cimg_help(" - In macros definitions, expressions starting with '$' are used to reference macro arguments :");
4216 cimg_help(" $i and ${i} are replaced by the value of the i-th macro argument.");
4217 cimg_help(" $# and ${#} are replaced by the number of macro arguments.");
4218 cimg_help(" $* and ${*} are replaced by the entire string of macro arguments.");
4219 cimg_help(" ${i*} is replaced by all macro arguments following the i-th argument (included).");
4220 cimg_help(" ${i=$#} is replaced by $i if defined, or by its new value $# else.");
4221 cimg_help(" ${i=$j} is replaced by $i if defined, or by its new value $j else.");
4222 cimg_help(" ${i=default} is replaced by $i if defined, or by its new value 'default' else.");
4223 cimg_help("\n A list of available native and macro instructions is available below.");
4224 cimg_help(" A parameter specified in '[]' is optional, except when standing for '[indices]' where it");
4225 cimg_help(" corresponds to one or several indices of the image list, as described above. In this case, the '[' and ']'");
4226 cimg_help(" characters must explicitly appear when writting the item.");
4228 cimg_help("\n Global options\n"
4229 " --------------");
4230 cimg_option("-help","(no args)","Display this help (eq. to '-h').");
4231 cimg_option("-verbose+","(no args)","Increment verbosity level (eq. to '-v+').");
4232 cimg_option("-verbose-","(no args)","Decrement verbosity level (eq. to '-v-').");
4233 cimg_option("-macros","'filename'","Load macro file from specified filename (eq. to '-m').");
4234 cimg_option("-debug","(no args)","Switch debug flag (when on, displays internal infos for debugging).");
4235 cimg_option("-fullpath","(no args)","Switch full path flag (when on, displays full filename paths).");
4237 cimg_help("\n Arithmetic operators\n"
4238 " --------------------");
4239 cimg_option("-add","'value', '[indice]' or (no args)","Add 'value' or '[indice]' to image(s)");
4240 cimg_help(" "
4241 "or add image(s) together (eq. to '-+').");
4242 cimg_option("-sub","'value', '[indice]' or (no args)","Substract 'value' or '[indice]' to image(s)");
4243 cimg_help(" "
4244 "or substract image(s) together (eq. to '--').");
4245 cimg_option("-mul","'value', '[indice]' or (no args)","Multiply image(s) by 'value' or '[indice]'");
4246 cimg_help(" "
4247 "or multiply image(s) together (eq. to '-*').");
4248 cimg_option("-div","'value', '[indice]' or (no args)","Divide image(s) by 'value' or '[indice]'");
4249 cimg_help(" "
4250 "or divide image(s) together (eq. to '-/').");
4251 cimg_option("-pow","'value', '[indice]' or (no args)","Compute image(s) to the power of 'value' or '[indice]'");
4252 cimg_help(" "
4253 "or power of the image(s) together (eq. to '-^').");
4254 cimg_option("-min","'value', '[indice]' or (no args)","Compute minimum between image(s) and 'value' or '[indice]'");
4255 cimg_help(" "
4256 "or minimum of image(s) together.");
4257 cimg_option("-max","'value', '[indice]' or (no args)","Compute maximum between image(s) and 'value' or '[indice]'");
4258 cimg_help(" "
4259 "or maximum of image(s) together.");
4260 cimg_option("-mod","'value', '[indice]' or (no args)","Compute modulo of image(s) with 'value' or '[indice]'");
4261 cimg_help(" "
4262 "or modulo with image(s) together.");
4263 cimg_option("-and","'value', '[indice]' or (no args)","Compute bitwise AND of image(s) with 'value' or '[indice]'");
4264 cimg_help(" "
4265 "or bitwise AND of image(s) together.");
4266 cimg_option("-or","'value', '[indice]' or (no args)","Compute bitwise OR of image(s) with 'value' or '[indice]'");
4267 cimg_help(" "
4268 "or bitwise OR of image(s) together.");
4269 cimg_option("-xor","'value', '[indice]' or (no args)","Compute bitwise XOR of image(s) with 'value' '[indice]'");
4270 cimg_help(" "
4271 "or bitwise XOR of image(s) together.");
4272 cimg_option("-cos","(no args)","Compute cosine of image(s) values.");
4273 cimg_option("-sin","(no args)","Compute sine of image(s) values.");
4274 cimg_option("-tan","(no args)","Compute tangent of image(s) values.");
4275 cimg_option("-acos","(no args)","Compute arccosine of image(s) values.");
4276 cimg_option("-asin","(no args)","Compute arcsine of image(s) values.");
4277 cimg_option("-atan","(no args)","Compute arctangent of image(s) values.");
4278 cimg_option("-abs","(no args)","Compute absolute value of image(s) values.");
4279 cimg_option("-sqr","(no args)","Compute square of image(s) values.");
4280 cimg_option("-sqrt","(no args)","Compute square root of image(s) values.");
4281 cimg_option("-exp","(no args)","Compute exponential of image(s) values.");
4282 cimg_option("-log","(no args)","Compute logarithm of image(s) values.");
4283 cimg_option("-log10","(no args)","Compute logarithm_10 of image(s) values.");
4285 cimg_help("\n Pointwise pixel manipulation\n"
4286 " ----------------------------");
4287 cimg_option("-type","'value_type'","Cast all images into specified value type (eq. to '-t').");
4288 cimg_help(" "
4289 "('value_type' can be 'bool','uchar','char','ushort','short',");
4290 cimg_help(" "
4291 "'uint','int','float','double').");
4292 cimg_option("-set","'value[,x[,y[,z[,v]]]]'","Set scalar value in image(s) at specified position (eq. to '-=').");
4293 cimg_option("-endian","(no args)","Invert endianness of the image(s) buffers.");
4294 cimg_option("-fill","'value1,value2,...'","Fill image(s) with scalar values in a repetitive way (eq. to '-f').");
4295 cimg_option("-threshold","'value[%][,soft]' or (noargs)","Threshold pixel values ((noargs) for interactive mode).");
4296 cimg_option("-cut","'{value1[%],[indice]},{value2[%],[indice]}' or (noargs)","Cut pixel values in specified range ");
4297 cimg_help(" "
4298 "((noargs) for interactive mode).");
4299 cimg_option("-normalize","'{value1[%],[indice]},{value2[%],[indice]}'",
4300 "Normalize pixel values in specified range (eq. to '-n').");
4301 cimg_option("-round","'round_value[,round_type]'","Round pixel values.");
4302 cimg_option("-equalize","'nb_levels'","Equalize image(s) histogram(s) using specified number of levels.");
4303 cimg_option("-quantize","'nb_levels'","Quantize image(s) with 'nb_levels' levels.");
4304 cimg_option("-noise","'std[%][,noise_type]'","Add noise with specified standard deviation");
4305 cimg_help(" "
4306 "('noise_type' can be '{0=gaussian, 1=uniform, 2=salt&pepper, 3=poisson}'.");
4307 cimg_option("-norm","(no args)","Compute pointwise L2-norm of vector-valued image(s).");
4308 cimg_option("-orientation","(no args)","Compute pointwise orientation of vector-valued image(s).");
4310 cimg_help("\n Color bases conversions\n"
4311 " -----------------------");
4312 cimg_option("-rgb2hsv","(no args)","Convert image(s) from RGB to HSV colorbases.");
4313 cimg_option("-rgb2hsl","(no args)","Convert image(s) from RGB to HSL colorbases.");
4314 cimg_option("-rgb2hsi","(no args)","Convert image(s) from RGB to HSI colorbases.");
4315 cimg_option("-rgb2yuv","(no args)","Convert image(s) from RGB to YUV colorbases.");
4316 cimg_option("-rgb2ycbcr","(no args)","Convert image(s) from RGB to YCbCr colorbases.");
4317 cimg_option("-rgb2xyz","(no args)","Convert image(s) from RGB to XYZ colorbases.");
4318 cimg_option("-rgb2lab","(no args)","Convert image(s) from RGB to Lab colorbases.");
4319 cimg_option("-rgb2cmy","(no args)","Convert image(s) from RGB to CMY colorbases.");
4320 cimg_option("-rgb2cmyk","(no args)","Convert image(s) from RGB to CMYK colorbases.");
4321 cimg_option("-rgb2lut","'[indice][,dithering]' or 'LUT_type[,dithering]'","Index image(s) with color palette ");
4322 cimg_help(" "
4323 "('LUT_type' can be '{0=default, 1=rainbow, 2=contrast}'");
4324 cimg_help(" "
4325 "'dithering' can be '{0=off, 1=on}').");
4326 cimg_option("-hsv2rgb","(no args)","Convert image(s) from HSV to RGB colorbases.");
4327 cimg_option("-hsl2rgb","(no args)","Convert image(s) from HSL to RGB colorbases.");
4328 cimg_option("-hsi2rgb","(no args)","Convert image(s) from HSI to RGB colorbases.");
4329 cimg_option("-yuv2rgb","(no args)","Convert image(s) from YUV to RGB colorbases.");
4330 cimg_option("-ycbcr2rgb","(no args)","Convert image(s) from YCbCr to RGB colorbases.");
4331 cimg_option("-xyz2rgb","(no args)","Convert image(s) from XYZ to RGB colorbases.");
4332 cimg_option("-lab2rgb","(no args)","Convert image(s) from Lab to RGB colorbases.");
4333 cimg_option("-cmy2rgb","(no args)","Convert image(s) from CMY to RGB colorbases.");
4334 cimg_option("-cmyk2rgb","(no args)","Convert image(s) from CMYK to RGB colorbases.");
4335 cimg_option("-lut2rgb","'[indice]' or 'LUT_type'","Map color palette to image(s) ");
4336 cimg_help(" "
4337 "('LUT_type' can be '{0=default, 1=rainbow, 2=contrast}'.");
4339 cimg_help("\n Geometric manipulation\n"
4340 " ----------------------");
4341 cimg_option("-resize","'[indice][,interpolation[,borders[,center]]]' or ","");
4342 cimg_help(" "
4343 "'{[indice],width[%]}[x{[indice],height[%]}[x{[indice],depth[%]}[x{[indice],dim[%]}[,interpolation[,borders[,center]]]]]]'");
4344 cimg_help(" "
4345 "or (noargs)");
4346 cimg_help(" "
4347 "Resize image(s) to specified geometry ((noargs) for interactive mode) (eq. to '-r')");
4348 cimg_help(" "
4349 "('interpolation' can be '{0=none, 1=nearest, 2=average, 3=linear, 4=grid, 5=cubic}').");
4350 cimg_option("-resize2x","(no args)","Resize image(s) with Scale2x.");
4351 cimg_option("-resize3x","(no args)","Resize image(s) with Scale3x.");
4352 cimg_option("-crop","'x0[%],x1[%][,border_conditions]' or 'x0[%],y0[%],x1[%],y1[%][,border_conditions]' or ","");
4353 cimg_help(" "
4354 "'x0[%],y0[%],z0[%],x1[%],y1[%],z1[%][,border_conditions]' or ");
4355 cimg_help(" "
4356 "'x0[%],y0[%],z0[%],v0[%],x1[%],y1[%],z1[%],v1[%][,border_conditions]' or (noargs).");
4357 cimg_help(" "
4358 "Crop image(s) using specified geometry ((noargs) for interactive mode) (eq. to '-c') ");
4359 cimg_help(" "
4360 "('border_conditions' can be '{0=zero, 1=nearest}')");
4361 cimg_help(" "
4362 "((no args) for interactive mode).");
4363 cimg_option("-autocrop","'value1,value2,...'","Autocrop image(s) using specified background color.");
4364 cimg_option("-channels","'{[ind0],v0[%]}[,{[ind1],v1[%]}]'","Select channels v0..v1 of multi-spectral image(s).");
4365 cimg_option("-slices","'{[ind0],z0[%]}[,{[ind1],z1[%]}]'","Select slices z0..z1 of volumetric image(s).");
4366 cimg_option("-lines","'{[ind0],y0[%]}[,{[ind1],y1[%]}]'","Select lines y0..y1 of image(s).");
4367 cimg_option("-columns","'{[ind0],x0[%]}[,{[ind1],x1[%]}]'","Select columns x0..x1 of image(s).");
4368 cimg_option("-rotate","'angle[,border_conditions]'","Rotate image(s) with a given angle ");
4369 cimg_help(" "
4370 "('border_conditions' can be '{-3=cyclic (in-place), -2=nearest(ip), -1=zero(ip), 0=zero, 1=nearest, 2=cyclic}'");
4371 cimg_help(" "
4372 "and 'interpolation' can be '{0=none, 1=linear, 2=cubic}').");
4373 cimg_option("-mirror","'axis'",
4374 "Mirror image(s) along specified axis ('axis' can be '{x,y,z,v}').");
4375 cimg_option("-translate","'tx[%][,ty[%][,tz[%][,tv[%][,border_conditions]]]]'",
4376 "Translate image(s) by vector (dx,dy,dz,dv)");
4377 cimg_help(" "
4378 "('border_conditions' can be '{0=zero, 1=nearest, 2=cyclic}').");
4379 cimg_option("-transpose","(no args)","Transpose image(s).");
4380 cimg_option("-invert","(no args)","Compute inverse matrix.");
4381 cimg_option("-permute","'permutation'","Permute image axes by the specified permutation "
4382 "('permutation' can be 'yxzv',...).");
4383 cimg_option("-unroll","'axis'",
4384 "Unroll image(s) along specified axis ('axis' can be '{x,y,z,v}').");
4385 cimg_option("-split","'axis[,nb_parts]' or 'value[,keep]'",
4386 "Split image(s) along specified axis or value ('axis' can be '{x,y,z,v}') (eq. to '-s').");
4387 cimg_option("-append","'axis,[alignement]'","Append image(s) along specified axis and alignement (eq. to '-a')");
4388 cimg_help(" "
4389 "('axis' can be '{x,y,z,v}' and 'alignement' can be '{p=left, c=center, n=right)'.");
4390 cimg_option("-warp","'[indice][,relative[,interpolation[,border_conditions[,nb_frames]]]]'",
4391 "Warp image(s) in 'nb_frames' with field '[indice]' ");
4392 cimg_help(" "
4393 "('relative' can be '{0,1}', 'interpolation' can be '{0,1}', "
4394 "'border_conditions' can be '{0=zero, 1=nearest}').");
4396 cimg_help("\n Image filtering\n"
4397 " ---------------");
4398 cimg_option("-blur","'std[,border_conditions]'",
4399 "Apply gaussian blur of specified standard deviation");
4400 cimg_help(" "
4401 "('border_conditions' can be '{0=zero, 1=nearest}').");
4402 cimg_option("-bilateral","'stdevs,stdevr'",
4403 "Apply bilateral filtering with specified standard deviations 'stdevs' and 'stdevr'.");
4404 cimg_option("-smooth","'amplitude[,sharpness[,anisotropy[,alpha[,sigma[,dl[,da[,prec[,interp[,fast]]]]]]]]]'","");
4405 cimg_help(" "
4406 "Smooth image(s) anisotropically with specified parameters.");
4407 cimg_option("-denoise","'patch_size[,stdev_p[,stdev_s[,lookup_size]]]'",
4408 "Denoise image(s) with a patch-averaging procedure.");
4409 cimg_option("-median","'size'","Apply median filter with specified size.");
4410 cimg_option("-sharpen","'amplitude[,0]' or 'amplitude,1[,edge[,alpha[,sigma]]]'",
4411 "Sharpen image(s) with inverse diffusion or shock filters.");
4412 cimg_option("-convolve","'[indice][,border_conditions]'",
4413 "Convolve image(s) by the specified mask");
4414 cimg_help(" "
4415 "('border_conditions' can be '{0=zero, 1=nearest}').");
4416 cimg_option("-correlate","'[indice][,border_conditions]'",
4417 "Correlate image(s) by the specified mask (same parameters as above).");
4418 cimg_option("-erode","'size[,border_conditions]' or '[indice][,border_conditions]'","");
4419 cimg_help(" "
4420 "Erode image(s) by the specified mask (same parameters as above)').");
4421 cimg_option("-dilate","'size[,border_conditions]' or '[indice][,border_conditions]'","");
4422 cimg_help(" "
4423 "Dilate image(s) by the specified mask (same parameters as above).");
4424 cimg_option("-gradient","'x', 'xy', 'xyz' or (no args)","Compute image gradient.");
4425 cimg_option("-hessian","'{xx,xy,xz,yy,yz,zz}' or (no args)","Compute image Hessian.");
4426 cimg_option("-fft","(no args)","Compute direct Fourier transform.");
4427 cimg_option("-ifft","(no args)","Compute inverse Fourier transform.");
4429 cimg_help("\n Image creation and drawing\n"
4430 " --------------------------");
4431 cimg_option("-dimensions","(no args)","Get dimensions of the image(s) as a 1x4 vector.");
4432 cimg_option("-stats","(no args)","Get statistics of the image(s) as a 1x6 vector.");
4433 cimg_option("-histogram","'nb_values[%]'","Compute histogram of image(s) with 'nb_values' values.");
4434 cimg_option("-distance","'isovalue'","Compute distance function(s) to specified isovalue.");
4435 cimg_option("-hamilton","'nb_iter[,band_size]'","Apply Hamilton-Jacobi PDE to compute distance to 0.");
4436 cimg_option("-label","(no args)","Label connected components of image(s).");
4437 cimg_option("-displacement","'[indice][,smoothness[,precision[,nbscales[,itermax]]]]","Estimate smooth displacement field between image(s) "
4438 "and specified target '[indice]'.");
4439 cimg_option("-sort","(no args)","Sort values of image(s) in increasing order.");
4440 cimg_option("-psnr","'max_value' or (noargs)","Compute PSNR between specified image(s).");
4441 cimg_option("-point","'x[%],y[%][,z[%][,opacity[,color]]]'","Draw 3D colored point on specified image(s).");
4442 cimg_option("-line","'x0[%],y0[%],x1[%],y1[%][,opacity[,color]]'","Draw 2D colored line on specified image(s).");
4443 cimg_option("-polygon","'N,x0[%],y0[%],..,xN[%],yN[%][,opacity[,color]]'","Draw a 2D colored N-vertices polygon on specified image(s).");
4444 cimg_option("-ellipse","'x[%],y[%],r,R[,u,v[,opacity[,color]]]'","Draw 2D colored ellipse on specified image(s).");
4445 cimg_option("-text","text,x[%],y[%],size[,opacity[,color]]'",
4446 "Draw specified text at position (x,y) and with specified font size.");
4447 cimg_option("-image","'[indice][,x[%][,y[%][,z[%][,opacity[,ind_mask]]]]]'","Draw sprite image on specified image(s).");
4448 cimg_option("-object3d","'[indice][,x[%][,y[%][,z[,opacity]]]]'","Draw 3D object on specified image(s).");
4449 cimg_option("-plasma","'alpha[,beta[,opacity]]'","Draw plasma on specified image(s).");
4450 cimg_option("-mandelbrot","'z0r,z0i,z1r,z1i[,itermax[,julia,c0r,c0i[,opacity]]]'","Draw Mandelbrot/Julia fractals on specified image(s).");
4451 cimg_option("-flood","'x[%][,y[%][,z[%][,tolerance[,opacity[,color]]]]]'",
4452 "Flood-fill image(s) starting from (x,y,z) with specified tolerance.");
4454 cimg_help("\n List manipulation\n"
4455 " -----------------");
4456 cimg_option("-remove","(no args)","Remove image(s) from list (eq. to '-rm').");
4457 cimg_option("-keep","(no args)","Keep only specified image(s) (eq. to '-k').");
4458 cimg_option("-move","'position'","Move image(s) at specified position (eq. to '-mv').");
4459 cimg_option("-reverse","(no args)","Reverse image(s) order.");
4460 cimg_option("-name","\"name\"","Set display name of image(s).");
4462 cimg_help("\n 3D Rendering\n"
4463 " ------------");
4464 cimg_option("-cube3d","'size'","Insert a 3D cube at the end of the list.");
4465 cimg_option("-cone3d","'radius[,height[,subdivisions]]'","Insert a 3D cube at the end of the list.");
4466 cimg_option("-cylinder3d","'radius[,height[,subdivisions]]'","Insert a 3D cylinder at the end of the list.");
4467 cimg_option("-torus3d","'radius1,radius2[,subdivisions1,subdivisions2]'","Insert a 3D torus at the end of the list.");
4468 cimg_option("-plane3d","'sizex,sizey[,subdivisionsx,subdisivionsy]'","Insert a 3D plane at the end of the list.");
4469 cimg_option("-sphere3d","'radius[,subdivisions]'","Insert a 3D sphere at the end of the list.");
4470 cimg_option("-elevation3d","'z-factor' or '[indice]'",
4471 "Generate 3D elevation(s) of image(s) using specified z-factor or elevation map.");
4472 cimg_option("-isovalue3d","'value'","Generate 3D object(s) by retrieving isophote or isosurface of image(s).");
4473 cimg_option("-center3d","(no args)","Center 3D object(s) (eq. to '-c3d').");
4474 cimg_option("-normalize3d","(no args)","Normalize 3D object(s) to a unit size (eq. to '-n3d').");
4475 cimg_option("-rotate3d","'u,v,w,angle'","Rotate 3D object(s) around axis (u,v,w) with specified angle (eq. to '-rot3d').");
4476 cimg_option("-add3d","'[indice]' or 'tx,ty,tz' or (noargs)","Append or translate 3D object(s), or append 3D object(s) together (eq. to '-+3d').");
4477 cimg_option("-sub3d","'tx,ty,tz'","Translate 3D object(s) with the opposite of the specified vector (eq. to '--3d').");
4478 cimg_option("-mul3d","'fact' or 'factx,facty[,factz]'","Scale 3D object(s) with specified factor (eq. to '-*3d').");
4479 cimg_option("-div3d","'fact' or 'factx,facty[,factz]'","Scale 3D object(s) with specified inverse factor (eq. to '-/3d').");
4480 cimg_option("-color3d","'R,G,B[,opacity]'","Set color of 3D object(s) (eq. to '-col3d').");
4481 cimg_option("-opacity3d","'opacity'","Set opacity of 3D object(s) (eq. to '-opac3d').");
4482 cimg_option("-invert3d","(no args)","Invert primitive orientations of 3D object(s) (eq. to '-i3d').");
4483 cimg_option("-split3d","(no args)","Split 3D object data into 6 data vectors 'header,N,vertices,primitives,colors,opacities' (eq. to '-s3d').");
4484 cimg_option("-light3d","'posx,posy,posz'","Set the 3D position of the light for 3D rendering (eq. to '-l3d').");
4485 cimg_option("-focale3d","'value'","Set focale value for 3D rendering (eq. to '-f3d').");
4486 cimg_option("-specl3d","'value'","Set amount of specular light for 3D rendering (eq. to '-sl3d').");
4487 cimg_option("-specs3d","'value'","Set shininess of specular light for 3D rendering (eq. to '-ss3d').");
4488 cimg_option("-orient3d","(no args)","Switch double-sided mode for 3D rendering (eq. to '-o3d').");
4489 cimg_option("-render3d","'mode'","Set 3D rendering mode");
4490 cimg_help(" "
4491 "(can be '{-1=bounding-box, 0=pointwise, 1=linear, 2=flat, 3=flat-shaded, 4=Gouraud-shaded, 5=Phong-shaded}') (eq. to '-r3d').");
4492 cimg_option("-renderd3d","'mode'","Set dynamic rendering mode in 3D viewer (same values as above) (eq. to '-rd3d').");
4493 cimg_option("-background3d","'R,G,B'","Define background color in 3D viewer (eq. to '-b3d').");
4495 cimg_help("\n Program controls\n"
4496 " ----------------");
4497 cimg_option("-nop","(no args)","Do nothing.");
4498 cimg_option("-skip","(any args)","Do nothing but skip the next argument.");
4499 cimg_option("-echo","'text'","Output specified message (eq. to '-e').");
4500 cimg_option("-print","(no args)","Print image(s) informations (eq. to '-p').");
4501 cimg_option("-quit","(no args)","Force interpreter to quit (eq. to '-q').");
4502 cimg_option("-do","(no args)","Start a 'do-while' code bloc.");
4503 cimg_option("-while","'cond'","End a 'do-while' code bloc and go back to associated '-do' if 'cond' is a strictly positive value.");
4504 cimg_option("-if","'cond'","Start a 'if-then-else' code bloc and test if 'cond' is a strictly positive value.");
4505 cimg_option("-else","(no args)","Execute following commands if previous '-if' condition failed.");
4506 cimg_option("-endif","(no args)","End a 'if-then-else' code bloc");
4507 cimg_option("-repeat","'N'","Start a 'repeat-done' code bloc.");
4508 cimg_option("-done","(no args)","End a 'repeat-done' code bloc, and go to associated '-repeat' if iterations remain.");
4509 cimg_option("-int","'arg1,...,argN'","Check if all specified arguments are integer. If not, print an error message and exit.");
4510 cimg_option("-float","'arg1,...,argN","Check if all specified arguments are float values. If not, print an error message and exit.");
4512 cimg_help("\n Input/output\n"
4513 " ------------");
4514 cimg_option("-input","'filename' or 'width[%][xheight[%][xdepth[%][xdim[%][xN]]]]'","");
4515 cimg_help(" "
4516 "or '[indice][xN]' or '(v11{,;/^}v21...vLM)[xN]'");
4517 cimg_help(" "
4518 "Input filename, empty image, image copy, or image with specified values (eq. to '-i' or (no args)).");
4519 cimg_option("-output","'filename'","Output image(s) in specified filename (eq. to '-o').");
4520 cimg_option("-display","(no args)","Display image(s) (eq. to '-d').");
4521 cimg_option("-display3d","(no args)","Display 3D object(s) (eq. to '-d3d').");
4522 cimg_option("-plot","'[plot_type[,vertex_type[,xmin[,xmax[,ymin[,ymax]]]]]]'",
4523 "Display image(s) as 1D plot(s)");
4524 cimg_help(" "
4525 "('plot_type' can be '{0=none, 1=lines, 2=splines, 3=bar}').");
4526 cimg_help(" "
4527 "('vertex_type' can be in '[0-7]').");
4528 cimg_option("-select","'select_type'","Select feature from image(s) in an interactive way");
4529 cimg_help(" "
4530 "('select_type' can be in '{0=point, 1=line, 2=rectangle, 3=circle').");
4532 // Print descriptions of default macros.
4533 char line[256*1024] = { 0 }, name[4096] = { 0 }, args[4096] = { 0 }, desc[4096] = { 0 };
4534 bool first_description = true;
4535 for (const char *data = data_def; *data; ) {
4536 if (*data=='\n') ++data;
4537 else {
4538 if (std::sscanf(data,"%262143[^\n]",line)>0) data += cimg::strlen(line);
4539 if (line[0]=='#' && std::sscanf(line,"#@gmic %4095[^:] : %4095[^:] : %4095[^:]",name,args,desc)>0) {
4540 if (first_description) cimg_help("\n Commands : Default macros\n"
4541 " -------------------------");
4542 std::fprintf(cimg_stdout,"%s %s-%-15s%s %-24s %s%s%s",
4543 first_description?"":"\n",
4544 cimg::t_bold,name,cimg::t_normal,args,cimg::t_green,desc,cimg::t_normal);
4545 first_description = false;
4546 }
4547 }
4548 }
4550 // Print descriptions of use-defined macros.
4551 first_description = true;
4552 for (int i = 1; i<argc-1; ++i) if (!cimg::strcmp("-m",argv[i]) || !cimg::strcmp("-macros",argv[i])) {
4553 std::FILE *file = cimg::fopen(argv[i+1],"r");
4554 if (file) {
4555 int err = 0;
4556 while ((err=std::fscanf(file,"%262143[^\n] ",line)>=0)) {
4557 if (err) { // Non empty-line
4558 name[0] = args[0] = desc[0] = 0;
4559 if (line[0]=='#' && std::sscanf(line,"#@gmic %4095[^:] : %4095[^:] : %4095[^:]",name,args,desc)>0) {
4560 if (first_description) cimg_help("\n\n Commands : User-defined macros\n"
4561 " ------------------------------");
4562 std::fprintf(cimg_stdout,"%s %s-%-15s%s %-24s %s%s%s",
4563 first_description?"":"\n",
4564 cimg::t_bold,name,cimg::t_normal,args,cimg::t_green,desc,cimg::t_normal);
4565 first_description = false;
4566 }
4567 }
4568 }
4569 }
4570 cimg::fclose(file);
4571 }
4573 cimg_help("\n\n Viewers shortcuts\n"
4574 " -----------------");
4575 cimg_help(" When displaying image(s) or 3D object(s) with G'MIC, you can use these shortcuts in viewers :");
4576 cimg_help(" - CTRL+D : Increase window size.");
4577 cimg_help(" - CTRL+C : Decrease window size.");
4578 cimg_help(" - CTRL+R : Reset window size.");
4579 cimg_help(" - CTRL+F : Toggle fullscreen mode.");
4580 cimg_help(" - CTRL+S : Save current window snapshot.");
4581 cimg_help(" - CTRL+O : Save current instance of viewed image or 3D object.\n");
4582 cimg_help(" Specific options for the viewer of image(s) :");
4583 cimg_help(" - CTRL+P : Play stack of frames as a movie.");
4584 cimg_help(" - CTRL+(mousewheel) : Zoom in/out.");
4585 cimg_help(" - SHIFT+(mousewheel) : Go left/right.");
4586 cimg_help(" - ALT+(mousewheel) : Go up/down.");
4587 cimg_help(" - Numeric PAD : Zoom in/out (+/-) and move zoomed region (numbers).");
4588 cimg_help(" - BACKSPACE : Reset zoom.\n");
4589 cimg_help(" Specific options for the viewer of 3D object(s) :");
4590 cimg_help(" - (mouse) + (left mouse button) : Rotate object.");
4591 cimg_help(" - (mouse) + (right mouse button) : Zoom object.");
4592 cimg_help(" - (mouse) + (middle mouse button) : Translate object.");
4593 cimg_help(" - (mousewheel) : Zoom in/out.");
4594 cimg_help(" - CTRL + Z : Enable/disable Z-buffer rendering");
4596 cimg_help("\n File options\n"
4597 " ------------");
4598 cimg_help(" G'MIC is able to read/write most of the classical image file formats, including :");
4599 cimg_help(" - 2D grayscale/color images : PNG,JPEG,GIF,PNM,TIFF,BMP,....");
4600 cimg_help(" - 3D volumetric images : DICOM,HDR,NII,PAN,CIMG,INR,....");
4601 cimg_help(" - Video files : MPEG,AVI,MOV,OGG,FLV,...");
4602 cimg_help(" - Generic data files : DLM,ASC,RAW,TXT.");
4603 cimg_help(" - 3D objects : OFF.\n");
4604 cimg_help(" Specific options :");
4605 cimg_help(" - For video files : you can read only sub-frames of the image sequence (recommended) with the expression");
4606 cimg_help(" 'video.ext,[first_frame[%][,last_frame[%][,step]]]'.");
4607 cimg_help(" - For RAW files : you must specify the image dimensions with the expression");
4608 cimg_help(" 'file.raw,width[,height[,depth[,dim]]]]'.");
4609 cimg_help(" - For YUV files : you must specify the image dimensions and can read only sub-frames of the image sequence with the expression");
4610 cimg_help(" 'file.yuv,width,height[,first_frame[,last_frame[,step]]]'.");
4611 cimg_help(" - For JPEG files : you can specify the quality (in %) of an output jpeg file format with the expression");
4612 cimg_help(" 'file.jpg,30%'.");
4614 cimg_help("\n Examples of use\n"
4615 " ---------------");
4616 cimg_help(" G'MIC is a simple but quite complete interpreter of image processing instructions, and can be used for a wide variety of");
4617 cimg_help(" image processing tasks. Here are (very few) examples of how the command line tool G'MIC can be used :\n");
4618 cimg_help(" - View image data : ");
4619 cimg_help(" gmic file1.bmp file2.jpeg");
4620 cimg_help(" - Convert image files : ");
4621 cimg_help(" gmic input.bmp -o output.jpg");
4622 cimg_help(" - Create volumetric image(s) from movie sequence : ");
4623 cimg_help(" gmic input.mpg -a z -o output.hdr");
4624 cimg_help(" - Compute image gradient norm : ");
4625 cimg_help(" gmic input.bmp -gradient_norm");
4626 cimg_help(" - Create G'MIC 3D logo : ");
4627 cimg_help(" gmic 180x70x1x3 -text G\\'MIC,30,5,50,1,1 -blur 2 -n 0,100 [0] -plasma[1] \\");
4628 cimg_help(" -+ -blur 1 -elevation -0.1 -rd3d 4");
4629 cimg_help("\n See also the macros defined in the provided macro file 'gmic_def.raw' for other examples.");
4631 cimg_help("\n ** G'MIC comes with ABSOLUTELY NO WARRANTY; "
4632 "for details visit http://gmic.sourceforge.net **");
4633 std::exit(0);
4634 }
4636 // Launch G'MIC instance.
4637 //-----------------------
4638 CImgList<float> images;
4639 try {
4640 gmic(argc,argv,images);
4641 } catch (gmic_exception &e) {
4642 std::fprintf(cimg_stdout,"\n** Error occurred : %s **\n",e.message);
4643 }
4644 return 0;
4645 }
4646 #endif
4648 #endif // #ifdef cimg_plugin ... #else ...