PTdecode/CImg-1.3.0/examples/gmic.cpp

Mon, 03 Aug 2009 23:39:53 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 03 Aug 2009 23:39:53 +0100
changeset 10
604c205d9163
parent 5
1204ebf9340d
permissions
-rwxr-xr-x

add basic test routine for Ptouch library

philpem@5 1 /*
philpem@5 2 #
philpem@5 3 # File : gmic.cpp
philpem@5 4 # ( C++ source file )
philpem@5 5 #
philpem@5 6 # Description : GREYC's Magic Image Converter (library and executable)
philpem@5 7 # ( http://gmic.sourceforge.net )
philpem@5 8 # This file is a part of the CImg Library project.
philpem@5 9 # ( http://cimg.sourceforge.net )
philpem@5 10 #
philpem@5 11 # Copyright : David Tschumperle
philpem@5 12 # ( http://www.greyc.ensicaen.fr/~dtschump/ )
philpem@5 13 #
philpem@5 14 # License : CeCILL v2.0
philpem@5 15 # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
philpem@5 16 #
philpem@5 17 # This software is governed by the CeCILL license under French law and
philpem@5 18 # abiding by the rules of distribution of free software. You can use,
philpem@5 19 # modify and/ or redistribute the software under the terms of the CeCILL
philpem@5 20 # license as circulated by CEA, CNRS and INRIA at the following URL
philpem@5 21 # "http://www.cecill.info".
philpem@5 22 #
philpem@5 23 # As a counterpart to the access to the source code and rights to copy,
philpem@5 24 # modify and redistribute granted by the license, users are provided only
philpem@5 25 # with a limited warranty and the software's author, the holder of the
philpem@5 26 # economic rights, and the successive licensors have only limited
philpem@5 27 # liability.
philpem@5 28 #
philpem@5 29 # In this respect, the user's attention is drawn to the risks associated
philpem@5 30 # with loading, using, modifying and/or developing or reproducing the
philpem@5 31 # software by the user in light of its specific status of free software,
philpem@5 32 # that may mean that it is complicated to manipulate, and that also
philpem@5 33 # therefore means that it is reserved for developers and experienced
philpem@5 34 # professionals having in-depth computer knowledge. Users are therefore
philpem@5 35 # encouraged to load and test the software's suitability as regards their
philpem@5 36 # requirements in conditions enabling the security of their systems and/or
philpem@5 37 # data to be ensured and, more generally, to use and operate it in the
philpem@5 38 # same conditions as regards security.
philpem@5 39 #
philpem@5 40 # The fact that you are presently reading this means that you have had
philpem@5 41 # knowledge of the CeCILL license and that you accept its terms.
philpem@5 42 #
philpem@5 43 */
philpem@5 44
philpem@5 45 // Add specific G'MIC methods to the CImg<T> class.
philpem@5 46 //-------------------------------------------------
philpem@5 47 #ifdef cimg_plugin
philpem@5 48
philpem@5 49 template<typename t>
philpem@5 50 CImg<T> get_replace(const CImg<t>& img) const {
philpem@5 51 return +img;
philpem@5 52 }
philpem@5 53
philpem@5 54 template<typename t>
philpem@5 55 CImg<T>& replace(CImg<t>& img) {
philpem@5 56 return img.transfer_to(*this);
philpem@5 57 }
philpem@5 58
philpem@5 59 CImg<T>& gmic_set(const double value, const int x, const int y, const int z, const int v) {
philpem@5 60 (*this).atXYZV(x,y,z,v,0) = (T)value;
philpem@5 61 return *this;
philpem@5 62 }
philpem@5 63
philpem@5 64 CImg<T> get_gmic_set(const double value, const int x, const int y, const int z, const int v) const {
philpem@5 65 return (+*this).gmic_set(value,x,y,z,v);
philpem@5 66 }
philpem@5 67
philpem@5 68 CImg<T> get_draw_point(const int x, const int y, const int z,
philpem@5 69 const CImg<T>& col, const float opacity) const {
philpem@5 70 return (+*this).draw_point(x,y,z,col,opacity);
philpem@5 71 }
philpem@5 72
philpem@5 73 CImg<T> get_draw_line(const int x0, const int y0, const int x1, const int y1,
philpem@5 74 const CImg<T>& col, const float opacity) const {
philpem@5 75 return (+*this).draw_line(x0,y0,x1,y1,col,opacity);
philpem@5 76 }
philpem@5 77
philpem@5 78 template<typename t>
philpem@5 79 CImg<T> get_draw_polygon(const CImg<t>& pts, const CImg<T>& col, const float opacity) const {
philpem@5 80 return (+*this).draw_polygon(pts,col,opacity);
philpem@5 81 }
philpem@5 82
philpem@5 83 CImg<T> get_draw_ellipse(const int x, const int y, const float r0, const float r1,
philpem@5 84 const float ru, const float rv, const CImg<T>& col, const float opacity) const {
philpem@5 85 return (+*this).draw_ellipse(x,y,r0,r1,ru,rv,col,opacity);
philpem@5 86 }
philpem@5 87
philpem@5 88 CImg<T> get_draw_text(const int x, const int y, const char *const text, const T *const col,
philpem@5 89 const int bg, const float opacity,const int siz) const {
philpem@5 90 return (+*this).draw_text(x,y,text,col,bg,opacity,siz);
philpem@5 91 }
philpem@5 92
philpem@5 93 CImg<T> get_draw_image(const int x, const int y, const int z,
philpem@5 94 const CImg<T>& sprite, const CImg<T>& mask, const float opacity) const {
philpem@5 95 return (+*this).draw_image(x,y,z,sprite,mask,opacity);
philpem@5 96 }
philpem@5 97
philpem@5 98 CImg<T> get_draw_image(const int x, const int y, const int z,
philpem@5 99 const CImg<T>& sprite, const float opacity) const {
philpem@5 100 return (+*this).draw_image(x,y,z,sprite,opacity);
philpem@5 101 }
philpem@5 102
philpem@5 103 CImg<T> get_draw_plasma(const float alpha, const float beta, const float opacity) const {
philpem@5 104 return (+*this).draw_plasma(alpha,beta,opacity);
philpem@5 105 }
philpem@5 106
philpem@5 107 CImg<T> get_draw_mandelbrot(const CImg<T>& color_palette, const float opacity,
philpem@5 108 const double z0r, const double z0i, const double z1r, const double z1i,
philpem@5 109 const unsigned int itermax, const bool normalized_iteration,
philpem@5 110 const bool julia_set, const double paramr, const double parami) const {
philpem@5 111 return (+*this).draw_mandelbrot(color_palette,opacity,z0r,z0i,z1r,z1i,itermax,
philpem@5 112 normalized_iteration,julia_set,paramr,parami);
philpem@5 113 }
philpem@5 114
philpem@5 115 CImg<T> get_draw_fill(const int x, const int y, const int z,
philpem@5 116 const CImg<T>& col, const float opacity, const float tolerance) const {
philpem@5 117 return (+*this).draw_fill(x,y,z,col,opacity,tolerance);
philpem@5 118 }
philpem@5 119
philpem@5 120 bool is_CImg3d() const {
philpem@5 121 const bool is_header = (width==1 && height>=8 && depth==1 && dim==1 &&
philpem@5 122 (*this)[0]=='C'+0.5f && (*this)[1]=='I'+0.5f &&
philpem@5 123 (*this)[2]=='m'+0.5f && (*this)[3]=='g'+0.5f &&
philpem@5 124 (*this)[4]=='3'+0.5f && (*this)[5]=='d'+0.5f);
philpem@5 125 if (!is_header) return false;
philpem@5 126 const int
philpem@5 127 nbv = (int)(*this)[6],
philpem@5 128 nbp = (int)(*this)[7];
philpem@5 129 if (nbv<=0 || nbp<=0) return false;
philpem@5 130 const T *ptrs = ptr() + 8 + 3*nbv, *const ptre = end();
philpem@5 131 if (ptrs>=ptre) return false;
philpem@5 132 for (int i = 0; i<nbp && ptrs<ptre; ++i) {
philpem@5 133 const int N = (int)*(ptrs++);
philpem@5 134 if (N<=0 || N>=8) return false;
philpem@5 135 ptrs+=N;
philpem@5 136 }
philpem@5 137 ptrs+=4*nbp;
philpem@5 138 if (ptrs>ptre) return false;
philpem@5 139 return true;
philpem@5 140 }
philpem@5 141
philpem@5 142 template<typename tp, typename tf, typename tc, typename to>
philpem@5 143 CImg<T> get_draw_object3d(const float x0, const float y0, const float z0,
philpem@5 144 const CImg<tp>& points, const CImgList<tf>& primitives,
philpem@5 145 const CImgList<tc>& colors, const CImg<to>& opacities,
philpem@5 146 const unsigned int render_type, const bool double_sided,
philpem@5 147 const float focale, const float lightx, const float lighty,
philpem@5 148 const float lightz, const float specular_light, const float specular_shine,
philpem@5 149 float *const zbuffer) const {
philpem@5 150 return (+*this).draw_object3d(x0,y0,z0,points,primitives,colors,opacities,render_type,double_sided,focale,
philpem@5 151 lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
philpem@5 152 }
philpem@5 153
philpem@5 154 template<typename tp, typename tc, typename to>
philpem@5 155 CImg<T>& object3dtoCImg3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImg<to>& opacities) {
philpem@5 156 if (is_empty() || !primitives) { primitives.assign(); colors.assign(); opacities.assign(); return *this; }
philpem@5 157 const unsigned int primitives_size = primitives.size;
philpem@5 158 CImgList<floatT> res;
philpem@5 159 res.insert(CImg<floatT>("CImg3d",1,6,1,1,false)+=0.5f);
philpem@5 160 res.insert(CImg<floatT>::vector((float)width,(float)primitives.size));
philpem@5 161 res.insert(1); resize(-100,3,1,1,0).transpose().unroll('y').transfer_to(res.last());
philpem@5 162 cimglist_for(primitives,p) {
philpem@5 163 res.insert(CImg<floatT>::vector((float)primitives[p].size())).insert(primitives[p]).last().unroll('y');
philpem@5 164 primitives[p].assign();
philpem@5 165 }
philpem@5 166 primitives.assign();
philpem@5 167 const unsigned int defined_colors = colors.size;
philpem@5 168 cimglist_for(colors,c) { res.insert(colors[c]).last().resize(1,3,1,1,-1); colors[c].assign(); }
philpem@5 169 colors.assign();
philpem@5 170 if (defined_colors<primitives_size) res.insert(1).last().assign(1,3*(primitives_size-defined_colors),1,1,200);
philpem@5 171 const unsigned int defined_opacities = opacities.size();
philpem@5 172 res.insert(opacities).last().unroll('y');
philpem@5 173 opacities.assign();
philpem@5 174 if (defined_opacities<primitives.size) res.insert(1).last().assign(1,primitives_size-defined_opacities,1,1,1);
philpem@5 175 return res.get_append('y').transfer_to(*this);
philpem@5 176 }
philpem@5 177
philpem@5 178 template<typename tp, typename tc, typename to>
philpem@5 179 CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImg<to>& opacities) {
philpem@5 180 const T *ptrs = ptr() + 6;
philpem@5 181 const unsigned int
philpem@5 182 nbv = (unsigned int)*(ptrs++),
philpem@5 183 nbp = (unsigned int)*(ptrs++);
philpem@5 184 CImg<T> points(nbv,3);
philpem@5 185 primitives.assign(nbp);
philpem@5 186 colors.assign(nbp,1,3,1,1);
philpem@5 187 opacities.assign(nbp);
philpem@5 188 cimg_forX(points,x) { points(x,0) = (T)*(ptrs++); points(x,1) = (T)*(ptrs++); points(x,2) = (T)*(ptrs++); }
philpem@5 189 cimglist_for(primitives,p) {
philpem@5 190 const unsigned int N = (unsigned int)*(ptrs++);
philpem@5 191 primitives[p].assign(ptrs,1,N,1,1,false);
philpem@5 192 ptrs+=N;
philpem@5 193 }
philpem@5 194 cimglist_for(colors,c) { colors(c,0) = (tc)*(ptrs++); colors(c,1) = (tc)*(ptrs++); colors(c,2) = (tc)*(ptrs++); }
philpem@5 195 opacities.assign(ptrs,1,nbp,1,1,false);
philpem@5 196 return assign(points);
philpem@5 197 }
philpem@5 198
philpem@5 199 CImg<T> get_appendCImg3d(const CImg<T>& img) const {
philpem@5 200 CImg<T> res(1,img.size() + size() - 8);
philpem@5 201 const T *ptrs = ptr() + 6, *ptrs0 = img.ptr() + 6;
philpem@5 202 T *ptrd = res.ptr();
philpem@5 203 *(ptrd++) = (T)('C' + 0.5f); *(ptrd++) = (T)('I' + 0.5f);
philpem@5 204 *(ptrd++) = (T)('m' + 0.5f); *(ptrd++) = (T)('g' + 0.5f);
philpem@5 205 *(ptrd++) = (T)('3' + 0.5f); *(ptrd++) = (T)('d' + 0.5f);
philpem@5 206 const unsigned int
philpem@5 207 nbv = (unsigned int)*(ptrs++),
philpem@5 208 nbv0 = (unsigned int)*(ptrs0++),
philpem@5 209 nbp = (unsigned int)*(ptrs++),
philpem@5 210 nbp0 = (unsigned int)*(ptrs0++);
philpem@5 211 *(ptrd++) = (T)(nbv + nbv0);
philpem@5 212 *(ptrd++) = (T)(nbp + nbp0);
philpem@5 213 std::memcpy(ptrd,ptrs,sizeof(T)*nbv*3);
philpem@5 214 ptrd+=3*nbv; ptrs+=3*nbv;
philpem@5 215 std::memcpy(ptrd,ptrs0,sizeof(T)*nbv0*3);
philpem@5 216 ptrd+=3*nbv0; ptrs0+=3*nbv0;
philpem@5 217 for (unsigned int i = 0; i<nbp; ++i) {
philpem@5 218 const unsigned int N = (unsigned int)*(ptrs++);
philpem@5 219 *(ptrd++) = (T)N;
philpem@5 220 std::memcpy(ptrd,ptrs,sizeof(T)*N);
philpem@5 221 ptrd+=N; ptrs+=N;
philpem@5 222 }
philpem@5 223 for (unsigned int i = 0; i<nbp0; ++i) {
philpem@5 224 const unsigned int N = (unsigned int)*(ptrs0++);
philpem@5 225 *(ptrd++) = (T)N;
philpem@5 226 for (unsigned int j = 0; j<N; ++j) *(ptrd++) = (T)(*(ptrs0++) + nbv);
philpem@5 227 }
philpem@5 228 std::memcpy(ptrd,ptrs,sizeof(T)*nbp*3);
philpem@5 229 ptrd+=3*nbp; ptrs+=3*nbp;
philpem@5 230 std::memcpy(ptrd,ptrs0,sizeof(T)*nbp0*3);
philpem@5 231 ptrd+=3*nbp0; ptrs0+=3*nbp0;
philpem@5 232 std::memcpy(ptrd,ptrs,sizeof(T)*nbp);
philpem@5 233 ptrd+=nbp;
philpem@5 234 std::memcpy(ptrd,ptrs0,sizeof(T)*nbp0);
philpem@5 235 return res;
philpem@5 236 }
philpem@5 237
philpem@5 238 CImg<T>& appendCImg3d(const CImg<T>& img) {
philpem@5 239 return get_appendCImg3d(img).transfer_to(*this);
philpem@5 240 }
philpem@5 241
philpem@5 242 CImg<T>& centerCImg3d() {
philpem@5 243 const unsigned int nbv = (unsigned int)(*this)[6];
philpem@5 244 const T *ptrs = ptr() + 8;
philpem@5 245 float xm = cimg::type<float>::max(), ym = xm, zm = xm, xM = cimg::type<float>::min(), yM = xM, zM = xM;
philpem@5 246 for (unsigned int i = 0; i<nbv; ++i) {
philpem@5 247 const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
philpem@5 248 if (x<xm) xm = x; if (x>xM) xM = x;
philpem@5 249 if (y<ym) ym = y; if (y>yM) yM = y;
philpem@5 250 if (z<zm) zm = z; if (z>zM) zM = z;
philpem@5 251 }
philpem@5 252 const float xc = (xm + xM)/2, yc = (ym + yM)/2, zc = (zm + zM)/2;
philpem@5 253 T *ptrd = ptr() + 8;
philpem@5 254 for (unsigned int i = 0; i<nbv; ++i) { *(ptrd++)-=(T)xc; *(ptrd++)-=(T)yc; *(ptrd++)-=(T)zc; }
philpem@5 255 return *this;
philpem@5 256 }
philpem@5 257
philpem@5 258 CImg<T> get_centerCImg3d() const {
philpem@5 259 return (+*this).centerCImg3d();
philpem@5 260 }
philpem@5 261
philpem@5 262 CImg<T>& normalizeCImg3d() {
philpem@5 263 const unsigned int nbv = (unsigned int)(*this)[6];
philpem@5 264 const T *ptrs = ptr() + 8;
philpem@5 265 float xm = cimg::type<float>::max(), ym = xm, zm = xm, xM = cimg::type<float>::min(), yM = xM, zM = xM;
philpem@5 266 for (unsigned int i = 0; i<nbv; ++i) {
philpem@5 267 const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
philpem@5 268 if (x<xm) xm = x; if (x>xM) xM = x;
philpem@5 269 if (y<ym) ym = y; if (y>yM) yM = y;
philpem@5 270 if (z<zm) zm = z; if (z>zM) zM = z;
philpem@5 271 }
philpem@5 272 const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
philpem@5 273 if (delta>0) {
philpem@5 274 T *ptrd = ptr() + 8;
philpem@5 275 for (unsigned int i = 0; i<3*nbv; ++i) *(ptrd++)/=(T)delta;
philpem@5 276 }
philpem@5 277 return *this;
philpem@5 278 }
philpem@5 279
philpem@5 280 CImg<T> get_normalizeCImg3d() const {
philpem@5 281 return (+*this).normalizeCImg3d();
philpem@5 282 }
philpem@5 283
philpem@5 284 template<typename t>
philpem@5 285 CImg<T>& rotateCImg3d(const CImg<t>& rot) {
philpem@5 286 const unsigned int nbv = (unsigned int)(*this)[6];
philpem@5 287 const T *ptrs = ptr() + 8;
philpem@5 288 const float
philpem@5 289 a = (float)rot(0,0), b = (float)rot(1,0), c = (float)rot(2,0),
philpem@5 290 d = (float)rot(0,1), e = (float)rot(1,1), f = (float)rot(2,1),
philpem@5 291 g = (float)rot(0,2), h = (float)rot(1,2), i = (float)rot(2,2);
philpem@5 292 T *ptrd = ptr() + 8;
philpem@5 293 for (unsigned int j = 0; j<nbv; ++j) {
philpem@5 294 const float x = (float)*(ptrs++), y = (float)*(ptrs++), z = (float)*(ptrs++);
philpem@5 295 *(ptrd++) = (T)(a*x + b*y + c*z);
philpem@5 296 *(ptrd++) = (T)(d*x + e*y + f*z);
philpem@5 297 *(ptrd++) = (T)(g*x + h*y + i*z);
philpem@5 298 }
philpem@5 299 return *this;
philpem@5 300 }
philpem@5 301
philpem@5 302 template<typename t>
philpem@5 303 CImg<T> get_rotateCImg3d(const CImg<t>& rot) const {
philpem@5 304 return (+*this).rotateCImg3d(rot);
philpem@5 305 }
philpem@5 306
philpem@5 307 CImg<T>& translateCImg3d(const float tx, const float ty, const float tz) {
philpem@5 308 const unsigned int nbv = (unsigned int)(*this)[6];
philpem@5 309 T *ptrd = ptr() + 8;
philpem@5 310 for (unsigned int j = 0; j<nbv; ++j) { *(ptrd++) += (T)tx; *(ptrd++) += (T)ty; *(ptrd++) += (T)tz; }
philpem@5 311 return *this;
philpem@5 312 }
philpem@5 313
philpem@5 314 CImg<T> get_translateCImg3d(const float tx, const float ty, const float tz) const {
philpem@5 315 return (+*this).translateCImg3d(tx,ty,tz);
philpem@5 316 }
philpem@5 317
philpem@5 318 CImg<T>& coloropacityCImg3d(const float R, const float G, const float B, const float opacity, const bool set_RGB, const bool set_opacity) {
philpem@5 319 T *ptrd = ptr() + 6;
philpem@5 320 const unsigned int
philpem@5 321 nbv = (unsigned int)*(ptrd++),
philpem@5 322 nbp = (unsigned int)*(ptrd++);
philpem@5 323 ptrd+=3*nbv;
philpem@5 324 for (unsigned int i = 0; i<nbp; ++i) { const unsigned int N = (unsigned int)*(ptrd++); ptrd+=N; }
philpem@5 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;
philpem@5 326 if (set_opacity) for (unsigned int o = 0; o<nbp; ++o) *(ptrd++) = (T)opacity;
philpem@5 327 return *this;
philpem@5 328 }
philpem@5 329
philpem@5 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 {
philpem@5 331 return (+*this).coloropacityCImg3d(R,G,B,opacity,set_RGB,set_opacity);
philpem@5 332 }
philpem@5 333
philpem@5 334 #else // eq. to #ifndef cimg_plugin
philpem@5 335
philpem@5 336 #define cimg_debug 1
philpem@5 337 #ifndef cimg_gmic_cpp
philpem@5 338 #define cimg_gmic_cpp "examples/gmic.cpp"
philpem@5 339 #define cimg_cimg_h "../CImg.h"
philpem@5 340 #endif
philpem@5 341 #define cimg_stdout stdout
philpem@5 342 #define cimg_plugin cimg_gmic_cpp
philpem@5 343 #include cimg_cimg_h
philpem@5 344 #include "gmic.h"
philpem@5 345 using namespace cimg_library;
philpem@5 346
philpem@5 347 // The lines below are necessary when using a non-standard compiler such as visualcpp6.
philpem@5 348 #ifdef cimg_use_visualcpp6
philpem@5 349 #define std
philpem@5 350 #endif
philpem@5 351 #ifdef min
philpem@5 352 #undef min
philpem@5 353 #undef max
philpem@5 354 #endif
philpem@5 355
philpem@5 356 #if !defined(gmic_main) || !defined(gmic_separate_compilation)
philpem@5 357
philpem@5 358 // Define some useful macros.
philpem@5 359 //---------------------------
philpem@5 360
philpem@5 361 // Code for validity checking of indices.
philpem@5 362 #define gmic_inds indices2string(indices,true)
philpem@5 363 #define gmic_check_indice(ind,funcname) { \
philpem@5 364 const int indo = (int)ind; \
philpem@5 365 if (ind<0) ind+=images.size; \
philpem@5 366 if (ind<0 || ind>=(int)images.size) { \
philpem@5 367 if (images.size) error(funcname " : Invalid indice '[%d]' (valid indice range is -%u...%u).",gmic_inds,indo,images.size,images.size-1); \
philpem@5 368 else error(funcname " : Invalid indice '[%d]' (image list is empty).",gmic_inds,indo); \
philpem@5 369 } \
philpem@5 370 }
philpem@5 371
philpem@5 372 // Code for having 'get' or 'non-get' versions of G'MIC commands.
philpem@5 373 #define gmic_apply(instance,function) { \
philpem@5 374 if (get_version) { \
philpem@5 375 unsigned int posi = 0; \
philpem@5 376 if (images.contains(instance,posi)) filenames.insert(filenames[posi]); \
philpem@5 377 else filenames.insert(CImg<char>("(gmic)",7,1,1,1,false)); \
philpem@5 378 CImg<T> res = instance.get_##function; \
philpem@5 379 images.insert(1); res.transfer_to(images.last()); \
philpem@5 380 } else instance.function; \
philpem@5 381 }
philpem@5 382
philpem@5 383 // Code for simple commands that has no parameters and act on images.
philpem@5 384 #define gmic_simple_item(option,function,description) \
philpem@5 385 if (!cimg::strcmp(option,item0)) { \
philpem@5 386 print(description,gmic_inds); cimg_foroff(indices,l) gmic_apply(images[indices[l]],function()); \
philpem@5 387 continue; \
philpem@5 388 }
philpem@5 389
philpem@5 390 // Code for the type cast command.
philpem@5 391 #define gmic_cast(pixel_type,st_type) \
philpem@5 392 if (!cimg::strcmp(#pixel_type,argument)) { \
philpem@5 393 print("Set pixel type to '%s'.",#pixel_type); ++position; \
philpem@5 394 if (!cimg::strcmp(st_type,cimg::type<T>::string())) continue; \
philpem@5 395 CImgList<pixel_type> casted_images; \
philpem@5 396 while (images) { casted_images.insert(images[0]); images.remove(0); } \
philpem@5 397 return parse_##pixel_type(casted_images); \
philpem@5 398 }
philpem@5 399
philpem@5 400 // Code for G'MIC arithmetic commands.
philpem@5 401 #define gmic_arithmetic_item(option1,option2,\
philpem@5 402 function1,description1,arg1_1,arg1_2,value_type1, \
philpem@5 403 function2,description2_1,description2_2,arg2_1,arg2_2,description3) \
philpem@5 404 if (!cimg::strcmp(option1,item0) || !cimg::strcmp(option2,item0)) { \
philpem@5 405 double value = 0; char inds[4096] = { 0 }, sep = 0, end = 0; \
philpem@5 406 if (std::sscanf(argument,"%lf%c",&value,&end)==1) { \
philpem@5 407 print(description1 ".",arg1_1,arg1_2); \
philpem@5 408 cimg_foroff(indices,l) \
philpem@5 409 if (get_version) { \
philpem@5 410 images.insert(images[indices[l]]); images.last().function1((value_type1)value); \
philpem@5 411 filenames.insert(filenames[indices[l]]); } \
philpem@5 412 else images[indices[l]].function1((value_type1)value); \
philpem@5 413 ++position; \
philpem@5 414 } else if (std::sscanf(argument,"[%4095[0-9.eE%+-]%c%c",inds,&sep,&end)==2 && sep==']') { \
philpem@5 415 const CImg<unsigned int> ind = indices2cimg(inds,images.size,option1); \
philpem@5 416 if (ind.size()!=1) error(description2_1 " : Argument '[%s]' should contain one indice.",gmic_inds,inds); \
philpem@5 417 print(description2_2 ".",arg2_1,arg2_2); \
philpem@5 418 const CImg<T> img0 = images[ind[0]]; \
philpem@5 419 cimg_foroff(indices,l) \
philpem@5 420 if (get_version) { \
philpem@5 421 images.insert(images[indices[l]]); images.last().function2(img0); \
philpem@5 422 filenames.insert(filenames[indices[l]]); } \
philpem@5 423 else images[indices[l]].function2(img0); \
philpem@5 424 ++position; \
philpem@5 425 } else { \
philpem@5 426 print(description3 ".",gmic_inds); \
philpem@5 427 if (images && indices) { \
philpem@5 428 for (unsigned int siz = indices.size(), ind0 = indices[0], off = 0, l = 1; l<siz; ++l) { \
philpem@5 429 const unsigned int ind = indices[l] - off; \
philpem@5 430 images[ind0].function2(images[ind]); \
philpem@5 431 images.remove(ind); filenames.remove(ind); \
philpem@5 432 ++off; \
philpem@5 433 }}} continue; \
philpem@5 434 }
philpem@5 435
philpem@5 436 // Constructors.
philpem@5 437 //--------------
philpem@5 438 #if defined(gmic_float) || !defined(gmic_separate_compilation)
philpem@5 439
philpem@5 440 #include "gmic_def.h"
philpem@5 441
philpem@5 442 gmic_exception::gmic_exception() {
philpem@5 443 message[0] = '\0';
philpem@5 444 }
philpem@5 445
philpem@5 446 gmic_exception::gmic_exception(const char *format, ...) {
philpem@5 447 std::va_list ap;
philpem@5 448 va_start(ap,format);
philpem@5 449 std::vsprintf(message,format,ap);
philpem@5 450 va_end(ap);
philpem@5 451 }
philpem@5 452
philpem@5 453 gmic_exception::gmic_exception(const char *format, std::va_list ap) {
philpem@5 454 std::vsprintf(message,format,ap);
philpem@5 455 }
philpem@5 456
philpem@5 457 gmic::gmic() {
philpem@5 458 assign(0);
philpem@5 459 }
philpem@5 460
philpem@5 461 // Set default values of G'MIC parameters and macros.
philpem@5 462 //----------------------------------------------------
philpem@5 463 gmic& gmic::assign(const unsigned int size, const char *const custom_macros, const bool add_macros_start) {
philpem@5 464 filenames.assign(size,CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 465 position = 0;
philpem@5 466 verbosity_level = 0;
philpem@5 467 is_released = true;
philpem@5 468 is_debug = false;
philpem@5 469 is_begin = true;
philpem@5 470 background3d[0] = 120;
philpem@5 471 background3d[1] = 120;
philpem@5 472 background3d[2] = 140;
philpem@5 473 render3d = 4;
philpem@5 474 renderd3d = -1;
philpem@5 475 is_oriented3d = false;
philpem@5 476 focale3d = 500;
philpem@5 477 light3d_x = 0;
philpem@5 478 light3d_y = 0;
philpem@5 479 light3d_z = -5000;
philpem@5 480 specular_light3d = 0.15f;
philpem@5 481 specular_shine3d = 0.8f;
philpem@5 482 is_fullpath = false;
philpem@5 483 add_macros(data_def,sizeof(data_def)-1,true);
philpem@5 484 add_macros(custom_macros,cimg::strlen(custom_macros)-1,add_macros_start);
philpem@5 485 return *this;
philpem@5 486 }
philpem@5 487
philpem@5 488 // Error procedure.
philpem@5 489 //-----------------
philpem@5 490 const gmic& gmic::error(const char *format, ...) const {
philpem@5 491 va_list ap;
philpem@5 492 va_start(ap,format);
philpem@5 493 char message[1024] = { 0 };
philpem@5 494 std::vsprintf(message,format,ap);
philpem@5 495 va_end(ap);
philpem@5 496 if (verbosity_level>=0) {
philpem@5 497 std::fprintf(cimg_stdout,"\n<gmic-#%u> ** Error ** %s",filenames.size,message);
philpem@5 498 std::fprintf(cimg_stdout,"\n<gmic-#%u> Abort G'MIC instance.\n",filenames.size);
philpem@5 499 std::fflush(cimg_stdout);
philpem@5 500 }
philpem@5 501 throw gmic_exception(message);
philpem@5 502 return *this;
philpem@5 503 }
philpem@5 504
philpem@5 505 // Warning procedure.
philpem@5 506 //-------------------
philpem@5 507 const gmic& gmic::warning(const char *format, ...) const {
philpem@5 508 va_list ap;
philpem@5 509 va_start(ap,format);
philpem@5 510 if (verbosity_level>=0) {
philpem@5 511 std::fprintf(cimg_stdout,"\n<gmic-#%u> ** Warning ** ",filenames.size);
philpem@5 512 std::vfprintf(cimg_stdout,format,ap);
philpem@5 513 std::fflush(cimg_stdout);
philpem@5 514 }
philpem@5 515 va_end(ap);
philpem@5 516 return *this;
philpem@5 517 }
philpem@5 518
philpem@5 519 // Print debug messages.
philpem@5 520 //----------------------
philpem@5 521 const gmic& gmic::debug(const char *format, ...) const {
philpem@5 522 const char t_normal[] = { 0x1b,'[','0',';','0',';','0','m','\0' };
philpem@5 523 const char t_red[] = { 0x1b,'[','4',';','3','1',';','5','9','m','\0' };
philpem@5 524 const char t_bold[] = { 0x1b,'[','1','m','\0' };
philpem@5 525 if (is_debug) {
philpem@5 526 va_list ap;
philpem@5 527 va_start(ap,format);
philpem@5 528 std::fprintf(cimg_stdout,"\n%s%s<gmic-debug-#%u>%s ",t_bold,t_red,filenames.size,t_normal);
philpem@5 529 std::vfprintf(cimg_stdout,format,ap);
philpem@5 530 va_end(ap);
philpem@5 531 std::fflush(cimg_stdout);
philpem@5 532 }
philpem@5 533 return *this;
philpem@5 534 }
philpem@5 535
philpem@5 536 // Print status messages.
philpem@5 537 //-----------------------
philpem@5 538 const gmic& gmic::print(const char *format, ...) const {
philpem@5 539 va_list ap;
philpem@5 540 va_start(ap,format);
philpem@5 541 if (verbosity_level>=0) {
philpem@5 542 std::fprintf(cimg_stdout,"\n<gmic-#%u> ",filenames.size);
philpem@5 543 std::vfprintf(cimg_stdout,format,ap);
philpem@5 544 std::fflush(cimg_stdout);
philpem@5 545 }
philpem@5 546 va_end(ap);
philpem@5 547 return *this;
philpem@5 548 }
philpem@5 549
philpem@5 550 // Add macros from a char* buffer.
philpem@5 551 //---------------------------------
philpem@5 552 gmic& gmic::add_macros(const char *const data_macros, const unsigned int data_size, const bool add_macros_at_start) {
philpem@5 553 if (!data_macros || !data_size) return *this;
philpem@5 554 char mac[4096] = { 0 }, com[256*1024] = { 0 }, line[256*1024] = { 0 }, sep = 0;
philpem@5 555 const char *data = data_macros, *const data_end = data_macros + data_size;
philpem@5 556 while (data<data_end) {
philpem@5 557 if (*data=='\n') ++data;
philpem@5 558 else {
philpem@5 559 if (std::sscanf(data,"%262143[^\n]",line)>0) data += cimg::strlen(line) + 1;
philpem@5 560 if (line[0]!='#') { // Useful line (not a comment)
philpem@5 561 mac[0] = com[0] = 0;
philpem@5 562 if (std::sscanf(line,"%4095[^: ] %c %262143[^\n]",mac,&sep,com)>=2 && sep==':' &&
philpem@5 563 std::sscanf(mac,"%4095s",line)==1) { // Macro definition.
philpem@5 564 macros.insert(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),add_macros_at_start?0:macros.size);
philpem@5 565 commands.insert(CImg<char>(com,cimg::strlen(com)+1,1,1,1,false),add_macros_at_start?0:commands.size);
philpem@5 566 } else { // Possible continuation of a previous macro definition.
philpem@5 567 if (!macros) error("Fatal error : Invalid G'MIC macros data.");
philpem@5 568 CImg<char> &last = commands[add_macros_at_start?0:commands.size-1];
philpem@5 569 last[last.size()-1] = ' ';
philpem@5 570 last.append(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),'x');
philpem@5 571 }
philpem@5 572 }
philpem@5 573 }
philpem@5 574 }
philpem@5 575 return *this;
philpem@5 576 }
philpem@5 577
philpem@5 578 // Add macros from a macro file.
philpem@5 579 //------------------------------
philpem@5 580 gmic& gmic::add_macros(std::FILE *const file, const bool add_macros_at_start) {
philpem@5 581 if (!file) return *this;
philpem@5 582 char mac[4096] = { 0 }, com[256*1024] = { 0 }, line[256*1024] = { 0 }, sep = 0;
philpem@5 583 int err = 0;
philpem@5 584 while ((err=std::fscanf(file,"%262143[^\n] ",line)>=0)) {
philpem@5 585 if (err) { // Non empty-line
philpem@5 586 mac[0] = com[0] = 0;
philpem@5 587 if (line[0]!='#') { // Useful line (not a comment).
philpem@5 588 if (std::sscanf(line,"%4095[^: ] %c %262143[^\n]",mac,&sep,com)>=2 && sep==':' &&
philpem@5 589 std::sscanf(mac,"%4095s",line)==1) { // Macro definition.
philpem@5 590 macros.insert(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),add_macros_at_start?0:macros.size);
philpem@5 591 commands.insert(CImg<char>(com,cimg::strlen(com)+1,1,1,1,false),add_macros_at_start?0:commands.size);
philpem@5 592 } else { // Possible continuation of a previous macro definition.
philpem@5 593 if (!macros) error("Fatal error : Invalid G'MIC macros data.");
philpem@5 594 CImg<char> &last = commands[add_macros_at_start?0:commands.size-1];
philpem@5 595 last[last.size()-1] = ' ';
philpem@5 596 last.append(CImg<char>(line,cimg::strlen(line)+1,1,1,1,false),'x');
philpem@5 597 }
philpem@5 598 }
philpem@5 599 }
philpem@5 600 }
philpem@5 601 return *this;
philpem@5 602 }
philpem@5 603
philpem@5 604 // Return indices of the images from a string.
philpem@5 605 //--------------------------------------------
philpem@5 606 CImg<unsigned int> gmic::indices2cimg(const char *const string, const unsigned int indice_max,
philpem@5 607 const char *const command) const {
philpem@5 608 if (!cimg::strlen(string)) return CImg<unsigned int>();
philpem@5 609 CImgList<unsigned int> inds;
philpem@5 610 const char *it = string;
philpem@5 611 for (bool stopflag = false; !stopflag; ) {
philpem@5 612 char sep = 0, end = 0, item0[4096] = { 0 }, item1[4096] = { 0 };
philpem@5 613 float ind0 = 0, ind1 = 0, step = 1;
philpem@5 614 if (std::sscanf(it,"%4095[^,]%c",item0,&end)!=2) stopflag = true;
philpem@5 615 else it += 1 + cimg::strlen(item0);
philpem@5 616 const int err = std::sscanf(item0,"%4095[^:]%c%f%c",item1,&sep,&step,&end);
philpem@5 617 if (err!=1 && err!=3) error("Command '%s' : Invalid indice(s) '[%s]'.",command,string);
philpem@5 618 if (std::sscanf(item1,"%f%%-%f%c%c",&ind0,&ind1,&sep,&end)==3 && sep=='%') {
philpem@5 619 ind0 = (float)cimg::round(ind0*indice_max/100,1);
philpem@5 620 ind1 = (float)cimg::round(ind1*indice_max/100,1);
philpem@5 621 } else if (std::sscanf(item1,"%f%%-%f%c",&ind0,&ind1,&end)==2)
philpem@5 622 ind0 = (float)cimg::round(ind0*indice_max/100,1);
philpem@5 623 else if (std::sscanf(item1,"%f-%f%c%c",&ind0,&ind1,&sep,&end)==3 && sep=='%')
philpem@5 624 ind1 = (float)cimg::round(ind1*indice_max/100,1);
philpem@5 625 else if (std::sscanf(item1,"%f-%f%c",&ind0,&ind1,&end)==2) { }
philpem@5 626 else if (std::sscanf(item1,"%f%c%c",&ind0,&sep,&end)==2 && sep=='%')
philpem@5 627 ind1 = (ind0 = (float)cimg::round(ind0*indice_max/100,1));
philpem@5 628 else if (std::sscanf(item1,"%f%c",&ind0,&end)==1)
philpem@5 629 ind1 = ind0;
philpem@5 630 else error("Command '%s' : Invalid indice(s) '[%s]'.",command,string);
philpem@5 631 if (ind0<0) ind0+=indice_max;
philpem@5 632 if (ind1<0) ind1+=indice_max;
philpem@5 633 if (ind0<0 || ind0>=indice_max || ind1<0 || ind1>=indice_max || step<=0) {
philpem@5 634 if (indice_max) error("Command '%s' : Invalid indice(s) '[%s]' (valid indice range is -%u...%u).",
philpem@5 635 command,string,indice_max,indice_max-1);
philpem@5 636 else error("Command '%s' : Invalid indice(s) '[%s]' (image list is empty).",
philpem@5 637 command,string);
philpem@5 638 }
philpem@5 639 if (ind0>ind1) cimg::swap(ind0,ind1);
philpem@5 640 const unsigned int
philpem@5 641 iind0 = (unsigned int)ind0,
philpem@5 642 _ind1 = (unsigned int)ind1,
philpem@5 643 iind1 = (unsigned int)(_ind1 - cimg::mod((float)_ind1,step));
philpem@5 644 if (iind0==iind1) inds.insert(CImg<unsigned int>::vector(iind0));
philpem@5 645 else inds.insert(CImg<unsigned int>::sequence((unsigned int)(1+(iind1-iind0)/step),
philpem@5 646 (unsigned int)iind0,
philpem@5 647 (unsigned int)iind1).get_split('y'));
philpem@5 648 }
philpem@5 649 inds = inds.get_append('y').sort().get_split('y');
philpem@5 650 cimglist_for(inds,l) if (l!=inds.size-1 && inds(l,0)==inds(l+1,0)) inds.remove(l--);
philpem@5 651 if (is_debug) {
philpem@5 652 debug("Indices : ");
philpem@5 653 inds.get_append('y').print(); // List indices if debug mode is activated.
philpem@5 654 }
philpem@5 655 return inds.get_append('y').sort();
philpem@5 656 }
philpem@5 657
philpem@5 658 // Return stringified version of indices or filenames.
philpem@5 659 //----------------------------------------------------
philpem@5 660 char* gmic::indices2string(const CImg<unsigned int>& indices, const bool display_indices) const {
philpem@5 661 static char res0[4096] = { 0 }, res1[4096] = { 0 };
philpem@5 662 const unsigned int siz = indices.size();
philpem@5 663 if (display_indices) {
philpem@5 664 switch (siz) {
philpem@5 665 case 0: std::sprintf(res0," []"); break;
philpem@5 666 case 1: std::sprintf(res0," [%u]",indices[0]); break;
philpem@5 667 case 2: std::sprintf(res0,"s [%u,%u]",indices[0],indices[1]); break;
philpem@5 668 case 3: std::sprintf(res0,"s [%u,%u,%u]",indices[0],indices[1],indices[2]); break;
philpem@5 669 case 4: std::sprintf(res0,"s [%u,%u,%u,%u]",indices[0],indices[1],indices[2],indices[3]); break;
philpem@5 670 default: std::sprintf(res0,"s [%u,...,%u]",indices[0],indices[siz-1]);
philpem@5 671 }
philpem@5 672 return res0;
philpem@5 673 }
philpem@5 674 switch (siz) {
philpem@5 675 case 0: std::sprintf(res1," "); break;
philpem@5 676 case 1: std::sprintf(res1,"%s",filenames[indices[0]].ptr()); break;
philpem@5 677 case 2: std::sprintf(res1,"%s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr()); break;
philpem@5 678 case 3: std::sprintf(res1,"%s, %s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr(),
philpem@5 679 filenames[indices[2]].ptr()); break;
philpem@5 680 case 4: std::sprintf(res1,"%s, %s, %s, %s",filenames[indices[0]].ptr(),filenames[indices[1]].ptr(),
philpem@5 681 filenames[indices[2]].ptr(), filenames[indices[3]].ptr()); break;
philpem@5 682 default: std::sprintf(res1,"%s, ..., %s",filenames[indices[0]].ptr(),filenames[indices[siz-1]].ptr());
philpem@5 683 }
philpem@5 684 return res1;
philpem@5 685 }
philpem@5 686 #endif // #if defined(gmic_float) || !defined(gmic_separate_compilation)
philpem@5 687
philpem@5 688 // Template constructors.
philpem@5 689 //-----------------------
philpem@5 690 template<typename T>
philpem@5 691 gmic::gmic(const int argc, const char *const *const argv, CImgList<T>& images, const char *custom_macros, const bool add_macros_at_start) {
philpem@5 692 assign(images.size,custom_macros,add_macros_at_start);
philpem@5 693 for (int pos = 1; pos<argc; ++pos)
philpem@5 694 command_line.insert(CImg<char>(argv[pos],cimg::strlen(argv[pos])+1,1,1,1,false));
philpem@5 695 is_released = false;
philpem@5 696 parse(images);
philpem@5 697 }
philpem@5 698
philpem@5 699 template<typename T>
philpem@5 700 gmic::gmic(const char *const command, CImgList<T>& images, const char *custom_macros, const bool add_macros_at_start) {
philpem@5 701 assign(images.size,custom_macros,add_macros_at_start);
philpem@5 702 char item[4096] = { 0 };
philpem@5 703 for (const char *ncommand = command; *ncommand; ) {
philpem@5 704 if (std::sscanf(ncommand,"%[^ ]",item)==1) {
philpem@5 705 const int l = cimg::strlen(item);
philpem@5 706 command_line.insert(CImg<char>(item,l+1,1,1,1,false));
philpem@5 707 ncommand += l;
philpem@5 708 while (*ncommand==' ') ++ncommand;
philpem@5 709 } else break;
philpem@5 710 }
philpem@5 711 is_released = true;
philpem@5 712 parse(images);
philpem@5 713 }
philpem@5 714
philpem@5 715 // Display specified image(s).
philpem@5 716 //-----------------------------
philpem@5 717 template<typename T>
philpem@5 718 bool gmic::display_images(const CImgList<T>& images, const CImg<unsigned int>& indices,
philpem@5 719 const bool verbose) const {
philpem@5 720 if (!images || !indices) { print("Display image []."); return false; }
philpem@5 721 CImgList<unsigned int> inds = indices.get_unroll('x').get_split('x');
philpem@5 722 CImgList<T> visu;
philpem@5 723 unsigned int max_height = 0;
philpem@5 724 cimglist_for(inds,l) {
philpem@5 725 const CImg<T>& img = images[inds(l,0)];
philpem@5 726 if (img.height>max_height && !img.is_CImg3d()) max_height = img.height;
philpem@5 727 }
philpem@5 728 cimglist_for(inds,l) {
philpem@5 729 const unsigned int ind = inds(l,0);
philpem@5 730 const CImg<T> &img = images[ind];
philpem@5 731 if (img) {
philpem@5 732 if (!max_height || img.height<max_height) visu.insert(img,~0U,true);
philpem@5 733 else visu.insert(img.get_lines(0,max_height-1));
philpem@5 734 } else if (verbose) { warning("Display image : Image [%d] is empty.",ind); inds.remove(l--); }
philpem@5 735 }
philpem@5 736 const CImg<unsigned int> nindices = inds.get_append('x');
philpem@5 737 const char *const fnames = indices2string(nindices,false);
philpem@5 738 print("Display image%s = '%s'.\n\n",gmic_inds,fnames);
philpem@5 739 if (visu.size) {
philpem@5 740 if (visu.size!=1) visu.display(fnames,verbosity_level>=0,'x','p');
philpem@5 741 else {
philpem@5 742 const CImg<T> &img = visu[0];
philpem@5 743 char title[4096] = { 0 };
philpem@5 744 std::sprintf(title,"%s (%dx%dx%dx%d)",fnames,img.dimx(),img.dimy(),img.dimz(),img.dimv());
philpem@5 745 img.display(title,verbosity_level>=0);
philpem@5 746 }
philpem@5 747 }
philpem@5 748 return true;
philpem@5 749 }
philpem@5 750
philpem@5 751 // Display plots of specified image(s).
philpem@5 752 //--------------------------------------
philpem@5 753 template<typename T>
philpem@5 754 bool gmic::display_plots(const CImgList<T>& images, const CImg<unsigned int>& indices,
philpem@5 755 const unsigned int plot_type, const unsigned int vertex_type,
philpem@5 756 const double xmin, const double xmax,
philpem@5 757 const double ymin, const double ymax,
philpem@5 758 const bool verbose) const {
philpem@5 759 if (!images || !indices) { print("Plot image []."); return false; }
philpem@5 760 CImgDisplay disp(cimg_fitscreen(640,480,1),0,0);
philpem@5 761 cimg_foroff(indices,l) {
philpem@5 762 const unsigned int ind = indices[l];
philpem@5 763 const CImg<T>& img = images[ind];
philpem@5 764 if (img) {
philpem@5 765 print("Plot image%s = '%s'.\n",gmic_inds,indices2string(indices,false));
philpem@5 766 if (verbosity_level>=0) { std::fputc('\n',cimg_stdout); img.print(filenames[ind].ptr()); }
philpem@5 767 char title[4096] = { 0 };
philpem@5 768 std::sprintf(title,"%s (%dx%dx%dx%d)",
philpem@5 769 filenames[ind].ptr(),img.dimx(),img.dimy(),img.dimz(),img.dimv());
philpem@5 770 img.display_graph(disp.set_title(title),plot_type,vertex_type,0,xmin,xmax,0,ymin,ymax);
philpem@5 771 } else if (verbose) warning("Plot image : Image [%d] is empty.",ind);
philpem@5 772 }
philpem@5 773 return true;
philpem@5 774 }
philpem@5 775
philpem@5 776 // Display specified 3D object(s).
philpem@5 777 //--------------------------------
philpem@5 778 template<typename T>
philpem@5 779 bool gmic::display_objects3d(const CImgList<T>& images, const CImg<unsigned int>& indices,
philpem@5 780 const bool verbose) const {
philpem@5 781 if (!indices) { print("Display 3D object []."); return false; }
philpem@5 782 CImg<unsigned char> background;
philpem@5 783 bool exist3d = false;
philpem@5 784 CImgDisplay disp;
philpem@5 785 cimg_foroff(indices,l) {
philpem@5 786 const unsigned int ind = indices[l];
philpem@5 787 const CImg<T> &img = images[ind];
philpem@5 788 if (!img.is_CImg3d()) {
philpem@5 789 if (verbose) warning("Display 3D object : Image [%d] is not a 3D object.",ind);
philpem@5 790 } else {
philpem@5 791 exist3d = true;
philpem@5 792 if (!background || !disp) {
philpem@5 793 background.assign(cimg_fitscreen(640,480,1),1,3);
philpem@5 794 cimg_forV(background,k) background.get_shared_channel(k).fill(background3d[k]);
philpem@5 795 disp.assign(background);
philpem@5 796 }
philpem@5 797 CImgList<unsigned int> primitives3d;
philpem@5 798 CImgList<unsigned char> colors3d;
philpem@5 799 CImg<float> opacities3d;
philpem@5 800 CImg<float> points3d(img);
philpem@5 801 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
philpem@5 802 print("Display 3D object [%u] = '%s' (%d points, %u primitives).",
philpem@5 803 ind,filenames[ind].ptr(),points3d.dimx(),primitives3d.size);
philpem@5 804 disp.set_title("%s (%d points, %u primitives)",
philpem@5 805 filenames[ind].ptr(),points3d.dimx(),primitives3d.size);
philpem@5 806 background.display_object3d(disp,points3d,primitives3d,colors3d,opacities3d,
philpem@5 807 true,render3d,renderd3d,!is_oriented3d,focale3d,specular_light3d,specular_shine3d);
philpem@5 808 if (disp.is_closed) break;
philpem@5 809 }
philpem@5 810 }
philpem@5 811 return exist3d;
philpem@5 812 }
philpem@5 813
philpem@5 814 // Substitute '@' expressions.
philpem@5 815 //----------------------------
philpem@5 816 template<typename T>
philpem@5 817 CImg<char> gmic::substitute_arobace(const char *const argument, const CImgList<T>& images) const {
philpem@5 818 if (!argument) return CImg<char>();
philpem@5 819 CImgList<char> _largument;
philpem@5 820 char range[4096] = { 0 };
philpem@5 821 for (const char *nargument = argument; *nargument; ) {
philpem@5 822 if (*nargument=='@') {
philpem@5 823 char argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
philpem@5 824 int ind = 0, bcond = 0; *range = 0; char sepx = 0, sepy = 0, sepz = 0, sepv = 0, sep = 0, end = 0;
philpem@5 825 float x = 0, y = 0, z = 0, v = 0, m = 0, M = 1;
philpem@5 826 if (nargument[1]=='#' ||
philpem@5 827 (std::sscanf(nargument,"@{#%c",&sep)==1 && sep=='}')) {
philpem@5 828 std::sprintf(range,"%u",images.size);
philpem@5 829 _largument.insert(CImg<char>(range,cimg::strlen(range),1,1,1,true));
philpem@5 830 if (sep=='}') nargument+=4; else nargument+=2;
philpem@5 831 } else if (std::sscanf(nargument,"@%d",&ind)==1 ||
philpem@5 832 (std::sscanf(nargument,"@{%d%c",&ind,&sep)==2 && sep=='}') ||
philpem@5 833 (std::sscanf(nargument,"@{%d%*c%4095[^}]%c",&ind,range,&sep)==3 && sep=='}')) {
philpem@5 834 int nind = ind;
philpem@5 835 if (nind<0) nind+=images.size;
philpem@5 836 if (nind<0 || nind>=(int)images.size) {
philpem@5 837 if (images.size) error("Invalid indice '%d' in '@argument' (valid indice range is -%u...%u).",
philpem@5 838 ind,images.size,images.size-1);
philpem@5 839 else error("Invalid indice '%d' in '@argument' (image list is empty).",ind);
philpem@5 840 }
philpem@5 841 const unsigned int sizrange = cimg::strlen(range);
philpem@5 842 const CImg<T>& img = images[nind];
philpem@5 843 CImg<T> values;
philpem@5 844 if (sizrange) {
philpem@5 845 const CImg<unsigned int> iinds = indices2cimg(range,img.size(),"parsing");
philpem@5 846 values.assign(iinds.size());
philpem@5 847 cimg_foroff(iinds,p) values[p] = img[iinds(p)];
philpem@5 848 } else values = img.get_shared();
philpem@5 849 const CImg<char> vs = values.value_string();
philpem@5 850 const unsigned int vsl = vs.size();
philpem@5 851 if (vsl>1) _largument.insert(CImg<char>(vs.ptr(),vsl-1,1,1,1,true));
philpem@5 852 nargument+= 1 + (sep?2:0) + (sizrange?1:0) + sizrange + std::sprintf(range,"%d",ind);
philpem@5 853 } else if (((std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%c",&ind,argx,&sep)==3 && sep==')') ||
philpem@5 854 (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind,argx,argy,&sep)==4 && sep==')') ||
philpem@5 855 (std::sscanf(nargument,"@(%d%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
philpem@5 856 &ind,argx,argy,argz,&sep)==5 && sep==')') ||
philpem@5 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",
philpem@5 858 &ind,argx,argy,argz,argv,&sep)==6 && sep==')') ||
philpem@5 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",
philpem@5 860 &ind,argx,argy,argz,argv,&bcond,&sep)==7 && sep==')')) &&
philpem@5 861 (!*argx ||
philpem@5 862 (std::sscanf(argx,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
philpem@5 863 std::sscanf(argx,"%f%c",&x,&end)==1) &&
philpem@5 864 (!*argy ||
philpem@5 865 (std::sscanf(argy,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
philpem@5 866 std::sscanf(argy,"%f%c",&y,&end)==1) &&
philpem@5 867 (!*argz ||
philpem@5 868 (std::sscanf(argz,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
philpem@5 869 std::sscanf(argz,"%f%c",&z,&end)==1) &&
philpem@5 870 (!*argv ||
philpem@5 871 (std::sscanf(argv,"%f%c%c",&v,&sepv,&end)==2 && sepv=='%') ||
philpem@5 872 std::sscanf(argv,"%f%c",&v,&end)==1)) {
philpem@5 873 int nind = ind;
philpem@5 874 if (nind<0) nind+=images.size;
philpem@5 875 if (nind<0 || nind>=(int)images.size) {
philpem@5 876 if (images.size) error("Invalid indice '%d' in '@argument' (valid indice range is -%u...%u).",
philpem@5 877 ind,images.size,images.size-1);
philpem@5 878 else error("Invalid indice '%d' in '@argument' (image list is empty).",ind);
philpem@5 879 }
philpem@5 880 const CImg<T>& img = images[nind];
philpem@5 881 const int
philpem@5 882 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
philpem@5 883 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
philpem@5 884 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1),
philpem@5 885 nv = (int)cimg::round(sepv=='%'?v*(img.dimv()-1)/100:v,1);
philpem@5 886 std::sprintf(range,"%g",bcond?(double)img.atXYZV(nx,ny,nz,nv):(double)img.atXYZV(nx,ny,nz,nv,0));
philpem@5 887 _largument.insert(CImg<char>(range,cimg::strlen(range)));
philpem@5 888 while (*nargument!=')') ++nargument; ++nargument;
philpem@5 889 } else if ((std::sscanf(nargument,"@%c",&sep)==1 && sep=='?') ||
philpem@5 890 (std::sscanf(nargument,"@{?%c",&sep)==1 && sep=='}') ||
philpem@5 891 (std::sscanf(nargument,"@[?%c",&sep)==1 && sep==']') ||
philpem@5 892 (std::sscanf(nargument,"@{?%*c%f%c",&M,&sep)==2 && sep=='}') ||
philpem@5 893 (std::sscanf(nargument,"@[?%*c%f%c",&M,&sep)==2 && sep==']') ||
philpem@5 894 (std::sscanf(nargument,"@{?%*c%f%*c%f%c",&m,&M,&sep)==3 && sep=='}') ||
philpem@5 895 (std::sscanf(nargument,"@[?%*c%f%*c%f%c",&m,&M,&sep)==3 && sep==']')) {
philpem@5 896 const double r = m + (M-m)*cimg::rand();
philpem@5 897 if (sep==']') std::sprintf(range,"%d",(int)cimg::round(r,1)); else std::sprintf(range,"%g",r);
philpem@5 898 _largument.insert(CImg<char>(range,cimg::strlen(range),1,1,1,true));
philpem@5 899 if (sep=='?') nargument+=2; else { while (*nargument!=sep) ++nargument; ++nargument; }
philpem@5 900 } else _largument.insert(CImg<char>(nargument++,1,1,1,1,true));
philpem@5 901 } else {
philpem@5 902 std::sscanf(nargument,"%4095[^@]",range);
philpem@5 903 const int l = cimg::strlen(range);
philpem@5 904 _largument.insert(CImg<char>(range,l,1,1,1,true));
philpem@5 905 nargument+=l;
philpem@5 906 }
philpem@5 907 }
philpem@5 908 _largument.insert(CImg<char>::vector(0));
philpem@5 909 return _largument.get_append('x');
philpem@5 910 }
philpem@5 911
philpem@5 912 // Main parsing procedure.
philpem@5 913 //------------------------
philpem@5 914 template<typename T>
philpem@5 915 gmic& gmic::parse(CImgList<T> &images) {
philpem@5 916 const unsigned int command_line_maxsize = 65535;
philpem@5 917 const int no_ind = (int)(~0U>>1);
philpem@5 918 cimg::exception_mode() = 0;
philpem@5 919
philpem@5 920 // Begin command line parsing.
philpem@5 921 while (position<command_line.size && command_line.size<command_line_maxsize) {
philpem@5 922 const char
philpem@5 923 *const orig_item = command_line[position].ptr(),
philpem@5 924 *const orig_argument = position+1<command_line.size?command_line[position+1].ptr():"";
philpem@5 925
philpem@5 926 // Substitute '@' expressions in 'orig_item' and 'orig_argument' if necessary.
philpem@5 927 CImg<char> _item, _argument, _argument_text;
philpem@5 928 if (*orig_item=='-' || *orig_item=='[' || *orig_item=='(') {
philpem@5 929 if (std::strchr(orig_item,'@')) {
philpem@5 930 _item = substitute_arobace(orig_item,images);
philpem@5 931 if (_item.size()>4095) error("Buffer overflow when substituting item '%s'.",orig_item);
philpem@5 932 }
philpem@5 933 if (*orig_item=='-' &&
philpem@5 934 (*orig_argument!='-' ||
philpem@5 935 (*orig_argument=='-' && (orig_argument[1]=='.' || orig_argument[1]=='@' ||
philpem@5 936 (orig_argument[1]>='0' && orig_argument[1]<='9'))))
philpem@5 937 && std::strchr(orig_argument,'@')) {
philpem@5 938 _argument = substitute_arobace(orig_argument,images);
philpem@5 939 if (_argument.size()>4095) error("Buffer overflow when substituting argument '%s'.",orig_argument);
philpem@5 940 }
philpem@5 941 }
philpem@5 942 const char
philpem@5 943 *item = _item?_item.ptr():orig_item,
philpem@5 944 *argument = _argument?_argument.ptr():orig_argument;
philpem@5 945 const char *argument_text = argument;
philpem@5 946 if (cimg::strlen(argument)>=64) {
philpem@5 947 _argument_text.assign(64,1,1,1,0);
philpem@5 948 std::memcpy(_argument_text.ptr(),argument,60*sizeof(char));
philpem@5 949 _argument_text[60] = _argument_text[61] = _argument_text[62] = '.'; _argument_text[63] = 0;
philpem@5 950 argument_text = _argument_text.ptr();
philpem@5 951 }
philpem@5 952
philpem@5 953 // Get current item/command from the command line.
philpem@5 954 char item0[4096] = { 0 }, item1[4096] = { 0 };
philpem@5 955 bool get_version = false;
philpem@5 956 CImg<unsigned int> indices;
philpem@5 957 if (item[0]=='-' && item[1] && item[1]!='.') {
philpem@5 958 char sep0 = 0, sep1 = 0, end = 0;
philpem@5 959 if (item[1]=='-' && item[2] && item[2]!='[' && item[2]!='3' && item[3]!='d') { ++item; get_version = true; }
philpem@5 960 const int err = std::sscanf(item,"%4095[^[]%c%4095[0-9.eE%,:+-]%c%c",item0,&sep0,item1,&sep1,&end);
philpem@5 961 if (err==1) indices = CImg<unsigned int>::sequence(images.size,0,images.size-1);
philpem@5 962 else if (err==4 && sep1==']')
philpem@5 963 indices = indices2cimg(item1,(!strcmp(item0,"-i")||!strcmp(item0,"-input"))?images.size+1:images.size,item0);
philpem@5 964 else { std::strcpy(item0,item); item1[0] = 0; }
philpem@5 965 }
philpem@5 966 ++position;
philpem@5 967
philpem@5 968 // Check for verbosity commands.
philpem@5 969 if (*item=='-') {
philpem@5 970 if (!cimg::strcmp("-verbose+",item) || !cimg::strcmp("-v+",item)) ++verbosity_level;
philpem@5 971 else if (!cimg::strcmp("-verbose-",item) || !cimg::strcmp("-v-",item)) --verbosity_level;
philpem@5 972 }
philpem@5 973
philpem@5 974 if (is_begin) { print("Start G'MIC instance."); is_begin = false; }
philpem@5 975 debug("Item : '%s', Argument : '%s'.",item,argument);
philpem@5 976
philpem@5 977 // Begin command interpretation.
philpem@5 978 try {
philpem@5 979 if (*item=='-') {
philpem@5 980
philpem@5 981 //----------------
philpem@5 982 // Global options
philpem@5 983 //----------------
philpem@5 984
philpem@5 985 // Verbosity (actually, just continue to next command since verbosity has been already processed above).
philpem@5 986 if (!cimg::strcmp("-verbose+",item) || !cimg::strcmp("-v+",item) ||
philpem@5 987 !cimg::strcmp("-verbose-",item) || !cimg::strcmp("-v-",item)) continue;
philpem@5 988
philpem@5 989 // Load macro file.
philpem@5 990 if (!cimg::strcmp("-macros",item) || !cimg::strcmp("-m",item)) {
philpem@5 991 print("Load macro file '%s'",cimg::basename(argument));
philpem@5 992 std::FILE *const file = cimg::fopen(argument,"r");
philpem@5 993 if (file) {
philpem@5 994 const unsigned int siz = macros.size;
philpem@5 995 add_macros(file,true);
philpem@5 996 cimg::fclose(file);
philpem@5 997 if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u macros added).",macros.size-siz);
philpem@5 998 }
philpem@5 999 else error("Load macro file '%s' : File not found.",argument);
philpem@5 1000 ++position; continue;
philpem@5 1001 }
philpem@5 1002
philpem@5 1003 // Switch 'is_debug' flag.
philpem@5 1004 if (!cimg::strcmp("-debug",item)) {
philpem@5 1005 is_debug = !is_debug;
philpem@5 1006 continue;
philpem@5 1007 }
philpem@5 1008
philpem@5 1009 // Switch 'is_fullpath' flag.
philpem@5 1010 if (!cimg::strcmp("-fullpath",item)) {
philpem@5 1011 is_fullpath = !is_fullpath;
philpem@5 1012 continue;
philpem@5 1013 }
philpem@5 1014
philpem@5 1015 //----------------------
philpem@5 1016 // Arithmetic operators
philpem@5 1017 //----------------------
philpem@5 1018
philpem@5 1019 // Common arithmetic operators.
philpem@5 1020 gmic_arithmetic_item("-add","-+",
philpem@5 1021 operator+=,"Add %g to image%s",value,gmic_inds,T,
philpem@5 1022 operator+=,"Add to image%s",
philpem@5 1023 "Add image [%d] to image%s",ind[0],gmic_inds,
philpem@5 1024 "Add image%s together");
philpem@5 1025
philpem@5 1026 gmic_arithmetic_item("-sub","--",
philpem@5 1027 operator-=,"Substract %g to image%s",value,gmic_inds,T,
philpem@5 1028 operator-=,"Substract to image%s",
philpem@5 1029 "Substract image [%d] to image%s",ind[0],gmic_inds,
philpem@5 1030 "Substract image%s together");
philpem@5 1031
philpem@5 1032 gmic_arithmetic_item("-mul","-*",
philpem@5 1033 operator*=,"Multiply image%s by %g",gmic_inds,value,double,
philpem@5 1034 mul,"Multiply image%s",
philpem@5 1035 "Multiply image%s by image [%d]",gmic_inds,ind[0],
philpem@5 1036 "Multiply image%s together");
philpem@5 1037
philpem@5 1038 gmic_arithmetic_item("-div","-/",
philpem@5 1039 operator/=,"Divide image%s by %g",gmic_inds,value,double,
philpem@5 1040 div,"Divide image%s",
philpem@5 1041 "Divide image%s by image [%d]",gmic_inds,ind[0],
philpem@5 1042 "Divide image%s together");
philpem@5 1043
philpem@5 1044 gmic_arithmetic_item("-pow","-^",
philpem@5 1045 pow,"Compute image%s to the power of %g",gmic_inds,value,double,
philpem@5 1046 pow,"Compute power of image%s",
philpem@5 1047 "Compute image%s to the power of image [%d]",gmic_inds,ind[0],
philpem@5 1048 "Compute the power of image%s together");
philpem@5 1049
philpem@5 1050 gmic_arithmetic_item("-min","-min",
philpem@5 1051 min,"Compute pointwise minimum between image%s and %g",gmic_inds,value,T,
philpem@5 1052 min,"Compute pointwise minimum with image%s",
philpem@5 1053 "Compute pointwise minimum between image%s and image [%d]",gmic_inds,ind[0],
philpem@5 1054 "Compute pointwise minimum between image%s together");
philpem@5 1055
philpem@5 1056 gmic_arithmetic_item("-max","-max",
philpem@5 1057 max,"Compute pointwise maximum between image%s and %g",gmic_inds,value,T,
philpem@5 1058 max,"Compute pointwise maximum with image%s",
philpem@5 1059 "Compute pointwise maximum between image%s and image [%d]",gmic_inds,ind[0],
philpem@5 1060 "Compute pointwise maximum between image%s together");
philpem@5 1061
philpem@5 1062 gmic_arithmetic_item("-mod","-%",
philpem@5 1063 operator%=,"Compute pointwise modulo between image%s and %g.",gmic_inds,value,T,
philpem@5 1064 operator%=,"Compute pointwise modulo with image%s",
philpem@5 1065 "Compute pointwise modulo between image%s and image [%d]",gmic_inds,ind[0],
philpem@5 1066 "Compute pointwise modulo between image%s together");
philpem@5 1067
philpem@5 1068 gmic_arithmetic_item("-and","-and",
philpem@5 1069 operator&=,"Compute bitwise AND between image%s and %g.",gmic_inds,value,unsigned int,
philpem@5 1070 operator&=,"Compute bitwise AND with image%s",
philpem@5 1071 "Compute bitwise AND between image%s and image [%d]",gmic_inds,ind[0],
philpem@5 1072 "Compute bitwise AND between image%s together");
philpem@5 1073
philpem@5 1074 gmic_arithmetic_item("-or","-or",
philpem@5 1075 operator|=,"Compute bitwise OR between image%s and %g.",gmic_inds,value,unsigned int,
philpem@5 1076 operator|=,"Compute bitwise OR with image%s",
philpem@5 1077 "Compute bitwise OR between image%s and image [%d]",gmic_inds,ind[0],
philpem@5 1078 "Compute bitwise OR between image%s together");
philpem@5 1079
philpem@5 1080 gmic_arithmetic_item("-xor","-xor",
philpem@5 1081 operator^=,"Compute bitwise XOR between image%s and %g.",gmic_inds,value,unsigned int,
philpem@5 1082 operator^=,"Compute bitwise XOR with image%s",
philpem@5 1083 "Compute bitwise XOR between image%s and image [%d]",gmic_inds,ind[0],
philpem@5 1084 "Compute bitwise XOR between image%s together");
philpem@5 1085
philpem@5 1086 // Other arithmetic operators.
philpem@5 1087 gmic_simple_item("-cos",cos,"Compute cosine of image%s.");
philpem@5 1088 gmic_simple_item("-sin",sin,"Compute sine of image%s.");
philpem@5 1089 gmic_simple_item("-tan",tan,"Compute tangent of image%s.");
philpem@5 1090 gmic_simple_item("-acos",acos,"Compute arccosine of image%s.");
philpem@5 1091 gmic_simple_item("-asin",asin,"Compute arcsine of image%s.");
philpem@5 1092 gmic_simple_item("-atan",atan,"Compute arctangent of image%s.");
philpem@5 1093 gmic_simple_item("-abs",abs,"Compute absolute value of image%s.");
philpem@5 1094 gmic_simple_item("-sqr",sqr,"Compute square function of image%s.");
philpem@5 1095 gmic_simple_item("-sqrt",sqrt,"Compute square root of image%s.");
philpem@5 1096 gmic_simple_item("-exp",exp,"Compute exponential of image%s.");
philpem@5 1097 gmic_simple_item("-log",log,"Compute logarithm of image%s.");
philpem@5 1098 gmic_simple_item("-log10",log10,"Compute logarithm_10 of image%s.");
philpem@5 1099
philpem@5 1100 //---------------------------------------
philpem@5 1101 // Pointwise operations on pixel values
philpem@5 1102 //---------------------------------------
philpem@5 1103
philpem@5 1104 // Type cast.
philpem@5 1105 if (!cimg::strcmp("-type",item) || !cimg::strcmp("-t",item)) {
philpem@5 1106 typedef unsigned char uchar;
philpem@5 1107 typedef unsigned short ushort;
philpem@5 1108 typedef unsigned int uint;
philpem@5 1109 #ifndef gmic_minimal
philpem@5 1110 gmic_cast(bool,"bool");
philpem@5 1111 gmic_cast(uchar,"unsigned char");
philpem@5 1112 gmic_cast(char,"char");
philpem@5 1113 gmic_cast(ushort,"unsigned short");
philpem@5 1114 gmic_cast(short,"short");
philpem@5 1115 gmic_cast(uint,"unsigned int");
philpem@5 1116 gmic_cast(int,"int");
philpem@5 1117 gmic_cast(double,"double");
philpem@5 1118 #endif
philpem@5 1119 gmic_cast(float,"float");
philpem@5 1120 error("Set pixel type : Invalid argument '%s' "
philpem@5 1121 "(should be '{bool,uchar,char,ushort,short,uint,int,float,double}').",
philpem@5 1122 argument_text);
philpem@5 1123 }
philpem@5 1124
philpem@5 1125 // Set scalar value.
philpem@5 1126 if (!cimg::strcmp("-set",item0) || !cimg::strcmp("-=",item0)) {
philpem@5 1127 double value = 0; float x = 0, y = 0, z = 0, v = 0; char sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0;
philpem@5 1128 char argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
philpem@5 1129 if ((std::sscanf(argument,"%lf%c",&value,&end)==1 ||
philpem@5 1130 std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%c",&value,argx,&end)==2 ||
philpem@5 1131 std::sscanf(argument,"%lf%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&value,argx,argy,&end)==3 ||
philpem@5 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 ||
philpem@5 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) &&
philpem@5 1134 (!*argx ||
philpem@5 1135 (std::sscanf(argx,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
philpem@5 1136 std::sscanf(argx,"%f%c",&x,&end)==1) &&
philpem@5 1137 (!*argy ||
philpem@5 1138 (std::sscanf(argy,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
philpem@5 1139 std::sscanf(argy,"%f%c",&y,&end)==1) &&
philpem@5 1140 (!*argz ||
philpem@5 1141 (std::sscanf(argz,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
philpem@5 1142 std::sscanf(argz,"%f%c",&z,&end)==1) &&
philpem@5 1143 (!*argv ||
philpem@5 1144 (std::sscanf(argv,"%f%c%c",&v,&sepv,&end)==2 && sepv=='%') ||
philpem@5 1145 std::sscanf(argv,"%f%c",&v,&end)==1)) {
philpem@5 1146 print("Set scalar value %g at position (%g%s,%g%s,%g%s,%g%s) in image%s",
philpem@5 1147 value,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",v,sepv=='%'?"%":"",gmic_inds);
philpem@5 1148 cimg_foroff(indices,l) {
philpem@5 1149 CImg<T> &img = images[indices[l]];
philpem@5 1150 const int
philpem@5 1151 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
philpem@5 1152 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
philpem@5 1153 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1),
philpem@5 1154 nv = (int)cimg::round(sepv=='%'?v*(img.dimv()-1)/100:v,1);
philpem@5 1155 gmic_apply(images[indices[l]],gmic_set(value,nx,ny,nz,nv));
philpem@5 1156 }
philpem@5 1157 } else error("Set scalar value in image%s : Invalid argument '%s' "
philpem@5 1158 "(should be 'value,x[,y[,z[,v]]]').",
philpem@5 1159 argument_text);
philpem@5 1160 ++position; continue;
philpem@5 1161 }
philpem@5 1162
philpem@5 1163 // Invert endianness.
philpem@5 1164 gmic_simple_item("-endian",invert_endianness,"Invert endianness of image%s.");
philpem@5 1165
philpem@5 1166 // Fill.
philpem@5 1167 if (!cimg::strcmp("-fill",item0) || !cimg::strcmp("-f",item0)) {
philpem@5 1168 char sep = 0, end = 0; int ind0 = no_ind;
philpem@5 1169 if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
philpem@5 1170 gmic_check_indice(ind0,"Fill image%s");
philpem@5 1171 print("Fill image%s with values of image [%d].",gmic_inds,ind0);
philpem@5 1172 const CImg<T> values = images[ind0];
philpem@5 1173 cimg_foroff(indices,l) gmic_apply(images[indices[l]],fill(values));
philpem@5 1174 } else {
philpem@5 1175 print("Fill image%s with values '%s'.",gmic_inds,argument_text);
philpem@5 1176 cimg_foroff(indices,l) gmic_apply(images[indices[l]],fill(argument,true));
philpem@5 1177 }
philpem@5 1178 ++position; continue;
philpem@5 1179 }
philpem@5 1180
philpem@5 1181 // Threshold.
philpem@5 1182 if (!cimg::strcmp("-threshold",item0)) {
philpem@5 1183 char sep = 0, end = 0; int soft = 0; double value = 0;
philpem@5 1184 if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
philpem@5 1185 (std::sscanf(argument,"%lf%c%c",&value,&sep,&end)==2 && sep=='%') ||
philpem@5 1186 std::sscanf(argument,"%lf%*c%d%c",&value,&soft,&end)==2 ||
philpem@5 1187 (std::sscanf(argument,"%lf%c%*c%d%c",&value,&sep,&soft,&end)==3 && sep=='%')) {
philpem@5 1188 print("Threshold image%s with %g%s (%s threshold).",gmic_inds,value,sep=='%'?"%":"",soft?"soft":"hard");
philpem@5 1189 cimg_foroff(indices,l) {
philpem@5 1190 CImg<T> &img = images[indices[l]];
philpem@5 1191 double vmin = 0, vmax = 0, nvalue = value;
philpem@5 1192 if (sep=='%') { vmin = img.minmax(vmax); nvalue = vmin + (vmax - vmin)*value/100; }
philpem@5 1193 gmic_apply(img,threshold((T)nvalue,soft?true:false));
philpem@5 1194 }
philpem@5 1195 ++position;
philpem@5 1196 } else {
philpem@5 1197 print("Threshold image%s : Interactive mode.",gmic_inds);
philpem@5 1198 CImgDisplay disp;
philpem@5 1199 char title[4096] = { 0 };
philpem@5 1200 cimg_foroff(indices,l) {
philpem@5 1201 CImg<T>
philpem@5 1202 &img = images[indices[l]],
philpem@5 1203 visu = img.depth>1?img.get_projections2d(img.dimx()/2,img.dimy()/2,img.dimz()/2).
philpem@5 1204 channels(0,cimg::min(3,img.dimv())-1):img.get_channels(0,cimg::min(3,img.dimv()-1));
philpem@5 1205 if (disp) disp.resize(cimg_fitscreen(visu.dimx(),visu.dimy(),1),false);
philpem@5 1206 else disp.assign(cimg_fitscreen(visu.dimx(),visu.dimy(),1),0,1);
philpem@5 1207 double
philpem@5 1208 vmin = 0, vmax = (double)img.maxmin(vmin),
philpem@5 1209 distmax = std::sqrt(cimg::sqr(disp.dimx()-1.0) + cimg::sqr(disp.dimy()-1.0)),
philpem@5 1210 amount = 50;
philpem@5 1211 bool stopflag = false, obutt = false;
philpem@5 1212 int omx = -1, omy = -1;
philpem@5 1213 CImg<T> res;
philpem@5 1214 for (disp.show().button = disp.key = 0; !stopflag; ) {
philpem@5 1215 const unsigned int key = disp.key;
philpem@5 1216 if (!res) {
philpem@5 1217 std::sprintf(title,"%s : Interactive threshold %.3g%%",filenames[indices[l]].ptr(),amount);
philpem@5 1218 disp.display(res=visu.get_threshold((T)(vmin + amount*(vmax-vmin)/100))).
philpem@5 1219 set_title(title).wait();
philpem@5 1220 }
philpem@5 1221 const int mx = disp.mouse_x, my = disp.mouse_y;
philpem@5 1222 if (disp.button && mx>=0 && my>=0) {
philpem@5 1223 if (omx==mx && omy==my && !obutt) break;
philpem@5 1224 omx = mx; omy = my; obutt = true;
philpem@5 1225 const double dist = std::sqrt((double)cimg::sqr(mx) + cimg::sqr(my));
philpem@5 1226 amount = dist*100/distmax;
philpem@5 1227 res.assign();
philpem@5 1228 } else if (!disp.button) obutt = false;
philpem@5 1229 if (disp.is_closed || (key && key!=cimg::keyCTRLLEFT)) stopflag = true;
philpem@5 1230 if (key==cimg::keyD && disp.is_keyCTRLLEFT &&
philpem@5 1231 (disp.resize(cimg_fitscreen(3*disp.width/2,3*disp.height/2,1),stopflag=false).key=0)==0)
philpem@5 1232 disp.is_resized = true;
philpem@5 1233 if (key==cimg::keyC && disp.is_keyCTRLLEFT &&
philpem@5 1234 (disp.resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),stopflag=false).key=0)==0)
philpem@5 1235 disp.is_resized = true;
philpem@5 1236 if (disp.is_resized) {
philpem@5 1237 disp.resize(false).display(res);
philpem@5 1238 distmax = std::sqrt(cimg::sqr(disp.dimx()-1.0) + cimg::sqr(disp.dimy()-1.0));
philpem@5 1239 }
philpem@5 1240 }
philpem@5 1241 gmic_apply(img,threshold((T)(vmin + amount*(vmax-vmin)/100)));
philpem@5 1242 }
philpem@5 1243 }
philpem@5 1244 continue;
philpem@5 1245 }
philpem@5 1246
philpem@5 1247 // Cut.
philpem@5 1248 if (!cimg::strcmp("-cut",item0)) {
philpem@5 1249 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
philpem@5 1250 double value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
philpem@5 1251 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
philpem@5 1252 ((std::sscanf(arg0,"[%d%c%c",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1253 (std::sscanf(arg0,"%lf%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1254 std::sscanf(arg0,"%lf%c",&value0,&end)==1) &&
philpem@5 1255 ((std::sscanf(arg1,"[%d%c%c",&ind1,&sep1,&end)==2 && sep1==']') ||
philpem@5 1256 (std::sscanf(arg1,"%lf%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
philpem@5 1257 std::sscanf(arg1,"%lf%c",&value1,&end)==1)) {
philpem@5 1258 if (ind0!=no_ind) { gmic_check_indice(ind0,"Cut image%s"); value0 = images[ind0].min(); sep0 = 0; }
philpem@5 1259 if (ind1!=no_ind) { gmic_check_indice(ind1,"Cut image%s"); value1 = images[ind1].max(); sep1 = 0; }
philpem@5 1260 print("Cut image%s in [%g%s,%g%s].",gmic_inds,value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"");
philpem@5 1261 cimg_foroff(indices,l) {
philpem@5 1262 CImg<T> &img = images[indices[l]];
philpem@5 1263 double vmin = 0, vmax = 0, nvalue0 = value0, nvalue1 = value1;
philpem@5 1264 if (sep0=='%') { vmin = img.minmax(vmax); nvalue0 = vmin + (vmax - vmin)*value0/100; }
philpem@5 1265 if (sep1=='%') { vmin = img.minmax(vmax); nvalue1 = vmin + (vmax - vmin)*value1/100; }
philpem@5 1266 gmic_apply(img,cut((T)nvalue0,(T)nvalue1));
philpem@5 1267 }
philpem@5 1268 ++position;
philpem@5 1269 } else if (std::sscanf(argument,"[%d%c%c",&(ind0=no_ind),&sep0,&end)==2) {
philpem@5 1270 if (ind0!=no_ind) gmic_check_indice(ind0,"Cut image%s");
philpem@5 1271 value0 = images[ind0].minmax(value1);
philpem@5 1272 print("Cut image%s in [%g,%g].",gmic_inds,value0,value1);
philpem@5 1273 cimg_foroff(indices,l) gmic_apply(images[indices[l]],cut((T)value0,(T)value1));
philpem@5 1274 ++position;
philpem@5 1275 } else {
philpem@5 1276 print("Cut image%s : Interactive mode.",gmic_inds);
philpem@5 1277 CImgDisplay disp;
philpem@5 1278 char title[4096] = { 0 };
philpem@5 1279 cimg_foroff(indices,l) {
philpem@5 1280 CImg<T>
philpem@5 1281 &img = images[indices[l]],
philpem@5 1282 visu = img.depth>1?img.get_projections2d(img.dimx()/2,img.dimy()/2,img.dimz()/2).
philpem@5 1283 channels(0,cimg::min(3,img.dimv())-1):img.get_channels(0,cimg::min(3,img.dimv()-1));
philpem@5 1284 if (disp) disp.resize(cimg_fitscreen(visu.dimx(),visu.dimy(),1),false);
philpem@5 1285 else disp.assign(cimg_fitscreen(visu.dimx(),visu.dimy(),1),0,1);
philpem@5 1286 double vmin = 0, vmax = (double)img.maxmin(vmin), amount0 = 0, amount1 = 100;
philpem@5 1287 bool stopflag = false, obutt = false;
philpem@5 1288 int omx = -1, omy = -1;
philpem@5 1289 CImg<T> res;
philpem@5 1290 for (disp.show().button = disp.key = 0; !stopflag; ) {
philpem@5 1291 const unsigned int key = disp.key;
philpem@5 1292 if (!res) {
philpem@5 1293 std::sprintf(title,"%s : Interactive cut [%.3g%%,%.3g%%]",
philpem@5 1294 filenames[indices[l]].ptr(),amount0,amount1);
philpem@5 1295 disp.display(res = visu.get_cut((T)(vmin + amount0*(vmax-vmin)/100),
philpem@5 1296 (T)(vmin + amount1*(vmax-vmin)/100))).
philpem@5 1297 set_title(title).wait();
philpem@5 1298 }
philpem@5 1299 const int mx = disp.mouse_x, my = disp.mouse_y;
philpem@5 1300 if (disp.button && mx>=0 && my>=0) {
philpem@5 1301 if (omx==mx && omy==my && !obutt) break;
philpem@5 1302 omx = mx; omy = my; obutt = true;
philpem@5 1303 amount0 = mx*100/disp.dimx(); amount1 = my*100/disp.dimy();
philpem@5 1304 res.assign();
philpem@5 1305 } else if (!disp.button) obutt = false;
philpem@5 1306 if (disp.is_closed || (key && key!=cimg::keyCTRLLEFT)) stopflag = true;
philpem@5 1307 if (key==cimg::keyD && disp.is_keyCTRLLEFT &&
philpem@5 1308 (disp.resize(cimg_fitscreen(3*disp.width/2,3*disp.height/2,1),stopflag=false).key=0)==0)
philpem@5 1309 disp.is_resized = true;
philpem@5 1310 if (key==cimg::keyC && disp.is_keyCTRLLEFT &&
philpem@5 1311 (disp.resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),stopflag=false).key=0)==0)
philpem@5 1312 disp.is_resized = true;
philpem@5 1313 if (disp.is_resized) disp.resize(false).display(res);
philpem@5 1314 }
philpem@5 1315 gmic_apply(img,cut((T)(vmin + amount0*(vmax-vmin)/100),(T)(vmin + amount1*(vmax-vmin)/100)));
philpem@5 1316 }
philpem@5 1317 }
philpem@5 1318 continue;
philpem@5 1319 }
philpem@5 1320
philpem@5 1321 // Normalize.
philpem@5 1322 if (!cimg::strcmp("-normalize",item0) || !cimg::strcmp("-n",item0)) {
philpem@5 1323 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
philpem@5 1324 double value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
philpem@5 1325 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
philpem@5 1326 ((std::sscanf(arg0,"[%d%c%c",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1327 (std::sscanf(arg0,"%lf%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1328 std::sscanf(arg0,"%lf%c",&value0,&end)==1) &&
philpem@5 1329 ((std::sscanf(arg1,"[%d%c%c",&ind1,&sep1,&end)==2 && sep1==']') ||
philpem@5 1330 (std::sscanf(arg1,"%lf%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
philpem@5 1331 std::sscanf(arg1,"%lf%c",&value1,&end)==1)) {
philpem@5 1332 if (ind0!=no_ind) { gmic_check_indice(ind0,"Normalize image%s"); value0 = images[ind0].min(); sep0 = 0; }
philpem@5 1333 if (ind1!=no_ind) { gmic_check_indice(ind1,"Normalize image%s"); value1 = images[ind1].max(); sep1 = 0; }
philpem@5 1334 print("Normalize image%s in [%g%s,%g%s].",gmic_inds,value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"");
philpem@5 1335 cimg_foroff(indices,l) {
philpem@5 1336 CImg<T> &img = images[indices[l]];
philpem@5 1337 double vmin = 0, vmax = 0, nvalue0 = value0, nvalue1 = value1;
philpem@5 1338 if (sep0=='%') { vmin = img.minmax(vmax); nvalue0 = vmin + (vmax - vmin)*value0/100; }
philpem@5 1339 if (sep1=='%') { vmin = img.minmax(vmax); nvalue1 = vmin + (vmax - vmin)*value1/100; }
philpem@5 1340 gmic_apply(img,normalize((T)nvalue0,(T)nvalue1));
philpem@5 1341 }
philpem@5 1342 } else if (std::sscanf(argument,"[%d%c%c",&(ind0=no_ind),&sep0,&end)==2) {
philpem@5 1343 if (ind0!=no_ind) gmic_check_indice(ind0,"Normalize image%s");
philpem@5 1344 value0 = images[ind0].minmax(value1);
philpem@5 1345 print("Normalize image%s in [%g,%g].",gmic_inds,value0,value1);
philpem@5 1346 cimg_foroff(indices,l) gmic_apply(images[indices[l]],normalize((T)value0,(T)value1));
philpem@5 1347 } else error("Normalize image%s : Invalid argument '%s' "
philpem@5 1348 "(should be '{value1[%%],[indice]},{value2[%%],[indice]}').",gmic_inds,argument_text);
philpem@5 1349 ++position; continue;
philpem@5 1350 }
philpem@5 1351
philpem@5 1352 // Round.
philpem@5 1353 if (!cimg::strcmp("-round",item0)) {
philpem@5 1354 char end = 0; double value = 0; int rtype = 0;
philpem@5 1355 if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
philpem@5 1356 std::sscanf(argument,"%lf%*c%d%c",&value,&rtype,&end)==2) {
philpem@5 1357 print("Round image%s with value %g (%s rounding).",
philpem@5 1358 gmic_inds,value,rtype<0?"backward":rtype>0?"forward":"nearest");
philpem@5 1359 cimg_foroff(indices,l) gmic_apply(images[indices[l]],round((float)value,rtype));
philpem@5 1360 } else error("Round image%s : Invalid argument '%s' "
philpem@5 1361 "(should be 'round_value[,round_type]').",gmic_inds,argument_text);
philpem@5 1362 ++position; continue;
philpem@5 1363 }
philpem@5 1364
philpem@5 1365 // Equalize histogram.
philpem@5 1366 if (!cimg::strcmp("-equalize",item0)) {
philpem@5 1367 float nb = 256; char sep = 0, end = 0;
philpem@5 1368 if (std::sscanf(argument,"%f%c",&nb,&end)==1 ||
philpem@5 1369 (std::sscanf(argument,"%f%c%c",&nb,&sep,&end)==2 && sep=='%')) {
philpem@5 1370 if (nb<=0) error("Equalize image%s : Invalid cluster number %g.",gmic_inds,nb);
philpem@5 1371 print("Equalize image%s with %g%s clusters.",gmic_inds,nb,sep=='%'?"%":"");
philpem@5 1372 cimg_foroff(indices,l) {
philpem@5 1373 CImg<T>& img = images[indices[l]];
philpem@5 1374 unsigned int N = (unsigned int)nb;
philpem@5 1375 if (sep=='%') { double m, M = img.maxmin(m); N = (unsigned int)cimg::round((M-m)*nb/100,1); }
philpem@5 1376 gmic_apply(img,equalize((int)nb));
philpem@5 1377 }
philpem@5 1378 } else error("Equalize image%s : Invalid argument '%s' "
philpem@5 1379 "(should be 'nb_clusters[%%]').",gmic_inds,argument_text);
philpem@5 1380 ++position; continue;
philpem@5 1381 }
philpem@5 1382
philpem@5 1383 // Quantize.
philpem@5 1384 if (!cimg::strcmp("-quantize",item0)) {
philpem@5 1385 int nb = 0; char end = 0;
philpem@5 1386 if (std::sscanf(argument,"%d%c",&nb,&end)==1) {
philpem@5 1387 print("Quantize image%s with %d levels.",gmic_inds,nb);
philpem@5 1388 cimg_foroff(indices,l) gmic_apply(images[indices[l]],quantize(nb));
philpem@5 1389 } else error("Quantize image%s : Invalid argument '%s' "
philpem@5 1390 "(should be 'nb_levels').",gmic_inds,argument_text);
philpem@5 1391 ++position; continue;
philpem@5 1392 }
philpem@5 1393
philpem@5 1394 // Add noise.
philpem@5 1395 if (!cimg::strcmp("-noise",item0)) {
philpem@5 1396 float sigma = 0; char sep = 0, end = 0; int ntype = 0;
philpem@5 1397 if (std::sscanf(argument,"%f%c",&sigma,&end)==1 ||
philpem@5 1398 (std::sscanf(argument,"%f%c%c",&sigma,&sep,&end)==2 && sep=='%') ||
philpem@5 1399 std::sscanf(argument,"%f%*c%d%c",&sigma,&ntype,&end)==2 ||
philpem@5 1400 (std::sscanf(argument,"%f%c%*c%d%c",&sigma,&sep,&ntype,&end)==3 && sep=='%')) {
philpem@5 1401 const char *st_type = ntype==0?"gaussian":ntype==1?"uniform":ntype==2?"salt&pepper":"poisson";
philpem@5 1402 if (sep=='%') sigma = -sigma;
philpem@5 1403 print("Add %s noise with standard deviation %g%s to image%s.",
philpem@5 1404 st_type,cimg::abs(sigma),sigma<0?"%":"",gmic_inds);
philpem@5 1405 cimg_foroff(indices,l) gmic_apply(images[indices[l]],noise(sigma,ntype));
philpem@5 1406 } else error("Add noise to image%s : Invalid argument '%s' "
philpem@5 1407 "(should be 'std[%%][,noise_type]').",gmic_inds,argument_text);
philpem@5 1408 ++position; continue;
philpem@5 1409 }
philpem@5 1410
philpem@5 1411 // Rand.
philpem@5 1412 if (!cimg::strcmp("-rand",item0)) {
philpem@5 1413 double value0 = 0, value1 = 0; char end = 0;
philpem@5 1414 if (std::sscanf(argument,"%lf%*c%lf%c",&value0,&value1,&end)==2) {
philpem@5 1415 print("Fill image%s with random values in [%g,%g].",gmic_inds,value0,value1);
philpem@5 1416 cimg_foroff(indices,l) gmic_apply(images[indices[l]],rand((T)value0,(T)value1));
philpem@5 1417 } else error("Fill image%s with random values : Invalid argument '%s' "
philpem@5 1418 "(should be 'valmin,valmax').",gmic_inds,argument_text);
philpem@5 1419 ++position; continue;
philpem@5 1420 }
philpem@5 1421
philpem@5 1422 // Compute pointwise norms and orientations.
philpem@5 1423 gmic_simple_item("-norm",pointwise_norm,"Compute vector norm.");
philpem@5 1424 gmic_simple_item("-orientation",pointwise_orientation,"Compute vector orientation.");
philpem@5 1425
philpem@5 1426 //------------------------
philpem@5 1427 // Color base conversions
philpem@5 1428 //------------------------
philpem@5 1429 gmic_simple_item("-rgb2hsv",RGBtoHSV,"Convert image%s from RGB to HSV colorbases.");
philpem@5 1430 gmic_simple_item("-rgb2hsl",RGBtoHSL,"Convert image%s from RGB to HSL colorbases.");
philpem@5 1431 gmic_simple_item("-rgb2hsi",RGBtoHSI,"Convert image%s from RGB to HSI colorbases.");
philpem@5 1432 gmic_simple_item("-rgb2yuv",RGBtoYUV,"Convert image%s from RGB to YUV colorbases.");
philpem@5 1433 gmic_simple_item("-rgb2ycbcr",RGBtoYCbCr,"Convert image%s from RGB to YCbCr colorbases.");
philpem@5 1434 gmic_simple_item("-rgb2xyz",RGBtoXYZ,"Convert image%s from RGB to XYZ colorbases.");
philpem@5 1435 gmic_simple_item("-rgb2lab",RGBtoLab,"Convert image%s from RGB to Lab colorbases.");
philpem@5 1436 gmic_simple_item("-rgb2cmy",RGBtoCMY,"Convert image%s from RGB to CMY colorbases.");
philpem@5 1437 gmic_simple_item("-rgb2cmyk",RGBtoCMYK,"Convert image%s from RGB to CMYK colorbases.");
philpem@5 1438 gmic_simple_item("-cmyk2rgb",CMYKtoRGB,"Convert image%s from CMYK to RGB colorbases.");
philpem@5 1439 gmic_simple_item("-cmy2rgb",CMYtoRGB,"Convert image%s from CMY to RGB colorbases.");
philpem@5 1440 gmic_simple_item("-lab2rgb",LabtoRGB,"Convert image%s from Lab to RGB colorbases.");
philpem@5 1441 gmic_simple_item("-xyz2rgb",XYZtoRGB,"Convert image%s from XYZ to RGB colorbases.");
philpem@5 1442 gmic_simple_item("-ycbcr2rgb",YCbCrtoRGB,"Convert image%s from YCbCr to RGB colorbases.");
philpem@5 1443 gmic_simple_item("-yuv2rgb",YUVtoRGB,"Convert image%s from YUV to RGB colorbases.");
philpem@5 1444 gmic_simple_item("-hsi2rgb",HSItoRGB,"Convert image%s from HSI to RGB colorbases.");
philpem@5 1445 gmic_simple_item("-hsl2rgb",HSLtoRGB,"Convert image%s from HSL to RGB colorbases.");
philpem@5 1446 gmic_simple_item("-hsv2rgb",HSVtoRGB,"Convert image%s from HSV to RGB colorbases.");
philpem@5 1447
philpem@5 1448 // Apply LUT.
philpem@5 1449 if (!cimg::strcmp("-lut2rgb",item0)) {
philpem@5 1450 int nb = 0, ind0 = 0; char sep = 0, end = 0;
philpem@5 1451 if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
philpem@5 1452 gmic_check_indice(ind0,"Map LUT on image%s");
philpem@5 1453 print("Map LUT [%d] on image%s.",ind0,gmic_inds);
philpem@5 1454 const CImg<T> palette = images[ind0];
philpem@5 1455 cimg_foroff(indices,l) gmic_apply(images[indices[l]],pointwise_norm().LUTtoRGB(palette));
philpem@5 1456 } else if (std::sscanf(argument,"%d%c",&nb,&end)==1) {
philpem@5 1457 if (nb<0 || nb>2) error("Map LUT on image%s : Invalid LUT number %d.",gmic_inds,nb);
philpem@5 1458 print("Map %s LUT on image%s.",nb==0?"default":nb==1?"rainbow":"cluster",gmic_inds);
philpem@5 1459 cimg_foroff(indices,l) gmic_apply(images[indices[l]],pointwise_norm().
philpem@5 1460 LUTtoRGB(nb==0?CImg<T>::default_LUT8():nb==1?CImg<T>::rainbow_LUT8():CImg<T>::contrast_LUT8()));
philpem@5 1461 } else error("Map LUT on image%s : Invalid argument '%s' "
philpem@5 1462 "(should be '[indice]' or '{0,1,2}').",gmic_inds,argument_text);
philpem@5 1463 ++position; continue;
philpem@5 1464 }
philpem@5 1465
philpem@5 1466 // Convert to LUT.
philpem@5 1467 if (!cimg::strcmp("-rgb2lut",item0)) {
philpem@5 1468 int nb = 0, ind0 = 0, dithering = 0; char sep = 0, end = 0;
philpem@5 1469 if (std::sscanf(argument,"[%d%c%*c%d",&ind0,&sep,&dithering)>=2 && sep==']') {
philpem@5 1470 gmic_check_indice(ind0,"Index image%s with LUT");
philpem@5 1471 print("Index image%s with LUT [%d] %s dithering.",gmic_inds,ind0,dithering?"with":"without");
philpem@5 1472 const CImg<T> palette = images[ind0];
philpem@5 1473 cimg_foroff(indices,l) gmic_apply(images[indices[l]],RGBtoLUT(palette,dithering,true));
philpem@5 1474 } else if (std::sscanf(argument,"%d%*c%d%c",&nb,&dithering,&end)==2 ||
philpem@5 1475 std::sscanf(argument,"%d%c",&nb,&end)==1) {
philpem@5 1476 if (nb<0 || nb>2) error("Index image%s with LUT : Invalid LUT number %d.",gmic_inds,nb);
philpem@5 1477 print("Index image%s with %s LUT.",gmic_inds,nb==0?"default":nb==1?"rainbow":"cluster");
philpem@5 1478 cimg_foroff(indices,l) gmic_apply(images[indices[l]],
philpem@5 1479 RGBtoLUT(nb==0?CImg<T>::default_LUT8():nb==1?CImg<T>::rainbow_LUT8():CImg<T>::contrast_LUT8(),
philpem@5 1480 dithering,true));
philpem@5 1481 } else error("Index image%s with LUT : Invalid argument '%s' "
philpem@5 1482 "(should be '[indice][,dithering]', or '{0,1,2}[,dithering]').",gmic_inds,argument_text);
philpem@5 1483 ++position; continue;
philpem@5 1484 }
philpem@5 1485
philpem@5 1486 //-----------------------
philpem@5 1487 // Geometric manipulation
philpem@5 1488 //-----------------------
philpem@5 1489
philpem@5 1490 // Resize.
philpem@5 1491 if (!cimg::strcmp("-resize",item0) || !cimg::strcmp("-r",item0)) {
philpem@5 1492 char
philpem@5 1493 sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0,
philpem@5 1494 argx[4096] = { 0 }, argy[4096] = { 0 }, argz[4096] = { 0 }, argv[4096] = { 0 };
philpem@5 1495 float valx = 0, valy = 0, valz = 0, valv = 0;
philpem@5 1496 int interpolation = 1, borders = -1, center = 0, indx = no_ind, indy = no_ind, indz = no_ind, indv = no_ind;
philpem@5 1497 if ((std::sscanf(argument,"[%d%c%c",&indx,&sepx,&end)==2 ||
philpem@5 1498 std::sscanf(argument,"[%d%c%*c%d%c",&indx,&sepx,&interpolation,&end)==3 ||
philpem@5 1499 std::sscanf(argument,"[%d%c%*c%d%*c%d%c",&indx,&sepx,&interpolation,&borders,&end)==4 ||
philpem@5 1500 std::sscanf(argument,"[%d%c%*c%d%*c%d%*c%d%c",&indx,&sepx,&interpolation,&borders,&center,&end)==5)
philpem@5 1501 && sepx==']') {
philpem@5 1502 gmic_check_indice(indx,"Resize image%s");
philpem@5 1503 const int
philpem@5 1504 ivalx = images[indx].dimx(),
philpem@5 1505 ivaly = images[indx].dimy(),
philpem@5 1506 ivalz = images[indx].dimz(),
philpem@5 1507 ivalv = images[indx].dimv();
philpem@5 1508 print("Resize image%s to %dx%dx%dx%d with %s interpolation.",
philpem@5 1509 gmic_inds,ivalx,ivaly,ivalz,ivalv,
philpem@5 1510 interpolation==0?"no":interpolation==1?"nearest neighbor":
philpem@5 1511 interpolation==2?"moving average":interpolation==3?"linear":
philpem@5 1512 interpolation==4?"grid":"cubic");
philpem@5 1513 cimg_foroff(indices,l) gmic_apply(images[indices[l]],resize(ivalx,ivaly,ivalz,ivalv,interpolation,borders,center?true:false));
philpem@5 1514 ++position;
philpem@5 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",
philpem@5 1516 argx,argy,argz,argv,&(interpolation=1),&(borders=-1),&(center=0),&end)==7 ||
philpem@5 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",
philpem@5 1518 argx,argy,argz,argv,&interpolation,&borders,&end)==6 ||
philpem@5 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",
philpem@5 1520 argx,argy,argz,argv,&interpolation,&end)==5 ||
philpem@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",
philpem@5 1522 argx,argy,argz,argv,&end)==4) &&
philpem@5 1523 ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
philpem@5 1524 (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
philpem@5 1525 std::sscanf(argx,"%f%c",&valx,&end)==1) &&
philpem@5 1526 ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
philpem@5 1527 (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
philpem@5 1528 std::sscanf(argy,"%f%c",&valy,&end)==1) &&
philpem@5 1529 ((std::sscanf(argz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']') ||
philpem@5 1530 (std::sscanf(argz,"%f%c%c",&valz,&sepz,&end)==2 && sepz=='%') ||
philpem@5 1531 std::sscanf(argz,"%f%c",&valz,&end)==1) &&
philpem@5 1532 ((std::sscanf(argv,"[%d%c%c",&indv,&sepv,&end)==2 && sepv==']') ||
philpem@5 1533 (std::sscanf(argv,"%f%c%c",&valv,&sepv,&end)==2 && sepv=='%') ||
philpem@5 1534 std::sscanf(argv,"%f%c",&valv,&end)==1)) ||
philpem@5 1535 ((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
philpem@5 1536 argx,argy,argz,&end)==3) &&
philpem@5 1537 ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
philpem@5 1538 (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
philpem@5 1539 std::sscanf(argx,"%f%c",&valx,&end)==1) &&
philpem@5 1540 ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
philpem@5 1541 (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
philpem@5 1542 std::sscanf(argy,"%f%c",&valy,&end)==1) &&
philpem@5 1543 ((std::sscanf(argz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']') ||
philpem@5 1544 (std::sscanf(argz,"%f%c%c",&valz,&sepz,&end)==2 && sepz=='%') ||
philpem@5 1545 std::sscanf(argz,"%f%c",&valz,&end)==1)) ||
philpem@5 1546 ((std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",
philpem@5 1547 argx,argy,&end)==2) &&
philpem@5 1548 ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
philpem@5 1549 (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
philpem@5 1550 std::sscanf(argx,"%f%c",&valx,&end)==1) &&
philpem@5 1551 ((std::sscanf(argy,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']') ||
philpem@5 1552 (std::sscanf(argy,"%f%c%c",&valy,&sepy,&end)==2 && sepy=='%') ||
philpem@5 1553 std::sscanf(argy,"%f%c",&valy,&end)==1)) ||
philpem@5 1554 ((std::sscanf(argument,"%4095[][0-9.eE%+-]%c",
philpem@5 1555 argx,&end)==1) &&
philpem@5 1556 ((std::sscanf(argx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']') ||
philpem@5 1557 (std::sscanf(argx,"%f%c%c",&valx,&sepx,&end)==2 && sepx=='%') ||
philpem@5 1558 std::sscanf(argx,"%f%c",&valx,&end)==1))) {
philpem@5 1559 if (indx!=no_ind) { gmic_check_indice(indx,"Resize image%s"); valx = (float)images[indx].dimx(); sepx = 0; }
philpem@5 1560 if (indy!=no_ind) { gmic_check_indice(indy,"Resize image%s"); valy = (float)images[indy].dimy(); sepy = 0; }
philpem@5 1561 if (indz!=no_ind) { gmic_check_indice(indz,"Resize image%s"); valz = (float)images[indz].dimz(); sepz = 0; }
philpem@5 1562 if (indv!=no_ind) { gmic_check_indice(indv,"Resize image%s"); valv = (float)images[indv].dimv(); sepv = 0; }
philpem@5 1563 if (!valx) { valx = 100; sepx = '%'; }
philpem@5 1564 if (!valy) { valy = 100; sepy = '%'; }
philpem@5 1565 if (!valz) { valz = 100; sepz = '%'; }
philpem@5 1566 if (!valv) { valv = 100; sepv = '%'; }
philpem@5 1567 print("Resize image%s to %g%s%g%s%g%s%g%s with %s interpolation.",
philpem@5 1568 gmic_inds,valx,sepx=='%'?"%x":"x",valy,sepy=='%'?"%x":"x",valz,
philpem@5 1569 sepz=='%'?"%x":"x",valv,sepv=='%'?"% ":" ",
philpem@5 1570 interpolation==0?"no":interpolation==1?"nearest neighbor":
philpem@5 1571 interpolation==2?"moving average":interpolation==3?"linear":
philpem@5 1572 interpolation==4?"grid":"cubic");
philpem@5 1573
philpem@5 1574 cimg_foroff(indices,l) {
philpem@5 1575 CImg<T>& img = images[indices[l]];
philpem@5 1576 const int
philpem@5 1577 ivalx0 = (int)cimg::round(sepx=='%'?valx*img.dimx()/100:valx,1),
philpem@5 1578 ivaly0 = (int)cimg::round(sepy=='%'?valy*img.dimy()/100:valy,1),
philpem@5 1579 ivalz0 = (int)cimg::round(sepz=='%'?valz*img.dimz()/100:valz,1),
philpem@5 1580 ivalv0 = (int)cimg::round(sepv=='%'?valv*img.dimv()/100:valv,1),
philpem@5 1581 ivalx = ivalx0?ivalx0:1,
philpem@5 1582 ivaly = ivaly0?ivaly0:1,
philpem@5 1583 ivalz = ivalz0?ivalz0:1,
philpem@5 1584 ivalv = ivalv0?ivalv0:1;
philpem@5 1585 gmic_apply(img,resize(ivalx,ivaly,ivalz,ivalv,interpolation,borders,center?true:false));
philpem@5 1586 }
philpem@5 1587 ++position;
philpem@5 1588 } else {
philpem@5 1589 print("Resize image%s : Interactive mode.",gmic_inds);
philpem@5 1590 char title[4096] = { 0 };
philpem@5 1591 cimg_foroff(indices,l) {
philpem@5 1592 CImg<T>& img = images[indices[l]];
philpem@5 1593 CImgDisplay disp(img,0,1);
philpem@5 1594 std::sprintf(title,"%s : Interactive resize",filenames[indices[l]].ptr());
philpem@5 1595 disp.set_title(title);
philpem@5 1596 img.get_select(0,disp);
philpem@5 1597 print("Resize image [%d] to %dx%d.",indices[l],disp.dimx(),disp.dimy());
philpem@5 1598 gmic_apply(img,resize(disp));
philpem@5 1599 }
philpem@5 1600 }
philpem@5 1601 continue;
philpem@5 1602 }
philpem@5 1603
philpem@5 1604 // Resize2x. and Resize3x.
philpem@5 1605 gmic_simple_item("-resize2x",resize_doubleXY,"Resize image%s using Scale2x algorithm.");
philpem@5 1606 gmic_simple_item("-resize3x",resize_doubleXY,"Resize image%s using Scale3x algorithm.");
philpem@5 1607
philpem@5 1608 // Crop.
philpem@5 1609 if (!cimg::strcmp("-crop",item0) || !cimg::strcmp("-c",item0)) {
philpem@5 1610 char st0[4096] = { 0 }, st1[4096] = { 0 }, st2[4096] = { 0 }, st3[4096] = { 0 };
philpem@5 1611 char st4[4096] = { 0 }, st5[4096] = { 0 }, st6[4096] = { 0 }, st7[4096] = { 0 };
philpem@5 1612 char sep0 = 0, sep1 = 0, sep2 = 0, sep3 = 0, sep4 = 0, sep5 = 0, sep6 = 0, sep7 = 0, end = 0;
philpem@5 1613 float a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0, a7 = 0; int borders = 0;
philpem@5 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"
philpem@5 1615 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
philpem@5 1616 st0,st1,st2,st3,st4,st5,st6,st7,&borders,&end)==9 ||
philpem@5 1617 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
philpem@5 1618 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
philpem@5 1619 st0,st1,st2,st3,st4,st5,st6,st7,&end)==8) &&
philpem@5 1620 (std::sscanf(st0,"%f%c",&a0,&end)==1 || (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
philpem@5 1621 (std::sscanf(st1,"%f%c",&a1,&end)==1 || (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
philpem@5 1622 (std::sscanf(st2,"%f%c",&a2,&end)==1 || (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
philpem@5 1623 (std::sscanf(st3,"%f%c",&a3,&end)==1 || (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%')) &&
philpem@5 1624 (std::sscanf(st4,"%f%c",&a4,&end)==1 || (std::sscanf(st4,"%f%c%c",&a4,&sep4,&end)==2 && sep4=='%')) &&
philpem@5 1625 (std::sscanf(st5,"%f%c",&a5,&end)==1 || (std::sscanf(st5,"%f%c%c",&a5,&sep5,&end)==2 && sep5=='%')) &&
philpem@5 1626 (std::sscanf(st6,"%f%c",&a6,&end)==1 || (std::sscanf(st6,"%f%c%c",&a6,&sep6,&end)==2 && sep6=='%')) &&
philpem@5 1627 (std::sscanf(st7,"%f%c",&a7,&end)==1 || (std::sscanf(st7,"%f%c%c",&a7,&sep7,&end)==2 && sep7=='%'))) {
philpem@5 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,
philpem@5 1629 a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%,":",",
philpem@5 1630 a2,sep2=='%'?"%,":",",a3,sep3=='%'?"%)":")",
philpem@5 1631 a4,sep4=='%'?"%,":",",a5,sep5=='%'?"%,":",",
philpem@5 1632 a6,sep6=='%'?"%,":",",a7,sep7=='%'?"%)":")");
philpem@5 1633 cimg_foroff(indices,l) {
philpem@5 1634 CImg<T> &img = images[indices[l]];
philpem@5 1635 const int
philpem@5 1636 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
philpem@5 1637 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
philpem@5 1638 z0 = (int)cimg::round(sep2=='%'?a2*img.dimz()/100:a2,1),
philpem@5 1639 v0 = (int)cimg::round(sep3=='%'?a3*img.dimv()/100:a3,1),
philpem@5 1640 x1 = (int)cimg::round(sep4=='%'?a4*img.dimx()/100:a4,1),
philpem@5 1641 y1 = (int)cimg::round(sep5=='%'?a5*img.dimy()/100:a5,1),
philpem@5 1642 z1 = (int)cimg::round(sep6=='%'?a6*img.dimz()/100:a6,1),
philpem@5 1643 v1 = (int)cimg::round(sep7=='%'?a7*img.dimv()/100:a7,1);
philpem@5 1644 gmic_apply(img,crop(x0,y0,z0,v0,x1,y1,z1,v1,borders?true:false));
philpem@5 1645 }
philpem@5 1646 ++position;
philpem@5 1647 } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
philpem@5 1648 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
philpem@5 1649 st0,st1,st2,st3,st4,st5,&borders,&end)==7 ||
philpem@5 1650 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
philpem@5 1651 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
philpem@5 1652 st0,st1,st2,st3,st4,st5,&end)==6) &&
philpem@5 1653 (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
philpem@5 1654 (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
philpem@5 1655 (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
philpem@5 1656 (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
philpem@5 1657 (std::sscanf(st2,"%f%c",&a2,&end)==1 ||
philpem@5 1658 (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
philpem@5 1659 (std::sscanf(st3,"%f%c",&a3,&end)==1 ||
philpem@5 1660 (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%')) &&
philpem@5 1661 (std::sscanf(st4,"%f%c",&a4,&end)==1 ||
philpem@5 1662 (std::sscanf(st4,"%f%c%c",&a4,&sep4,&end)==2 && sep4=='%')) &&
philpem@5 1663 (std::sscanf(st5,"%f%c",&a5,&end)==1 ||
philpem@5 1664 (std::sscanf(st5,"%f%c%c",&a5,&sep5,&end)==2 && sep5=='%'))) {
philpem@5 1665 print("Crop image%s with (%g%s%g%s%g%s x (%g%s%g%s%g%s.",gmic_inds,
philpem@5 1666 a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%,":",",a2,sep2=='%'?"%)":")",
philpem@5 1667 a3,sep3=='%'?"%,":",",a4,sep4=='%'?"%,":",",a5,sep5=='%'?"%)":")");
philpem@5 1668 cimg_foroff(indices,l) {
philpem@5 1669 CImg<T> &img = images[indices[l]];
philpem@5 1670 const int
philpem@5 1671 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
philpem@5 1672 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
philpem@5 1673 z0 = (int)cimg::round(sep2=='%'?a2*img.dimz()/100:a2,1),
philpem@5 1674 x1 = (int)cimg::round(sep3=='%'?a3*img.dimx()/100:a3,1),
philpem@5 1675 y1 = (int)cimg::round(sep4=='%'?a4*img.dimy()/100:a4,1),
philpem@5 1676 z1 = (int)cimg::round(sep5=='%'?a5*img.dimz()/100:a5,1);
philpem@5 1677 gmic_apply(img,crop(x0,y0,z0,x1,y1,z1,borders?true:false));
philpem@5 1678 }
philpem@5 1679 ++position;
philpem@5 1680 } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
philpem@5 1681 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",
philpem@5 1682 st0,st1,st2,st3,&borders,&end)==5 ||
philpem@5 1683 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c"
philpem@5 1684 "%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",
philpem@5 1685 st0,st1,st2,st3,&end)==4) &&
philpem@5 1686 (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
philpem@5 1687 (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
philpem@5 1688 (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
philpem@5 1689 (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%')) &&
philpem@5 1690 (std::sscanf(st2,"%f%c",&a2,&end)==1 ||
philpem@5 1691 (std::sscanf(st2,"%f%c%c",&a2,&sep2,&end)==2 && sep2=='%')) &&
philpem@5 1692 (std::sscanf(st3,"%f%c",&a3,&end)==1 ||
philpem@5 1693 (std::sscanf(st3,"%f%c%c",&a3,&sep3,&end)==2 && sep3=='%'))) {
philpem@5 1694 print("Crop image%s with (%g%s%g%s x (%g%s%g%s.",gmic_inds,
philpem@5 1695 a0,sep0=='%'?"%,":",",a1,sep1=='%'?"%)":")",
philpem@5 1696 a2,sep2=='%'?"%,":",",a3,sep3=='%'?"%)":")");
philpem@5 1697 cimg_foroff(indices,l) {
philpem@5 1698 CImg<T> &img = images[indices[l]];
philpem@5 1699 const int
philpem@5 1700 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
philpem@5 1701 y0 = (int)cimg::round(sep1=='%'?a1*img.dimy()/100:a1,1),
philpem@5 1702 x1 = (int)cimg::round(sep2=='%'?a2*img.dimx()/100:a2,1),
philpem@5 1703 y1 = (int)cimg::round(sep3=='%'?a3*img.dimy()/100:a3,1);
philpem@5 1704 gmic_apply(img,crop(x0,y0,x1,y1,borders?true:false));
philpem@5 1705 }
philpem@5 1706 ++position;
philpem@5 1707 } else if ((std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%c",st0,st1,&borders,&end)==3 ||
philpem@5 1708 std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",st0,st1,&end)==2) &&
philpem@5 1709 (std::sscanf(st0,"%f%c",&a0,&end)==1 ||
philpem@5 1710 (std::sscanf(st0,"%f%c%c",&a0,&sep0,&end)==2 && sep0=='%')) &&
philpem@5 1711 (std::sscanf(st1,"%f%c",&a1,&end)==1 ||
philpem@5 1712 (std::sscanf(st1,"%f%c%c",&a1,&sep1,&end)==2 && sep1=='%'))) {
philpem@5 1713 print("Crop image%s with (%g%s x (%g%s.",gmic_inds,
philpem@5 1714 a0,sep0=='%'?"%)":")",a1,sep1=='%'?"%)":")");
philpem@5 1715 cimg_foroff(indices,l) {
philpem@5 1716 CImg<T> &img = images[indices[l]];
philpem@5 1717 const int
philpem@5 1718 x0 = (int)cimg::round(sep0=='%'?a0*img.dimx()/100:a0,1),
philpem@5 1719 x1 = (int)cimg::round(sep1=='%'?a1*img.dimx()/100:a1,1);
philpem@5 1720 gmic_apply(img,crop(x0,x1,borders?true:false));
philpem@5 1721 }
philpem@5 1722 ++position;
philpem@5 1723 } else {
philpem@5 1724 print("Crop image%s : Interactive mode.",gmic_inds);
philpem@5 1725 char title[4096] = { 0 };
philpem@5 1726 cimg_foroff(indices,l) {
philpem@5 1727 CImg<T>& img = images[indices[l]];
philpem@5 1728 CImgDisplay disp(cimg_fitscreen(img.dimx(),img.dimy(),1),0,1);
philpem@5 1729 std::sprintf(title,"%s : Interactive crop",filenames[indices[l]].ptr());
philpem@5 1730 disp.set_title(title);
philpem@5 1731 const CImg<int> s = img.get_select(disp,2);
philpem@5 1732 print("Crop image [%d] with (%d,%d,%d) x (%d,%d,%d).",
philpem@5 1733 indices[l],s[0],s[1],s[2],s[3],s[4],s[5]);
philpem@5 1734 gmic_apply(img,crop(s[0],s[1],s[2],s[3],s[4],s[5]));
philpem@5 1735 }
philpem@5 1736 }
philpem@5 1737 continue;
philpem@5 1738 }
philpem@5 1739
philpem@5 1740 // Autocrop.
philpem@5 1741 if (!cimg::strcmp("-autocrop",item0)) {
philpem@5 1742 print("Autocrop image%s with color '%s'.",gmic_inds,argument_text);
philpem@5 1743 cimg_foroff(indices,l) {
philpem@5 1744 CImg<T>& img = images[indices[l]];
philpem@5 1745 const CImg<T> col = CImg<T>(img.dimv()).fill(argument,true);
philpem@5 1746 gmic_apply(img,autocrop(col));
philpem@5 1747 }
philpem@5 1748 ++position; continue;
philpem@5 1749 }
philpem@5 1750
philpem@5 1751 // Select channels.
philpem@5 1752 if (!cimg::strcmp("-channels",item0)) {
philpem@5 1753 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
philpem@5 1754 float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
philpem@5 1755 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
philpem@5 1756 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1757 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1758 std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
philpem@5 1759 ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
philpem@5 1760 (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
philpem@5 1761 std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
philpem@5 1762 if (ind0!=no_ind) { gmic_check_indice(ind0,"Keep channels of image%s"); value0 = images[ind0].dimv()-1.0f; sep0 = 0; }
philpem@5 1763 if (ind1!=no_ind) { gmic_check_indice(ind1,"Keep channels of image%s"); value1 = images[ind1].dimv()-1.0f; sep1 = 0; }
philpem@5 1764 print("Keep channels %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
philpem@5 1765 cimg_foroff(indices,l) {
philpem@5 1766 CImg<T> &img = images[indices[l]];
philpem@5 1767 const int
philpem@5 1768 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,1),
philpem@5 1769 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimv()-1)/100:value1,1);
philpem@5 1770 gmic_apply(img,channels(nvalue0,nvalue1));
philpem@5 1771 }
philpem@5 1772 } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
philpem@5 1773 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1774 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1775 std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
philpem@5 1776 if (ind0!=no_ind) { gmic_check_indice(ind0,"Keep channel of image%s"); value0 = images[ind0].dimv()-1.0f; sep0 = 0; }
philpem@5 1777 print("Keep channel %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
philpem@5 1778 cimg_foroff(indices,l) {
philpem@5 1779 CImg<T> &img = images[indices[l]];
philpem@5 1780 const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,1);
philpem@5 1781 gmic_apply(img,channel(nvalue0));
philpem@5 1782 }
philpem@5 1783 } else error("Keep channels of image%s : Invalid argument '%s' "
philpem@5 1784 "(should be 'channel0[%%][,channel1[%%]]').",gmic_inds,argument_text);
philpem@5 1785 ++position; continue;
philpem@5 1786 }
philpem@5 1787
philpem@5 1788 // Select slices.
philpem@5 1789 if (!cimg::strcmp("-slices",item0)) {
philpem@5 1790 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
philpem@5 1791 float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
philpem@5 1792 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
philpem@5 1793 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1794 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1795 std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
philpem@5 1796 ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
philpem@5 1797 (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
philpem@5 1798 std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
philpem@5 1799 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select slices of image%s"); value0 = images[ind0].dimz()-1.0f; sep0 = 0; }
philpem@5 1800 if (ind1!=no_ind) { gmic_check_indice(ind1,"Select slices of image%s"); value1 = images[ind1].dimz()-1.0f; sep1 = 0; }
philpem@5 1801 print("Select slices %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
philpem@5 1802 cimg_foroff(indices,l) {
philpem@5 1803 CImg<T> &img = images[indices[l]];
philpem@5 1804 const int
philpem@5 1805 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimv()-1)/100:value0,0),
philpem@5 1806 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimv()-1)/100:value1,0);
philpem@5 1807 gmic_apply(img,slices(nvalue0,nvalue1));
philpem@5 1808 }
philpem@5 1809 } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
philpem@5 1810 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1811 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1812 std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
philpem@5 1813 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select slice of image%s"); value0 = images[ind0].dimz()-1.0f; sep0 = 0; }
philpem@5 1814 print("Select slice %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
philpem@5 1815 cimg_foroff(indices,l) {
philpem@5 1816 CImg<T> &img = images[indices[l]];
philpem@5 1817 const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimz()-1)/100:value0,1);
philpem@5 1818 gmic_apply(img,slice(nvalue0));
philpem@5 1819 }
philpem@5 1820 } else error("Select slices of image%s : Invalid argument '%s' "
philpem@5 1821 "(should be 'slice0[%%][,slice1[%%]]').",gmic_inds,argument_text);
philpem@5 1822 ++position; continue;
philpem@5 1823 }
philpem@5 1824
philpem@5 1825 // Select lines.
philpem@5 1826 if (!cimg::strcmp("-lines",item0) || !cimg::strcmp("-l",item0)) {
philpem@5 1827 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
philpem@5 1828 float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
philpem@5 1829 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
philpem@5 1830 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1831 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1832 std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
philpem@5 1833 ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
philpem@5 1834 (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
philpem@5 1835 std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
philpem@5 1836 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select lines of image%s"); value0 = images[ind0].dimy()-1.0f; sep0 = 0; }
philpem@5 1837 if (ind1!=no_ind) { gmic_check_indice(ind1,"Select lines of image%s"); value1 = images[ind1].dimy()-1.0f; sep1 = 0; }
philpem@5 1838 print("Select lines %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
philpem@5 1839 cimg_foroff(indices,l) {
philpem@5 1840 CImg<T> &img = images[indices[l]];
philpem@5 1841 const int
philpem@5 1842 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimy()-1)/100:value0,1),
philpem@5 1843 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimy()-1)/100:value1,1);
philpem@5 1844 gmic_apply(img,lines(nvalue0,nvalue1));
philpem@5 1845 }
philpem@5 1846 } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
philpem@5 1847 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1848 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1849 std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
philpem@5 1850 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select lines of image%s"); value0 = images[ind0].dimy()-1.0f; sep0 = 0; }
philpem@5 1851 print("Select lines %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
philpem@5 1852 cimg_foroff(indices,l) {
philpem@5 1853 CImg<T> &img = images[indices[l]];
philpem@5 1854 const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimy()-1)/100:value0,1);
philpem@5 1855 gmic_apply(img,line(nvalue0));
philpem@5 1856 }
philpem@5 1857 } else error("Select lines of image%s : Invalid argument '%s' "
philpem@5 1858 "(should be 'line0[%%][,line1[%%]]').",gmic_inds,argument_text);
philpem@5 1859 ++position; continue;
philpem@5 1860 }
philpem@5 1861
philpem@5 1862 // Columns.
philpem@5 1863 if (!cimg::strcmp("-columns",item0)) {
philpem@5 1864 char sep0 = 0, sep1 = 0, end = 0, arg0[4096] = { 0 }, arg1[4096] = { 0 };
philpem@5 1865 float value0 = 0, value1 = 0; int ind0 = no_ind, ind1 = no_ind;
philpem@5 1866 if (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",arg0,arg1,&end)==2 &&
philpem@5 1867 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1868 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1869 std::sscanf(arg0,"%f%c",&value0,&end)==1) &&
philpem@5 1870 ((std::sscanf(arg1,"[%d%c%c]",&ind1,&sep1,&end)==2 && sep1==']') ||
philpem@5 1871 (std::sscanf(arg1,"%f%c%c",&value1,&sep1,&end)==2 && sep1=='%') ||
philpem@5 1872 std::sscanf(arg1,"%f%c",&value1,&end)==1)) {
philpem@5 1873 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select columns of image%s"); value0 = images[ind0].dimx()-1.0f; sep0 = 0; }
philpem@5 1874 if (ind1!=no_ind) { gmic_check_indice(ind1,"Select columns of image%s"); value1 = images[ind1].dimx()-1.0f; sep1 = 0; }
philpem@5 1875 print("Select columns %g%s..%g%s of image%s.",value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",gmic_inds);
philpem@5 1876 cimg_foroff(indices,l) {
philpem@5 1877 CImg<T> &img = images[indices[l]];
philpem@5 1878 const int
philpem@5 1879 nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimx()-1)/100:value0,1),
philpem@5 1880 nvalue1 = (int)cimg::round(sep1=='%'?value1*(img.dimx()-1)/100:value1,1);
philpem@5 1881 gmic_apply(img,lines(nvalue0,nvalue1));
philpem@5 1882 }
philpem@5 1883 } else if (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",arg0,&end)==1 &&
philpem@5 1884 ((std::sscanf(arg0,"[%d%c%c]",&ind0,&sep0,&end)==2 && sep0==']') ||
philpem@5 1885 (std::sscanf(arg0,"%f%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 1886 std::sscanf(arg0,"%f%c",&value0,&end)==1)) {
philpem@5 1887 if (ind0!=no_ind) { gmic_check_indice(ind0,"Select columns of image%s"); value0 = images[ind0].dimx()-1.0f; sep0 = 0; }
philpem@5 1888 print("Select columns %g%s of image%s.",value0,sep0=='%'?"%":"",gmic_inds);
philpem@5 1889 cimg_foroff(indices,l) {
philpem@5 1890 CImg<T> &img = images[indices[l]];
philpem@5 1891 const int nvalue0 = (int)cimg::round(sep0=='%'?value0*(img.dimx()-1)/100:value0,1);
philpem@5 1892 gmic_apply(img,line(nvalue0));
philpem@5 1893 }
philpem@5 1894 } else error("Select columns of image%s : Invalid argument '%s' "
philpem@5 1895 "(should be 'column0[%%][,column1[%%]]').",gmic_inds,argument_text);
philpem@5 1896 ++position; continue;
philpem@5 1897 }
philpem@5 1898
philpem@5 1899 // Rotate.
philpem@5 1900 if (!cimg::strcmp("-rotate",item0)) {
philpem@5 1901 float angle = 0; int borders = 0, interpolation = 1; char end = 0;
philpem@5 1902 if (std::sscanf(argument,"%f%c",&angle,&end)==1 ||
philpem@5 1903 std::sscanf(argument,"%f%*c%d%c",&angle,&borders,&end)==2 ||
philpem@5 1904 std::sscanf(argument,"%f%*c%d%*c%d%c",&angle,&borders,&interpolation,&end)==3) {
philpem@5 1905 print("Rotate image%s with an angle of %g deg and %s interpolation.",
philpem@5 1906 gmic_inds,angle,interpolation?"linear":"nearest-neighbor");
philpem@5 1907 if (borders>=0) { cimg_foroff(indices,l) gmic_apply(images[indices[l]],rotate(angle,borders,interpolation)); }
philpem@5 1908 else cimg_foroff(indices,l) {
philpem@5 1909 CImg<T> &img = images[indices[l]];
philpem@5 1910 gmic_apply(img,rotate(angle,img.dimx()/2.0f,img.dimy()/2.0f,1,-1-borders,interpolation));
philpem@5 1911 }
philpem@5 1912 } else error("Rotate image%s : Invalid argument '%s' "
philpem@5 1913 "(should be 'angle[,border_conditions[,interpolation]]').",gmic_inds,argument_text);
philpem@5 1914 ++position;
philpem@5 1915 continue;
philpem@5 1916 }
philpem@5 1917
philpem@5 1918 // Mirror.
philpem@5 1919 if (!cimg::strcmp("-mirror",item0)) {
philpem@5 1920 const char axis = cimg::uncase(*argument);
philpem@5 1921 if (cimg::strlen(argument)==1) {
philpem@5 1922 print("Mirror image%s along the %c-axis.",gmic_inds,axis);
philpem@5 1923 cimg_foroff(indices,l) gmic_apply(images[indices[l]],mirror(axis));
philpem@5 1924 } else error("Mirror image%s : Invalid argument '%s' "
philpem@5 1925 "(should be '{x,y,z,v}').",gmic_inds,argument_text);
philpem@5 1926 ++position; continue;
philpem@5 1927 }
philpem@5 1928
philpem@5 1929 // Translate.
philpem@5 1930 if (!cimg::strcmp("-translate",item0)) {
philpem@5 1931 char stx[4096] = { 0 }, sty[4096] = { 0 }, stz[4096] = { 0 }, stv[4096] = { 0 };
philpem@5 1932 char sepx = 0, sepy = 0, sepz = 0, sepv = 0, end = 0;
philpem@5 1933 float dx = 0, dy = 0, dz = 0, dv = 0; int borders = 0;
philpem@5 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",
philpem@5 1935 stx,sty,stz,stv,&borders,&end)==5 ||
philpem@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",
philpem@5 1937 stx,sty,stz,stv,&end)==4) &&
philpem@5 1938 (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
philpem@5 1939 (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%')) &&
philpem@5 1940 (std::sscanf(stz,"%f%c",&dz,&end)==1 || (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%')) &&
philpem@5 1941 (std::sscanf(stv,"%f%c",&dv,&end)==1 || (std::sscanf(stv,"%f%c%c",&dv,&sepv,&end)==2 && sepv=='%'))) ||
philpem@5 1942 (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",stx,sty,stz,&end)==3 &&
philpem@5 1943 (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
philpem@5 1944 (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%')) &&
philpem@5 1945 (std::sscanf(stz,"%f%c",&dz,&end)==1 || (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%'))) ||
philpem@5 1946 (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",stx,sty,&end)==2 &&
philpem@5 1947 (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')) &&
philpem@5 1948 (std::sscanf(sty,"%f%c",&dy,&end)==1 || (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%'))) ||
philpem@5 1949 (std::sscanf(argument,"%4095[0-9.eE%+-]%c",stx,&end)==1 &&
philpem@5 1950 (std::sscanf(stx,"%f%c",&dx,&end)==1 || (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%')))) {
philpem@5 1951 print("Translate image%s with vector (%g%s,%g%s,%g%s,%g%s).",
philpem@5 1952 gmic_inds,dx,sepx=='%'?"%":"",dy,sepy=='%'?"%":"",dz,sepz=='%'?"%":"",dv,sepv=='%'?"%":"");
philpem@5 1953 cimg_foroff(indices,l) {
philpem@5 1954 CImg<T> &img = images[indices[l]];
philpem@5 1955 const int
philpem@5 1956 ndx = (int)cimg::round(sepx=='%'?dx*img.dimx()/100:dx,1),
philpem@5 1957 ndy = (int)cimg::round(sepy=='%'?dy*img.dimy()/100:dy,1),
philpem@5 1958 ndz = (int)cimg::round(sepz=='%'?dz*img.dimz()/100:dz,1),
philpem@5 1959 ndv = (int)cimg::round(sepv=='%'?dv*img.dimv()/100:dv,1);
philpem@5 1960 gmic_apply(images[indices[l]],translate(ndx,ndy,ndz,ndv,borders));
philpem@5 1961 }
philpem@5 1962 } else error("Translate image%s : Invalid argument '%s' "
philpem@5 1963 "(should be 'tx[%%][,ty[%%][,tz[%%][,tv[%%][,border_conditions]]]]').",gmic_inds,argument_text);
philpem@5 1964 ++position; continue;
philpem@5 1965 }
philpem@5 1966
philpem@5 1967 // Transpose.
philpem@5 1968 gmic_simple_item("-transpose",transpose,"Transpose image%s.");
philpem@5 1969
philpem@5 1970 // Invert.
philpem@5 1971 gmic_simple_item("-invert",invert,"Compute matrix inversion of image%s.");
philpem@5 1972
philpem@5 1973 // Permute axes.
philpem@5 1974 if (!cimg::strcmp("-permute",item0)) {
philpem@5 1975 print("Permute axes of image%s with permutation '%s'.",gmic_inds,argument_text);
philpem@5 1976 cimg_foroff(indices,l) gmic_apply(images[indices[l]],permute_axes(argument));
philpem@5 1977 ++position; continue;
philpem@5 1978 }
philpem@5 1979
philpem@5 1980 // Unroll.
philpem@5 1981 if (!cimg::strcmp("-unroll",item0)) {
philpem@5 1982 const char axis = cimg::uncase(*argument);
philpem@5 1983 if (cimg::strlen(argument)==1 && (axis=='x' || axis=='y' || axis=='z' || axis=='v')) {
philpem@5 1984 print("Unroll image%s along the %c-axis.",gmic_inds,axis);
philpem@5 1985 cimg_foroff(indices,l) gmic_apply(images[indices[l]],unroll(axis));
philpem@5 1986 } else error("Unroll image%s : Invalid argument '%s' "
philpem@5 1987 "(should be '{x,y,z,v}').",gmic_inds,argument_text);
philpem@5 1988 ++position; continue;
philpem@5 1989 }
philpem@5 1990
philpem@5 1991 // Split image(s).
philpem@5 1992 if (!cimg::strcmp("-split",item0) || !cimg::strcmp("-s",item0)) {
philpem@5 1993 char axis = cimg::uncase(*argument), foo = 0, end = 0; int nb = 0, keep_value = 0; double value = 0;
philpem@5 1994 if ((std::sscanf(argument,"%c%c",&foo,&end)==1 ||
philpem@5 1995 std::sscanf(argument,"%c%*c%d%c",&foo,&nb,&end)==2) &
philpem@5 1996 (axis=='x' || axis=='y' || axis=='z' || axis=='v')) {
philpem@5 1997 if (nb<0) error("Split image%s along the %c-axis in %d part : Invalid number of parts.",
philpem@5 1998 gmic_inds,axis,nb);
philpem@5 1999 if (nb>0) print("Split image%s along the %c-axis in %d parts.",gmic_inds,axis,nb);
philpem@5 2000 else print("Split image%s along the %c-axis.",gmic_inds,axis);
philpem@5 2001 unsigned int off = 0;
philpem@5 2002 cimg_foroff(indices,l) {
philpem@5 2003 const unsigned int ind = indices[l] + off;
philpem@5 2004 const CImg<T>& img = images[ind];
philpem@5 2005 const CImg<char> filename = filenames[ind];
philpem@5 2006 const CImgList<T> split = img.get_split(axis,nb);
philpem@5 2007 if (get_version) {
philpem@5 2008 images.insert(split);
philpem@5 2009 filenames.insert(split.size,filename);
philpem@5 2010 } else {
philpem@5 2011 images.remove(ind); images.insert(split,ind);
philpem@5 2012 filenames.remove(ind); filenames.insert(split.size,filename,ind);
philpem@5 2013 off+=split.size-1;
philpem@5 2014 }
philpem@5 2015 }
philpem@5 2016 } else if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
philpem@5 2017 std::sscanf(argument,"%lf%*c%d%c",&value,&keep_value,&end)==2) {
philpem@5 2018 print("Split image%s according to value %g.",gmic_inds,value);
philpem@5 2019 unsigned int off = 0;
philpem@5 2020 cimg_foroff(indices,l) {
philpem@5 2021 const unsigned int ind = indices[l] + off;
philpem@5 2022 CImg<T>& img = images[ind];
philpem@5 2023 const CImg<char> filename = filenames[ind];
philpem@5 2024 const CImgList<T> split = img.get_split((T)value,keep_value,false);
philpem@5 2025 if (get_version) {
philpem@5 2026 images.insert(split);
philpem@5 2027 filenames.insert(split.size,filename);
philpem@5 2028 } else {
philpem@5 2029 images.remove(ind); images.insert(split,ind);
philpem@5 2030 filenames.remove(ind); filenames.insert(split.size,filename,ind);
philpem@5 2031 off+=split.size-1;
philpem@5 2032 }
philpem@5 2033 }
philpem@5 2034 } else error("Split image%s : Invalid argument '%s' "
philpem@5 2035 "(should be 'axis[,nb_parts]' where 'axis' can be '{x,y,z,v}').",gmic_inds,argument_text);
philpem@5 2036 ++position; continue;
philpem@5 2037 }
philpem@5 2038
philpem@5 2039 // Append image(s).
philpem@5 2040 if (!cimg::strcmp("-append",item0) || !cimg::strcmp("-a",item0)) {
philpem@5 2041 char axis = 0, align='p', end = 0;
philpem@5 2042 if ((std::sscanf(argument,"%c%c",&axis,&end)==1 ||
philpem@5 2043 std::sscanf(argument,"%c%*c%c%c",&axis,&align,&end)==2)) {
philpem@5 2044 axis = cimg::uncase(axis);
philpem@5 2045 print("Append image%s along the %c-axis with %s alignment.",
philpem@5 2046 gmic_inds,axis,align=='p'?"left":align=='c'?"center":"right");
philpem@5 2047 CImgList<T> subimages; cimg_foroff(indices,l) subimages.insert(images[indices[l]],~0U,true);
philpem@5 2048 if (get_version) {
philpem@5 2049 images.insert(subimages.get_append(axis,align));
philpem@5 2050 filenames.insert(filenames[indices[0]]);
philpem@5 2051 } else {
philpem@5 2052 images.insert(subimages.get_append(axis,align),indices[0]);
philpem@5 2053 filenames.insert(filenames[indices[0]],indices[0]);
philpem@5 2054 int off = 1;
philpem@5 2055 cimg_foroff(indices,l) {
philpem@5 2056 const int ind = indices[l] + off;
philpem@5 2057 images.remove(ind); filenames.remove(ind);
philpem@5 2058 --off;
philpem@5 2059 }
philpem@5 2060 }
philpem@5 2061 } else error("Append image%s : Invalid argument '%s' "
philpem@5 2062 "(should be 'axis[,alignement]' where 'axis' can be '{x,y,z,v}' "
philpem@5 2063 "and alignement '{p,c,n}').",gmic_inds,argument_text);
philpem@5 2064 ++position; continue;
philpem@5 2065 }
philpem@5 2066
philpem@5 2067 // Warp image(s).
philpem@5 2068 if (!cimg::strcmp("-warp",item0)) {
philpem@5 2069 int ind0 = no_ind, interpolation = 1, relative = 0, nb = 1, borders = 1; char end = 0, sep = 0;
philpem@5 2070 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']')||
philpem@5 2071 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&relative,&end)==2 ||
philpem@5 2072 std::sscanf(argument,"[%d]%*c%d%*c%d%c",&ind0,&relative,&interpolation,&end)==3 ||
philpem@5 2073 std::sscanf(argument,"[%d]%*c%d%*c%d%*c%d%c",&ind0,&relative,&interpolation,&borders,&end)==4 ||
philpem@5 2074 std::sscanf(argument,"[%d]%*c%d%*c%d%*c%d%*c%d%c",&ind0,&relative,&interpolation,&borders,&nb,&end)==5) {
philpem@5 2075 gmic_check_indice(ind0,"Warp image%s");
philpem@5 2076 if (nb!=1) print("Warp image%s with %s field [%u] and %d frames.",
philpem@5 2077 gmic_inds,relative?"relative":"absolute",ind0,nb);
philpem@5 2078 else print("Warp image%s with %s field [%u].",gmic_inds,relative?"relative":"absolute",ind0);
philpem@5 2079 if (nb>=1) {
philpem@5 2080 const CImg<T> warp = images[ind0];
philpem@5 2081 unsigned int off = 0;
philpem@5 2082 cimg_foroff(indices,l) {
philpem@5 2083 const unsigned int ind = indices[l] + off;
philpem@5 2084 CImg<T> &img = images[ind];
philpem@5 2085 CImgList<T> frames(nb);
philpem@5 2086 cimglist_for(frames,t) {
philpem@5 2087 const CImg<T> nwarp = warp.get_resize(img.dimx(),img.dimy(),img.dimz(),warp.dimv(),3)*=(t+1.0f)/nb;
philpem@5 2088 frames[t] = img.get_warp(nwarp,relative?true:false,interpolation?true:false,borders);
philpem@5 2089 }
philpem@5 2090 if (get_version) {
philpem@5 2091 images.insert(frames);
philpem@5 2092 filenames.insert(nb-1,filenames[ind]);
philpem@5 2093 } else {
philpem@5 2094 images.remove(ind); images.insert(frames,ind);
philpem@5 2095 filenames.insert(nb-1,filenames[ind],ind);
philpem@5 2096 off+=nb-1;
philpem@5 2097 }
philpem@5 2098 }
philpem@5 2099 }
philpem@5 2100 } else error("Warp image%s : Invalid argument '%s' "
philpem@5 2101 "(should be '[indice][,relative[,interpolation[,border_conditions[,nb_frames]]]]').",
philpem@5 2102 gmic_inds,argument_text);
philpem@5 2103 ++position; continue;
philpem@5 2104 }
philpem@5 2105
philpem@5 2106 //-----------------------
philpem@5 2107 // Image filtering
philpem@5 2108 //-----------------------
philpem@5 2109
philpem@5 2110 // Gaussian blur.
philpem@5 2111 if (!cimg::strcmp("-blur",item0)) {
philpem@5 2112 float sigma = -1; int borders = 1; char end = 0;
philpem@5 2113 if ((std::sscanf(argument,"%f%c",&sigma,&end)==1 ||
philpem@5 2114 std::sscanf(argument,"%f%*c%d%c",&sigma,&borders,&end)==2)
philpem@5 2115 && sigma>=0) {
philpem@5 2116 print("Blur image%s with standard deviation %g.",gmic_inds,sigma);
philpem@5 2117 cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur(sigma,borders?true:false));
philpem@5 2118 } else error("Blur image%s : Invalid argument '%s' "
philpem@5 2119 "(should be 'stdev[,border_conditions]', with stdev>=0).",gmic_inds,argument_text);
philpem@5 2120 ++position; continue;
philpem@5 2121 }
philpem@5 2122
philpem@5 2123 // Bilateral filter.
philpem@5 2124 if (!cimg::strcmp("-bilateral",item0)) {
philpem@5 2125 float sigmas = 0, sigmar = 0; char end = 0;
philpem@5 2126 if (std::sscanf(argument,"%f%*c%f%c",&sigmas,&sigmar,&end)==2) {
philpem@5 2127 print("Apply bilateral filter on image%s with standart deviations %g and %g.",
philpem@5 2128 gmic_inds,sigmas,sigmar);
philpem@5 2129 cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_bilateral(sigmas,sigmar));
philpem@5 2130 } else error("Apply bilateral filter on image%s : Invalid argument '%s' "
philpem@5 2131 "(should be 'stdevs,stdevr').",gmic_inds,argument_text);
philpem@5 2132 ++position; continue;
philpem@5 2133 }
philpem@5 2134
philpem@5 2135 // Smooth.
philpem@5 2136 if (!cimg::strcmp("-smooth",item0)) {
philpem@5 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;
philpem@5 2138 unsigned int interpolation_type = 0, fast_approx = 1;
philpem@5 2139 char end = 0;
philpem@5 2140 if (std::sscanf(argument,"%f%c",&amplitude,&end)==1 ||
philpem@5 2141 std::sscanf(argument,"%f%*c%f%c",&amplitude,&sharpness,&end)==2 ||
philpem@5 2142 std::sscanf(argument,"%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&end)==3 ||
philpem@5 2143 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&end)==4 ||
philpem@5 2144 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&sigma,&end)==5 ||
philpem@5 2145 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&end)==6 ||
philpem@5 2146 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",&amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&end)==7 ||
philpem@5 2147 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%c",
philpem@5 2148 &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&end)==8 ||
philpem@5 2149 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%u%c",
philpem@5 2150 &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&interpolation_type,&end)==9 ||
philpem@5 2151 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%f%*c%u%*c%u%c",
philpem@5 2152 &amplitude,&sharpness,&anisotropy,&alpha,&sigma,&dl,&da,&gauss_prec,&interpolation_type,&fast_approx,&end)==10) {
philpem@5 2153 print("Smooth image%s anisotropically with "
philpem@5 2154 "amplitude %g, sharpness %g, anisotropy %g, alpha %g and sigma %g.",
philpem@5 2155 gmic_inds,amplitude,sharpness,anisotropy,alpha,sigma);
philpem@5 2156 cimg_foroff(indices,l)
philpem@5 2157 gmic_apply(images[indices[l]],blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,
philpem@5 2158 dl,da,gauss_prec,interpolation_type,fast_approx?true:false));
philpem@5 2159 } else error("Smooth image%s anisotropically : Invalid argument '%s' "
philpem@5 2160 "(should be 'amplitude[,sharpness[,anisotropy[,alpha[,sigma[,dl[,da[,prec[,interp[,fast]]]]]]]]]').",
philpem@5 2161 gmic_inds,argument_text);
philpem@5 2162 ++position; continue;
philpem@5 2163 }
philpem@5 2164
philpem@5 2165 // Patch averaging.
philpem@5 2166 if (!cimg::strcmp("-denoise",item0)) {
philpem@5 2167 float sigmas = 10, sigmar = 10; int psize = 5, rsize = 6; char end = 0;
philpem@5 2168 if (std::sscanf(argument,"%f%c",&sigmas,&end)==1 ||
philpem@5 2169 std::sscanf(argument,"%f%*c%f%c",&sigmas,&sigmar,&end)==2 ||
philpem@5 2170 std::sscanf(argument,"%f%*c%f%*c%d%c",&sigmas,&sigmar,&psize,&end)==3 ||
philpem@5 2171 std::sscanf(argument,"%f%*c%f%*c%d%*c%d%c",&sigmas,&sigmar,&psize,&rsize,&end)==4) {
philpem@5 2172 if (sigmas<0 || sigmar<0 || psize<0 || rsize<0)
philpem@5 2173 error("Denoise image%s with %dx%d patches, standard deviations %lg,%g and lookup size %d : "
philpem@5 2174 "Invalid parameters.",gmic_inds,psize,psize,sigmas,sigmar,rsize);
philpem@5 2175 print("Denoise image%s with %dx%d patches, standard deviations %lg,%g and lookup size %d.",
philpem@5 2176 gmic_inds,psize,psize,sigmas,sigmar,rsize);
philpem@5 2177 cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_patch(psize,sigmas,sigmar,rsize));
philpem@5 2178 } else error("Denoise image%s : Invalid argument '%s' "
philpem@5 2179 "(should be 'stdev_s[,stdev_p[,patch_size[,lookup_size]]]').",
philpem@5 2180 gmic_inds,argument_text);
philpem@5 2181 ++position; continue;
philpem@5 2182 }
philpem@5 2183
philpem@5 2184 // Median filter.
philpem@5 2185 if (!cimg::strcmp("-median",item0)) {
philpem@5 2186 int siz = 3; char end = 0;
philpem@5 2187 if (std::sscanf(argument,"%d%c",&siz,&end)==1) {
philpem@5 2188 if (siz<=0) error("Apply median filter on image%s : Invalid size %d.",gmic_inds,siz);
philpem@5 2189 print("Apply median filter of size %d on image%s.",siz,gmic_inds);
philpem@5 2190 cimg_foroff(indices,l) gmic_apply(images[indices[l]],blur_median(siz));
philpem@5 2191 } else error("Apply median filter on image%s : Invalid argument '%s' "
philpem@5 2192 "(should be 'size').",gmic_inds,argument_text);
philpem@5 2193 ++position; continue;
philpem@5 2194 }
philpem@5 2195
philpem@5 2196 // Sharpen.
philpem@5 2197 if (!cimg::strcmp("-sharpen",item0)) {
philpem@5 2198 float amplitude = 0, edge = 1, alpha = 0, sigma = 0; int sharpen_type = 0; char end = 0;
philpem@5 2199 if (std::sscanf(argument,"%f%c",&amplitude,&end)==1 ||
philpem@5 2200 std::sscanf(argument,"%f%*c%d%c",&amplitude,&sharpen_type,&end)==2 ||
philpem@5 2201 std::sscanf(argument,"%f%*c%d%*c%f%c",&amplitude,&sharpen_type,&edge,&end)==3 ||
philpem@5 2202 std::sscanf(argument,"%f%*c%d%*c%f%*c%f%c",&amplitude,&sharpen_type,&edge,&alpha,&end)==4 ||
philpem@5 2203 std::sscanf(argument,"%f%*c%d%*c%f%*c%f%*c%f%c",&amplitude,&sharpen_type,&edge,&alpha,&sigma,&end)==5) {
philpem@5 2204 if (sharpen_type)
philpem@5 2205 print("Sharpen image%s with shock filters and amplitude %g, edge %g, alpha %g and sigma %g.",
philpem@5 2206 gmic_inds,amplitude,edge,alpha,sigma);
philpem@5 2207 else
philpem@5 2208 print("Sharpen image%s with inverse diffusion and amplitude %g.",gmic_inds,amplitude);
philpem@5 2209 cimg_foroff(indices,l) gmic_apply(images[indices[l]],sharpen(amplitude,sharpen_type?true:false,edge,alpha,sigma));
philpem@5 2210 } else error("Sharpen image%s : Invalid argument '%s' "
philpem@5 2211 "(should be 'amplitude[,sharpen_type[,edge[,alpha[,sigma]]]]', "
philpem@5 2212 "where 'sharpen_type' can be '{0=inverse diffusion, 1=shock filters}').",
philpem@5 2213 gmic_inds,argument_text);
philpem@5 2214 ++position; continue;
philpem@5 2215 }
philpem@5 2216
philpem@5 2217 // Convolve.
philpem@5 2218 if (!cimg::strcmp("-convolve",item0)) {
philpem@5 2219 int ind0 = no_ind, borders = 1; char sep = 0, end = 0;
philpem@5 2220 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
philpem@5 2221 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
philpem@5 2222 gmic_check_indice(ind0,"Convolve image%s");
philpem@5 2223 print("Convolve image%s with mask [%d].",gmic_inds,ind0);
philpem@5 2224 const CImg<T> mask = images[ind0];
philpem@5 2225 cimg_foroff(indices,l) gmic_apply(images[indices[l]],convolve(mask,borders));
philpem@5 2226 } else error("Convolve image%s : Invalid argument '%s' "
philpem@5 2227 "(should be '[indice][,border_conditions]').",gmic_inds,argument_text);
philpem@5 2228 ++position; continue;
philpem@5 2229 }
philpem@5 2230
philpem@5 2231 // Correlate.
philpem@5 2232 if (!cimg::strcmp("-correlate",item0)) {
philpem@5 2233 int ind0 = no_ind, borders = 1; char sep = 0, end = 0;
philpem@5 2234 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
philpem@5 2235 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
philpem@5 2236 gmic_check_indice(ind0,"Correlate image%s");
philpem@5 2237 print("Correlate image%s with mask [%d].",gmic_inds,ind0);
philpem@5 2238 const CImg<T> mask = images[ind0];
philpem@5 2239 cimg_foroff(indices,l) gmic_apply(images[indices[l]],correlate(mask,borders));
philpem@5 2240 } else error("Correlate image%s : Invalid argument '%s' "
philpem@5 2241 "(should be '[indice][,border_conditions]').",gmic_inds,argument_text);
philpem@5 2242 ++position; continue;
philpem@5 2243 }
philpem@5 2244
philpem@5 2245 // Erode.
philpem@5 2246 if (!cimg::strcmp("-erode",item0)) {
philpem@5 2247 int siz = 3, ind0 = no_ind, borders = 1; char sep = 0, end = 0;
philpem@5 2248 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
philpem@5 2249 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
philpem@5 2250 gmic_check_indice(ind0,"Erode image%s");
philpem@5 2251 print("Erode image%s with mask [%d].",gmic_inds,ind0);
philpem@5 2252 const CImg<T> mask = images[ind0];
philpem@5 2253 cimg_foroff(indices,l) gmic_apply(images[indices[l]],erode(mask,borders));
philpem@5 2254 } else if (std::sscanf(argument,"%d%c",&siz,&end)==1 ||
philpem@5 2255 std::sscanf(argument,"%d%*c%d%c",&siz,&borders,&end)==2) {
philpem@5 2256 if (siz<=0) error("Erode image%s : Invalid size %d.",gmic_inds,siz);
philpem@5 2257 print("Erode image%s with size %d.",gmic_inds,siz);
philpem@5 2258 cimg_foroff(indices,l) gmic_apply(images[indices[l]],erode(siz,borders));
philpem@5 2259 } else error("Erode image%s : Invalid argument '%s' "
philpem@5 2260 "(should be '[indice]' or 'size').",gmic_inds,argument_text);
philpem@5 2261 ++position; continue;
philpem@5 2262 }
philpem@5 2263
philpem@5 2264 // Dilate.
philpem@5 2265 if (!cimg::strcmp("-dilate",item0)) {
philpem@5 2266 int siz = 3, ind0 = no_ind, borders = 1; char sep = 0, end = 0;
philpem@5 2267 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
philpem@5 2268 std::sscanf(argument,"[%d]%*c%d%c",&ind0,&borders,&end)==2) {
philpem@5 2269 gmic_check_indice(ind0,"Dilate image%s");
philpem@5 2270 print("Dilate image%s with mask [%d].",gmic_inds,ind0);
philpem@5 2271 const CImg<T> mask = images[ind0];
philpem@5 2272 cimg_foroff(indices,l) gmic_apply(images[indices[l]],dilate(mask,borders));
philpem@5 2273 } else if (std::sscanf(argument,"%d%c",&siz,&end)==1 ||
philpem@5 2274 std::sscanf(argument,"%d%*c%d%c",&siz,&borders,&end)==2) {
philpem@5 2275 if (siz<=0) error("Dilate image%s : Invalid size %d.",gmic_inds,siz);
philpem@5 2276 print("Dilate image%s with size %d.",gmic_inds,siz);
philpem@5 2277 cimg_foroff(indices,l) gmic_apply(images[indices[l]],dilate(siz,borders));
philpem@5 2278 } else error("Dilate image%s : Invalid argument '%s' "
philpem@5 2279 "(should be '[indice]' or 'size').",gmic_inds,argument_text);
philpem@5 2280 ++position; continue;
philpem@5 2281 }
philpem@5 2282
philpem@5 2283 // Compute gradient.
philpem@5 2284 if (!cimg::strcmp("-gradient",item0)) {
philpem@5 2285 char axes[4096] = { 0 }, *naxes = 0, end = 0; int scheme = 3;
philpem@5 2286 print("Compute gradient of image%s.",gmic_inds);
philpem@5 2287 if (std::sscanf(argument,"%4095[xyz]%c",axes,&end)==1 ||
philpem@5 2288 std::sscanf(argument,"%4095[xyz]%*c%d%c",axes,&scheme,&end)==2) { naxes = axes; ++position; }
philpem@5 2289 unsigned int off = 0;
philpem@5 2290 cimg_foroff(indices,l) {
philpem@5 2291 const unsigned int ind = indices[l] + off;
philpem@5 2292 CImg<T>& img = images[ind];
philpem@5 2293 const CImg<char> filename = filenames[ind];
philpem@5 2294 const CImgList<T> gradient = img.get_gradient(naxes,scheme);
philpem@5 2295 if (get_version) {
philpem@5 2296 images.insert(gradient);
philpem@5 2297 filenames.insert(gradient.size,filename);
philpem@5 2298 } else {
philpem@5 2299 images.remove(ind); images.insert(gradient,ind);
philpem@5 2300 filenames.remove(ind); filenames.insert(gradient.size,filename,ind);
philpem@5 2301 off+=gradient.size-1;
philpem@5 2302 }
philpem@5 2303 }
philpem@5 2304 continue;
philpem@5 2305 }
philpem@5 2306
philpem@5 2307 // Compute Hessian.
philpem@5 2308 if (!cimg::strcmp("-hessian",item0)) {
philpem@5 2309 char axes[4096] = { 0 }, *naxes = 0, end = 0;
philpem@5 2310 print("Compute Hessian of image%s.",gmic_inds);
philpem@5 2311 if (std::sscanf(argument,"%4095[xyz]%c",axes,&end)==1) { naxes = axes; ++position; }
philpem@5 2312 unsigned int off = 0;
philpem@5 2313 cimg_foroff(indices,l) {
philpem@5 2314 const unsigned int ind = indices[l] + off;
philpem@5 2315 CImg<T>& img = images[ind];
philpem@5 2316 const CImg<char> filename = filenames[ind];
philpem@5 2317 const CImgList<T> hessian = img.get_hessian(naxes);
philpem@5 2318 if (get_version) {
philpem@5 2319 images.insert(hessian);
philpem@5 2320 filenames.insert(hessian.size,filename);
philpem@5 2321 } else {
philpem@5 2322 images.remove(ind); images.insert(hessian,ind);
philpem@5 2323 filenames.remove(ind); filenames.insert(hessian.size,filename,ind);
philpem@5 2324 off+=hessian.size-1;
philpem@5 2325 }
philpem@5 2326 }
philpem@5 2327 continue;
philpem@5 2328 }
philpem@5 2329
philpem@5 2330 // Compute direct or inverse FFT.
philpem@5 2331 const bool inv_fft = !cimg::strcmp("-ifft",item0);
philpem@5 2332 if (!cimg::strcmp("-fft",item0) || inv_fft) {
philpem@5 2333 print("Compute %sFourier Transform of complex data",inv_fft?"inverse ":"");
philpem@5 2334 cimg_foroff(indices,l) {
philpem@5 2335 const unsigned int ind0 = indices[l], ind1 = l+1<_maxl?indices[l+1]:~0U;
philpem@5 2336 if (ind1!=~0U) {
philpem@5 2337 if (verbosity_level>=0) std::fprintf(cimg_stdout," ([%u],[%u])%c",ind0,ind1,l==_maxl-1?'.':',');
philpem@5 2338 CImgList<T> fft(images[ind0],images[ind1],!get_version);
philpem@5 2339 fft.FFT(inv_fft);
philpem@5 2340 if (get_version) {
philpem@5 2341 images.insert(2);
philpem@5 2342 fft[0].transfer_to(images[images.size-2]);
philpem@5 2343 fft[1].transfer_to(images[images.size-1]);
philpem@5 2344 filenames.insert(filenames[ind0]);
philpem@5 2345 filenames.insert(filenames[ind1]);
philpem@5 2346 } else {
philpem@5 2347 fft[0].transfer_to(images[ind0]);
philpem@5 2348 fft[1].transfer_to(images[ind1]);
philpem@5 2349 }
philpem@5 2350 ++l;
philpem@5 2351 } else {
philpem@5 2352 if (verbosity_level>=0) std::fprintf(cimg_stdout," ([%u],0)",ind0);
philpem@5 2353 CImgList<T> fft(images[ind0],!get_version);
philpem@5 2354 fft.insert(fft[0],~0U,false);
philpem@5 2355 fft[1].fill(0);
philpem@5 2356 fft.FFT(inv_fft);
philpem@5 2357 if (get_version) {
philpem@5 2358 images.insert(2);
philpem@5 2359 fft[0].transfer_to(images[images.size-2]);
philpem@5 2360 fft[1].transfer_to(images[images.size-1]);
philpem@5 2361 filenames.insert(2,filenames[ind0]);
philpem@5 2362 } else {
philpem@5 2363 fft[0].transfer_to(images[ind0]);
philpem@5 2364 images.insert(fft[1],1+ind0);
philpem@5 2365 filenames.insert(filenames[ind0],1+ind0);
philpem@5 2366 }
philpem@5 2367 }
philpem@5 2368 }
philpem@5 2369 continue;
philpem@5 2370 }
philpem@5 2371
philpem@5 2372 //-----------------------------
philpem@5 2373 // Image creation and drawing
philpem@5 2374 //-----------------------------
philpem@5 2375
philpem@5 2376 // Dimensions.
philpem@5 2377 if (!cimg::strcmp("-dimensions",item0)) {
philpem@5 2378 print("Get dimensions of image%s.",gmic_inds);
philpem@5 2379 cimg_foroff(indices,l) {
philpem@5 2380 CImg<T>& img = images[indices[l]];
philpem@5 2381 CImg<int> dims = CImg<int>::vector(img.dimx(),img.dimy(),img.dimz(),img.dimv());
philpem@5 2382 gmic_apply(img,replace(dims));
philpem@5 2383 }
philpem@5 2384 continue;
philpem@5 2385 }
philpem@5 2386
philpem@5 2387 // Stats.
philpem@5 2388 if (!cimg::strcmp("-stats",item0)) {
philpem@5 2389 print("Get statistics of image%s.",gmic_inds);
philpem@5 2390 cimg_foroff(indices,l) gmic_apply(images[indices[l]],stats());
philpem@5 2391 continue;
philpem@5 2392 }
philpem@5 2393
philpem@5 2394 // Histogram.
philpem@5 2395 if (!cimg::strcmp("-histogram",item0)) {
philpem@5 2396 int nb_levels = 256; char sep = 0, end = 0;
philpem@5 2397 if (std::sscanf(argument,"%d%c",&nb_levels,&end)==1 ||
philpem@5 2398 (std::sscanf(argument,"%d%c%c",&nb_levels,&sep,&end)==2 && sep=='%')) {
philpem@5 2399 print("Compute histogram of image%s using %d%s levels.",gmic_inds,nb_levels,sep=='%'?"%":"");
philpem@5 2400 cimg_foroff(indices,l) {
philpem@5 2401 CImg<T> &img = images[indices[l]];
philpem@5 2402 int nnb_levels = nb_levels;
philpem@5 2403 if (sep=='%') { double m, M = img.maxmin(m); nnb_levels = (int)cimg::round(nb_levels*(1+M-m)/100,1); }
philpem@5 2404 gmic_apply(images[indices[l]],histogram(nnb_levels));
philpem@5 2405 }
philpem@5 2406 } else error("Compute histogram of image%s : Invalid argument '%s' "
philpem@5 2407 "(should be 'nb_levels[%%]').",gmic_inds,argument_text);
philpem@5 2408 ++position; continue;
philpem@5 2409 }
philpem@5 2410
philpem@5 2411 // Distance function.
philpem@5 2412 if (!cimg::strcmp("-distance",item0)) {
philpem@5 2413 double value = 0; char sep = 0, end = 0;
philpem@5 2414 if (std::sscanf(argument,"%lf%c",&value,&end)==1 ||
philpem@5 2415 (std::sscanf(argument,"%lf%c%c",&value,&sep,&end)==2 && sep=='%')) {
philpem@5 2416 print("Compute distance map of image%s to isovalue %g%s.",gmic_inds,value,sep=='%'?"%":"");
philpem@5 2417 cimg_foroff(indices,l) {
philpem@5 2418 CImg<T> &img = images[indices[l]];
philpem@5 2419 double isovalue = value;
philpem@5 2420 if (sep=='%') { double m, M = img.maxmin(m); isovalue = m + value*(M - m)/100; }
philpem@5 2421 gmic_apply(img,distance((T)isovalue));
philpem@5 2422 }
philpem@5 2423 } else error("Compute distance function of image%s : Invalid argument '%s' "
philpem@5 2424 "(should be 'value[%%]').",gmic_inds,argument_text);
philpem@5 2425 ++position; continue;
philpem@5 2426 }
philpem@5 2427
philpem@5 2428 // Apply Hamilton-Jacobi PDE to compute distance to 0.
philpem@5 2429 if (!cimg::strcmp("-hamilton",item0)) {
philpem@5 2430 int nb_iter = 0; float band_size = 0; char end = 0;
philpem@5 2431 if (std::sscanf(argument,"%d%c",&nb_iter,&end)==1 ||
philpem@5 2432 std::sscanf(argument,"%d%*c%f%c",&nb_iter,&band_size,&end)==2) {
philpem@5 2433 print("Apply %d iterations of Hamilton-Jacobi PDE on image%s.",nb_iter,gmic_inds);
philpem@5 2434 cimg_foroff(indices,l) gmic_apply(images[indices[l]],distance_hamilton((unsigned int)nb_iter,band_size));
philpem@5 2435 } else error("Apply %d iterations of Hamilton-Jacobi PDE on image%s : Invalid argument '%s' "
philpem@5 2436 "(should be 'nb_iter[,band_size]', with band_size>0).",nb_iter,gmic_inds,argument_text);
philpem@5 2437 ++position; continue;
philpem@5 2438 }
philpem@5 2439
philpem@5 2440 // Label regions.
philpem@5 2441 gmic_simple_item("-label",label_regions,"Label regions on image%s.");
philpem@5 2442
philpem@5 2443 // Displacement field.
philpem@5 2444 if (!cimg::strcmp("-displacement",item0)) {
philpem@5 2445 float smooth = 0.1f, precision = 0.1f; int ind0 = no_ind, nbscales = 0, itermax = 1000; char sep = 0, end = 0;
philpem@5 2446 if ((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
philpem@5 2447 std::sscanf(argument,"[%d]%*c%f%c",&ind0,&smooth,&end)==2 ||
philpem@5 2448 std::sscanf(argument,"[%d]%*c%f%*c%f%c",&ind0,&smooth,&precision,&end)==3 ||
philpem@5 2449 std::sscanf(argument,"[%d]%*c%f%*c%f%*c%d%c",&ind0,&smooth,&precision,&nbscales,&end)==4 ||
philpem@5 2450 std::sscanf(argument,"[%d]%*c%f%*c%f%*c%d%*c%d%c",&ind0,&smooth,&precision,&nbscales,&itermax,&end)==5) {
philpem@5 2451 gmic_check_indice(ind0,"Compute displacement field of image%s");
philpem@5 2452 print("Compute displacement field of image%s with target [%u] and smoothness %g.",
philpem@5 2453 gmic_inds,ind0,smooth);
philpem@5 2454 const CImg<T> target = images[ind0];
philpem@5 2455 cimg_foroff(indices,l) gmic_apply(images[indices[l]],displacement_field(target,smooth,precision,nbscales,itermax));
philpem@5 2456 } else error("Compute displacement field of image%s : Invalid argument '%s' "
philpem@5 2457 "(should be '[indice][,smoothness[,precision[,nbscales[,itermax]]]]').",gmic_inds,argument_text);
philpem@5 2458 ++position; continue;
philpem@5 2459 }
philpem@5 2460
philpem@5 2461 // Sort.
philpem@5 2462 gmic_simple_item("-sort",sort,"Sort values in image%s.");
philpem@5 2463
philpem@5 2464 // PSNR.
philpem@5 2465 if (!cimg::strcmp("-psnr",item0)) {
philpem@5 2466 double valmax = 255; char end = 0;
philpem@5 2467 if (std::sscanf(argument,"%lf%c",&valmax,&end)==1) ++position;
philpem@5 2468 if (images.size) {
philpem@5 2469 const unsigned int siz = indices.size();
philpem@5 2470 print("Compute a %ux%u matrix [%u] of PSNR values (max. pixel value is %g).",siz,siz,images.size,valmax);
philpem@5 2471 CImg<T> res(siz,siz,1,1,(T)-1);
philpem@5 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);
philpem@5 2473 images.insert(res);
philpem@5 2474 filenames.insert(CImg<char>("PSNR",5,1,1,1,false));
philpem@5 2475 } else error("Compute PSNR : image list is empty.");
philpem@5 2476 continue;
philpem@5 2477 }
philpem@5 2478
philpem@5 2479 // Draw point.
philpem@5 2480 if (!cimg::strcmp("-point",item0)) {
philpem@5 2481 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, color[4096] = { 0 };
philpem@5 2482 char sepx0 = 0, sepy0 = 0, sepz0 = 0, end = 0;
philpem@5 2483 float x0 = 0, y0 = 0, z0 = 0, opacity = 1;
philpem@5 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,+-]",
philpem@5 2485 arg0,arg1,arg2,&opacity,color)>=2 &&
philpem@5 2486 ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
philpem@5 2487 std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
philpem@5 2488 ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
philpem@5 2489 std::sscanf(arg1,"%f%c",&y0,&end)==1) &&
philpem@5 2490 ((std::sscanf(arg2,"%f%c%c",&z0,&sepz0,&end)==2 && sepz0=='%') ||
philpem@5 2491 std::sscanf(arg2,"%f%c",&z0,&end)==1 || !arg2[0])) {
philpem@5 2492 print("Draw point (%g%s,%g%s,%g%s) with color '%s' and opacity %g on image%s.",
philpem@5 2493 x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",z0,sepz0=='%'?"%":"",
philpem@5 2494 color[0]?color:"default",opacity,gmic_inds);
philpem@5 2495 cimg_foroff(indices,l) {
philpem@5 2496 CImg<T> &img = images[indices[l]];
philpem@5 2497 CImg<T> col(img.dimv(),1,1,1,0);
philpem@5 2498 col.fill(color,true);
philpem@5 2499 const int
philpem@5 2500 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
philpem@5 2501 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1),
philpem@5 2502 nz0 = (int)cimg::round(sepz0=='%'?z0*(img.dimz()-1)/100:z0,1);
philpem@5 2503 gmic_apply(img,draw_point(nx0,ny0,nz0,col,opacity));
philpem@5 2504 }
philpem@5 2505 } else error("Draw point on image%s : Invalid argument '%s' "
philpem@5 2506 "(should be 'x[%%],y[%%][,z[%%][,opacity[,color]]])",gmic_inds,argument_text);
philpem@5 2507 ++position; continue;
philpem@5 2508 }
philpem@5 2509
philpem@5 2510 // Draw line.
philpem@5 2511 if (!cimg::strcmp("-line",item0)) {
philpem@5 2512 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, arg3[4096] = { 0 }, color[4096] = { 0 };
philpem@5 2513 char sepx0 = 0, sepy0 = 0, sepx1 = 0, sepy1 = 0, end = 0;
philpem@5 2514 float x0 = 0, y0 = 0, x1 = 0, y1 = 0, opacity = 1;
philpem@5 2515 if (std::sscanf(argument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]"
philpem@5 2516 "%*c%f%*c%4095[0-9.eE,+-]",
philpem@5 2517 arg0,arg1,arg2,arg3,&opacity,color)>=4 &&
philpem@5 2518 ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
philpem@5 2519 std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
philpem@5 2520 ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
philpem@5 2521 std::sscanf(arg1,"%f%c",&y0,&end)==1) &&
philpem@5 2522 ((std::sscanf(arg2,"%f%c%c",&x1,&sepx1,&end)==2 && sepx1=='%') ||
philpem@5 2523 std::sscanf(arg2,"%f%c",&x1,&end)==1) &&
philpem@5 2524 ((std::sscanf(arg3,"%f%c%c",&y1,&sepy1,&end)==2 && sepy1=='%') ||
philpem@5 2525 std::sscanf(arg3,"%f%c",&y1,&end)==1)) {
philpem@5 2526 print("Draw line (%g%s,%g%s) - (%g%s,%g%s) with color '%s' and opacity %g on image%s.",
philpem@5 2527 x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",x1,sepx1=='%'?"%":"",y1,sepy1=='%'?"%":"",
philpem@5 2528 color[0]?color:"default",opacity,gmic_inds);
philpem@5 2529 cimg_foroff(indices,l) {
philpem@5 2530 CImg<T> &img = images[indices[l]];
philpem@5 2531 CImg<T> col(img.dimv(),1,1,1,0);
philpem@5 2532 col.fill(color,true);
philpem@5 2533 const int
philpem@5 2534 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
philpem@5 2535 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1),
philpem@5 2536 nx1 = (int)cimg::round(sepx1=='%'?x1*(img.dimx()-1)/100:x1,1),
philpem@5 2537 ny1 = (int)cimg::round(sepy1=='%'?y1*(img.dimy()-1)/100:y1,1);
philpem@5 2538 gmic_apply(img,draw_line(nx0,ny0,nx1,ny1,col,opacity));
philpem@5 2539 }
philpem@5 2540 } else error("Draw line on image%s : Invalid argument '%s' "
philpem@5 2541 "(should be 'x0[%%],y0[%%],x1[%%],y1[%%][,opacity[,color]]')",gmic_inds,argument_text);
philpem@5 2542 ++position; continue;
philpem@5 2543 }
philpem@5 2544
philpem@5 2545 // Draw polygon.
philpem@5 2546 if (!cimg::strcmp("-polygon",item0)) {
philpem@5 2547 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, tmp[4096] = { 0 }, sepx0 = 0, sepy0 = 0, end = 0;
philpem@5 2548 int N = 0; float x0 = 0, y0 = 0, opacity = 1;
philpem@5 2549 if (std::sscanf(argument,"%d%c",&N,&end)==2 && N>2) {
philpem@5 2550 const char
philpem@5 2551 *nargument = argument + std::sprintf(tmp,"%d",N) + 1,
philpem@5 2552 *const eargument = argument + cimg::strlen(argument);
philpem@5 2553 CImg<float> coords0(N,2,1,1,0);
philpem@5 2554 CImg<bool> percents(N,2,1,1,0);
philpem@5 2555 for (int n = 0; n<N; ++n) if (nargument<eargument) {
philpem@5 2556 if (std::sscanf(nargument,"%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]",arg0,arg1)==2 &&
philpem@5 2557 ((std::sscanf(arg0,"%f%c%c",&x0,&(sepx0=0),&end)==2 && sepx0=='%') ||
philpem@5 2558 std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
philpem@5 2559 ((std::sscanf(arg1,"%f%c%c",&y0,&(sepy0=0),&end)==2 && sepy0=='%') ||
philpem@5 2560 std::sscanf(arg1,"%f%c",&y0,&end)==1)) {
philpem@5 2561 coords0(n,0) = x0; percents(n,0) = (sepx0=='%');
philpem@5 2562 coords0(n,1) = y0; percents(n,1) = (sepy0=='%');
philpem@5 2563 nargument+=cimg::strlen(arg0) + cimg::strlen(arg1) + 2;
philpem@5 2564 } else error("Draw polygon on image%s : Invalid or incomplete argument '%s' "
philpem@5 2565 "(should be 'N,x0[%%],y0[%%],x1[%%],y1[%%],..,xN[%%],yN[%%][,opacity[,color]]' with N>=3)",
philpem@5 2566 gmic_inds,argument_text);
philpem@5 2567 } else error("Draw polygon on image%s : Incomplete argument '%s' "
philpem@5 2568 "(%d xy-coordinates should be defined)",
philpem@5 2569 gmic_inds,argument_text,N);
philpem@5 2570 if (nargument<eargument && std::sscanf(nargument,"%4095[0-9.eE+-]",arg0)==1 &&
philpem@5 2571 std::sscanf(arg0,"%f",&opacity)==1) nargument+=cimg::strlen(arg0)+1;
philpem@5 2572 const char *const color = nargument<eargument?nargument:&(end=0);
philpem@5 2573 print("Draw %d-vertices polygon with color '%s' and opacity %g on image%s.",
philpem@5 2574 N,color[0]?color:"default",opacity,gmic_inds);
philpem@5 2575 cimg_foroff(indices,l) {
philpem@5 2576 CImg<T> &img = images[indices[l]];
philpem@5 2577 CImg<int> coords(coords0);
philpem@5 2578 cimg_forX(coords,p) {
philpem@5 2579 if (percents(p,0)) coords(p,0) = (int)cimg::round(coords0(p,0)*(img.dimx()-1)/100,1);
philpem@5 2580 if (percents(p,1)) coords(p,1) = (int)cimg::round(coords0(p,1)*(img.dimy()-1)/100,1);
philpem@5 2581 }
philpem@5 2582 CImg<T> col(img.dimv(),1,1,1,0);
philpem@5 2583 col.fill(color,true);
philpem@5 2584 gmic_apply(img,draw_polygon(coords,col,opacity));
philpem@5 2585 }
philpem@5 2586 } else error("Draw polygon on image%s : Invalid argument '%s' "
philpem@5 2587 "(should be 'N,x0[%%],y0[%%],x1[%%],y1[%%],..,xN[%%],yN[%%][,opacity[,color]]' with N>=3)",
philpem@5 2588 gmic_inds,argument_text);
philpem@5 2589 ++position; continue;
philpem@5 2590 }
philpem@5 2591
philpem@5 2592 // Draw ellipse.
philpem@5 2593 if (!cimg::strcmp("-ellipse",item0)) {
philpem@5 2594 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, color[4096] = { 0 };
philpem@5 2595 char sepx0 = 0, sepy0 = 0, end = 0;
philpem@5 2596 float x0 = 0, y0 = 0, r0 = 0, r1 = 0, ru = 1, rv = 0, opacity = 1;
philpem@5 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,+-]",
philpem@5 2598 arg0,arg1,&r0,&r1,&ru,&rv,&opacity,color)>=4 &&
philpem@5 2599 ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
philpem@5 2600 std::sscanf(arg0,"%f%c",&x0,&end)==1) &&
philpem@5 2601 ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
philpem@5 2602 std::sscanf(arg1,"%f%c",&y0,&end)==1)) {
philpem@5 2603 print("Draw ellipse centered at (%g%s,%g%s) with radii (%g,%g), orientation (%g,%g), color '%s' "
philpem@5 2604 "and opacity %g on image%s.",
philpem@5 2605 x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",
philpem@5 2606 r0,r1,ru,rv,color[0]?color:"default",opacity,gmic_inds);
philpem@5 2607 cimg_foroff(indices,l) {
philpem@5 2608 CImg<T> &img = images[indices[l]];
philpem@5 2609 CImg<T> col(img.dimv(),1,1,1,0);
philpem@5 2610 col.fill(color,true);
philpem@5 2611 const int
philpem@5 2612 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
philpem@5 2613 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1);
philpem@5 2614 gmic_apply(img,draw_ellipse(nx0,ny0,r0,r1,ru,rv,col,opacity));
philpem@5 2615 }
philpem@5 2616 } else error("Draw ellipse on image%s : Invalid argument '%s' "
philpem@5 2617 "(should be 'x[%%],y[%%],r,R[,u,v[,opacity[,color]]])",
philpem@5 2618 gmic_inds,argument_text);
philpem@5 2619 ++position; continue;
philpem@5 2620 }
philpem@5 2621
philpem@5 2622 // Draw text.
philpem@5 2623 if (!cimg::strcmp("-text",item0)) {
philpem@5 2624 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, color[4096] = { 0 }, text[4096] = { 0 };
philpem@5 2625 char sepx0 = 0, sepy0 = 0, end = 0;
philpem@5 2626 float x0 = 0, y0 = 0, opacity = 1; int siz = 11;
philpem@5 2627 if (std::sscanf(argument,"%4095[^,],%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%d%*c%f%*c%4095[0-9.eE,+-]",
philpem@5 2628 text,arg0,arg1,&siz,&opacity,color)>=1 &&
philpem@5 2629 ((std::sscanf(arg0,"%f%c%c",&x0,&sepx0,&end)==2 && sepx0=='%') ||
philpem@5 2630 std::sscanf(arg0,"%f%c",&x0,&end)==1 || !arg0[0]) &&
philpem@5 2631 ((std::sscanf(arg1,"%f%c%c",&y0,&sepy0,&end)==2 && sepy0=='%') ||
philpem@5 2632 std::sscanf(arg1,"%f%c",&y0,&end)==1 || !arg1[0])) {
philpem@5 2633 cimg::strclean(text); cimg::strescape(text);
philpem@5 2634 print("Draw text \"%s\" at position (%g%s,%g%s) with font size %d, color '%s' "
philpem@5 2635 "and opacity %f on image%s.",
philpem@5 2636 text,x0,sepx0=='%'?"%":"",y0,sepy0=='%'?"%":"",siz,color[0]?color:"default",opacity,gmic_inds);
philpem@5 2637 cimg_foroff(indices,l) {
philpem@5 2638 CImg<T> &img = images[indices[l]];
philpem@5 2639 CImg<T> col(img.dimv(),1,1,1,0);
philpem@5 2640 col.fill(color,true);
philpem@5 2641 const int
philpem@5 2642 nx0 = (int)cimg::round(sepx0=='%'?x0*(img.dimx()-1)/100:x0,1),
philpem@5 2643 ny0 = (int)cimg::round(sepy0=='%'?y0*(img.dimy()-1)/100:y0,1);
philpem@5 2644 gmic_apply(img,draw_text(nx0,ny0,text,col.ptr(),0,opacity,siz));
philpem@5 2645 }
philpem@5 2646 } else error("Draw text on image%s : Invalid argument '%s' "
philpem@5 2647 "(should be 'text[,x[%%],y[%%][,size[,opacity[,color]]]]').",
philpem@5 2648 gmic_inds,argument_text);
philpem@5 2649 ++position; continue;
philpem@5 2650 }
philpem@5 2651
philpem@5 2652 // Draw image.
philpem@5 2653 if (!cimg::strcmp("-image",item0)) {
philpem@5 2654 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, sep = 0, sepx = 0, sepy = 0, sepz = 0, end = 0;
philpem@5 2655 int ind0 = no_ind, indm0 = no_ind; float x = 0, y = 0, z = 0, opacity = 1;
philpem@5 2656 if (((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==1 && sep==']') ||
philpem@5 2657 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,&end)==2 ||
philpem@5 2658 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,&end)==3 ||
philpem@5 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 ||
philpem@5 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 ||
philpem@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",
philpem@5 2662 &ind0,arg0,arg1,arg2,&opacity,&indm0,&sep,&end)==7) &&
philpem@5 2663 (!*arg0 ||
philpem@5 2664 std::sscanf(arg0,"%f%c",&x,&end)==1 ||
philpem@5 2665 (std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%')) &&
philpem@5 2666 (!*arg1 ||
philpem@5 2667 std::sscanf(arg1,"%f%c",&y,&end)==1 ||
philpem@5 2668 (std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%')) &&
philpem@5 2669 (!*arg2 ||
philpem@5 2670 std::sscanf(arg2,"%f%c",&z,&end)==1 ||
philpem@5 2671 (std::sscanf(arg2,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%'))) {
philpem@5 2672 gmic_check_indice(ind0,"Draw image on image%s");
philpem@5 2673 const CImg<T> sprite = images[ind0];
philpem@5 2674 CImg<T> mask;
philpem@5 2675 if (indm0!=no_ind) {
philpem@5 2676 gmic_check_indice(indm0,"Draw image on image%s");
philpem@5 2677 mask = images[indm0];
philpem@5 2678 print("Draw image [%d] at (%g%s,%g%s,%g%s), with mask [%d] and opacity %f on image%s.",
philpem@5 2679 ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",indm0,opacity,gmic_inds);
philpem@5 2680 } else print("Draw image [%d] at (%g%s,%g%s,%g%s) with opacity %f on image%s.",
philpem@5 2681 ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",opacity,gmic_inds);
philpem@5 2682 cimg_foroff(indices,l) {
philpem@5 2683 CImg<T> &img = images[indices[l]];
philpem@5 2684 const int
philpem@5 2685 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
philpem@5 2686 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
philpem@5 2687 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1);
philpem@5 2688 if (indm0!=no_ind) { gmic_apply(img,draw_image(nx,ny,nz,sprite,mask,opacity)); }
philpem@5 2689 else { gmic_apply(img,draw_image(nx,ny,nz,sprite,opacity)); }
philpem@5 2690 }
philpem@5 2691 } else error("Draw image on image%s : Invalid argument '%s' "
philpem@5 2692 "(should be '[indice][,x[%%][,y[%%][,z[%%][,opacity[,indice_mask]]]]]').",
philpem@5 2693 gmic_inds,argument_text);
philpem@5 2694 ++position; continue;
philpem@5 2695 }
philpem@5 2696
philpem@5 2697 // Draw 3D object.
philpem@5 2698 if (!cimg::strcmp("-object3d",item0)) {
philpem@5 2699 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, sep = 0, sepx = 0, sepy = 0, end = 0;
philpem@5 2700 float x = 0, y = 0, z = 0, opacity = 1;
philpem@5 2701 int ind0 = no_ind;
philpem@5 2702 if (((std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') ||
philpem@5 2703 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,&end)==2 ||
philpem@5 2704 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%c",&ind0,arg0,arg1,&end)==3 ||
philpem@5 2705 std::sscanf(argument,"[%d]%*c%4095[0-9.eE%+-]%*c%4095[0-9.eE%+-]%*c%f%c",&ind0,arg0,arg1,&z,&end)==4 ||
philpem@5 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) &&
philpem@5 2707 (!*arg0 ||
philpem@5 2708 std::sscanf(arg0,"%f%c",&x,&end)==1 ||
philpem@5 2709 (std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%')) &&
philpem@5 2710 (!*arg1 ||
philpem@5 2711 std::sscanf(arg1,"%f%c",&y,&end)==1 ||
philpem@5 2712 (std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%'))) {
philpem@5 2713 gmic_check_indice(ind0,"Draw 3D object on image%s");
philpem@5 2714 if (!images[ind0].is_CImg3d())
philpem@5 2715 error("Draw 3D object on image%s : Image [%d] is not a 3D object.",gmic_inds,ind0);
philpem@5 2716 print("Draw 3D object [%d] at (%g%s,%g%s,%g) on image%s, with opacity %g.",
philpem@5 2717 ind0,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,gmic_inds,opacity);
philpem@5 2718 CImgList<unsigned int> primitives3d;
philpem@5 2719 CImgList<unsigned char> colors3d;
philpem@5 2720 CImg<float> opacities3d, points3d(images[ind0]);
philpem@5 2721 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
philpem@5 2722 opacities3d*=opacity;
philpem@5 2723 cimg_foroff(indices,l) {
philpem@5 2724 CImg<T> &img = images[indices[l]];
philpem@5 2725 const float
philpem@5 2726 nx = (float)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
philpem@5 2727 ny = (float)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1);
philpem@5 2728 gmic_apply(img,draw_object3d(nx,ny,z,points3d,primitives3d,colors3d,opacities3d,
philpem@5 2729 render3d,!is_oriented3d,focale3d,light3d_x,light3d_y,light3d_z,specular_light3d,
philpem@5 2730 specular_shine3d,0));
philpem@5 2731 }
philpem@5 2732 } else error("Draw 3D object on image%s : Invalid argument '%s' "
philpem@5 2733 "(should be '[indice][,x[%%][,y[%%][,z[,opacity[,zoom[,u1,v1,w1,angle1[,...]]]]]]]').",
philpem@5 2734 gmic_inds,argument_text);
philpem@5 2735 ++position; continue;
philpem@5 2736 }
philpem@5 2737
philpem@5 2738 // Draw plasma fractal.
philpem@5 2739 if (!cimg::strcmp("-plasma",item0)) {
philpem@5 2740 float alpha = 1, beta = 1, opacity = 1; char end = 0;
philpem@5 2741 if (std::sscanf(argument,"%f%c",&alpha,&end)==1 ||
philpem@5 2742 std::sscanf(argument,"%f%*c%f%c",&alpha,&beta,&end)==2 ||
philpem@5 2743 std::sscanf(argument,"%f%*c%f%*c%f%c",&alpha,&beta,&opacity,&end)==3) {
philpem@5 2744 print("Draw plasma in image%s with alpha %g, beta %g and opacity %g.",gmic_inds,alpha,beta,opacity);
philpem@5 2745 cimg_foroff(indices,l) gmic_apply(images[indices[l]],draw_plasma(alpha,beta,opacity));
philpem@5 2746 } else error("Draw plasma in image%d : Invalid argument '%s' "
philpem@5 2747 "(should be 'alpha[,beta[,opacity]]').",gmic_inds,argument_text);
philpem@5 2748 ++position; continue;
philpem@5 2749 }
philpem@5 2750
philpem@5 2751 // Draw Mandelbrot/Julia fractal.
philpem@5 2752 if (!cimg::strcmp("-mandelbrot",item0)) {
philpem@5 2753 double z0r = -2, z0i = -2, z1r = 2, z1i = 2, paramr = 0, parami = 0; char end = 0;
philpem@5 2754 float opacity = 1; int itermax = 100, julia = 0;
philpem@5 2755 if (std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%c",&z0r,&z0i,&z1r,&z1i,&end)==4 ||
philpem@5 2756 std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%c",&z0r,&z0i,&z1r,&z1i,&itermax,&end)==5 ||
philpem@5 2757 std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%*c%d%*c%lf%*c%lf%c",
philpem@5 2758 &z0r,&z0i,&z1r,&z1i,&itermax,&julia,&paramr,&parami,&end)==8 ||
philpem@5 2759 std::sscanf(argument,"%lf%*c%lf%*c%lf%*c%lf%*c%d%*c%d%*c%lf%*c%lf%*c%f%c",
philpem@5 2760 &z0r,&z0i,&z1r,&z1i,&itermax,&julia,&paramr,&parami,&opacity,&end)==9) {
philpem@5 2761 print("Draw %s fractal in image%s from complex area (%g,%g)-(%g,%g) with c0 = (%g,%g) (%d iterations).",
philpem@5 2762 julia?"Julia":"Mandelbrot",gmic_inds,z0r,z0i,z1r,z1i,paramr,parami,itermax);
philpem@5 2763 cimg_foroff(indices,l)
philpem@5 2764 gmic_apply(images[indices[l]],draw_mandelbrot(CImg<T>(),opacity,z0r,z0i,z1r,z1i,itermax,true,
philpem@5 2765 julia?true:false,paramr,parami));
philpem@5 2766 } else error("Draw fractal in image%s : Invalid argument '%s' "
philpem@5 2767 "(should be 'z0r,z0i,z1r,z1i[,itermax[,julia,c0r,c0i[,opacity]]]').",gmic_inds,argument_text);
philpem@5 2768 ++position; continue;
philpem@5 2769 }
philpem@5 2770
philpem@5 2771 // Flood fill.
philpem@5 2772 if (!cimg::strcmp("-flood",item0)) {
philpem@5 2773 char arg0[4096] = { 0 }, arg1[4096] = { 0 }, arg2[4096] = { 0 }, color[4096] = { 0 };
philpem@5 2774 char sepx = 0, sepy = 0, sepz = 0, end = 0;
philpem@5 2775 float x = 0, y = 0, z = 0, tolerance = 0, opacity = 1;
philpem@5 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,+-]",
philpem@5 2777 arg0,arg1,arg2,&tolerance,&opacity,color)>=1 &&
philpem@5 2778 ((std::sscanf(arg0,"%f%c%c",&x,&sepx,&end)==2 && sepx=='%') ||
philpem@5 2779 std::sscanf(arg0,"%f%c",&x,&end)==1) &&
philpem@5 2780 ((std::sscanf(arg1,"%f%c%c",&y,&sepy,&end)==2 && sepy=='%') ||
philpem@5 2781 std::sscanf(arg1,"%f%c",&y,&end)==1 || !arg1[0]) &&
philpem@5 2782 ((std::sscanf(arg2,"%f%c%c",&z,&sepz,&end)==2 && sepz=='%') ||
philpem@5 2783 std::sscanf(arg2,"%f%c",&z,&end)==1 || !arg2[0])) {
philpem@5 2784 print("Flood fill image%s from (%g%s,%g%s,%g%s) with tolerance %g, opacity %g and color '%s'.",
philpem@5 2785 gmic_inds,x,sepx=='%'?"%":"",y,sepy=='%'?"%":"",z,sepz=='%'?"%":"",tolerance,opacity,color);
philpem@5 2786 cimg_foroff(indices,l) {
philpem@5 2787 CImg<T> &img = images[indices[l]];
philpem@5 2788 CImg<T> col(img.dimv(),1,1,1,0);
philpem@5 2789 col.fill(color,true);
philpem@5 2790 const int
philpem@5 2791 nx = (int)cimg::round(sepx=='%'?x*(img.dimx()-1)/100:x,1),
philpem@5 2792 ny = (int)cimg::round(sepy=='%'?y*(img.dimy()-1)/100:y,1),
philpem@5 2793 nz = (int)cimg::round(sepz=='%'?z*(img.dimz()-1)/100:z,1);
philpem@5 2794 gmic_apply(img,draw_fill(nx,ny,nz,col,opacity,tolerance));
philpem@5 2795 }
philpem@5 2796 } else error("Flood fill image%s : Invalid argument '%s' "
philpem@5 2797 "(should be 'x[,y[,z[,tolerance[,opacity[,color]]]]]').",gmic_inds,argument_text);
philpem@5 2798 ++position; continue;
philpem@5 2799 }
philpem@5 2800
philpem@5 2801 //-------------------------
philpem@5 2802 // Image list manipulation
philpem@5 2803 //-------------------------
philpem@5 2804
philpem@5 2805 // Remove specified image(s).
philpem@5 2806 if (!cimg::strcmp("-remove",item0) || !cimg::strcmp("-rm",item0)) {
philpem@5 2807 print("Remove image%s",gmic_inds);
philpem@5 2808 unsigned int off = 0;
philpem@5 2809 cimg_foroff(indices,l) {
philpem@5 2810 const unsigned int ind = indices[l] - off;
philpem@5 2811 images.remove(ind); filenames.remove(ind);
philpem@5 2812 ++off;
philpem@5 2813 }
philpem@5 2814 if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u image%s left).",images.size,images.size==1?"":"s");
philpem@5 2815 continue;
philpem@5 2816 }
philpem@5 2817
philpem@5 2818 // Keep specified image(s).
philpem@5 2819 if (!cimg::strcmp("-keep",item0) || !cimg::strcmp("-k",item0)) {
philpem@5 2820 print("Keep image%s",gmic_inds);
philpem@5 2821 CImgList<T> nimages(indices.size());
philpem@5 2822 cimg_foroff(indices,l) nimages[l].swap(images[indices[l]]);
philpem@5 2823 nimages.transfer_to(images);
philpem@5 2824 if (verbosity_level>=0) std::fprintf(cimg_stdout," (%u image%s left).",images.size,images.size==1?"":"s");
philpem@5 2825 continue;
philpem@5 2826 }
philpem@5 2827
philpem@5 2828 // Move image(s) to specified position.
philpem@5 2829 if (!cimg::strcmp("-move",item0) || !cimg::strcmp("-mv",item0)) {
philpem@5 2830 int ind0 = no_ind; char end = 0;
philpem@5 2831 if (std::sscanf(argument,"%d%c",&ind0,&end)==1) {
philpem@5 2832 if (ind0<0) ind0+=images.size;
philpem@5 2833 if (ind0<0) ind0 = 0;
philpem@5 2834 if (ind0>(int)images.size) ind0 = images.size;
philpem@5 2835 print("Move image%s to position %d.",gmic_inds,ind0);
philpem@5 2836 CImgList<T> nimages;
philpem@5 2837 CImgList<char> nfilenames;
philpem@5 2838 cimg_foroff(indices,l) {
philpem@5 2839 const unsigned int ind = indices[l];
philpem@5 2840 nimages.insert(1); nimages.last().swap(images[ind]);
philpem@5 2841 nfilenames.insert(1); nfilenames.last().swap(filenames[ind]);
philpem@5 2842 }
philpem@5 2843 images.insert(nimages,ind0); filenames.insert(nfilenames,ind0);
philpem@5 2844 { cimglist_for(images,l) if (!images[l]) { images.remove(l); filenames.remove(l--); }}
philpem@5 2845 } else error("Move image%s : Invalid argument '%s' "
philpem@5 2846 "(should be 'position').",gmic_inds,argument_text);
philpem@5 2847 ++position; continue;
philpem@5 2848 }
philpem@5 2849
philpem@5 2850 // Reverse images order.
philpem@5 2851 if (!cimg::strcmp("-reverse",item0)) {
philpem@5 2852 print("Reverse images order.");
philpem@5 2853 CImgList<T> nimages(indices.size());
philpem@5 2854 CImgList<char> nfilenames(indices.size());
philpem@5 2855 cimg_foroff(indices,l) { nimages[l].swap(images[indices[l]]); nfilenames[l].swap(filenames[indices[l]]); }
philpem@5 2856 nimages.reverse(); nfilenames.reverse();
philpem@5 2857 { cimg_foroff(indices,l) { nimages[l].swap(images[indices[l]]); nfilenames[l].swap(filenames[indices[l]]); }}
philpem@5 2858 continue;
philpem@5 2859 }
philpem@5 2860
philpem@5 2861 // Set image name.
philpem@5 2862 if (!cimg::strcmp("-name",item0)) {
philpem@5 2863 cimg_foroff(indices,l) filenames[indices[l]].assign(argument,cimg::strlen(argument)+1,1,1,1,false);
philpem@5 2864 ++position; continue;
philpem@5 2865 }
philpem@5 2866
philpem@5 2867 //-------------------------
philpem@5 2868 // 3D objects manipulation
philpem@5 2869 //-------------------------
philpem@5 2870
philpem@5 2871 // Generate 3D cube.
philpem@5 2872 if (!cimg::strcmp("-cube3d",item)) {
philpem@5 2873 float size = 100; char end = 0;
philpem@5 2874 if (std::sscanf(argument,"%f%c",&size,&end)==1) {
philpem@5 2875 print("Generate 3D cube with size %g.",size);
philpem@5 2876 CImgList<unsigned int> primitives3d;
philpem@5 2877 CImg<float> points3d = CImg<T>::cube3d(primitives3d,size);
philpem@5 2878 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
philpem@5 2879 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
philpem@5 2880 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 2881 images.insert(points3d);
philpem@5 2882 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 2883 } else error("Generate 3D cube : Invalid argument '%s' "
philpem@5 2884 "(should be 'size').",argument_text);
philpem@5 2885 ++position; continue;
philpem@5 2886 }
philpem@5 2887
philpem@5 2888 // Generate 3D cone.
philpem@5 2889 if (!cimg::strcmp("-cone3d",item)) {
philpem@5 2890 float radius = 100, height = 200; char end = 0; unsigned int subdivisions = 24;
philpem@5 2891 if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
philpem@5 2892 std::sscanf(argument,"%f%*c%f%c",&radius,&height,&end)==2 ||
philpem@5 2893 std::sscanf(argument,"%f%*c%f%*c%u%c",&radius,&height,&subdivisions,&end)==3) {
philpem@5 2894 print("Generate 3D cone with radius %g, height %g and %u subdivisions.",radius,height,subdivisions);
philpem@5 2895 CImgList<unsigned int> primitives3d;
philpem@5 2896 CImg<float> points3d = CImg<T>::cone3d(primitives3d,radius,height,subdivisions);
philpem@5 2897 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
philpem@5 2898 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
philpem@5 2899 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 2900 images.insert(points3d);
philpem@5 2901 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 2902 } else error("Generate 3D cone : Invalid argument '%s' "
philpem@5 2903 "(should be 'radius[,height[,subdivisions]]').",argument_text);
philpem@5 2904 ++position; continue;
philpem@5 2905 }
philpem@5 2906
philpem@5 2907 // Generate 3D cylinder.
philpem@5 2908 if (!cimg::strcmp("-cylinder3d",item)) {
philpem@5 2909 float radius = 100, height = 200; char end = 0; unsigned int subdivisions = 24;
philpem@5 2910 if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
philpem@5 2911 std::sscanf(argument,"%f%*c%f%c",&radius,&height,&end)==2 ||
philpem@5 2912 std::sscanf(argument,"%f%*c%f%*c%u%c",&radius,&height,&subdivisions,&end)==3) {
philpem@5 2913 print("Generate 3D cylinder with radius %g, height %g and %u subdivisions.",radius,height,subdivisions);
philpem@5 2914 CImgList<unsigned int> primitives3d;
philpem@5 2915 CImg<float> points3d = CImg<T>::cylinder3d(primitives3d,radius,height,subdivisions);
philpem@5 2916 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
philpem@5 2917 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
philpem@5 2918 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 2919 images.insert(points3d);
philpem@5 2920 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 2921 } else error("Generate 3D cylinder : Invalid argument '%s' "
philpem@5 2922 "(should be 'radius[,height[,subdivisions]]').",argument_text);
philpem@5 2923 ++position; continue;
philpem@5 2924 }
philpem@5 2925
philpem@5 2926 // Generate 3D torus.
philpem@5 2927 if (!cimg::strcmp("-torus3d",item)) {
philpem@5 2928 float radius1 = 100, radius2 = 30; char end = 0; unsigned int subdivisions1 = 24, subdivisions2 = 12;
philpem@5 2929 if (std::sscanf(argument,"%f%*c%f%c",&radius1,&radius2,&end)==2 ||
philpem@5 2930 std::sscanf(argument,"%f%*c%f%*c%u%*c%u%c",&radius1,&radius2,&subdivisions1,&subdivisions2,&end)==4) {
philpem@5 2931 print("Generate 3D torus with radii %g and %g, and subdivisions %u and %u.",radius1,radius2,subdivisions1,subdivisions2);
philpem@5 2932 CImgList<unsigned int> primitives3d;
philpem@5 2933 CImg<float> points3d = CImg<T>::torus3d(primitives3d,radius1,radius2,subdivisions1,subdivisions2);
philpem@5 2934 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
philpem@5 2935 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
philpem@5 2936 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 2937 images.insert(points3d);
philpem@5 2938 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 2939 } else error("Generate 3D torus : Invalid argument '%s' "
philpem@5 2940 "(should be 'radius1,radius2[,subdivisions1,subdivisions2]').",argument_text);
philpem@5 2941 ++position; continue;
philpem@5 2942 }
philpem@5 2943
philpem@5 2944 // Generate 3D plane.
philpem@5 2945 if (!cimg::strcmp("-plane3d",item)) {
philpem@5 2946 float sizex = 100, sizey = 30; char end = 0; unsigned int subdivisionsx = 24, subdivisionsy = 12;
philpem@5 2947 if (std::sscanf(argument,"%f%*c%f%c",&sizex,&sizey,&end)==2 ||
philpem@5 2948 std::sscanf(argument,"%f%*c%f%*c%u%*c%u%c",&sizex,&sizey,&subdivisionsx,&subdivisionsy,&end)==4) {
philpem@5 2949 print("Generate 3D plane with dimensions %g and %g, and subdivisions %u and %u.",sizex,sizey,subdivisionsx,subdivisionsy);
philpem@5 2950 CImgList<unsigned int> primitives3d;
philpem@5 2951 CImg<float> points3d = CImg<T>::plane3d(primitives3d,sizex,sizey,subdivisionsx,subdivisionsy);
philpem@5 2952 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
philpem@5 2953 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
philpem@5 2954 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 2955 images.insert(points3d);
philpem@5 2956 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 2957 } else error("Generate 3D plane : Invalid argument '%s' "
philpem@5 2958 "(should be 'sizex,sizey[,subdivisionsx,subdivisionsy]').",argument_text);
philpem@5 2959 ++position; continue;
philpem@5 2960 }
philpem@5 2961
philpem@5 2962 // Generate 3D sphere.
philpem@5 2963 if (!cimg::strcmp("-sphere3d",item)) {
philpem@5 2964 float radius = 100; char end = 0; unsigned int subdivisions = 3;
philpem@5 2965 if (std::sscanf(argument,"%f%c",&radius,&end)==1 ||
philpem@5 2966 std::sscanf(argument,"%f%*c%u%c",&radius,&subdivisions,&end)==2) {
philpem@5 2967 print("Generate 3D sphere with radius %g and %u subdivisions.",radius,subdivisions);
philpem@5 2968 CImgList<unsigned int> primitives3d;
philpem@5 2969 CImg<float> points3d = CImg<T>::sphere3d(primitives3d,radius,subdivisions);
philpem@5 2970 CImgList<unsigned char> colors3d(primitives3d.size,1,3,1,1,200);
philpem@5 2971 CImg<float> opacities3d(1,primitives3d.size,1,1,1);
philpem@5 2972 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 2973 images.insert(points3d);
philpem@5 2974 filenames.insert(CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 2975 } else error("Generate 3D sphere : Invalid argument '%s' "
philpem@5 2976 "(should be 'radius[,subdivisions]').",argument_text);
philpem@5 2977 ++position; continue;
philpem@5 2978 }
philpem@5 2979
philpem@5 2980 // Build 3D elevation.
philpem@5 2981 if (!cimg::strcmp("-elevation3d",item0)) {
philpem@5 2982 float zfact = 0.2f; char end = 0, sep = 0; int ind0 = no_ind;
philpem@5 2983 if (std::sscanf(argument,"%f%c",&zfact,&end)==1 ||
philpem@5 2984 (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']')) {
philpem@5 2985 CImg<typename CImg<T>::Tfloat> elev;
philpem@5 2986 if (ind0!=no_ind) {
philpem@5 2987 gmic_check_indice(ind0,"Build 3D elevation of image%s");
philpem@5 2988 print("Build 3D elevation of image%s with elevation map [%d].",gmic_inds,ind0);
philpem@5 2989 if (images[ind0].dimv()==1) elev = images[ind0];
philpem@5 2990 else elev = images[ind0].get_pointwise_norm();
philpem@5 2991 } else print("Build 3D elevation of image%s with z-factor %g.",gmic_inds,zfact);
philpem@5 2992 cimg_foroff(indices,l) {
philpem@5 2993 CImg<T>& img = images[indices[l]];
philpem@5 2994 CImgList<unsigned int> primitives3d;
philpem@5 2995 CImgList<unsigned char> colors3d;
philpem@5 2996 CImg<float> opacities3d, points3d;
philpem@5 2997 if (elev) points3d = img.get_elevation3d(primitives3d,colors3d,elev);
philpem@5 2998 else {
philpem@5 2999 if (img.dimv()==1) (elev = img)*=zfact; else (elev = img.get_pointwise_norm())*=zfact;
philpem@5 3000 points3d = img.get_elevation3d(primitives3d,colors3d,elev);
philpem@5 3001 elev.assign();
philpem@5 3002 }
philpem@5 3003 opacities3d.assign(1,primitives3d.size,1,1,1);
philpem@5 3004 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 3005 gmic_apply(img,replace(points3d));
philpem@5 3006 }
philpem@5 3007 } else error("Build 3D elevation : invalid argument '%s' "
philpem@5 3008 "(should be 'z-factor' or '[indice]').",argument_text);
philpem@5 3009 ++position; continue;
philpem@5 3010 }
philpem@5 3011
philpem@5 3012 // Build 3D isovalue.
philpem@5 3013 if (!cimg::strcmp("-isovalue3d",item0)) {
philpem@5 3014 float value = 0; char end = 0;
philpem@5 3015 if (std::sscanf(argument,"%f%c",&value,&end)==1) {
philpem@5 3016 print("Build 3D isovalue %g of image%s.",value,gmic_inds);
philpem@5 3017 cimg_foroff(indices,l) {
philpem@5 3018 const unsigned int ind = indices[l];
philpem@5 3019 CImg<T>& img = images[ind];
philpem@5 3020 CImg<float> points3d;
philpem@5 3021 CImgList<unsigned int> primitives3d;
philpem@5 3022 CImgList<unsigned char> colors3d;
philpem@5 3023 CImg<float> opacities3d;
philpem@5 3024 CImg<unsigned char> palette;
philpem@5 3025 palette.assign(3,img.dim,1,1,220).noise(35,1);
philpem@5 3026 if (img.dim==1) palette(0) = palette(1) = palette(2) = 255;
philpem@5 3027 else {
philpem@5 3028 palette(0,0) = 255; palette(1,0) = 30; palette(2,0) = 30;
philpem@5 3029 palette(0,1) = 30; palette(1,1) = 255; palette(2,1) = 30;
philpem@5 3030 if (img.dim>=3) palette(0,2) = 30; palette(1,2) = 30; palette(2,2) = 255;
philpem@5 3031 }
philpem@5 3032 cimg_forV(img,k) {
philpem@5 3033 CImgList<unsigned int> prims;
philpem@5 3034 const CImg<float> pts = img.get_shared_channel(k).get_isovalue3d(prims,value);
philpem@5 3035 if (pts) {
philpem@5 3036 points3d.append_object3d(primitives3d,pts,prims);
philpem@5 3037 colors3d.insert(prims.size,
philpem@5 3038 CImg<unsigned char>::vector(palette(0,k),palette(1,k),palette(2,k)));
philpem@5 3039 }
philpem@5 3040 }
philpem@5 3041 opacities3d.assign(1,primitives3d.size,1,1,1);
philpem@5 3042 if (!points3d)
philpem@5 3043 warning("Build 3D isovalue of image [%u] : Isovalue %g not found.",ind,value);
philpem@5 3044 else points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 3045 gmic_apply(img,replace(points3d));
philpem@5 3046 }
philpem@5 3047 } else error("Build 3D isovalue of image%s : Invalid argument '%s' "
philpem@5 3048 "(should be 'isovalue').",gmic_inds,argument_text);
philpem@5 3049 ++position; continue;
philpem@5 3050 }
philpem@5 3051
philpem@5 3052 // Center a 3D object.
philpem@5 3053 if (!cimg::strcmp("-center3d",item0) || !cimg::strcmp("-c3d",item0)) {
philpem@5 3054 print("Center 3D object%s.",gmic_inds);
philpem@5 3055 cimg_foroff(indices,l) {
philpem@5 3056 const unsigned int ind = indices[l];
philpem@5 3057 if (!images[ind].is_CImg3d())
philpem@5 3058 error("Center 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
philpem@5 3059 gmic_apply(images[ind],centerCImg3d());
philpem@5 3060 }
philpem@5 3061 continue;
philpem@5 3062 }
philpem@5 3063
philpem@5 3064 // Normalize a 3D object.
philpem@5 3065 if (!cimg::strcmp("-normalize3d",item0) || !cimg::strcmp("-n3d",item0)) {
philpem@5 3066 print("Normalize 3D object%s.",gmic_inds);
philpem@5 3067 cimg_foroff(indices,l) {
philpem@5 3068 const unsigned int ind = indices[l];
philpem@5 3069 if (!images[ind].is_CImg3d())
philpem@5 3070 error("Normalize 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
philpem@5 3071 gmic_apply(images[ind],normalizeCImg3d());
philpem@5 3072 }
philpem@5 3073 continue;
philpem@5 3074 }
philpem@5 3075
philpem@5 3076 // Rotate a 3D object.
philpem@5 3077 if (!cimg::strcmp("-rotate3d",item0) || !cimg::strcmp("-rot3d",item0)) {
philpem@5 3078 float u = 0, v = 0, w = 1, angle = 0; char end = 0;
philpem@5 3079 if (std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&u,&v,&w,&angle,&end)==4) {
philpem@5 3080 print("Rotate 3D object%s around axis (%g,%g,%g) with angle %g.",gmic_inds,u,v,w,angle);
philpem@5 3081 const CImg<float> rot = CImg<float>::rotation_matrix(u,v,w,(float)(angle*cimg::valuePI/180));
philpem@5 3082 cimg_foroff(indices,l) {
philpem@5 3083 const unsigned int ind = indices[l];
philpem@5 3084 if (!images[ind].is_CImg3d())
philpem@5 3085 error("Rotate 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
philpem@5 3086 gmic_apply(images[ind],rotateCImg3d(rot));
philpem@5 3087 }
philpem@5 3088 } else error("Rotate 3D object%s : Invalid argument '%s' "
philpem@5 3089 "(should be 'u,v,w,angle').",gmic_inds,argument_text);
philpem@5 3090 ++position; continue;
philpem@5 3091 }
philpem@5 3092
philpem@5 3093 // Add 3D objects together or translate a 3D object.
philpem@5 3094 if (!cimg::strcmp("-add3d",item0) || !cimg::strcmp("-+3d",item0)) {
philpem@5 3095 float tx = 0, ty = 0, tz = 0; int ind0 = no_ind; char sep = 0, end = 0;
philpem@5 3096 if (std::sscanf(argument,"%f%c",&tx,&end)==1 ||
philpem@5 3097 std::sscanf(argument,"%f%*c%f%c",&tx,&ty,&end)==2 ||
philpem@5 3098 std::sscanf(argument,"%f%*c%f%*c%f%c",&tx,&ty,&tz,&end)==3) {
philpem@5 3099 print("Translate 3D object%s with vector (%g,%g,%g).",gmic_inds,tx,ty,tz);
philpem@5 3100 cimg_foroff(indices,l) {
philpem@5 3101 const unsigned int ind = indices[l];
philpem@5 3102 if (!images[ind].is_CImg3d())
philpem@5 3103 error("Translate 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
philpem@5 3104 gmic_apply(images[ind],translateCImg3d(tx,ty,tz));
philpem@5 3105 }
philpem@5 3106 ++position;
philpem@5 3107 } else if (std::sscanf(argument,"[%d%c%c",&ind0,&sep,&end)==2 && sep==']') {
philpem@5 3108 gmic_check_indice(ind0,"Merge object with 3D object%s.");
philpem@5 3109 const CImg<T> img0 = images[ind0];
philpem@5 3110 if (!img0.is_CImg3d()) error("Merge object [%d] with 3D object%s : Image [%d] is not a 3D object.",ind0,gmic_inds,ind0);
philpem@5 3111 print("Merge object [%d] with 3D object%s.",ind0,gmic_inds);
philpem@5 3112 cimg_foroff(indices,l) {
philpem@5 3113 const unsigned int ind = indices[l];
philpem@5 3114 const CImg<T> &img = images[ind];
philpem@5 3115 if (!img.is_CImg3d())
philpem@5 3116 error("Merge object [%d] with 3D object%s : Image [%d] is not a 3D object.",ind0,gmic_inds,ind);
philpem@5 3117 gmic_apply(images[ind],appendCImg3d(img0));
philpem@5 3118 }
philpem@5 3119 ++position;
philpem@5 3120 } else {
philpem@5 3121 print("Merge 3D object%s together.",gmic_inds);
philpem@5 3122 if (indices) {
philpem@5 3123 const unsigned int ind0 = indices[0];
philpem@5 3124 if (!images[ind0].is_CImg3d())
philpem@5 3125 error("Merge 3D object%s together : Image [%d] is not a 3D object.",gmic_inds,ind0);
philpem@5 3126 for (unsigned int siz = indices.size(), off = 0, l = 1; l<siz; ++l) {
philpem@5 3127 const unsigned int ind = indices[l] - off;
philpem@5 3128 if (!images[ind].is_CImg3d())
philpem@5 3129 error("Merge 3D object%s together : Image [%d] is not a 3D object.",gmic_inds,ind);
philpem@5 3130 images[ind0].appendCImg3d(images[ind]);
philpem@5 3131 images.remove(ind); filenames.remove(ind);
philpem@5 3132 ++off;
philpem@5 3133 }
philpem@5 3134 }
philpem@5 3135 }
philpem@5 3136 continue;
philpem@5 3137 }
philpem@5 3138
philpem@5 3139 // Translate 3D object by the opposite vector.
philpem@5 3140 if (!cimg::strcmp("-sub3d",item0) || !cimg::strcmp("--3d",item0)) {
philpem@5 3141 float tx = 0, ty = 0, tz = 0; char end = 0;
philpem@5 3142 if (std::sscanf(argument,"%f%c",&tx,&end)==1 ||
philpem@5 3143 std::sscanf(argument,"%f%*c%f%c",&tx,&ty,&end)==2 ||
philpem@5 3144 std::sscanf(argument,"%f%*c%f%*c%f%c",&tx,&ty,&tz,&end)==3) {
philpem@5 3145 print("Translate 3D object%s with vector -(%g,%g,%g).",gmic_inds,tx,ty,tz);
philpem@5 3146 cimg_foroff(indices,l) {
philpem@5 3147 CImg<T>& img = images[indices[l]];
philpem@5 3148 CImgList<unsigned int> primitives3d;
philpem@5 3149 CImgList<unsigned char> colors3d;
philpem@5 3150 CImg<float> opacities3d;
philpem@5 3151 CImg<T> points3d;
philpem@5 3152 if (get_version) points3d.assign(img); else img.transfer_to(points3d);
philpem@5 3153 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
philpem@5 3154 points3d.get_shared_line(0)-=tx;
philpem@5 3155 points3d.get_shared_line(1)-=ty;
philpem@5 3156 points3d.get_shared_line(2)-=tz;
philpem@5 3157 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 3158 if (get_version) {
philpem@5 3159 images.insert(1); points3d.transfer_to(images.last());
philpem@5 3160 filenames.insert(filenames[indices[l]]);
philpem@5 3161 } else points3d.transfer_to(images[indices[l]]);
philpem@5 3162 }
philpem@5 3163 } else error("Translate 3D object%s : Invalid argument '%s' "
philpem@5 3164 "(should be 'tx,ty,tz').",gmic_inds,argument_text);
philpem@5 3165 ++position; continue;
philpem@5 3166 }
philpem@5 3167
philpem@5 3168 // Scale a 3D object.
philpem@5 3169 bool divide = false;
philpem@5 3170 if (!cimg::strcmp("-mul3d",item0) || !cimg::strcmp("-*3d",item0) ||
philpem@5 3171 ((divide=true)==true && (!cimg::strcmp("-div3d",item0) || !cimg::strcmp("-/3d",item0)))) {
philpem@5 3172 float sx = 0, sy = 1, sz = 1; char end = 0;
philpem@5 3173 if ((std::sscanf(argument,"%f%c",&sx,&end)==1 && (sy = sz = sx),1) ||
philpem@5 3174 std::sscanf(argument,"%f%*c%f%c",&sx,&sy,&end)==2 ||
philpem@5 3175 std::sscanf(argument,"%f%*c%f%*c%f%c",&sx,&sy,&sz,&end)==3) {
philpem@5 3176 if (divide) print("Scale 3D object%s with factors (1/%g,1/%g,1/%g).",gmic_inds,sx,sy,sz);
philpem@5 3177 else print("Scale 3D object%s with factors (%g,%g,%g).",gmic_inds,sx,sy,sz);
philpem@5 3178 cimg_foroff(indices,l) {
philpem@5 3179 CImg<T>& img = images[indices[l]];
philpem@5 3180 CImgList<unsigned int> primitives3d;
philpem@5 3181 CImgList<unsigned char> colors3d;
philpem@5 3182 CImg<float> opacities3d;
philpem@5 3183 CImg<T> points3d;
philpem@5 3184 if (get_version) points3d.assign(img); else img.transfer_to(points3d);
philpem@5 3185 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
philpem@5 3186 if (divide) {
philpem@5 3187 points3d.get_shared_line(0)/=sx;
philpem@5 3188 points3d.get_shared_line(1)/=sy;
philpem@5 3189 points3d.get_shared_line(2)/=sz;
philpem@5 3190 } else {
philpem@5 3191 points3d.get_shared_line(0)*=sx;
philpem@5 3192 points3d.get_shared_line(1)*=sy;
philpem@5 3193 points3d.get_shared_line(2)*=sz;
philpem@5 3194 }
philpem@5 3195 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 3196 if (get_version) {
philpem@5 3197 images.insert(1); points3d.transfer_to(images.last());
philpem@5 3198 filenames.insert(filenames[indices[l]]);
philpem@5 3199 } else points3d.transfer_to(images[indices[l]]);
philpem@5 3200 }
philpem@5 3201 } else error("Scale 3D object%s : Invalid argument '%s' "
philpem@5 3202 "(should be 'fact' or 'factx,facty[,factz]').",gmic_inds,argument_text);
philpem@5 3203 ++position; continue;
philpem@5 3204 }
philpem@5 3205
philpem@5 3206 // Set color of 3D object(s).
philpem@5 3207 if (!cimg::strcmp("-color3d",item0) || !cimg::strcmp("-col3d",item0)) {
philpem@5 3208 float R = 200, G = 200, B = 200, opacity = -1; char end = 0;
philpem@5 3209 if (std::sscanf(argument,"%f%*c%f%*c%f%c",&R,&G,&B,&end)==3 ||
philpem@5 3210 std::sscanf(argument,"%f%*c%f%*c%f%*c%f%c",&R,&G,&B,&opacity,&end)==4) {
philpem@5 3211 const bool set_opacity = (opacity>=0);
philpem@5 3212 R = (float)cimg::round(R,1); G = (float)cimg::round(G,1); B = (float)cimg::round(B,1);
philpem@5 3213 if (R<0) R = 0; if (R>255) R = 255;
philpem@5 3214 if (G<0) G = 0; if (G>255) G = 255;
philpem@5 3215 if (B<0) B = 0; if (B>255) B = 255;
philpem@5 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);
philpem@5 3217 else print("Set color of 3D object%s to (%g,%g,%g).",gmic_inds,R,G,B);
philpem@5 3218 cimg_foroff(indices,l) {
philpem@5 3219 const unsigned int ind = indices[l];
philpem@5 3220 if (!images[ind].is_CImg3d())
philpem@5 3221 error("Set color of 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
philpem@5 3222 gmic_apply(images[ind],coloropacityCImg3d(R,G,B,opacity,true,set_opacity));
philpem@5 3223 }
philpem@5 3224 } else error("Set color of 3D object%s : Invalid argument '%s' "
philpem@5 3225 "(should be 'R,G,B[,opacity]').",gmic_inds,argument_text);
philpem@5 3226 ++position; continue;
philpem@5 3227 }
philpem@5 3228
philpem@5 3229 // Set opacity of 3D object(s).
philpem@5 3230 if (!cimg::strcmp("-opacity3d",item0) || !cimg::strcmp("-opac3d",item0)) {
philpem@5 3231 float opacity = 1; char end = 0;
philpem@5 3232 if (std::sscanf(argument,"%f%c",&opacity,&end)==1) {
philpem@5 3233 print("Set opacity of 3D object%s to %g.",gmic_inds,opacity);
philpem@5 3234 cimg_foroff(indices,l) {
philpem@5 3235 const unsigned int ind = indices[l];
philpem@5 3236 if (!images[ind].is_CImg3d())
philpem@5 3237 error("Set opacity of 3D object%s : Image [%d] is not a 3D object.",gmic_inds,ind);
philpem@5 3238 gmic_apply(images[ind],coloropacityCImg3d(0,0,0,opacity,false,true));
philpem@5 3239 }
philpem@5 3240 } else error("Set opacity of 3D object%s : Invalid argument '%s' "
philpem@5 3241 "(should be 'opacity').",gmic_inds,argument_text);
philpem@5 3242 ++position; continue;
philpem@5 3243 }
philpem@5 3244
philpem@5 3245 // Invert 3D orientation.
philpem@5 3246 if (!cimg::strcmp("-invert3d",item0) || !cimg::strcmp("-i3d",item0)) {
philpem@5 3247 print("Invert orientation of 3D object%s.",gmic_inds);
philpem@5 3248 cimg_foroff(indices,l) {
philpem@5 3249 CImg<T> &img = images[indices[l]];
philpem@5 3250 CImgList<unsigned int> primitives3d;
philpem@5 3251 CImgList<unsigned char> colors3d;
philpem@5 3252 CImg<float> opacities3d;
philpem@5 3253 CImg<T> points3d;
philpem@5 3254 if (get_version) points3d.assign(img); else img.transfer_to(points3d);
philpem@5 3255 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
philpem@5 3256 if (primitives3d) primitives3d.invert_object3d();
philpem@5 3257 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 3258 if (get_version) {
philpem@5 3259 images.insert(1); points3d.transfer_to(images.last());
philpem@5 3260 filenames.insert(filenames[indices[l]]);
philpem@5 3261 } else points3d.transfer_to(images[indices[l]]);
philpem@5 3262 }
philpem@5 3263 continue;
philpem@5 3264 }
philpem@5 3265
philpem@5 3266 // Split 3D object(s) into 6 vector images {header,N,vertices,primitives,colors,opacities}
philpem@5 3267 if (!cimg::strcmp("-split3d",item0) || !cimg::strcmp("-s3d",item0)) {
philpem@5 3268 print("Split 3D object%s into its different characteristics.",gmic_inds);
philpem@5 3269 unsigned int off = 0;
philpem@5 3270 cimg_foroff(indices,l) {
philpem@5 3271 const unsigned int ind = indices[l] + off;
philpem@5 3272 CImg<T> &img = images[ind];
philpem@5 3273 const CImg<char> filename = filenames[ind];
philpem@5 3274 CImgList<unsigned int> primitives3d;
philpem@5 3275 CImgList<unsigned char> colors3d;
philpem@5 3276 CImg<float> opacities3d;
philpem@5 3277 CImg<T> points3d;
philpem@5 3278 if (get_version) points3d.assign(img); else img.transfer_to(points3d);
philpem@5 3279 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d);
philpem@5 3280 CImgList<T> split;
philpem@5 3281 split.insert(CImg<T>("CImg3d",1,6,1,1,false)+=0.5f);
philpem@5 3282 split.insert(CImg<T>::vector((T)points3d.dimx(),(T)primitives3d.size));
philpem@5 3283 split.insert(1); points3d.resize(-100,3,1,1,0).transpose().unroll('y').transfer_to(split.last());
philpem@5 3284 points3d.assign();
philpem@5 3285 CImgList<T> _prims;
philpem@5 3286 cimglist_for(primitives3d,p)
philpem@5 3287 _prims.insert(CImg<T>::vector((T)primitives3d[p].size())).insert(primitives3d[p]).last().unroll('y');
philpem@5 3288 primitives3d.assign();
philpem@5 3289 split.insert(_prims.get_append('y')); _prims.assign();
philpem@5 3290 split.insert(colors3d.get_append('x').transpose().unroll('y')); colors3d.assign();
philpem@5 3291 split.insert(1); opacities3d.transfer_to(split.last());
philpem@5 3292 if (get_version) {
philpem@5 3293 images.insert(split);
philpem@5 3294 filenames.insert(split.size,filename);
philpem@5 3295 } else {
philpem@5 3296 images.remove(ind); images.insert(split,ind);
philpem@5 3297 filenames.remove(ind); filenames.insert(split.size,filename,ind);
philpem@5 3298 off+=split.size-1;
philpem@5 3299 }
philpem@5 3300 }
philpem@5 3301 continue;
philpem@5 3302 }
philpem@5 3303
philpem@5 3304 // Set 3D light position.
philpem@5 3305 if (!cimg::strcmp("-light3d",item) || !cimg::strcmp("-l3d",item)) {
philpem@5 3306 float lx = 0, ly = 0, lz = -5000; char end = 0;
philpem@5 3307 if (std::sscanf(argument,"%f%*c%f%*c%f%c",&lx,&ly,&lz,&end)==3) {
philpem@5 3308 print("Set 3D light position at (%g,%g,%g).",lx,ly,lz);
philpem@5 3309 light3d_x = lx;
philpem@5 3310 light3d_y = ly;
philpem@5 3311 light3d_z = lz;
philpem@5 3312 } else error("Set 3D light position : Invalid argument '%s' "
philpem@5 3313 "(should be 'posx,posy,posz').",argument_text);
philpem@5 3314 ++position; continue;
philpem@5 3315 }
philpem@5 3316
philpem@5 3317 // Set 3D focale.
philpem@5 3318 if (!cimg::strcmp("-focale3d",item) || !cimg::strcmp("-f3d",item)) {
philpem@5 3319 float focale = 500; char end = 0;
philpem@5 3320 if (std::sscanf(argument,"%f%c",&focale,&end)==1) {
philpem@5 3321 focale3d = focale;
philpem@5 3322 print("Set 3D focale to %g.",focale);
philpem@5 3323 } else error("Set 3D focale : Invalid argument '%s' "
philpem@5 3324 "(should be 'value').");
philpem@5 3325 ++position; continue;
philpem@5 3326 }
philpem@5 3327
philpem@5 3328 // Set 3D specular light parameters.
philpem@5 3329 if (!cimg::strcmp("-specl3d",item) || !cimg::strcmp("-sl3d",item)) {
philpem@5 3330 float value = 0; char end = 0;
philpem@5 3331 if (std::sscanf(argument,"%f%c",&value,&end)==1) {
philpem@5 3332 specular_light3d = value;
philpem@5 3333 print("Set amount of 3D specular light to %g.",specular_light3d);
philpem@5 3334 }
philpem@5 3335 else error("Set amount of 3D specular light : invalid argument '%s'"
philpem@5 3336 "(should be 'value').",
philpem@5 3337 argument_text);
philpem@5 3338 ++position; continue;
philpem@5 3339 }
philpem@5 3340
philpem@5 3341 if (!cimg::strcmp("-specs3d",item) || !cimg::strcmp("-ss3d",item)) {
philpem@5 3342 float value = 0; char end = 0;
philpem@5 3343 if (std::sscanf(argument,"%f%c",&value,&end)==1) {
philpem@5 3344 specular_shine3d = value;
philpem@5 3345 print("Set shininess of 3D specular light to %g.",specular_shine3d);
philpem@5 3346 }
philpem@5 3347 else error("Set shininess of 3D specular light : invalid argument '%s'"
philpem@5 3348 "(should be 'value').",
philpem@5 3349 argument_text);
philpem@5 3350 ++position; continue;
philpem@5 3351 }
philpem@5 3352
philpem@5 3353 // Switch double-sided mode for 3D rendering.
philpem@5 3354 if (!cimg::strcmp("-orient3d",item) || !cimg::strcmp("-o3d",item)) {
philpem@5 3355 is_oriented3d = !is_oriented3d;
philpem@5 3356 continue;
philpem@5 3357 }
philpem@5 3358
philpem@5 3359 // Set 3D rendering mode.
philpem@5 3360 if (!cimg::strcmp("-render3d",item) || !cimg::strcmp("-r3d",item)) {
philpem@5 3361 unsigned int value = 0; char end = 0;
philpem@5 3362 if (std::sscanf(argument,"%u%c",&value,&end)==1) {
philpem@5 3363 render3d = value;
philpem@5 3364 print("Set static 3D render mode to %s.",
philpem@5 3365 render3d==-1?"bounding-box":
philpem@5 3366 render3d==0?"pointwise":render3d==1?"linear":render3d==2?"flat":
philpem@5 3367 render3d==3?"flat-shaded":render3d==4?"Gouraud-shaded":
philpem@5 3368 render3d==5?"Phong-shaded":"none");
philpem@5 3369 }
philpem@5 3370 else error("Set static 3D render mode : invalid argument '%s'"
philpem@5 3371 "(should be '{0=pointwise, 1=linear, 2=flat, 3=flat shaded, 4=Gouraud shaded, 5=Phong-shaded}').",
philpem@5 3372 argument_text);
philpem@5 3373 ++position; continue;
philpem@5 3374 }
philpem@5 3375
philpem@5 3376 if (!cimg::strcmp("-renderd3d",item) || !cimg::strcmp("-rd3d",item)) {
philpem@5 3377 unsigned int value = 0; char end = 0;
philpem@5 3378 if (std::sscanf(argument,"%u%c",&value,&end)==1) {
philpem@5 3379 renderd3d = value;
philpem@5 3380 print("Set dynamic 3D render mode to %s.",
philpem@5 3381 renderd3d==-1?"bounding-box":
philpem@5 3382 renderd3d==0?"pointwise":renderd3d==1?"linear":renderd3d==2?"flat":
philpem@5 3383 renderd3d==3?"flat-shaded":renderd3d==4?"Gouraud-shaded":
philpem@5 3384 renderd3d==5?"Phong-shaded":"none");
philpem@5 3385 }
philpem@5 3386 else error("Set dynamic 3D render mode : invalid argument '%s'"
philpem@5 3387 "(should be '{0=pointwise, 1=linear, 2=flat, 3=flat shaded, 4=Gouraud shaded, 5=Phong-shaded}').",
philpem@5 3388 argument_text);
philpem@5 3389 ++position; continue;
philpem@5 3390 }
philpem@5 3391
philpem@5 3392 // Set 3D background color.
philpem@5 3393 if (!cimg::strcmp("-background3d",item) || !cimg::strcmp("-b3d",item)) {
philpem@5 3394 int R = 0, G = 0, B = 0; char end = 0;
philpem@5 3395 const int nb = std::sscanf(argument,"%d%*c%d%*c%d%c",&R,&G,&B,&end);
philpem@5 3396 switch (nb) {
philpem@5 3397 case 1 : background3d[0] = background3d[1] = background3d[2] = R; break;
philpem@5 3398 case 2 : background3d[0] = R; background3d[1] = background3d[2] = G; break;
philpem@5 3399 case 3 : background3d[0] = R; background3d[1] = G; background3d[2] = B; break;
philpem@5 3400 default: error("Set 3D background color : Invalid argument '%s'.",argument_text);
philpem@5 3401 }
philpem@5 3402 print("Set 3D background color to (%d,%d,%d).",
philpem@5 3403 (int)background3d[0],(int)background3d[1],(int)background3d[2]);
philpem@5 3404 ++position; continue;
philpem@5 3405 }
philpem@5 3406
philpem@5 3407 //----------------
philpem@5 3408 // Other commands.
philpem@5 3409 //----------------
philpem@5 3410
philpem@5 3411 // No operations : do nothing
philpem@5 3412 if (!cimg::strcmp("-nop",item)) {
philpem@5 3413 continue;
philpem@5 3414 }
philpem@5 3415
philpem@5 3416 // Skip next argument;
philpem@5 3417 if (!cimg::strcmp("-skip",item)) {
philpem@5 3418 ++position;
philpem@5 3419 continue;
philpem@5 3420 }
philpem@5 3421
philpem@5 3422 // Echo.
philpem@5 3423 if (!cimg::strcmp("-echo",item) || !cimg::strcmp("-e",item)) {
philpem@5 3424 const int l = cimg::strlen(argument);
philpem@5 3425 if (l>=2 && argument[0]=='"' && argument[l-1]=='"') {
philpem@5 3426 if (l==2) print(""); else {
philpem@5 3427 CImg<char> nargument(argument+1,l-1,1,1,1,false);
philpem@5 3428 nargument(l-2)=0;
philpem@5 3429 print("%s",nargument.ptr());
philpem@5 3430 }
philpem@5 3431 } else print("%s",argument);
philpem@5 3432 ++position; continue;
philpem@5 3433 }
philpem@5 3434
philpem@5 3435 // Print.
philpem@5 3436 if (!cimg::strcmp("-print",item0) || !cimg::strcmp("-p",item0)) {
philpem@5 3437 if (images.size) {
philpem@5 3438 print("Print image%s.\n\n",gmic_inds);
philpem@5 3439 char title[4096];
philpem@5 3440 if (verbosity_level>=0) cimg_foroff(indices,l) {
philpem@5 3441 const unsigned int ind = indices[l];
philpem@5 3442 std::sprintf(title,"image [%u] = '%s'",ind,filenames[ind].ptr());
philpem@5 3443 images[ind].print(title);
philpem@5 3444 }
philpem@5 3445 is_released = true;
philpem@5 3446 } else print("Print image[].");
philpem@5 3447 continue;
philpem@5 3448 }
philpem@5 3449
philpem@5 3450 // Quit.
philpem@5 3451 if (!cimg::strcmp("-quit",item) || !cimg::strcmp("-q",item)) {
philpem@5 3452 print("Quit.");
philpem@5 3453 is_released = true;
philpem@5 3454 dowhile.assign();
philpem@5 3455 repeatdone.assign();
philpem@5 3456 position = command_line.size;
philpem@5 3457 continue;
philpem@5 3458 }
philpem@5 3459
philpem@5 3460 // Do...while.
philpem@5 3461 if (!cimg::strcmp("-do",item)) {
philpem@5 3462 dowhile.insert(CImg<int>::vector((int)position));
philpem@5 3463 continue;
philpem@5 3464 }
philpem@5 3465
philpem@5 3466 if (!cimg::strcmp("-while",item)) {
philpem@5 3467 double cond = 0; char end = 0;
philpem@5 3468 if (std::sscanf(argument,"%lf%c",&cond,&end)!=1) cond = 0;
philpem@5 3469 if (!dowhile) error("Directive '-while' is not associated with a '-do' command.");
philpem@5 3470 if (cond<=0) dowhile.remove();
philpem@5 3471 else { position = (unsigned int)dowhile.last()(0); continue; }
philpem@5 3472 ++position; continue;
philpem@5 3473 }
philpem@5 3474
philpem@5 3475 // If..else..endif
philpem@5 3476 if (!cimg::strcmp("-if",item)) {
philpem@5 3477 double cond = 0; char end = 0;
philpem@5 3478 if (std::sscanf(argument,"%lf%c",&cond,&end)!=1) cond = 0;
philpem@5 3479 if (cond<=0) {
philpem@5 3480 for (int nbifs = 1; nbifs && position<command_line.size; ++position) {
philpem@5 3481 const char *it = command_line[position].ptr();
philpem@5 3482 if (!cimg::strcmp("-if",it)) ++nbifs;
philpem@5 3483 if (!cimg::strcmp("-endif",it)) --nbifs;
philpem@5 3484 if (!cimg::strcmp("-else",it) && nbifs==1) --nbifs;
philpem@5 3485 }
philpem@5 3486 continue;
philpem@5 3487 }
philpem@5 3488 ++position; continue;
philpem@5 3489 }
philpem@5 3490 if (!cimg::strcmp("-else",item)) {
philpem@5 3491 for (int nbifs = 1; nbifs && position<command_line.size; ++position) {
philpem@5 3492 if (!cimg::strcmp("-if",command_line[position].ptr())) ++nbifs;
philpem@5 3493 if (!cimg::strcmp("-endif",command_line[position].ptr())) --nbifs;
philpem@5 3494 }
philpem@5 3495 continue;
philpem@5 3496 }
philpem@5 3497 if (!cimg::strcmp("-endif",item)) continue;
philpem@5 3498
philpem@5 3499 // Repeat...done
philpem@5 3500 if (!cimg::strcmp("-repeat",item)) {
philpem@5 3501 float fnb = 0; char end = 0;
philpem@5 3502 if (std::sscanf(argument,"%f%c",&fnb,&end)==1) {
philpem@5 3503 const int nb = (int)fnb;
philpem@5 3504 if (nb>0) repeatdone.insert(CImg<int>::vector((int)position+1,nb));
philpem@5 3505 else {
philpem@5 3506 int nbrepeats = 0;
philpem@5 3507 for (nbrepeats = 1; nbrepeats && position<command_line.size; ++position) {
philpem@5 3508 const char *it = command_line[position].ptr();
philpem@5 3509 if (!cimg::strcmp("-repeat",it)) ++nbrepeats;
philpem@5 3510 if (!cimg::strcmp("-done",it)) --nbrepeats;
philpem@5 3511 }
philpem@5 3512 if (nbrepeats && position>=command_line.size)
philpem@5 3513 error("Directive '-done' is missing after a '-repeat' command.");
philpem@5 3514 continue;
philpem@5 3515 }
philpem@5 3516 } else error("Repeat operation : Invalid argument '%s' "
philpem@5 3517 "(should be a number).",argument_text);
philpem@5 3518 ++position; continue;
philpem@5 3519 }
philpem@5 3520
philpem@5 3521 if (!cimg::strcmp("-done",item)) {
philpem@5 3522 if (!repeatdone) error("Directive '-done' is not associated with a '-repeat' command.");
philpem@5 3523 if (--repeatdone.last()(1))
philpem@5 3524 position = (unsigned int)repeatdone.last()(0);
philpem@5 3525 else repeatdone.remove();
philpem@5 3526 continue;
philpem@5 3527 }
philpem@5 3528
philpem@5 3529 // Check argument type
philpem@5 3530 if (!cimg::strcmp("-int",item)) {
philpem@5 3531 char it[4096], end = 0, sep = 0; int value = 0;
philpem@5 3532 if (*argument) for (const char *nargument = argument; *nargument; ) {
philpem@5 3533 const int nb = std::sscanf(nargument,"%4095[^,]%c",it,&sep);
philpem@5 3534 if (nb) {
philpem@5 3535 if (std::sscanf(it,"%d%c",&value,&end)==1) nargument+=cimg::strlen(it) + nb -1;
philpem@5 3536 else error("Argument '%s' is not an integer value.",it);
philpem@5 3537 } else error("Argument '%s' is not an integer value.",argument_text);
philpem@5 3538 }
philpem@5 3539 ++position; continue;
philpem@5 3540 }
philpem@5 3541
philpem@5 3542 if (!cimg::strcmp("-float",item)) {
philpem@5 3543 char it[4096], end = 0, sep = 0; double value = 0;
philpem@5 3544 if (*argument) for (const char *nargument = argument; *nargument; ) {
philpem@5 3545 const int nb = std::sscanf(nargument,"%4095[^,]%c",it,&sep);
philpem@5 3546 if (nb) {
philpem@5 3547 if (std::sscanf(it,"%lf%c",&value,&end)==1) nargument+=cimg::strlen(it) + nb -1;
philpem@5 3548 else error("Argument '%s' is not a float value.",it);
philpem@5 3549 } else error("Argument '%s' is not a float value.",argument_text);
philpem@5 3550 }
philpem@5 3551 ++position; continue;
philpem@5 3552 }
philpem@5 3553
philpem@5 3554 //--------------------------
philpem@5 3555 // Input/Output and Display
philpem@5 3556 //--------------------------
philpem@5 3557
philpem@5 3558 // Display.
philpem@5 3559 if (!cimg::strcmp("-display",item0) || !cimg::strcmp("-d",item0)) {
philpem@5 3560 if (display_images(images,indices,true)) is_released = true;
philpem@5 3561 continue;
philpem@5 3562 }
philpem@5 3563
philpem@5 3564 // Display 3D object.
philpem@5 3565 if (!cimg::strcmp("-display3d",item0) || !cimg::strcmp("-d3d",item0)) {
philpem@5 3566 if (display_objects3d(images,indices,true)) is_released = true;
philpem@5 3567 continue;
philpem@5 3568 }
philpem@5 3569
philpem@5 3570 // Display as a graph plot.
philpem@5 3571 if (!cimg::strcmp("-plot",item0)) {
philpem@5 3572 int plot_type = 1, vertex_type = 1; double ymin = 0, ymax = 0, xmin = 0, xmax = 0; char end = 0;
philpem@5 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);
philpem@5 3574 if (nb==1 || nb==2 || nb==4 || nb==6) ++position;
philpem@5 3575 else { plot_type = 1; vertex_type = 0; ymin = ymax = xmin = xmax = 0; }
philpem@5 3576 is_released |= display_plots(images,indices,plot_type,vertex_type,xmin,xmax,ymin,ymax,true);
philpem@5 3577 continue;
philpem@5 3578 }
philpem@5 3579
philpem@5 3580 // Select image feature.
philpem@5 3581 if (!cimg::strcmp("-select",item0)) {
philpem@5 3582 int select_type = 0; char end = 0;
philpem@5 3583 if (std::sscanf(argument,"%d%c",&select_type,&end)==1) {
philpem@5 3584 cimg_foroff(indices,l) gmic_apply(images[indices[l]],select(filenames[indices[l]].ptr(),select_type));
philpem@5 3585 } else error("Select image%s : Invalid argument '%s' "
philpem@5 3586 "(should be 'select_type').",gmic_inds,argument_text);
philpem@5 3587 ++position; continue;
philpem@5 3588 }
philpem@5 3589
philpem@5 3590 // Output.
philpem@5 3591 if (!cimg::strcmp("-output",item0) || !cimg::strcmp("-o",item0)) {
philpem@5 3592 char filename[4096] = { 0 }; char options[4096] = { 0 };
philpem@5 3593 if (std::sscanf(argument,"%4095[^,],%s",filename,options)!=2) std::strcpy(filename,argument);
philpem@5 3594 const char *const ext = cimg::split_filename(filename);
philpem@5 3595 if (!cimg::strcasecmp("off",ext)) {
philpem@5 3596 char nfilename[4096] = { 0 };
philpem@5 3597 std::strcpy(nfilename,filename);
philpem@5 3598 const unsigned int siz = indices.size();
philpem@5 3599 cimg_foroff(indices,l) {
philpem@5 3600 const unsigned int ind = indices[l];
philpem@5 3601 if (siz!=1) cimg::number_filename(filename,l,6,nfilename);
philpem@5 3602 if (!images[ind].is_CImg3d())
philpem@5 3603 error("Output 3D object [%u] as file '%s' : Image [%u] is not a 3D object.",ind,nfilename,ind);
philpem@5 3604 print("Output 3D object [%u] as file '%s'.",ind,nfilename);
philpem@5 3605 CImgList<unsigned int> primitives3d;
philpem@5 3606 CImgList<unsigned char> colors3d;
philpem@5 3607 CImg<float> opacities3d;
philpem@5 3608 CImg<float> points3d(images[ind]);
philpem@5 3609 points3d.CImg3dtoobject3d(primitives3d,colors3d,opacities3d).save_off(nfilename,primitives3d,colors3d);
philpem@5 3610 }
philpem@5 3611 } else if (!cimg::strcasecmp("jpeg",ext) || !cimg::strcasecmp("jpg",ext)) {
philpem@5 3612 int quality = 100; char end = 0;
philpem@5 3613 if (std::sscanf(options,"%d%c",&quality,&end)!=1) quality = 100;
philpem@5 3614 if (quality<0) quality = 0; else if (quality>100) quality = 100;
philpem@5 3615 CImgList<T> output_images;
philpem@5 3616 cimg_foroff(indices,l) output_images.insert(images[indices[l]],~0U,true);
philpem@5 3617 print("Output image%s as file '%s', with quality %u%%",gmic_inds,filename,quality);
philpem@5 3618 if (!output_images) throw CImgInstanceException("CImgList<%s>::save() : File '%s, instance list (%u,%p) is empty.",
philpem@5 3619 output_images.pixel_type(),filename,
philpem@5 3620 output_images.size,output_images.data);
philpem@5 3621 if (output_images.size==1) output_images[0].save_jpeg(filename,quality);
philpem@5 3622 else {
philpem@5 3623 char nfilename[1024];
philpem@5 3624 cimglist_for(output_images,l) {
philpem@5 3625 cimg::number_filename(filename,l,6,nfilename);
philpem@5 3626 output_images[l].save_jpeg(nfilename,quality);
philpem@5 3627 }
philpem@5 3628 }
philpem@5 3629 } else {
philpem@5 3630 CImgList<T> output_images;
philpem@5 3631 cimg_foroff(indices,l) output_images.insert(images[indices[l]],~0U,true);
philpem@5 3632 print("Output image%s as file '%s'.",gmic_inds,filename);
philpem@5 3633 output_images.save(filename);
philpem@5 3634 }
philpem@5 3635 is_released = true; ++position; continue;
philpem@5 3636 }
philpem@5 3637
philpem@5 3638 // Substitute macros commands if necessary.
philpem@5 3639 if (cimg::strcmp("-i",item0) && cimg::strcmp("-input",item0)) {
philpem@5 3640 bool macro_found = false;
philpem@5 3641 cimglist_for(macros,l) {
philpem@5 3642 const char
philpem@5 3643 *const macro = macros[l].ptr(),
philpem@5 3644 *const command = commands[l].ptr();
philpem@5 3645
philpem@5 3646 if (!cimg::strcmp(item+1,macro) && *command) {
philpem@5 3647 CImgList<char> arguments(256);
philpem@5 3648 unsigned int nb_arguments = 0;
philpem@5 3649 char s_argument[4096] = { 0 }, tmp[4096] = { 0 }, tmp2[4096] = { 0 };
philpem@5 3650 bool has_arguments = false;
philpem@5 3651 macro_found = true;
philpem@5 3652 debug("Found macro '%s', substituting by '%s'.",macro,command);
philpem@5 3653
philpem@5 3654 // Get command-line values of macro arguments.
philpem@5 3655 if (argument)
philpem@5 3656 for (const char *nargument = argument; nb_arguments<256 && *nargument &&
philpem@5 3657 std::sscanf(nargument,"%4095[^,]",s_argument)==1;) {
philpem@5 3658 CImg<char>(s_argument,cimg::strlen(s_argument)+1,1,1,1,false).transfer_to(arguments[nb_arguments++]);
philpem@5 3659 nargument+=cimg::strlen(s_argument);
philpem@5 3660 if (*nargument) ++nargument;
philpem@5 3661 }
philpem@5 3662
philpem@5 3663 // Substitute arguments in macro command expression.
philpem@5 3664 CImg<char> substituted_command;
philpem@5 3665 CImgList<char> lreplacement;
philpem@5 3666 for (const char *ncommand = command; *ncommand;) if (*ncommand=='$') {
philpem@5 3667 char *replace_text = 0, sep = 0;
philpem@5 3668 int ind = 0, ind1 = 0;
philpem@5 3669
philpem@5 3670 // Replace $# and ${#}.
philpem@5 3671 if (ncommand[1]=='#' || (ncommand[1]=='{' && ncommand[2]=='#' && ncommand[3]=='}')) {
philpem@5 3672 std::sprintf(replace_text=s_argument,"%u",nb_arguments);
philpem@5 3673 ncommand+=(ncommand[1]=='#')?2:4;
philpem@5 3674 has_arguments = true;
philpem@5 3675
philpem@5 3676 // Replace $* and ${*}.
philpem@5 3677 } else if (ncommand[1]=='*' || (ncommand[1]=='{' && ncommand[2]=='*' && ncommand[3]=='}')) {
philpem@5 3678 replace_text = &(s_argument[0]=0);
philpem@5 3679 for (unsigned int j = 1; j<=nb_arguments; ++j) {
philpem@5 3680 replace_text+=std::sprintf(replace_text,"%s",arguments[j-1].ptr());
philpem@5 3681 if (j<nb_arguments) *(replace_text++) = ',';
philpem@5 3682 }
philpem@5 3683 replace_text = s_argument;
philpem@5 3684 ncommand+=(ncommand[1]=='*')?2:4;
philpem@5 3685 has_arguments = true;
philpem@5 3686
philpem@5 3687 // Replace ${i*}.
philpem@5 3688 } else if (std::sscanf(ncommand,"${%d*%c",&ind,&sep)==2 &&
philpem@5 3689 ind>0 && ind<256 && sep=='}') {
philpem@5 3690 replace_text = &(s_argument[0]=0);
philpem@5 3691 for (unsigned int j = ind; j<=nb_arguments; ++j) {
philpem@5 3692 replace_text+=std::sprintf(replace_text,"%s",arguments[j-1].ptr());
philpem@5 3693 if (j<nb_arguments) *(replace_text++) = ',';
philpem@5 3694 }
philpem@5 3695 replace_text = s_argument;
philpem@5 3696 ncommand+=std::sprintf(tmp,"${%d*}",ind);
philpem@5 3697 has_arguments = true;
philpem@5 3698
philpem@5 3699 // Replace $i and ${i}.
philpem@5 3700 } else if ((std::sscanf(ncommand,"$%d",&ind)==1 ||
philpem@5 3701 (std::sscanf(ncommand,"${%d%c",&ind,&sep)==2 && sep=='}')) &&
philpem@5 3702 ind>0 && ind<256) {
philpem@5 3703 if (!arguments[ind-1]) {
philpem@5 3704 if (sep=='}') error("Macro '%s' : Argument '$%d' is undefined (in expression '${%d}').",macro,ind,ind);
philpem@5 3705 else error("Macro '%s' : Argument '$%d' is undefined (in expression '$%d').",macro,ind,ind);
philpem@5 3706 }
philpem@5 3707 replace_text = arguments[ind-1].ptr();
philpem@5 3708 ncommand+=std::sprintf(tmp,"$%d",ind) + (sep=='}'?2:0);
philpem@5 3709 has_arguments = true;
philpem@5 3710
philpem@5 3711 // Replace ${i=$#}.
philpem@5 3712 } else if (std::sscanf(ncommand,"${%d=$#%c",&ind,&sep)==2 &&
philpem@5 3713 ind>0 && ind<256 && sep=='}') {
philpem@5 3714 std::sprintf(replace_text=s_argument,"%g",(double)nb_arguments);
philpem@5 3715 CImg<char>(s_argument,cimg::strlen(s_argument)+1,1,1,1,false).transfer_to(arguments[ind-1]);
philpem@5 3716 ncommand+=std::sprintf(tmp,"${%d=$#}",ind);
philpem@5 3717 has_arguments = true;
philpem@5 3718
philpem@5 3719 // Replace ${i=$j}.
philpem@5 3720 } else if (std::sscanf(ncommand,"${%d=$%d%c",&ind,&ind1,&sep)==3 && sep=='}' &&
philpem@5 3721 ind>0 && ind<256 && ind1>0 && ind1<256) {
philpem@5 3722 if (!arguments[ind1-1])
philpem@5 3723 error("Macro '%s' : Argument '$%d' is undefined (in expression '${%d=$%d}').",macro,ind1,ind,ind1);
philpem@5 3724 if (!arguments[ind-1]) arguments[ind-1] = arguments[ind1-1];
philpem@5 3725 replace_text = arguments[ind-1].ptr();
philpem@5 3726 ncommand+=std::sprintf(tmp,"${%d=$%d}",ind,ind1);
philpem@5 3727 has_arguments = true;
philpem@5 3728
philpem@5 3729 // Replace ${i=default}.
philpem@5 3730 } else if (std::sscanf(ncommand,"${%d=%4095[^}]%c",&ind,tmp,&sep)==3 && sep=='}' &&
philpem@5 3731 ind>0 && ind<256) {
philpem@5 3732 if (!arguments[ind-1]) CImg<char>(tmp,cimg::strlen(tmp)+1,1,1,1,false).transfer_to(arguments[ind-1]);
philpem@5 3733 replace_text = arguments[ind-1].ptr();
philpem@5 3734 ncommand+=cimg::strlen(tmp) + 4 + std::sprintf(tmp2,"%d",ind);
philpem@5 3735 has_arguments = true;
philpem@5 3736
philpem@5 3737 // Any other expression starting by '$'.
philpem@5 3738 } else {
philpem@5 3739 replace_text = &(s_argument[0]='$');
philpem@5 3740 if (std::sscanf(ncommand,"%4095[^$]",s_argument+1)!=1) { s_argument[1] = 0; ++ncommand; }
philpem@5 3741 else ncommand+=cimg::strlen(s_argument);
philpem@5 3742 }
philpem@5 3743
philpem@5 3744 const int replace_length = cimg::strlen(replace_text);
philpem@5 3745 if (replace_length) {
philpem@5 3746 lreplacement.insert(1);
philpem@5 3747 CImg<char>(replace_text,replace_length,1,1,1,false).transfer_to(lreplacement.last());
philpem@5 3748 }
philpem@5 3749
philpem@5 3750 } else {
philpem@5 3751 std::sscanf(ncommand,"%4095[^$]",s_argument);
philpem@5 3752 const int replace_length = cimg::strlen(s_argument);
philpem@5 3753 if (replace_length) {
philpem@5 3754 lreplacement.insert(1);
philpem@5 3755 CImg<char>(s_argument,replace_length,1,1,1,false).transfer_to(lreplacement.last());
philpem@5 3756 ncommand+=cimg::strlen(s_argument);
philpem@5 3757 }
philpem@5 3758 }
philpem@5 3759 const CImg<char> zero(1,1,1,1,0);
philpem@5 3760 lreplacement.insert(zero).get_append('x').transfer_to(substituted_command);
philpem@5 3761
philpem@5 3762 // Substitute macro expression in command line.
philpem@5 3763 bool is_dquote = false;
philpem@5 3764 cimg_foroff(substituted_command,k)
philpem@5 3765 if (substituted_command[k]=='"') is_dquote = !is_dquote;
philpem@5 3766 else if (is_dquote && substituted_command[k]==' ') substituted_command[k] = 30;
philpem@5 3767 CImgList<char> command_items = substituted_command.get_split(' ',false,false);
philpem@5 3768 cimglist_for(command_items,k) {
philpem@5 3769 CImg<char> &item = command_items[k];
philpem@5 3770 cimg_foroff(item,l) if (item[l]==30) item[l]=' ';
philpem@5 3771 }
philpem@5 3772 cimglist_for(command_items,k) command_items[k].append(zero,'y');
philpem@5 3773 if (position<command_line.size && has_arguments) command_line.remove(position);
philpem@5 3774 command_line.remove(--position);
philpem@5 3775 command_line.insert(command_items,position);
philpem@5 3776 break;
philpem@5 3777 }
philpem@5 3778 }
philpem@5 3779 if (macro_found) continue;
philpem@5 3780 }
philpem@5 3781 }
philpem@5 3782
philpem@5 3783 // Input.
philpem@5 3784 if (!cimg::strcmp("-i",item0) || !cimg::strcmp("-input",item0)) ++position;
philpem@5 3785 else { if (get_version) --item; argument = item; item1[0] = 0; }
philpem@5 3786 if (!cimg::strlen(item1)) indices.assign(1,1,1,1,images.size);
philpem@5 3787 CImgList<T> input_images;
philpem@5 3788 CImgList<char> input_filenames;
philpem@5 3789 bool obj3d = false;
philpem@5 3790 char st_inds[4096] = { 0 }, stx[4096] = { 0 }, sty[4096] = { 0 }, stz[4096] = { 0 }, stv[4096] = { 0 };
philpem@5 3791 char end = 0, sep = 0, sepx = 0, sepy = 0, sepz = 0, sepv = 0;
philpem@5 3792 int nb = 1, indx = no_ind, indy = no_ind, indz = no_ind, indv = no_ind;
philpem@5 3793 float dx = 0, dy = 1, dz = 1, dv = 1;
philpem@5 3794
philpem@5 3795 if (std::sscanf(argument,"[%4095[0-9%,:-]]%*c%d%c",st_inds,&nb,&end)==2 ||
philpem@5 3796 (std::sscanf(argument,"[%4095[0-9%,:-]%c%c",st_inds,&sep,&end)==2 && sep==']')) {
philpem@5 3797
philpem@5 3798 // nb copies of existing sub-images.
philpem@5 3799 const CImg<unsigned int> indices0 = indices2cimg(st_inds,images.size,"-input");
philpem@5 3800 char st_tmp[4096] = { 0 }; std::strcpy(st_tmp,indices2string(indices0,true));
philpem@5 3801 if (nb<=0) error("Input %d copies of image%s : Invalid argument '%s'.",
philpem@5 3802 nb,st_tmp,argument_text);
philpem@5 3803 if (nb!=1) print("Input %d copies of image%s at position%s",nb,st_tmp,gmic_inds);
philpem@5 3804 else print("Input copy of image%s at position%s",st_tmp,gmic_inds);
philpem@5 3805 for (int i = 0; i<nb; ++i) cimg_foroff(indices0,l) {
philpem@5 3806 input_images.insert(images[indices0[l]]);
philpem@5 3807 input_filenames.insert(filenames[indices0[l]]);
philpem@5 3808 }
philpem@5 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",
philpem@5 3810 stx,sty,stz,stv,&(nb=1),&end)==5 ||
philpem@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",
philpem@5 3812 stx,sty,stz,stv,&end)==4) &&
philpem@5 3813 (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
philpem@5 3814 (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
philpem@5 3815 (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
philpem@5 3816 (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
philpem@5 3817 (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
philpem@5 3818 (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']')) &&
philpem@5 3819 (std::sscanf(stz,"%f%c",&dz,&end)==1 ||
philpem@5 3820 (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%') ||
philpem@5 3821 (std::sscanf(stz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']')) &&
philpem@5 3822 (std::sscanf(stv,"%f%c",&dv,&end)==1 ||
philpem@5 3823 (std::sscanf(stv,"%f%c%c",&dv,&sepv,&end)==2 && sepv=='%') ||
philpem@5 3824 (std::sscanf(stv,"[%d%c%c",&indv,&sepv,&end)==2 && sepv==']'))) ||
philpem@5 3825 (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",stx,sty,stz,&end)==3 &&
philpem@5 3826 (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
philpem@5 3827 (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
philpem@5 3828 (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
philpem@5 3829 (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
philpem@5 3830 (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
philpem@5 3831 (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']')) &&
philpem@5 3832 (std::sscanf(stz,"%f%c",&dz,&end)==1 ||
philpem@5 3833 (std::sscanf(stz,"%f%c%c",&dz,&sepz,&end)==2 && sepz=='%') ||
philpem@5 3834 (std::sscanf(stz,"[%d%c%c",&indz,&sepz,&end)==2 && sepz==']'))) ||
philpem@5 3835 (std::sscanf(argument,"%4095[][0-9.eE%+-]%*c%4095[][0-9.eE%+-]%c",stx,sty,&end)==2 &&
philpem@5 3836 (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
philpem@5 3837 (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
philpem@5 3838 (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')) &&
philpem@5 3839 (std::sscanf(sty,"%f%c",&dy,&end)==1 ||
philpem@5 3840 (std::sscanf(sty,"%f%c%c",&dy,&sepy,&end)==2 && sepy=='%') ||
philpem@5 3841 (std::sscanf(sty,"[%d%c%c",&indy,&sepy,&end)==2 && sepy==']'))) ||
philpem@5 3842 (std::sscanf(argument,"%4095[][0-9.eE%+-]%c",stx,&end)==1 &&
philpem@5 3843 (std::sscanf(stx,"%f%c",&dx,&end)==1 ||
philpem@5 3844 (std::sscanf(stx,"%f%c%c",&dx,&sepx,&end)==2 && sepx=='%') ||
philpem@5 3845 (std::sscanf(stx,"[%d%c%c",&indx,&sepx,&end)==2 && sepx==']')))) {
philpem@5 3846
philpem@5 3847 // nb new black image.
philpem@5 3848 if (indx!=no_ind) { gmic_check_indice(indx,"Input black image%s"); dx = (float)images[indx].dimx(); sepx = 0; }
philpem@5 3849 if (indy!=no_ind) { gmic_check_indice(indy,"Input black image%s"); dy = (float)images[indy].dimy(); sepy = 0; }
philpem@5 3850 if (indz!=no_ind) { gmic_check_indice(indz,"Input black image%s"); dz = (float)images[indz].dimz(); sepz = 0; }
philpem@5 3851 if (indv!=no_ind) { gmic_check_indice(indv,"Input black image%s"); dv = (float)images[indv].dimv(); sepv = 0; }
philpem@5 3852 if (sepx=='%') { dx = images.size?dx*images.last().dimx()/100:0; if (!(int)dx) ++dx; }
philpem@5 3853 if (sepy=='%') { dy = images.size?dy*images.last().dimy()/100:0; if (!(int)dy) ++dy; }
philpem@5 3854 if (sepz=='%') { dz = images.size?dz*images.last().dimz()/100:0; if (!(int)dz) ++dz; }
philpem@5 3855 if (sepv=='%') { dv = images.size?dv*images.last().dimv()/100:0; if (!(int)dv) ++dv; }
philpem@5 3856
philpem@5 3857 if (nb<=0) error("Input %d black image%s : Invalid number of copies.",nb,gmic_inds);
philpem@5 3858 if (dx<=0 || dy<=0 || dz<=0 || dv<=0)
philpem@5 3859 error("Input %d black image%s : Invalid image dimensions %gx%gx%gx%g.",
philpem@5 3860 nb,gmic_inds,dx,dy,dz,dv);
philpem@5 3861 if (nb!=1) print("Input %d black images at position%s",nb,gmic_inds);
philpem@5 3862 else print("Input black image at position%s",gmic_inds);
philpem@5 3863 CImg<T> empty((int)dx,(int)dy,(int)dz,(int)dv,0);
philpem@5 3864 input_images.insert(nb-1,empty); input_images.insert(1);
philpem@5 3865 input_images.last().swap(empty);
philpem@5 3866 filenames.insert(input_images.size,CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 3867 } else if (std::sscanf(argument,"(%4095[^)])x%d%c",stx,&(nb=1),&end)==2 ||
philpem@5 3868 (std::sscanf(argument,"(%4095[^)]%c%c",stx,&sep,&end)==2 && sep==')')) {
philpem@5 3869
philpem@5 3870 // Insert nb IxJxKxL image(s) with specified values.
philpem@5 3871 if (nb<=0) error("Input %d images : Invalid number of copies.",nb);
philpem@5 3872 unsigned int cx = 0, cy = 0, cz = 0, cv = 0, maxcx = 0, maxcy = 0, maxcz = 0;
philpem@5 3873 const char *nargument = 0;
philpem@5 3874 for (nargument = argument+1; *nargument!=')'; ) {
philpem@5 3875 char s_value[256] = { 0 }, separator = 0; double value = 0;
philpem@5 3876 if (std::sscanf(nargument,"%255[0-9.eE+-]%c",s_value,&separator)==2 && std::sscanf(s_value,"%lf",&value)==1) {
philpem@5 3877 if (cx>maxcx) maxcx = cx;
philpem@5 3878 if (cy>maxcy) maxcy = cy;
philpem@5 3879 if (cz>maxcz) maxcz = cz;
philpem@5 3880 switch (separator) {
philpem@5 3881 case '^' : cx = cy = cz = 0; ++cv; break;
philpem@5 3882 case '/' : cx = cy = 0; ++cz; break;
philpem@5 3883 case ';' : cx = 0; ++cy; break;
philpem@5 3884 default : ++cx;
philpem@5 3885 }
philpem@5 3886 nargument+=cimg::strlen(s_value) + (separator==')'?0:1);
philpem@5 3887 } else break;
philpem@5 3888 }
philpem@5 3889 if (*nargument!=')') error("Input %d images : Invalid input string '%s'.",nb,argument);
philpem@5 3890
philpem@5 3891 CImg<T> img(maxcx+1,maxcy+1,maxcz+1,cv+1,0);
philpem@5 3892 cx = cy = cz = cv = 0;
philpem@5 3893 for (nargument = argument+1; *nargument; ) {
philpem@5 3894 char s_value[256] = { 0 }, separator = 0; double value = 0;
philpem@5 3895 if (std::sscanf(nargument,"%255[0-9.eE+-]%c",s_value,&separator)==2 && std::sscanf(s_value,"%lf",&value)==1) {
philpem@5 3896 img(cx,cy,cz,cv) = (T)value;
philpem@5 3897 switch (separator) {
philpem@5 3898 case '^' : cx = cy = cz = 0; ++cv; break;
philpem@5 3899 case '/' : cx = cy = 0; ++cz; break;
philpem@5 3900 case ';' : cx = 0; ++cy; break;
philpem@5 3901 default : ++cx;
philpem@5 3902 }
philpem@5 3903 nargument+=cimg::strlen(s_value) + (separator==')'?0:1);
philpem@5 3904 } else break;
philpem@5 3905 }
philpem@5 3906 if (nb==1) print("Input image %dx%dx%dx%d",img.dimx(),img.dimy(),img.dimz(),img.dimv());
philpem@5 3907 else print("Input %d images %dx%d",nb,img.dimx(),img.dimy(),img.dimz(),img.dimv());
philpem@5 3908 input_images.insert(nb,img); filenames.insert(nb,CImg<char>("(gmic)",7,1,1,1,false));
philpem@5 3909 } else {
philpem@5 3910
philpem@5 3911 // Insert image as a loaded filename.
philpem@5 3912 char filename[4096] = { 0 }, options[4096] = { 0 }; const char *ext = 0, *basename = 0;
philpem@5 3913 if (argument[0]!='-' || (argument[1] && argument[1]!='.')) {
philpem@5 3914 std::FILE *file = std::fopen(argument,"r");
philpem@5 3915 if (file) { std::fclose(file); std::strcpy(filename,argument); }
philpem@5 3916 else {
philpem@5 3917 std::sscanf(argument,"%4095[^,],%s",filename,options);
philpem@5 3918 if (!(file=std::fopen(filename,"r"))) {
philpem@5 3919 if (filename[0]=='-') error("Input '%s' : Command not found.",filename);
philpem@5 3920 else error("Input '%s' : File not found.",filename);
philpem@5 3921 }
philpem@5 3922 std::fclose(file);
philpem@5 3923 }
philpem@5 3924 } else std::strcpy(filename,argument);
philpem@5 3925 basename = cimg::basename(filename);
philpem@5 3926 ext = cimg::split_filename(filename);
philpem@5 3927
philpem@5 3928 if (!cimg::strcasecmp("off",ext)) { // 3D object file.
philpem@5 3929 print("Input 3D object '%s'",filename);
philpem@5 3930 CImgList<unsigned int> primitives3d;
philpem@5 3931 CImgList<unsigned char> colors3d;
philpem@5 3932 CImg<float> opacities3d, points3d = CImg<float>::get_load_off(filename,primitives3d,colors3d);
philpem@5 3933 opacities3d.assign(1,primitives3d.size,1,1,1);
philpem@5 3934 points3d.object3dtoCImg3d(primitives3d,colors3d,opacities3d);
philpem@5 3935 input_images.insert(1); points3d.transfer_to(input_images[0]);
philpem@5 3936 input_filenames.insert(CImg<char>(is_fullpath?filename:basename,
philpem@5 3937 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
philpem@5 3938 obj3d = true;
philpem@5 3939 } else if (!cimg::strcasecmp(ext,"avi") ||
philpem@5 3940 !cimg::strcasecmp(ext,"mov") ||
philpem@5 3941 !cimg::strcasecmp(ext,"asf") ||
philpem@5 3942 !cimg::strcasecmp(ext,"divx") ||
philpem@5 3943 !cimg::strcasecmp(ext,"flv") ||
philpem@5 3944 !cimg::strcasecmp(ext,"mpg") ||
philpem@5 3945 !cimg::strcasecmp(ext,"m1v") ||
philpem@5 3946 !cimg::strcasecmp(ext,"m2v") ||
philpem@5 3947 !cimg::strcasecmp(ext,"m4v") ||
philpem@5 3948 !cimg::strcasecmp(ext,"mjp") ||
philpem@5 3949 !cimg::strcasecmp(ext,"mkv") ||
philpem@5 3950 !cimg::strcasecmp(ext,"mpe") ||
philpem@5 3951 !cimg::strcasecmp(ext,"movie") ||
philpem@5 3952 !cimg::strcasecmp(ext,"ogm") ||
philpem@5 3953 !cimg::strcasecmp(ext,"qt") ||
philpem@5 3954 !cimg::strcasecmp(ext,"rm") ||
philpem@5 3955 !cimg::strcasecmp(ext,"vob") ||
philpem@5 3956 !cimg::strcasecmp(ext,"wmv") ||
philpem@5 3957 !cimg::strcasecmp(ext,"xvid") ||
philpem@5 3958 !cimg::strcasecmp(ext,"mpeg")) {
philpem@5 3959 unsigned int value0 = 0, value1 = 0, step = 1; char sep0 = 0, sep1 = 0, end = 0;
philpem@5 3960 if ((std::sscanf(options,"%u%c%*c%u%c%*c%u%c",&value0,&sep0,&value1,&sep1,&step,&end)==5 && sep0=='%' && sep1=='%') ||
philpem@5 3961 (std::sscanf(options,"%u%c%*c%u%*c%u%c",&value0,&sep0,&value1,&step,&end)==4 && sep0=='%') ||
philpem@5 3962 (std::sscanf(options,"%u%*c%u%c%*c%u%c",&value0,&value1,&sep1,&step,&end)==4 && sep1=='%') ||
philpem@5 3963 (std::sscanf(options,"%u%*c%u%*c%u%c",&value0,&value1,&step,&end)==3) ||
philpem@5 3964 (std::sscanf(options,"%u%c%*c%u%c%c",&value0,&sep0,&value1,&sep1,&end)==4 && sep0=='%' && sep1=='%') ||
philpem@5 3965 (std::sscanf(options,"%u%c%*c%u%c",&value0,&sep0,&value1,&end)==3 && sep0=='%') ||
philpem@5 3966 (std::sscanf(options,"%u%*c%u%c%c",&value0,&value1,&sep1,&end)==3 && sep1=='%') ||
philpem@5 3967 (std::sscanf(options,"%u%*c%u%c",&value0,&value1,&end)==2)) { // Read several frames
philpem@5 3968 print("Input frames %u%s...%u%s with step %u of file '%s'",
philpem@5 3969 value0,sep0=='%'?"%":"",value1,sep1=='%'?"%":"",step,filename);
philpem@5 3970 if (sep0=='%' || sep1=='%') {
philpem@5 3971 const unsigned int nb_frames = CImg<unsigned int>::get_load_ffmpeg(filename,0,0,0)[0];
philpem@5 3972 if (sep0=='%') value0 = value0*nb_frames/100;
philpem@5 3973 if (sep1=='%') value1 = value1*nb_frames/100;
philpem@5 3974 }
philpem@5 3975 } else if ((std::sscanf(options,"%u%c%c",&value0,&sep0,&end)==2 && sep0=='%') ||
philpem@5 3976 (std::sscanf(options,"%u%c",&value0,&end)==1)) { // Read one frame
philpem@5 3977 print("Input frame %u%s of file '%s'",value0,sep0=='%'?"%":"",filename);
philpem@5 3978 if (sep0=='%') {
philpem@5 3979 const unsigned int nb_frames = CImg<unsigned int>::get_load_ffmpeg(filename,0,0,0)[0];
philpem@5 3980 value0 = value0*nb_frames/100;
philpem@5 3981 }
philpem@5 3982 value1 = value0; step = 1;
philpem@5 3983 } else { // Read all frames
philpem@5 3984 print("Input all frames of file '%s'",filename);
philpem@5 3985 value0 = 0; value1 = ~0U; sep0 = sep1 = 0; step = 1;
philpem@5 3986 }
philpem@5 3987 input_images.load_ffmpeg(filename,value0,value1,step);
philpem@5 3988 if (input_images)
philpem@5 3989 input_filenames.insert(input_images.size,CImg<char>(is_fullpath?filename:basename,
philpem@5 3990 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
philpem@5 3991 } else if (!cimg::strcasecmp("raw",ext)) { // Raw file.
philpem@5 3992 int dx = 0, dy = 1, dz = 1, dv = 1;
philpem@5 3993 if (std::sscanf(options,"%d%*c%d%*c%d%*c%d",&dx,&dy,&dz,&dv)>0) {
philpem@5 3994 if (dx<=0 || dy<=0 || dz<=0 || dv<=0)
philpem@5 3995 error("Input raw file '%s' : Invalid specified dimensions %dx%dx%dx%d.",filename,dx,dy,dz,dv);
philpem@5 3996 print("Input raw file '%s'",filename);
philpem@5 3997 input_images.insert(1); input_images[0].load_raw(filename,dx,dy,dz,dv);
philpem@5 3998 input_filenames.insert(CImg<char>(is_fullpath?filename:basename,
philpem@5 3999 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
philpem@5 4000 } else error("Input raw file '%s' : Image dimensions must be specified.",filename);
philpem@5 4001 } else if (!cimg::strcasecmp("yuv",ext)) { // YUV file.
philpem@5 4002 int dx = 0, dy = 0; unsigned int first = 0, last = ~0U, step = 1;
philpem@5 4003 if (std::sscanf(options,"%d%*c%d%*c%u%*c%u%*c%u",&dx,&dy,&first,&last,&step)>0) {
philpem@5 4004 if (dx<=0 || dy<=0)
philpem@5 4005 error("Input yuv file '%s' : Invalid specified dimensions %dx%d.",filename,dx,dy);
philpem@5 4006 print("Input yuv file '%s'",filename);
philpem@5 4007 input_images.load_yuv(filename,dx,dy,first,last,step);
philpem@5 4008 input_filenames.insert(input_images.size,CImg<char>(is_fullpath?filename:basename,
philpem@5 4009 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
philpem@5 4010 } else error("Input yuv file '%s' : Image dimensions must be specified.",filename);
philpem@5 4011 } else { // Other file type.
philpem@5 4012 print("Input file '%s'",filename);
philpem@5 4013 input_images.load(filename);
philpem@5 4014 input_filenames.insert(input_images.size,
philpem@5 4015 CImg<char>(is_fullpath?filename:basename,
philpem@5 4016 cimg::strlen(is_fullpath?filename:basename)+1,1,1,1,false));
philpem@5 4017 }
philpem@5 4018 }
philpem@5 4019
philpem@5 4020 if (verbosity_level>=0) {
philpem@5 4021 if (input_images) {
philpem@5 4022 const unsigned int last = input_images.size-1;
philpem@5 4023 if (obj3d)
philpem@5 4024 std::fprintf(cimg_stdout," (%d points, %u primitives, %u colors).",
philpem@5 4025 (unsigned int)input_images(0,6),
philpem@5 4026 (unsigned int)input_images(0,7),
philpem@5 4027 (unsigned int)input_images(0,8));
philpem@5 4028 else if (input_images.size==1)
philpem@5 4029 std::fprintf(cimg_stdout," (1 image %ux%ux%ux%u).",
philpem@5 4030 input_images[0].width,input_images[0].height,input_images[0].depth,
philpem@5 4031 input_images[0].dim);
philpem@5 4032 else std::fprintf(cimg_stdout," (%u images [0] = %ux%ux%ux%u, %s[%u] = %ux%ux%ux%u).",
philpem@5 4033 input_images.size,
philpem@5 4034 input_images[0].width,input_images[0].height,input_images[0].depth,
philpem@5 4035 input_images[0].dim,
philpem@5 4036 last==1?"":"...,",
philpem@5 4037 last,
philpem@5 4038 input_images[last].width,input_images[last].height,input_images[last].depth,
philpem@5 4039 input_images[last].dim);
philpem@5 4040 } else std::fprintf(cimg_stdout," (no available data).");
philpem@5 4041 }
philpem@5 4042
philpem@5 4043 for (unsigned int l = 0, siz = indices.size()-1, off = 0; l<=siz; ++l) {
philpem@5 4044 const unsigned int ind = indices[l] + off;
philpem@5 4045 if (l!=siz) images.insert(input_images,ind);
philpem@5 4046 else {
philpem@5 4047 images.insert(input_images.size,ind);
philpem@5 4048 cimglist_for(input_images,k) images[ind+k].swap(input_images[k]);
philpem@5 4049 }
philpem@5 4050 filenames.insert(input_filenames,ind);
philpem@5 4051 off+=input_images.size;
philpem@5 4052 }
philpem@5 4053
philpem@5 4054 } catch (CImgException &e) {
philpem@5 4055 const char *error_message = e.message;
philpem@5 4056 char tmp[4096] = { 0 }, sep = 0;
philpem@5 4057 if (std::sscanf(error_message,"%4095[^>]>:%c",tmp,&sep)==2 && sep==':') error_message+=cimg::strlen(tmp)+3;
philpem@5 4058 error(error_message);
philpem@5 4059 }
philpem@5 4060 }
philpem@5 4061
philpem@5 4062 // Check if command line has grown too much (possible recursive macro calls).
philpem@5 4063 if (command_line.size>=command_line_maxsize)
philpem@5 4064 error("Command line overflow : There are too much commands specified (possible recursive macro substitution).");
philpem@5 4065
philpem@5 4066 // Check if some loops have not been terminated.
philpem@5 4067 if (dowhile) warning("A '-while' directive is missing somewhere.");
philpem@5 4068 if (repeatdone) warning("A '-done' directive is missing somewhere.");
philpem@5 4069
philpem@5 4070 // Display final result if necessary (not 'released' before).
philpem@5 4071 if (images.size && !is_released) {
philpem@5 4072 if (!display_objects3d(images,CImg<unsigned int>::sequence(images.size,0,images.size-1),false))
philpem@5 4073 display_images(images,CImg<unsigned int>::sequence(images.size,0,images.size-1),true);
philpem@5 4074 }
philpem@5 4075
philpem@5 4076 print("End G'MIC instance.\n");
philpem@5 4077 return *this;
philpem@5 4078 }
philpem@5 4079
philpem@5 4080 // Small hack to separate the compilation of G'MIC in different pixel types.
philpem@5 4081 // (only intended to save computer memory when compiling !)
philpem@5 4082 //--------------------------------------------------------------------------
philpem@5 4083 #ifdef gmic_minimal
philpem@5 4084 gmic& gmic::parse_float(CImgList<float>& images) { return parse(images); }
philpem@5 4085 template gmic::gmic(const int, const char *const *const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4086 template gmic::gmic(const char* const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4087 #else
philpem@5 4088 #if defined(gmic_bool) || !defined(gmic_separate_compilation)
philpem@5 4089 gmic& gmic::parse_bool(CImgList<bool>& images) { return parse(images); }
philpem@5 4090 template gmic::gmic(const int, const char *const *const, CImgList<bool>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4091 template gmic::gmic(const char* const, CImgList<bool>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4092 #endif
philpem@5 4093 #if defined(gmic_uchar) || !defined(gmic_separate_compilation)
philpem@5 4094 gmic& gmic::parse_uchar(CImgList<unsigned char>& images) { return parse(images); }
philpem@5 4095 template gmic::gmic(const int, const char *const *const, CImgList<unsigned char>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4096 template gmic::gmic(const char* const, CImgList<unsigned char>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4097 #endif
philpem@5 4098 #if defined(gmic_char) || !defined(gmic_separate_compilation)
philpem@5 4099 gmic& gmic::parse_char(CImgList<char>& images) { return parse(images); }
philpem@5 4100 template gmic::gmic(const int, const char *const *const, CImgList<char>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4101 template gmic::gmic(const char* const, CImgList<char>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4102 #endif
philpem@5 4103 #if defined(gmic_ushort) || !defined(gmic_separate_compilation)
philpem@5 4104 gmic& gmic::parse_ushort(CImgList<unsigned short>& images) { return parse(images); }
philpem@5 4105 template gmic::gmic(const int, const char *const *const, CImgList<unsigned short>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4106 template gmic::gmic(const char* const, CImgList<unsigned short>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4107 #endif
philpem@5 4108 #if defined(gmic_short) || !defined(gmic_separate_compilation)
philpem@5 4109 gmic& gmic::parse_short(CImgList<short>& images) { return parse(images); }
philpem@5 4110 template gmic::gmic(const int, const char *const *const, CImgList<short>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4111 template gmic::gmic(const char* const, CImgList<short>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4112 #endif
philpem@5 4113 #if defined(gmic_uint) || !defined(gmic_separate_compilation)
philpem@5 4114 gmic& gmic::parse_uint(CImgList<unsigned int>& images) { return parse(images); }
philpem@5 4115 template gmic::gmic(const int, const char *const *const, CImgList<unsigned int>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4116 template gmic::gmic(const char* const, CImgList<unsigned int>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4117 #endif
philpem@5 4118 #if defined(gmic_int) || !defined(gmic_separate_compilation)
philpem@5 4119 gmic& gmic::parse_int(CImgList<int>& images) { return parse(images); }
philpem@5 4120 template gmic::gmic(const int, const char *const *const, CImgList<int>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4121 template gmic::gmic(const char* const, CImgList<int>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4122 #endif
philpem@5 4123 #if defined(gmic_float) || !defined(gmic_separate_compilation)
philpem@5 4124 gmic& gmic::parse_float(CImgList<float>& images) { return parse(images); }
philpem@5 4125 template gmic::gmic(const int, const char *const *const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4126 template gmic::gmic(const char* const, CImgList<float>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4127 #endif
philpem@5 4128 #if defined(gmic_double) || !defined(gmic_separate_compilation)
philpem@5 4129 gmic& gmic::parse_double(CImgList<double>& images) { return parse(images); }
philpem@5 4130 template gmic::gmic(const int, const char *const *const, CImgList<double>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4131 template gmic::gmic(const char* const, CImgList<double>&, const char *const custom_macros, const bool add_macros_at_start);
philpem@5 4132 #endif
philpem@5 4133 #endif
philpem@5 4134 #endif
philpem@5 4135
philpem@5 4136 //-----------------------
philpem@5 4137 // Start main procedure.
philpem@5 4138 //-----------------------
philpem@5 4139 #if defined(gmic_main) || (!defined(gmic_separate_compilation) && !defined(gmic_minimal))
philpem@5 4140 extern char data_def[];
philpem@5 4141
philpem@5 4142 int main(int argc, char **argv) {
philpem@5 4143
philpem@5 4144 // Display help if necessary.
philpem@5 4145 //---------------------------
philpem@5 4146 if (argc==1) {
philpem@5 4147 std::fprintf(cimg_stdout,"<gmic> No options or data provided. Try '%s -h' for help.\n",cimg::basename(argv[0]));
philpem@5 4148 std::exit(0);
philpem@5 4149 }
philpem@5 4150
philpem@5 4151 if (cimg_option("-h",false,0) || cimg_option("-help",false,0) || cimg_option("--help",false,0)) {
philpem@5 4152 cimg_usage("GREYC's Magic Image Converter");
philpem@5 4153
philpem@5 4154 char version[1024] = { 0 };
philpem@5 4155 std::sprintf(version," Version %d.%d.%d.%d, Copyright (C) 2008-2009, David Tschumperle (http://gmic.sourceforge.net)",
philpem@5 4156 gmic_version/1000,(gmic_version/100)%10,(gmic_version/10)%10,gmic_version%10);
philpem@5 4157 cimg_help(version);
philpem@5 4158
philpem@5 4159 cimg_help("\n Usage\n"
philpem@5 4160 " -----");
philpem@5 4161 cimg_help(" gmic [file_1] [file_2] .. [file_n] [command_1] .. [command_n] [file_n+1] ...\n");
philpem@5 4162 cimg_help(" G'MIC is an interpreter of image processing macros whose goal is to convert, manipulate and");
philpem@5 4163 cimg_help(" visualize generic 1D/2D/3D multi-spectral image and video files. It follows these simple rules :\n");
philpem@5 4164 cimg_help(" - G'MIC handles a numbered list of images which are all stored in computer memory.");
philpem@5 4165 cimg_help(" - The first image of the list has indice '[0]'.");
philpem@5 4166 cimg_help(" - Negative indices are treated in a cyclic way (i.e. '[-1]' is the last image,");
philpem@5 4167 cimg_help(" '[-2]' the penultimate one, and so on...).");
philpem@5 4168 cimg_help(" - Command line items tell how to add/remove/manipulate/display images of the list.");
philpem@5 4169 cimg_help(" - Items are read and executed in the order they appear on the command line, from the left to the right.");
philpem@5 4170 cimg_help(" - Items can thus appear more than one time on the command line.");
philpem@5 4171 cimg_help(" - An item starting by '-' is a G'MIC instruction.");
philpem@5 4172 cimg_help(" - One instruction may have two equivalent names (regular and short).");
philpem@5 4173 cimg_help(" - A G'MIC instruction may have mandatory or optional arguments.");
philpem@5 4174 cimg_help(" - When multiple arguments are needed, they are separated by commas ','.");
philpem@5 4175 cimg_help(" - Items that are not instructions are considered either as input filenames or input strings.");
philpem@5 4176 cimg_help(" - When an input filename is encountered, the corresponding image data are loaded");
philpem@5 4177 cimg_help(" and added to the end of the image list.");
philpem@5 4178 cimg_help(" (see section 'Filename options' below for more informations on file input/output).");
philpem@5 4179 cimg_help(" - Special filenames '-' or '-.ext' mean 'standard input/output' (optionally. in 'ext' format).");
philpem@5 4180 cimg_help(" - Special input strings can be used to insert new images to the list. They can be :");
philpem@5 4181 cimg_help(" - 'width[%][xheight[%][xdepth[%][xdim[%][xN]]]]' : Insert 'N' black images with specified size.");
philpem@5 4182 cimg_help(" (adding '%' to a dimension means 'percentage of to the same dimension in the last image'),");
philpem@5 4183 cimg_help(" - '[indice]' or '[indice]xN' : Insert 1 or N copies of the existing image [indice].");
philpem@5 4184 cimg_help(" - '(v1,v2,...)' or '(v1,v2,...)xN' : Insert 1 or N copies of the specified IxJxKxL image.");
philpem@5 4185 cimg_help(" Separators inside '(..)' can be ',' (column), ';' (line), '/' (slice) or '^' (channel).");
philpem@5 4186 cimg_help(" - A G'MIC instruction may be restricted to a specific subset of the list, by adding '[subset]' to");
philpem@5 4187 cimg_help(" the instruction name. Several usual expressions are possible for 'subset', for instance : ");
philpem@5 4188 cimg_help(" '-command[0,1,3]' : Apply instruction on images 0,1 and 3.");
philpem@5 4189 cimg_help(" '-command[3-5]' : Apply instruction on images 3 to 5.");
philpem@5 4190 cimg_help(" '-command[50%-100%] : Apply instruction on the second half of the image list.");
philpem@5 4191 cimg_help(" '-command[0,-2,-1]' : Apply instruction on the first and two latest images.");
philpem@5 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).");
philpem@5 4193 cimg_help(" '-command[0,2-4,50%--1]' : Apply instruction on images 0,2,3,4 and the second half of the list.");
philpem@5 4194 cimg_help(" - When no image subset is specified, a G'MIC instruction is applied on all images of the list.");
philpem@5 4195 cimg_help(" - Native (non-macro) instructions starting with '--' instead of '-' do not act 'in-place' but");
philpem@5 4196 cimg_help(" insert their result as a new image at the end of the list.");
philpem@5 4197 cimg_help(" - On the command line, any item of the form '@indice' or '@{indice}' is replaced");
philpem@5 4198 cimg_help(" by the values of the image '[indice]' separated by commas.");
philpem@5 4199 cimg_help(" - Items '@?' (or '@{?}'), '@{?,max}' or '@{?,min,max}' are replaced by a float random value between [0,1], [0,max] or [min,max].");
philpem@5 4200 cimg_help(" - Items '@[?]', '@[?,max]' or '@[?,min,max]' do the same but return integer random values.");
philpem@5 4201 cimg_help(" - Restrictions to a subset of image values can be specified with '@{indice,subset}' (as in '@{2,0-50%}').");
philpem@5 4202 cimg_help(" - Restriction to a particular pixel coordinate can be specified with '@(indice,x[,y[,z[,v[,borders]]]])'.");
philpem@5 4203 cimg_help(" - On the command line, the item '@#' is replaced by the number of images in the list.");
philpem@5 4204 cimg_help(" - Input filenames or commands may result to the generation of 3D objects.");
philpem@5 4205 cimg_help(" - A 3D object viewer, based on software rendering, is included in G'MIC.");
philpem@5 4206 cimg_help(" - A 3D object is stored as a single-column image containing all object data, in the following order :");
philpem@5 4207 cimg_help(" { magic header, vertices, faces, colors, opacities }.");
philpem@5 4208 cimg_help(" - Custom user-defined G'MIC instructions can be defined with the use of a macro file.");
philpem@5 4209 cimg_help(" - A macro file is a simple ASCII text file, each line being of the form");
philpem@5 4210 cimg_help(" 'instruction_name : substitution' or 'substitution (continuation)' or '# comment'.");
philpem@5 4211 cimg_help(" - Each invoked macro instruction is substituted as its defined content on the command line.");
philpem@5 4212 cimg_help(" - A macro file 'gmic_def.raw' is distributed within the G'MIC package.");
philpem@5 4213 cimg_help(" - The macros defined in 'gmic_def.raw' are included by default in G'MIC.");
philpem@5 4214 cimg_help(" - Macros arguments, separated by commas, can be added after the invokation of a macro instruction.");
philpem@5 4215 cimg_help(" - In macros definitions, expressions starting with '$' are used to reference macro arguments :");
philpem@5 4216 cimg_help(" $i and ${i} are replaced by the value of the i-th macro argument.");
philpem@5 4217 cimg_help(" $# and ${#} are replaced by the number of macro arguments.");
philpem@5 4218 cimg_help(" $* and ${*} are replaced by the entire string of macro arguments.");
philpem@5 4219 cimg_help(" ${i*} is replaced by all macro arguments following the i-th argument (included).");
philpem@5 4220 cimg_help(" ${i=$#} is replaced by $i if defined, or by its new value $# else.");
philpem@5 4221 cimg_help(" ${i=$j} is replaced by $i if defined, or by its new value $j else.");
philpem@5 4222 cimg_help(" ${i=default} is replaced by $i if defined, or by its new value 'default' else.");
philpem@5 4223 cimg_help("\n A list of available native and macro instructions is available below.");
philpem@5 4224 cimg_help(" A parameter specified in '[]' is optional, except when standing for '[indices]' where it");
philpem@5 4225 cimg_help(" corresponds to one or several indices of the image list, as described above. In this case, the '[' and ']'");
philpem@5 4226 cimg_help(" characters must explicitly appear when writting the item.");
philpem@5 4227
philpem@5 4228 cimg_help("\n Global options\n"
philpem@5 4229 " --------------");
philpem@5 4230 cimg_option("-help","(no args)","Display this help (eq. to '-h').");
philpem@5 4231 cimg_option("-verbose+","(no args)","Increment verbosity level (eq. to '-v+').");
philpem@5 4232 cimg_option("-verbose-","(no args)","Decrement verbosity level (eq. to '-v-').");
philpem@5 4233 cimg_option("-macros","'filename'","Load macro file from specified filename (eq. to '-m').");
philpem@5 4234 cimg_option("-debug","(no args)","Switch debug flag (when on, displays internal infos for debugging).");
philpem@5 4235 cimg_option("-fullpath","(no args)","Switch full path flag (when on, displays full filename paths).");
philpem@5 4236
philpem@5 4237 cimg_help("\n Arithmetic operators\n"
philpem@5 4238 " --------------------");
philpem@5 4239 cimg_option("-add","'value', '[indice]' or (no args)","Add 'value' or '[indice]' to image(s)");
philpem@5 4240 cimg_help(" "
philpem@5 4241 "or add image(s) together (eq. to '-+').");
philpem@5 4242 cimg_option("-sub","'value', '[indice]' or (no args)","Substract 'value' or '[indice]' to image(s)");
philpem@5 4243 cimg_help(" "
philpem@5 4244 "or substract image(s) together (eq. to '--').");
philpem@5 4245 cimg_option("-mul","'value', '[indice]' or (no args)","Multiply image(s) by 'value' or '[indice]'");
philpem@5 4246 cimg_help(" "
philpem@5 4247 "or multiply image(s) together (eq. to '-*').");
philpem@5 4248 cimg_option("-div","'value', '[indice]' or (no args)","Divide image(s) by 'value' or '[indice]'");
philpem@5 4249 cimg_help(" "
philpem@5 4250 "or divide image(s) together (eq. to '-/').");
philpem@5 4251 cimg_option("-pow","'value', '[indice]' or (no args)","Compute image(s) to the power of 'value' or '[indice]'");
philpem@5 4252 cimg_help(" "
philpem@5 4253 "or power of the image(s) together (eq. to '-^').");
philpem@5 4254 cimg_option("-min","'value', '[indice]' or (no args)","Compute minimum between image(s) and 'value' or '[indice]'");
philpem@5 4255 cimg_help(" "
philpem@5 4256 "or minimum of image(s) together.");
philpem@5 4257 cimg_option("-max","'value', '[indice]' or (no args)","Compute maximum between image(s) and 'value' or '[indice]'");
philpem@5 4258 cimg_help(" "
philpem@5 4259 "or maximum of image(s) together.");
philpem@5 4260 cimg_option("-mod","'value', '[indice]' or (no args)","Compute modulo of image(s) with 'value' or '[indice]'");
philpem@5 4261 cimg_help(" "
philpem@5 4262 "or modulo with image(s) together.");
philpem@5 4263 cimg_option("-and","'value', '[indice]' or (no args)","Compute bitwise AND of image(s) with 'value' or '[indice]'");
philpem@5 4264 cimg_help(" "
philpem@5 4265 "or bitwise AND of image(s) together.");
philpem@5 4266 cimg_option("-or","'value', '[indice]' or (no args)","Compute bitwise OR of image(s) with 'value' or '[indice]'");
philpem@5 4267 cimg_help(" "
philpem@5 4268 "or bitwise OR of image(s) together.");
philpem@5 4269 cimg_option("-xor","'value', '[indice]' or (no args)","Compute bitwise XOR of image(s) with 'value' '[indice]'");
philpem@5 4270 cimg_help(" "
philpem@5 4271 "or bitwise XOR of image(s) together.");
philpem@5 4272 cimg_option("-cos","(no args)","Compute cosine of image(s) values.");
philpem@5 4273 cimg_option("-sin","(no args)","Compute sine of image(s) values.");
philpem@5 4274 cimg_option("-tan","(no args)","Compute tangent of image(s) values.");
philpem@5 4275 cimg_option("-acos","(no args)","Compute arccosine of image(s) values.");
philpem@5 4276 cimg_option("-asin","(no args)","Compute arcsine of image(s) values.");
philpem@5 4277 cimg_option("-atan","(no args)","Compute arctangent of image(s) values.");
philpem@5 4278 cimg_option("-abs","(no args)","Compute absolute value of image(s) values.");
philpem@5 4279 cimg_option("-sqr","(no args)","Compute square of image(s) values.");
philpem@5 4280 cimg_option("-sqrt","(no args)","Compute square root of image(s) values.");
philpem@5 4281 cimg_option("-exp","(no args)","Compute exponential of image(s) values.");
philpem@5 4282 cimg_option("-log","(no args)","Compute logarithm of image(s) values.");
philpem@5 4283 cimg_option("-log10","(no args)","Compute logarithm_10 of image(s) values.");
philpem@5 4284
philpem@5 4285 cimg_help("\n Pointwise pixel manipulation\n"
philpem@5 4286 " ----------------------------");
philpem@5 4287 cimg_option("-type","'value_type'","Cast all images into specified value type (eq. to '-t').");
philpem@5 4288 cimg_help(" "
philpem@5 4289 "('value_type' can be 'bool','uchar','char','ushort','short',");
philpem@5 4290 cimg_help(" "
philpem@5 4291 "'uint','int','float','double').");
philpem@5 4292 cimg_option("-set","'value[,x[,y[,z[,v]]]]'","Set scalar value in image(s) at specified position (eq. to '-=').");
philpem@5 4293 cimg_option("-endian","(no args)","Invert endianness of the image(s) buffers.");
philpem@5 4294 cimg_option("-fill","'value1,value2,...'","Fill image(s) with scalar values in a repetitive way (eq. to '-f').");
philpem@5 4295 cimg_option("-threshold","'value[%][,soft]' or (noargs)","Threshold pixel values ((noargs) for interactive mode).");
philpem@5 4296 cimg_option("-cut","'{value1[%],[indice]},{value2[%],[indice]}' or (noargs)","Cut pixel values in specified range ");
philpem@5 4297 cimg_help(" "
philpem@5 4298 "((noargs) for interactive mode).");
philpem@5 4299 cimg_option("-normalize","'{value1[%],[indice]},{value2[%],[indice]}'",
philpem@5 4300 "Normalize pixel values in specified range (eq. to '-n').");
philpem@5 4301 cimg_option("-round","'round_value[,round_type]'","Round pixel values.");
philpem@5 4302 cimg_option("-equalize","'nb_levels'","Equalize image(s) histogram(s) using specified number of levels.");
philpem@5 4303 cimg_option("-quantize","'nb_levels'","Quantize image(s) with 'nb_levels' levels.");
philpem@5 4304 cimg_option("-noise","'std[%][,noise_type]'","Add noise with specified standard deviation");
philpem@5 4305 cimg_help(" "
philpem@5 4306 "('noise_type' can be '{0=gaussian, 1=uniform, 2=salt&pepper, 3=poisson}'.");
philpem@5 4307 cimg_option("-norm","(no args)","Compute pointwise L2-norm of vector-valued image(s).");
philpem@5 4308 cimg_option("-orientation","(no args)","Compute pointwise orientation of vector-valued image(s).");
philpem@5 4309
philpem@5 4310 cimg_help("\n Color bases conversions\n"
philpem@5 4311 " -----------------------");
philpem@5 4312 cimg_option("-rgb2hsv","(no args)","Convert image(s) from RGB to HSV colorbases.");
philpem@5 4313 cimg_option("-rgb2hsl","(no args)","Convert image(s) from RGB to HSL colorbases.");
philpem@5 4314 cimg_option("-rgb2hsi","(no args)","Convert image(s) from RGB to HSI colorbases.");
philpem@5 4315 cimg_option("-rgb2yuv","(no args)","Convert image(s) from RGB to YUV colorbases.");
philpem@5 4316 cimg_option("-rgb2ycbcr","(no args)","Convert image(s) from RGB to YCbCr colorbases.");
philpem@5 4317 cimg_option("-rgb2xyz","(no args)","Convert image(s) from RGB to XYZ colorbases.");
philpem@5 4318 cimg_option("-rgb2lab","(no args)","Convert image(s) from RGB to Lab colorbases.");
philpem@5 4319 cimg_option("-rgb2cmy","(no args)","Convert image(s) from RGB to CMY colorbases.");
philpem@5 4320 cimg_option("-rgb2cmyk","(no args)","Convert image(s) from RGB to CMYK colorbases.");
philpem@5 4321 cimg_option("-rgb2lut","'[indice][,dithering]' or 'LUT_type[,dithering]'","Index image(s) with color palette ");
philpem@5 4322 cimg_help(" "
philpem@5 4323 "('LUT_type' can be '{0=default, 1=rainbow, 2=contrast}'");
philpem@5 4324 cimg_help(" "
philpem@5 4325 "'dithering' can be '{0=off, 1=on}').");
philpem@5 4326 cimg_option("-hsv2rgb","(no args)","Convert image(s) from HSV to RGB colorbases.");
philpem@5 4327 cimg_option("-hsl2rgb","(no args)","Convert image(s) from HSL to RGB colorbases.");
philpem@5 4328 cimg_option("-hsi2rgb","(no args)","Convert image(s) from HSI to RGB colorbases.");
philpem@5 4329 cimg_option("-yuv2rgb","(no args)","Convert image(s) from YUV to RGB colorbases.");
philpem@5 4330 cimg_option("-ycbcr2rgb","(no args)","Convert image(s) from YCbCr to RGB colorbases.");
philpem@5 4331 cimg_option("-xyz2rgb","(no args)","Convert image(s) from XYZ to RGB colorbases.");
philpem@5 4332 cimg_option("-lab2rgb","(no args)","Convert image(s) from Lab to RGB colorbases.");
philpem@5 4333 cimg_option("-cmy2rgb","(no args)","Convert image(s) from CMY to RGB colorbases.");
philpem@5 4334 cimg_option("-cmyk2rgb","(no args)","Convert image(s) from CMYK to RGB colorbases.");
philpem@5 4335 cimg_option("-lut2rgb","'[indice]' or 'LUT_type'","Map color palette to image(s) ");
philpem@5 4336 cimg_help(" "
philpem@5 4337 "('LUT_type' can be '{0=default, 1=rainbow, 2=contrast}'.");
philpem@5 4338
philpem@5 4339 cimg_help("\n Geometric manipulation\n"
philpem@5 4340 " ----------------------");
philpem@5 4341 cimg_option("-resize","'[indice][,interpolation[,borders[,center]]]' or ","");
philpem@5 4342 cimg_help(" "
philpem@5 4343 "'{[indice],width[%]}[x{[indice],height[%]}[x{[indice],depth[%]}[x{[indice],dim[%]}[,interpolation[,borders[,center]]]]]]'");
philpem@5 4344 cimg_help(" "
philpem@5 4345 "or (noargs)");
philpem@5 4346 cimg_help(" "
philpem@5 4347 "Resize image(s) to specified geometry ((noargs) for interactive mode) (eq. to '-r')");
philpem@5 4348 cimg_help(" "
philpem@5 4349 "('interpolation' can be '{0=none, 1=nearest, 2=average, 3=linear, 4=grid, 5=cubic}').");
philpem@5 4350 cimg_option("-resize2x","(no args)","Resize image(s) with Scale2x.");
philpem@5 4351 cimg_option("-resize3x","(no args)","Resize image(s) with Scale3x.");
philpem@5 4352 cimg_option("-crop","'x0[%],x1[%][,border_conditions]' or 'x0[%],y0[%],x1[%],y1[%][,border_conditions]' or ","");
philpem@5 4353 cimg_help(" "
philpem@5 4354 "'x0[%],y0[%],z0[%],x1[%],y1[%],z1[%][,border_conditions]' or ");
philpem@5 4355 cimg_help(" "
philpem@5 4356 "'x0[%],y0[%],z0[%],v0[%],x1[%],y1[%],z1[%],v1[%][,border_conditions]' or (noargs).");
philpem@5 4357 cimg_help(" "
philpem@5 4358 "Crop image(s) using specified geometry ((noargs) for interactive mode) (eq. to '-c') ");
philpem@5 4359 cimg_help(" "
philpem@5 4360 "('border_conditions' can be '{0=zero, 1=nearest}')");
philpem@5 4361 cimg_help(" "
philpem@5 4362 "((no args) for interactive mode).");
philpem@5 4363 cimg_option("-autocrop","'value1,value2,...'","Autocrop image(s) using specified background color.");
philpem@5 4364 cimg_option("-channels","'{[ind0],v0[%]}[,{[ind1],v1[%]}]'","Select channels v0..v1 of multi-spectral image(s).");
philpem@5 4365 cimg_option("-slices","'{[ind0],z0[%]}[,{[ind1],z1[%]}]'","Select slices z0..z1 of volumetric image(s).");
philpem@5 4366 cimg_option("-lines","'{[ind0],y0[%]}[,{[ind1],y1[%]}]'","Select lines y0..y1 of image(s).");
philpem@5 4367 cimg_option("-columns","'{[ind0],x0[%]}[,{[ind1],x1[%]}]'","Select columns x0..x1 of image(s).");
philpem@5 4368 cimg_option("-rotate","'angle[,border_conditions]'","Rotate image(s) with a given angle ");
philpem@5 4369 cimg_help(" "
philpem@5 4370 "('border_conditions' can be '{-3=cyclic (in-place), -2=nearest(ip), -1=zero(ip), 0=zero, 1=nearest, 2=cyclic}'");
philpem@5 4371 cimg_help(" "
philpem@5 4372 "and 'interpolation' can be '{0=none, 1=linear, 2=cubic}').");
philpem@5 4373 cimg_option("-mirror","'axis'",
philpem@5 4374 "Mirror image(s) along specified axis ('axis' can be '{x,y,z,v}').");
philpem@5 4375 cimg_option("-translate","'tx[%][,ty[%][,tz[%][,tv[%][,border_conditions]]]]'",
philpem@5 4376 "Translate image(s) by vector (dx,dy,dz,dv)");
philpem@5 4377 cimg_help(" "
philpem@5 4378 "('border_conditions' can be '{0=zero, 1=nearest, 2=cyclic}').");
philpem@5 4379 cimg_option("-transpose","(no args)","Transpose image(s).");
philpem@5 4380 cimg_option("-invert","(no args)","Compute inverse matrix.");
philpem@5 4381 cimg_option("-permute","'permutation'","Permute image axes by the specified permutation "
philpem@5 4382 "('permutation' can be 'yxzv',...).");
philpem@5 4383 cimg_option("-unroll","'axis'",
philpem@5 4384 "Unroll image(s) along specified axis ('axis' can be '{x,y,z,v}').");
philpem@5 4385 cimg_option("-split","'axis[,nb_parts]' or 'value[,keep]'",
philpem@5 4386 "Split image(s) along specified axis or value ('axis' can be '{x,y,z,v}') (eq. to '-s').");
philpem@5 4387 cimg_option("-append","'axis,[alignement]'","Append image(s) along specified axis and alignement (eq. to '-a')");
philpem@5 4388 cimg_help(" "
philpem@5 4389 "('axis' can be '{x,y,z,v}' and 'alignement' can be '{p=left, c=center, n=right)'.");
philpem@5 4390 cimg_option("-warp","'[indice][,relative[,interpolation[,border_conditions[,nb_frames]]]]'",
philpem@5 4391 "Warp image(s) in 'nb_frames' with field '[indice]' ");
philpem@5 4392 cimg_help(" "
philpem@5 4393 "('relative' can be '{0,1}', 'interpolation' can be '{0,1}', "
philpem@5 4394 "'border_conditions' can be '{0=zero, 1=nearest}').");
philpem@5 4395
philpem@5 4396 cimg_help("\n Image filtering\n"
philpem@5 4397 " ---------------");
philpem@5 4398 cimg_option("-blur","'std[,border_conditions]'",
philpem@5 4399 "Apply gaussian blur of specified standard deviation");
philpem@5 4400 cimg_help(" "
philpem@5 4401 "('border_conditions' can be '{0=zero, 1=nearest}').");
philpem@5 4402 cimg_option("-bilateral","'stdevs,stdevr'",
philpem@5 4403 "Apply bilateral filtering with specified standard deviations 'stdevs' and 'stdevr'.");
philpem@5 4404 cimg_option("-smooth","'amplitude[,sharpness[,anisotropy[,alpha[,sigma[,dl[,da[,prec[,interp[,fast]]]]]]]]]'","");
philpem@5 4405 cimg_help(" "
philpem@5 4406 "Smooth image(s) anisotropically with specified parameters.");
philpem@5 4407 cimg_option("-denoise","'patch_size[,stdev_p[,stdev_s[,lookup_size]]]'",
philpem@5 4408 "Denoise image(s) with a patch-averaging procedure.");
philpem@5 4409 cimg_option("-median","'size'","Apply median filter with specified size.");
philpem@5 4410 cimg_option("-sharpen","'amplitude[,0]' or 'amplitude,1[,edge[,alpha[,sigma]]]'",
philpem@5 4411 "Sharpen image(s) with inverse diffusion or shock filters.");
philpem@5 4412 cimg_option("-convolve","'[indice][,border_conditions]'",
philpem@5 4413 "Convolve image(s) by the specified mask");
philpem@5 4414 cimg_help(" "
philpem@5 4415 "('border_conditions' can be '{0=zero, 1=nearest}').");
philpem@5 4416 cimg_option("-correlate","'[indice][,border_conditions]'",
philpem@5 4417 "Correlate image(s) by the specified mask (same parameters as above).");
philpem@5 4418 cimg_option("-erode","'size[,border_conditions]' or '[indice][,border_conditions]'","");
philpem@5 4419 cimg_help(" "
philpem@5 4420 "Erode image(s) by the specified mask (same parameters as above)').");
philpem@5 4421 cimg_option("-dilate","'size[,border_conditions]' or '[indice][,border_conditions]'","");
philpem@5 4422 cimg_help(" "
philpem@5 4423 "Dilate image(s) by the specified mask (same parameters as above).");
philpem@5 4424 cimg_option("-gradient","'x', 'xy', 'xyz' or (no args)","Compute image gradient.");
philpem@5 4425 cimg_option("-hessian","'{xx,xy,xz,yy,yz,zz}' or (no args)","Compute image Hessian.");
philpem@5 4426 cimg_option("-fft","(no args)","Compute direct Fourier transform.");
philpem@5 4427 cimg_option("-ifft","(no args)","Compute inverse Fourier transform.");
philpem@5 4428
philpem@5 4429 cimg_help("\n Image creation and drawing\n"
philpem@5 4430 " --------------------------");
philpem@5 4431 cimg_option("-dimensions","(no args)","Get dimensions of the image(s) as a 1x4 vector.");
philpem@5 4432 cimg_option("-stats","(no args)","Get statistics of the image(s) as a 1x6 vector.");
philpem@5 4433 cimg_option("-histogram","'nb_values[%]'","Compute histogram of image(s) with 'nb_values' values.");
philpem@5 4434 cimg_option("-distance","'isovalue'","Compute distance function(s) to specified isovalue.");
philpem@5 4435 cimg_option("-hamilton","'nb_iter[,band_size]'","Apply Hamilton-Jacobi PDE to compute distance to 0.");
philpem@5 4436 cimg_option("-label","(no args)","Label connected components of image(s).");
philpem@5 4437 cimg_option("-displacement","'[indice][,smoothness[,precision[,nbscales[,itermax]]]]","Estimate smooth displacement field between image(s) "
philpem@5 4438 "and specified target '[indice]'.");
philpem@5 4439 cimg_option("-sort","(no args)","Sort values of image(s) in increasing order.");
philpem@5 4440 cimg_option("-psnr","'max_value' or (noargs)","Compute PSNR between specified image(s).");
philpem@5 4441 cimg_option("-point","'x[%],y[%][,z[%][,opacity[,color]]]'","Draw 3D colored point on specified image(s).");
philpem@5 4442 cimg_option("-line","'x0[%],y0[%],x1[%],y1[%][,opacity[,color]]'","Draw 2D colored line on specified image(s).");
philpem@5 4443 cimg_option("-polygon","'N,x0[%],y0[%],..,xN[%],yN[%][,opacity[,color]]'","Draw a 2D colored N-vertices polygon on specified image(s).");
philpem@5 4444 cimg_option("-ellipse","'x[%],y[%],r,R[,u,v[,opacity[,color]]]'","Draw 2D colored ellipse on specified image(s).");
philpem@5 4445 cimg_option("-text","text,x[%],y[%],size[,opacity[,color]]'",
philpem@5 4446 "Draw specified text at position (x,y) and with specified font size.");
philpem@5 4447 cimg_option("-image","'[indice][,x[%][,y[%][,z[%][,opacity[,ind_mask]]]]]'","Draw sprite image on specified image(s).");
philpem@5 4448 cimg_option("-object3d","'[indice][,x[%][,y[%][,z[,opacity]]]]'","Draw 3D object on specified image(s).");
philpem@5 4449 cimg_option("-plasma","'alpha[,beta[,opacity]]'","Draw plasma on specified image(s).");
philpem@5 4450 cimg_option("-mandelbrot","'z0r,z0i,z1r,z1i[,itermax[,julia,c0r,c0i[,opacity]]]'","Draw Mandelbrot/Julia fractals on specified image(s).");
philpem@5 4451 cimg_option("-flood","'x[%][,y[%][,z[%][,tolerance[,opacity[,color]]]]]'",
philpem@5 4452 "Flood-fill image(s) starting from (x,y,z) with specified tolerance.");
philpem@5 4453
philpem@5 4454 cimg_help("\n List manipulation\n"
philpem@5 4455 " -----------------");
philpem@5 4456 cimg_option("-remove","(no args)","Remove image(s) from list (eq. to '-rm').");
philpem@5 4457 cimg_option("-keep","(no args)","Keep only specified image(s) (eq. to '-k').");
philpem@5 4458 cimg_option("-move","'position'","Move image(s) at specified position (eq. to '-mv').");
philpem@5 4459 cimg_option("-reverse","(no args)","Reverse image(s) order.");
philpem@5 4460 cimg_option("-name","\"name\"","Set display name of image(s).");
philpem@5 4461
philpem@5 4462 cimg_help("\n 3D Rendering\n"
philpem@5 4463 " ------------");
philpem@5 4464 cimg_option("-cube3d","'size'","Insert a 3D cube at the end of the list.");
philpem@5 4465 cimg_option("-cone3d","'radius[,height[,subdivisions]]'","Insert a 3D cube at the end of the list.");
philpem@5 4466 cimg_option("-cylinder3d","'radius[,height[,subdivisions]]'","Insert a 3D cylinder at the end of the list.");
philpem@5 4467 cimg_option("-torus3d","'radius1,radius2[,subdivisions1,subdivisions2]'","Insert a 3D torus at the end of the list.");
philpem@5 4468 cimg_option("-plane3d","'sizex,sizey[,subdivisionsx,subdisivionsy]'","Insert a 3D plane at the end of the list.");
philpem@5 4469 cimg_option("-sphere3d","'radius[,subdivisions]'","Insert a 3D sphere at the end of the list.");
philpem@5 4470 cimg_option("-elevation3d","'z-factor' or '[indice]'",
philpem@5 4471 "Generate 3D elevation(s) of image(s) using specified z-factor or elevation map.");
philpem@5 4472 cimg_option("-isovalue3d","'value'","Generate 3D object(s) by retrieving isophote or isosurface of image(s).");
philpem@5 4473 cimg_option("-center3d","(no args)","Center 3D object(s) (eq. to '-c3d').");
philpem@5 4474 cimg_option("-normalize3d","(no args)","Normalize 3D object(s) to a unit size (eq. to '-n3d').");
philpem@5 4475 cimg_option("-rotate3d","'u,v,w,angle'","Rotate 3D object(s) around axis (u,v,w) with specified angle (eq. to '-rot3d').");
philpem@5 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').");
philpem@5 4477 cimg_option("-sub3d","'tx,ty,tz'","Translate 3D object(s) with the opposite of the specified vector (eq. to '--3d').");
philpem@5 4478 cimg_option("-mul3d","'fact' or 'factx,facty[,factz]'","Scale 3D object(s) with specified factor (eq. to '-*3d').");
philpem@5 4479 cimg_option("-div3d","'fact' or 'factx,facty[,factz]'","Scale 3D object(s) with specified inverse factor (eq. to '-/3d').");
philpem@5 4480 cimg_option("-color3d","'R,G,B[,opacity]'","Set color of 3D object(s) (eq. to '-col3d').");
philpem@5 4481 cimg_option("-opacity3d","'opacity'","Set opacity of 3D object(s) (eq. to '-opac3d').");
philpem@5 4482 cimg_option("-invert3d","(no args)","Invert primitive orientations of 3D object(s) (eq. to '-i3d').");
philpem@5 4483 cimg_option("-split3d","(no args)","Split 3D object data into 6 data vectors 'header,N,vertices,primitives,colors,opacities' (eq. to '-s3d').");
philpem@5 4484 cimg_option("-light3d","'posx,posy,posz'","Set the 3D position of the light for 3D rendering (eq. to '-l3d').");
philpem@5 4485 cimg_option("-focale3d","'value'","Set focale value for 3D rendering (eq. to '-f3d').");
philpem@5 4486 cimg_option("-specl3d","'value'","Set amount of specular light for 3D rendering (eq. to '-sl3d').");
philpem@5 4487 cimg_option("-specs3d","'value'","Set shininess of specular light for 3D rendering (eq. to '-ss3d').");
philpem@5 4488 cimg_option("-orient3d","(no args)","Switch double-sided mode for 3D rendering (eq. to '-o3d').");
philpem@5 4489 cimg_option("-render3d","'mode'","Set 3D rendering mode");
philpem@5 4490 cimg_help(" "
philpem@5 4491 "(can be '{-1=bounding-box, 0=pointwise, 1=linear, 2=flat, 3=flat-shaded, 4=Gouraud-shaded, 5=Phong-shaded}') (eq. to '-r3d').");
philpem@5 4492 cimg_option("-renderd3d","'mode'","Set dynamic rendering mode in 3D viewer (same values as above) (eq. to '-rd3d').");
philpem@5 4493 cimg_option("-background3d","'R,G,B'","Define background color in 3D viewer (eq. to '-b3d').");
philpem@5 4494
philpem@5 4495 cimg_help("\n Program controls\n"
philpem@5 4496 " ----------------");
philpem@5 4497 cimg_option("-nop","(no args)","Do nothing.");
philpem@5 4498 cimg_option("-skip","(any args)","Do nothing but skip the next argument.");
philpem@5 4499 cimg_option("-echo","'text'","Output specified message (eq. to '-e').");
philpem@5 4500 cimg_option("-print","(no args)","Print image(s) informations (eq. to '-p').");
philpem@5 4501 cimg_option("-quit","(no args)","Force interpreter to quit (eq. to '-q').");
philpem@5 4502 cimg_option("-do","(no args)","Start a 'do-while' code bloc.");
philpem@5 4503 cimg_option("-while","'cond'","End a 'do-while' code bloc and go back to associated '-do' if 'cond' is a strictly positive value.");
philpem@5 4504 cimg_option("-if","'cond'","Start a 'if-then-else' code bloc and test if 'cond' is a strictly positive value.");
philpem@5 4505 cimg_option("-else","(no args)","Execute following commands if previous '-if' condition failed.");
philpem@5 4506 cimg_option("-endif","(no args)","End a 'if-then-else' code bloc");
philpem@5 4507 cimg_option("-repeat","'N'","Start a 'repeat-done' code bloc.");
philpem@5 4508 cimg_option("-done","(no args)","End a 'repeat-done' code bloc, and go to associated '-repeat' if iterations remain.");
philpem@5 4509 cimg_option("-int","'arg1,...,argN'","Check if all specified arguments are integer. If not, print an error message and exit.");
philpem@5 4510 cimg_option("-float","'arg1,...,argN","Check if all specified arguments are float values. If not, print an error message and exit.");
philpem@5 4511
philpem@5 4512 cimg_help("\n Input/output\n"
philpem@5 4513 " ------------");
philpem@5 4514 cimg_option("-input","'filename' or 'width[%][xheight[%][xdepth[%][xdim[%][xN]]]]'","");
philpem@5 4515 cimg_help(" "
philpem@5 4516 "or '[indice][xN]' or '(v11{,;/^}v21...vLM)[xN]'");
philpem@5 4517 cimg_help(" "
philpem@5 4518 "Input filename, empty image, image copy, or image with specified values (eq. to '-i' or (no args)).");
philpem@5 4519 cimg_option("-output","'filename'","Output image(s) in specified filename (eq. to '-o').");
philpem@5 4520 cimg_option("-display","(no args)","Display image(s) (eq. to '-d').");
philpem@5 4521 cimg_option("-display3d","(no args)","Display 3D object(s) (eq. to '-d3d').");
philpem@5 4522 cimg_option("-plot","'[plot_type[,vertex_type[,xmin[,xmax[,ymin[,ymax]]]]]]'",
philpem@5 4523 "Display image(s) as 1D plot(s)");
philpem@5 4524 cimg_help(" "
philpem@5 4525 "('plot_type' can be '{0=none, 1=lines, 2=splines, 3=bar}').");
philpem@5 4526 cimg_help(" "
philpem@5 4527 "('vertex_type' can be in '[0-7]').");
philpem@5 4528 cimg_option("-select","'select_type'","Select feature from image(s) in an interactive way");
philpem@5 4529 cimg_help(" "
philpem@5 4530 "('select_type' can be in '{0=point, 1=line, 2=rectangle, 3=circle').");
philpem@5 4531
philpem@5 4532 // Print descriptions of default macros.
philpem@5 4533 char line[256*1024] = { 0 }, name[4096] = { 0 }, args[4096] = { 0 }, desc[4096] = { 0 };
philpem@5 4534 bool first_description = true;
philpem@5 4535 for (const char *data = data_def; *data; ) {
philpem@5 4536 if (*data=='\n') ++data;
philpem@5 4537 else {
philpem@5 4538 if (std::sscanf(data,"%262143[^\n]",line)>0) data += cimg::strlen(line);
philpem@5 4539 if (line[0]=='#' && std::sscanf(line,"#@gmic %4095[^:] : %4095[^:] : %4095[^:]",name,args,desc)>0) {
philpem@5 4540 if (first_description) cimg_help("\n Commands : Default macros\n"
philpem@5 4541 " -------------------------");
philpem@5 4542 std::fprintf(cimg_stdout,"%s %s-%-15s%s %-24s %s%s%s",
philpem@5 4543 first_description?"":"\n",
philpem@5 4544 cimg::t_bold,name,cimg::t_normal,args,cimg::t_green,desc,cimg::t_normal);
philpem@5 4545 first_description = false;
philpem@5 4546 }
philpem@5 4547 }
philpem@5 4548 }
philpem@5 4549
philpem@5 4550 // Print descriptions of use-defined macros.
philpem@5 4551 first_description = true;
philpem@5 4552 for (int i = 1; i<argc-1; ++i) if (!cimg::strcmp("-m",argv[i]) || !cimg::strcmp("-macros",argv[i])) {
philpem@5 4553 std::FILE *file = cimg::fopen(argv[i+1],"r");
philpem@5 4554 if (file) {
philpem@5 4555 int err = 0;
philpem@5 4556 while ((err=std::fscanf(file,"%262143[^\n] ",line)>=0)) {
philpem@5 4557 if (err) { // Non empty-line
philpem@5 4558 name[0] = args[0] = desc[0] = 0;
philpem@5 4559 if (line[0]=='#' && std::sscanf(line,"#@gmic %4095[^:] : %4095[^:] : %4095[^:]",name,args,desc)>0) {
philpem@5 4560 if (first_description) cimg_help("\n\n Commands : User-defined macros\n"
philpem@5 4561 " ------------------------------");
philpem@5 4562 std::fprintf(cimg_stdout,"%s %s-%-15s%s %-24s %s%s%s",
philpem@5 4563 first_description?"":"\n",
philpem@5 4564 cimg::t_bold,name,cimg::t_normal,args,cimg::t_green,desc,cimg::t_normal);
philpem@5 4565 first_description = false;
philpem@5 4566 }
philpem@5 4567 }
philpem@5 4568 }
philpem@5 4569 }
philpem@5 4570 cimg::fclose(file);
philpem@5 4571 }
philpem@5 4572
philpem@5 4573 cimg_help("\n\n Viewers shortcuts\n"
philpem@5 4574 " -----------------");
philpem@5 4575 cimg_help(" When displaying image(s) or 3D object(s) with G'MIC, you can use these shortcuts in viewers :");
philpem@5 4576 cimg_help(" - CTRL+D : Increase window size.");
philpem@5 4577 cimg_help(" - CTRL+C : Decrease window size.");
philpem@5 4578 cimg_help(" - CTRL+R : Reset window size.");
philpem@5 4579 cimg_help(" - CTRL+F : Toggle fullscreen mode.");
philpem@5 4580 cimg_help(" - CTRL+S : Save current window snapshot.");
philpem@5 4581 cimg_help(" - CTRL+O : Save current instance of viewed image or 3D object.\n");
philpem@5 4582 cimg_help(" Specific options for the viewer of image(s) :");
philpem@5 4583 cimg_help(" - CTRL+P : Play stack of frames as a movie.");
philpem@5 4584 cimg_help(" - CTRL+(mousewheel) : Zoom in/out.");
philpem@5 4585 cimg_help(" - SHIFT+(mousewheel) : Go left/right.");
philpem@5 4586 cimg_help(" - ALT+(mousewheel) : Go up/down.");
philpem@5 4587 cimg_help(" - Numeric PAD : Zoom in/out (+/-) and move zoomed region (numbers).");
philpem@5 4588 cimg_help(" - BACKSPACE : Reset zoom.\n");
philpem@5 4589 cimg_help(" Specific options for the viewer of 3D object(s) :");
philpem@5 4590 cimg_help(" - (mouse) + (left mouse button) : Rotate object.");
philpem@5 4591 cimg_help(" - (mouse) + (right mouse button) : Zoom object.");
philpem@5 4592 cimg_help(" - (mouse) + (middle mouse button) : Translate object.");
philpem@5 4593 cimg_help(" - (mousewheel) : Zoom in/out.");
philpem@5 4594 cimg_help(" - CTRL + Z : Enable/disable Z-buffer rendering");
philpem@5 4595
philpem@5 4596 cimg_help("\n File options\n"
philpem@5 4597 " ------------");
philpem@5 4598 cimg_help(" G'MIC is able to read/write most of the classical image file formats, including :");
philpem@5 4599 cimg_help(" - 2D grayscale/color images : PNG,JPEG,GIF,PNM,TIFF,BMP,....");
philpem@5 4600 cimg_help(" - 3D volumetric images : DICOM,HDR,NII,PAN,CIMG,INR,....");
philpem@5 4601 cimg_help(" - Video files : MPEG,AVI,MOV,OGG,FLV,...");
philpem@5 4602 cimg_help(" - Generic data files : DLM,ASC,RAW,TXT.");
philpem@5 4603 cimg_help(" - 3D objects : OFF.\n");
philpem@5 4604 cimg_help(" Specific options :");
philpem@5 4605 cimg_help(" - For video files : you can read only sub-frames of the image sequence (recommended) with the expression");
philpem@5 4606 cimg_help(" 'video.ext,[first_frame[%][,last_frame[%][,step]]]'.");
philpem@5 4607 cimg_help(" - For RAW files : you must specify the image dimensions with the expression");
philpem@5 4608 cimg_help(" 'file.raw,width[,height[,depth[,dim]]]]'.");
philpem@5 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");
philpem@5 4610 cimg_help(" 'file.yuv,width,height[,first_frame[,last_frame[,step]]]'.");
philpem@5 4611 cimg_help(" - For JPEG files : you can specify the quality (in %) of an output jpeg file format with the expression");
philpem@5 4612 cimg_help(" 'file.jpg,30%'.");
philpem@5 4613
philpem@5 4614 cimg_help("\n Examples of use\n"
philpem@5 4615 " ---------------");
philpem@5 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");
philpem@5 4617 cimg_help(" image processing tasks. Here are (very few) examples of how the command line tool G'MIC can be used :\n");
philpem@5 4618 cimg_help(" - View image data : ");
philpem@5 4619 cimg_help(" gmic file1.bmp file2.jpeg");
philpem@5 4620 cimg_help(" - Convert image files : ");
philpem@5 4621 cimg_help(" gmic input.bmp -o output.jpg");
philpem@5 4622 cimg_help(" - Create volumetric image(s) from movie sequence : ");
philpem@5 4623 cimg_help(" gmic input.mpg -a z -o output.hdr");
philpem@5 4624 cimg_help(" - Compute image gradient norm : ");
philpem@5 4625 cimg_help(" gmic input.bmp -gradient_norm");
philpem@5 4626 cimg_help(" - Create G'MIC 3D logo : ");
philpem@5 4627 cimg_help(" gmic 180x70x1x3 -text G\\'MIC,30,5,50,1,1 -blur 2 -n 0,100 [0] -plasma[1] \\");
philpem@5 4628 cimg_help(" -+ -blur 1 -elevation -0.1 -rd3d 4");
philpem@5 4629 cimg_help("\n See also the macros defined in the provided macro file 'gmic_def.raw' for other examples.");
philpem@5 4630
philpem@5 4631 cimg_help("\n ** G'MIC comes with ABSOLUTELY NO WARRANTY; "
philpem@5 4632 "for details visit http://gmic.sourceforge.net **");
philpem@5 4633 std::exit(0);
philpem@5 4634 }
philpem@5 4635
philpem@5 4636 // Launch G'MIC instance.
philpem@5 4637 //-----------------------
philpem@5 4638 CImgList<float> images;
philpem@5 4639 try {
philpem@5 4640 gmic(argc,argv,images);
philpem@5 4641 } catch (gmic_exception &e) {
philpem@5 4642 std::fprintf(cimg_stdout,"\n** Error occurred : %s **\n",e.message);
philpem@5 4643 }
philpem@5 4644 return 0;
philpem@5 4645 }
philpem@5 4646 #endif
philpem@5 4647
philpem@5 4648 #endif // #ifdef cimg_plugin ... #else ...