From 6ea3a9068909590a59e944b4416adceb08bcf406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Tschumperl=C3=A9?= Date: Mon, 4 Jun 2018 10:24:52 +0200 Subject: [PATCH] Initial commit --- src/main/cpp/CImg.h | 4863 ++++++++++++++++++++++++------------------- 1 file changed, 2711 insertions(+), 2152 deletions(-) diff --git a/src/main/cpp/CImg.h b/src/main/cpp/CImg.h index 9bf1d67..8091509 100644 --- a/src/main/cpp/CImg.h +++ b/src/main/cpp/CImg.h @@ -20,9 +20,9 @@ # The CeCILL-C license is close to the GNU LGPL. # ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html ) # - # or CeCILL v2.0 + # or CeCILL v2.1 # The CeCILL license is compatible with the GNU GPL. - # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) + # ( http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html ) # # This software is governed either by the CeCILL or the CeCILL-C license # under French law and abiding by the rules of distribution of free software. @@ -54,7 +54,7 @@ // Set version number of the library. #ifndef cimg_version -#define cimg_version 203 +#define cimg_version 224 /*----------------------------------------------------------- # @@ -131,10 +131,17 @@ #pragma warning(disable:4804) #pragma warning(disable:4820) #pragma warning(disable:4996) + +#ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS 1 +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE 1 #endif +#endif // Define correct string functions for each compiler and OS. #if cimg_OS==2 && defined(_MSC_VER) @@ -222,7 +229,7 @@ #else -#if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) +#if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)) #define cimg_uint64 unsigned long long #define cimg_int64 long long #define cimg_fuint64 "%llu" @@ -297,61 +304,63 @@ // Configure the 'abort' signal handler (does nothing by default). // A typical signal handler can be defined in your own source like this: -// #define cimg_abort_test() if (is_abort) throw CImgAbortException("") +// #define cimg_abort_test if (is_abort) throw CImgAbortException("") // // where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method. -// 'cimg_abort_test2()' does the same but is called more often (in inner loops). +// 'cimg_abort_test2' does the same but is called more often (in inner loops). #if defined(cimg_abort_test) && defined(cimg_use_openmp) // Define abort macros to be used with OpenMP. -#ifndef cimg_abort_init -#define cimg_abort_init bool cimg_abort_go = true; cimg::unused(cimg_abort_go) +#ifndef _cimg_abort_init_omp +#define _cimg_abort_init_omp bool _cimg_abort_go_omp = true; cimg::unused(_cimg_abort_go_omp) #endif -#ifndef cimg_abort_try -#define cimg_abort_try if (cimg_abort_go) try +#ifndef _cimg_abort_try_omp +#define _cimg_abort_try_omp if (_cimg_abort_go_omp) try #endif -#ifndef cimg_abort_catch -#define cimg_abort_catch() catch (CImgAbortException&) { cimg_pragma(omp atomic) cimg_abort_go&=false; } +#ifndef _cimg_abort_catch_omp +#define _cimg_abort_catch_omp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_omp&=false; } #endif #ifdef cimg_abort_test2 -#ifndef cimg_abort_try2 -#define cimg_abort_try2 cimg_abort_try +#ifndef _cimg_abort_try_omp2 +#define _cimg_abort_try_omp2 _cimg_abort_try_omp #endif -#ifndef cimg_abort_catch2 -#define cimg_abort_catch2() cimg_abort_catch() +#ifndef _cimg_abort_catch_omp2 +#define _cimg_abort_catch_omp2 _cimg_abort_catch_omp +#endif +#ifndef _cimg_abort_catch_fill_omp +#define _cimg_abort_catch_fill_omp \ + catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg::string(e._message).move_to(is_error); \ + cimg_pragma(omp atomic) _cimg_abort_go_omp&=false; } #endif -#ifndef cimg_abort_catch_fill -#define cimg_abort_catch_fill() \ - catch (CImgException& e) { cimg_pragma(omp critical) CImg::string(e._message).move_to(is_error); \ - cimg_pragma(omp atomic) cimg_abort_go&=false; } #endif #endif +#ifndef _cimg_abort_init_omp +#define _cimg_abort_init_omp #endif - -#ifndef cimg_abort_test -#define cimg_abort_test() +#ifndef _cimg_abort_try_omp +#define _cimg_abort_try_omp #endif -#ifndef cimg_abort_test2 -#define cimg_abort_test2() +#ifndef _cimg_abort_catch_omp +#define _cimg_abort_catch_omp +#endif +#ifndef _cimg_abort_try_omp2 +#define _cimg_abort_try_omp2 +#endif +#ifndef _cimg_abort_catch_omp2 +#define _cimg_abort_catch_omp2 +#endif +#ifndef _cimg_abort_catch_fill_omp +#define _cimg_abort_catch_fill_omp #endif #ifndef cimg_abort_init #define cimg_abort_init #endif -#ifndef cimg_abort_try -#define cimg_abort_try +#ifndef cimg_abort_test +#define cimg_abort_test #endif -#ifndef cimg_abort_catch -#define cimg_abort_catch() -#endif -#ifndef cimg_abort_try2 -#define cimg_abort_try2 -#endif -#ifndef cimg_abort_catch2 -#define cimg_abort_catch2() -#endif -#ifndef cimg_abort_catch_fill -#define cimg_abort_catch_fill() +#ifndef cimg_abort_test2 +#define cimg_abort_test2 #endif #ifndef std_fopen #define std_fopen std::fopen @@ -2754,7 +2763,12 @@ namespace cimg_library_suffixed { return !is_nan(val) && (val::min() || val>cimg::type::max()); #endif } - static bool is_nan(const double val) { + static bool is_nan(const double val) { // Custom version that works with '-ffast-math' + if (sizeof(double)==8) { + cimg_uint64 u; + std::memcpy(&u,&val,sizeof(double)); + return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000; + } #ifdef isnan return (bool)isnan(val); #else @@ -2793,7 +2807,12 @@ namespace cimg_library_suffixed { return !is_nan(val) && (val::min() || val>cimg::type::max()); #endif } - static bool is_nan(const float val) { + static bool is_nan(const float val) { // Custom version that works with '-ffast-math' + if (sizeof(float)==4) { + unsigned int u; + std::memcpy(&u,&val,sizeof(float)); + return (u&0x7fffffff)>0x7f800000; + } #ifdef isnan return (bool)isnan(val); #else @@ -2849,12 +2868,13 @@ namespace cimg_library_suffixed { return !is_nan(val) && (val::min() || val>cimg::type::max()); #endif } - static bool is_nan(const long double val) { -#ifdef isnan - return (bool)isnan(val); -#else - return !(val==val); -#endif + static bool is_nan(const half val) { // Custom version that works with '-ffast-math' + if (sizeof(half)==2) { + short u; + std::memcpy(&u,&val,sizeof(short)); + return (bool)((u&0x7fff)>0x7c00); + } + return cimg::type::is_nan((float)val); } static half min() { return (half)-65504; } static half max() { return (half)65504; } @@ -4839,17 +4859,18 @@ namespace cimg_library_suffixed { but it does not open an extra console windows on Windows-based systems. **/ - inline int system(const char *const command, const char *const module_name=0) { + inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) { cimg::unused(module_name); #ifdef cimg_no_system_calls return -1; #else + if (is_verbose) return std::system(command); #if cimg_OS==1 const unsigned int l = (unsigned int)std::strlen(command); if (l) { - char *const ncommand = new char[l + 16]; - std::strncpy(ncommand,command,l); - std::strcpy(ncommand + l," 2> /dev/null"); // Make command silent. + char *const ncommand = new char[l + 24]; + std::memcpy(ncommand,command,l); + std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent. const int out_val = std::system(ncommand); delete[] ncommand; return out_val; @@ -4949,20 +4970,33 @@ namespace cimg_library_suffixed { inline void invert_endianness(T* const buffer, const cimg_ulong size) { if (size) switch (sizeof(T)) { case 1 : break; - case 2 : { for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) { - const unsigned short val = *(--ptr); - *ptr = (unsigned short)((val>>8)|((val<<8))); - } + case 2 : { + for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) { + const unsigned short val = *(--ptr); + *ptr = (unsigned short)((val>>8) | ((val<<8))); + } } break; - case 4 : { for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) { - const unsigned int val = *(--ptr); - *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24); - } + case 4 : { + for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) { + const unsigned int val = *(--ptr); + *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24); + } } break; - default : { for (T* ptr = buffer + size; ptr>buffer; ) { - unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); - for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe)); - } + case 8 : { + const cimg_uint64 + m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24, + m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56; + for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) { + const cimg_uint64 val = *(--ptr); + *ptr = (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) | + ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56)); + } + } break; + default : { + for (T* ptr = buffer + size; ptr>buffer; ) { + unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); + for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe)); + } } } } @@ -5092,24 +5126,23 @@ namespace cimg_library_suffixed { static cimg_ulong next = 0xB16B00B5; cimg::mutex(4); if (set_seed) next = (cimg_ulong)seed; - next = next*1103515245 + 12345U; + else next = next*1103515245 + 12345U; cimg::mutex(4,0); return (unsigned int)(next&0xFFFFFFU); } - inline void srand() { - const unsigned int t = (unsigned int)cimg::time(); + inline unsigned int srand() { + unsigned int t = (unsigned int)cimg::time(); #if cimg_OS==1 - cimg::_rand(t + (unsigned int)getpid(),true); + t+=(unsigned int)getpid(); #elif cimg_OS==2 - cimg::_rand(t + (unsigned int)_getpid(),true); -#else - cimg::_rand(t,true); + t+=(unsigned int)_getpid(); #endif + return cimg::_rand(t,true); } - inline void srand(const unsigned int seed) { - _rand(seed,true); + inline unsigned int srand(const unsigned int seed) { + return _rand(seed,true); } inline double rand(const double val_min, const double val_max) { @@ -5120,7 +5153,7 @@ namespace cimg_library_suffixed { #else // Use the system RNG. - inline void srand() { + inline unsigned int srand() { const unsigned int t = (unsigned int)cimg::time(); #if cimg_OS==1 || defined(__BORLANDC__) std::srand(t + (unsigned int)getpid()); @@ -5129,10 +5162,12 @@ namespace cimg_library_suffixed { #else std::srand(t); #endif + return t; } - inline void srand(const unsigned int seed) { + inline unsigned int srand(const unsigned int seed) { std::srand(seed); + return seed; } //! Return a random variable uniformely distributed between [val_min,val_max]. @@ -5839,8 +5874,8 @@ namespace cimg_library_suffixed { if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93 cimg_uint64 - fn1 = (cimg_uint64)1304969544928657U, - fn2 = (cimg_uint64)806515533049393U, + fn1 = (cimg_uint64)1304969544928657ULL, + fn2 = (cimg_uint64)806515533049393ULL, fn = 0; for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } return (double)fn; @@ -5848,6 +5883,12 @@ namespace cimg_library_suffixed { return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation } + //! Calculate greatest common divisor. + inline long gcd(long a, long b) { + while (a) { const long c = a; a = b%a; b = c; } + return b; + } + //! Convert ascii character to lower case. inline char lowercase(const char x) { return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a'); @@ -6163,7 +6204,7 @@ namespace cimg_library_suffixed { //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows). inline int fseek(FILE *stream, cimg_long offset, int origin) { -#if cimg_OS==2 +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) return _fseeki64(stream,(__int64)offset,origin); #else return std::fseek(stream,offset,origin); @@ -6172,7 +6213,7 @@ namespace cimg_library_suffixed { //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows). inline cimg_long ftell(FILE *stream) { -#if cimg_OS==2 +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) return (cimg_long)_ftelli64(stream); #else return (cimg_long)std::ftell(stream); @@ -6208,62 +6249,124 @@ namespace cimg_library_suffixed { return !is_directory(path); } - //! Get last write time of a given file or directory. + //! Get file size. + /** + \param filename Specified filename to get size from. + \return File size or '-1' if file does not exist. + **/ + inline cimg_int64 fsize(const char *const filename) { + std::FILE *const file = std::fopen(filename,"rb"); + if (!file) return (cimg_int64)-1; + std::fseek(file,0,SEEK_END); + const cimg_int64 siz = (cimg_int64)std::ftell(file); + std::fclose(file); + return siz; + } + + //! Get last write time of a given file or directory (multiple-attributes version). /** \param path Specified path to get attributes from. - \param attr Type of requested time attribute. - Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } - \return -1 if requested attribute could not be read. + \param[in,out] attr Type of requested time attributes. + Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } + Replaced by read attributes after return (or -1 if an error occured). + \param nb_attr Number of attributes to read/write. + \return Latest read attribute. **/ - inline int fdate(const char *const path, const unsigned int attr) { + template + inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) { +#define _cimg_fdate_err() for (unsigned int i = 0; i6) return -1; + if (!path || !*path) { _cimg_fdate_err(); return -1; } cimg::mutex(6); #if cimg_OS==2 HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if (file!=INVALID_HANDLE_VALUE) { FILETIME _ft; SYSTEMTIME ft; - if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) - res = (int)(attr==0?ft.wYear:attr==1?ft.wMonth:attr==2?ft.wDay:attr==3?ft.wDayOfWeek: - attr==4?ft.wHour:attr==5?ft.wMinute:ft.wSecond); + if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) { + for (unsigned int i = 0; i + inline int date(T *attr, const unsigned int nb_attr) { + int res = -1; + cimg::mutex(6); +#if cimg_OS==2 + SYSTEMTIME st; + GetLocalTime(&st); + for (unsigned int i = 0; itm_year + 1900:attr[i]==1?st->tm_mon + 1:attr[i]==2?st->tm_mday: + attr[i]==3?st->tm_wday:attr[i]==4?st->tm_hour:attr[i]==5?st->tm_min: + attr[i]==6?st->tm_sec:-1); + attr[i] = (T)res; } #endif cimg::mutex(6,0); return res; } - //! Get current local time. + //! Get current local time (single-attribute version). /** \param attr Type of requested time attribute. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } + \return Specified attribute or -1 if an error occured. **/ - inline int date(const unsigned int attr) { - int res; - cimg::mutex(6); -#if cimg_OS==2 - SYSTEMTIME st; - GetLocalTime(&st); - res = (int)(attr==0?st.wYear:attr==1?st.wMonth:attr==2?st.wDay:attr==3?st.wDayOfWeek: - attr==4?st.wHour:attr==5?st.wMinute:st.wSecond); -#else - time_t _st; - std::time(&_st); - struct tm *st = std::localtime(&_st); - res = (int)(attr==0?st->tm_year + 1900:attr==1?st->tm_mon + 1:attr==2?st->tm_mday:attr==3?st->tm_wday: - attr==4?st->tm_hour:attr==5?st->tm_min:st->tm_sec); -#endif - cimg::mutex(6,0); - return res; + inline int date(unsigned int attr) { + int out = (int)attr; + return date(&out,1); } // Get/set path to store temporary files. @@ -7240,10 +7343,10 @@ namespace cimg_library_suffixed { return _empty; } -#define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false), \ - CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true) +#define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,480,-85,false), \ + CImgDisplay::_fitscreen(dx,dy,dz,480,-85,true) static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz, - const int dmin, const int dmax,const bool return_y) { + const int dmin, const int dmax, const bool return_y) { const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0); unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1; const unsigned int @@ -11365,8 +11468,8 @@ namespace cimg_library_suffixed { if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c); if (_is_shared || values + siz<_data || values>=_data + size()) { assign(size_x,size_y,size_z,size_c); - if (_is_shared) std::memmove(_data,values,siz*sizeof(T)); - else std::memcpy(_data,values,siz*sizeof(T)); + if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T)); + else std::memcpy((void*)_data,(void*)values,siz*sizeof(T)); } else { T *new_data = 0; try { new_data = new T[siz]; } catch (...) { @@ -11377,7 +11480,7 @@ namespace cimg_library_suffixed { cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), size_x,size_y,size_z,size_c); } - std::memcpy(new_data,values,siz*sizeof(T)); + std::memcpy((void*)new_data,(void*)values,siz*sizeof(T)); delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; } return *this; @@ -13071,7 +13174,7 @@ namespace cimg_library_suffixed { //! Return a reference to the last pixel value. /** \note - - Writing \c img.end() is equivalent to img[img.size() - 1], or + - Writing \c img.back() is equivalent to img[img.size() - 1], or img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1). - It has been mainly defined for compatibility with STL naming conventions. **/ @@ -13704,15 +13807,13 @@ namespace cimg_library_suffixed { return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); } - //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. /** - Similar to cubic_atX(float,int,int,int,const T) const, except that you can specify the authorized minimum - and maximum of the returned value. + Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. **/ - Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = cubic_atX(fx,y,z,c,out_value); - return valmax_value?max_value:val; + T cubic_cut_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atX(fx,y,z,c,out_value)); } //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. @@ -13755,21 +13856,17 @@ namespace cimg_library_suffixed { return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); } - //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. /** - Similar to cubic_atX(float,int,int,int) const, except that you can specify the authorized minimum and maximum - of the returned value. + Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. **/ - Tfloat cubic_atX(const float fx, const int y, const int z, const int c, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = cubic_atX(fx,y,z,c); - return valmax_value?max_value:val; + T cubic_cut_atX(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(cubic_atX(fx,y,z,c)); } - Tfloat _cubic_atX(const float fx, const int y, const int z, const int c, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = _cubic_atX(fx,y,z,c); - return valmax_value?max_value:val; + T _cubic_cut_atX(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(_cubic_atX(fx,y,z,c)); } //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. @@ -13798,15 +13895,13 @@ namespace cimg_library_suffixed { return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); } - //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates. + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates. /** - Similar to cubic_atXY(float,float,int,int,const T) const, except that you can specify the authorized - minimum and maximum of the returned value. + Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. **/ - Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = cubic_atXY(fx,fy,z,c,out_value); - return valmax_value?max_value:val; + T cubic_cut_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atXY(fx,fy,z,c,out_value)); } //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. @@ -13850,21 +13945,17 @@ namespace cimg_library_suffixed { return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); } - //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates. + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates. /** - Similar to cubic_atXY(float,float,int,int) const, except that you can specify the authorized minimum and - maximum of the returned value. + Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. **/ - Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = cubic_atXY(fx,fy,z,c); - return valmax_value?max_value:val; + T cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(cubic_atXY(fx,fy,z,c)); } - Tfloat _cubic_atXY(const float fx, const float fy, const int z, const int c, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = _cubic_atXY(fx,fy,z,c); - return valmax_value?max_value:val; + T _cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(_cubic_atXY(fx,fy,z,c)); } //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. @@ -13954,15 +14045,13 @@ namespace cimg_library_suffixed { return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); } - //! Return damped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates. + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates. /** - Similar to cubic_atXYZ(float,float,float,int,const T) const, except that you can specify the authorized - minimum and maximum of the returned value. + Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay + in the min/max range of the datatype \c T. **/ - Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = cubic_atXYZ(fx,fy,fz,c,out_value); - return valmax_value?max_value:val; + T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atXYZ(fx,fy,fz,c,out_value)); } //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. @@ -14068,21 +14157,17 @@ namespace cimg_library_suffixed { return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); } - //! Return damped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates. + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates. /** - Similar to cubic_atXYZ(float,float,float,int) const, except that you can specify the authorized minimum and - maximum of the returned value. + Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. **/ - Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = cubic_atXYZ(fx,fy,fz,c); - return valmax_value?max_value:val; + T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(cubic_atXYZ(fx,fy,fz,c)); } - Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c, - const Tfloat min_value, const Tfloat max_value) const { - const Tfloat val = _cubic_atXYZ(fx,fy,fz,c); - return valmax_value?max_value:val; + T _cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(_cubic_atXYZ(fx,fy,fz,c)); } //! Set pixel value, using linear interpolation for the X-coordinates. @@ -14226,9 +14311,9 @@ namespace cimg_library_suffixed { Return a new \c CImg image whose buffer data() is a \c char* string describing the list of all pixel values of the image instance (written in base 10), separated by specified \c separator character. \param separator A \c char character which specifies the separator between values in the returned C-string. - \param max_size Maximum size of the returned image. - \param format For float-values, tell the printf format used to generate the ascii representation of the numbers. - (or \c 0 for default representation). + \param max_size Maximum size of the returned image (or \c 0 if no limits are set). + \param format For float/double-values, tell the printf format used to generate the ascii representation + of the numbers (or \c 0 for default representation). \note - The returned image is never empty. - For an empty image instance, the returned string is "". @@ -14238,13 +14323,13 @@ namespace cimg_library_suffixed { **/ CImg value_string(const char separator=',', const unsigned int max_size=0, const char *const format=0) const { - if (is_empty()) return CImg::string(""); + if (is_empty() || max_size==1) return CImg(1,1,1,1,0); CImgList items; CImg s_item(256); *s_item = 0; const T *ptrs = _data; unsigned int string_size = 0; const char *const _format = format?format:cimg::type::format(); - for (ulongT off = 0, siz = size(); off::format(*(ptrs++))); CImg item(s_item._data,printed_size); @@ -14254,7 +14339,7 @@ namespace cimg_library_suffixed { } CImg res; (items>'x').move_to(res); - if (max_size && res._width>max_size) res.crop(0,max_size); + if (max_size && res._width>=max_size) res.crop(0,max_size - 1); res.back() = 0; return res; } @@ -15128,7 +15213,7 @@ namespace cimg_library_suffixed { #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value? #define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable? #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector? -#define _cimg_mp_vector_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Vector size +#define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN) #define _cimg_mp_calling_function calling_function_s()._data #define _cimg_mp_op(s) s_op = s; ss_op = ss #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char) @@ -15138,6 +15223,7 @@ namespace cimg_library_suffixed { #define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char) #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; } +#define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan) #define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val))) #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op)) #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1)) @@ -15156,17 +15242,17 @@ namespace cimg_library_suffixed { // Constructors. _cimg_math_parser(const char *const expression, const char *const funcname=0, const CImg& img_input=CImg::const_empty(), CImg *const img_output=0, - const CImgList *const list_input=0, CImgList *const list_output=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0, const bool _is_fill=false): code(_code),p_break((CImg*)0 - 2), - imgin(img_input),listin(list_input?*list_input:CImgList::const_empty()), - imgout(img_output?*img_output:CImg::empty()),listout(list_output?*list_output:CImgList::empty()), + imgin(img_input),listin(list_inputs?*list_inputs:CImgList::const_empty()), + imgout(img_output?*img_output:CImg::empty()),listout(list_outputs?*list_outputs:CImgList::empty()), img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),user_macro(0), mem_img_median(~0U),debug_indent(0),result_dim(0),break_type(0),constcache_size(0), is_parallelizable(true),is_fill(_is_fill),need_input_copy(false), calling_function(funcname?funcname:"cimg_math_parser") { if (!expression || !*expression) - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Empty expression.", pixel_type(),_cimg_mp_calling_function); const char *_expression = expression; @@ -15231,17 +15317,17 @@ namespace cimg_library_suffixed { // Compile expression into a serie of opcodes. s_op = ""; ss_op = expr._data; - const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0); + const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false); if (!_cimg_mp_is_constant(ind_result)) { if (_cimg_mp_is_vector(ind_result)) - CImg(&mem[ind_result] + 1,_cimg_mp_vector_size(ind_result),1,1,1,true). + CImg(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true). fill(cimg::type::nan()); else mem[ind_result] = cimg::type::nan(); } // Free resources used for compiling expression and prepare evaluation. - result_dim = _cimg_mp_vector_size(ind_result); - mem.resize(mempos,1,1,1,-1); + result_dim = _cimg_mp_size(ind_result); + if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1); result = mem._data + ind_result; memtype.assign(); constcache_vals.assign(); @@ -15315,14 +15401,14 @@ namespace cimg_library_suffixed { } if (mode) { cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Unterminated string literal, in expression '%s'.", pixel_type(),_cimg_mp_calling_function, expr._data); } if (level) { cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.", pixel_type(),_cimg_mp_calling_function, expr._data); @@ -15352,10 +15438,11 @@ namespace cimg_library_suffixed { } // Compilation procedure. - unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref) { + unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref, + const bool is_single) { if (depth>256) { cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Call stack overflow (infinite recursion?), " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function, @@ -15379,7 +15466,7 @@ namespace cimg_library_suffixed { if (se<=ss || !*ss) { cimg::strellipsize(expr,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s Missing %s, in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", *s_op=='F'?"argument":"item", @@ -15417,8 +15504,8 @@ namespace cimg_library_suffixed { const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; bool is_sth, is_relative; CImg ref; - CImgList _opcode; CImg variable_name; + CImgList l_opcode; // Look for a single value or a pre-defined variable. int nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); @@ -15542,13 +15629,22 @@ namespace cimg_library_suffixed { } pos = ~0U; + is_sth = false; for (s0 = ss, s = ss1; s::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg2,p1,arg1,_cimg_mp_vector_size(arg2)).move_to(code); + arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code); } else { if (!imgout) _cimg_mp_return(arg2); if (*ss>='i') @@ -15622,28 +15718,28 @@ namespace cimg_library_suffixed { arg2,arg1).move_to(code); else CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg2,arg1,_cimg_mp_vector_size(arg2)).move_to(code); + arg2,arg1,_cimg_mp_size(arg2)).move_to(code); } _cimg_mp_return(arg2); } if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value - is_parallelizable = false; + if (!is_single) is_parallelizable = false; if (*ss2=='#') { // Index specified s0 = ss3; while (s01) { arg2 = arg1 + 1; @@ -15654,11 +15750,11 @@ namespace cimg_library_suffixed { } } else if (s1::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg5,p1,arg1,arg2,arg3,_cimg_mp_vector_size(arg5)).move_to(code); + arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); } else { if (!imgout) _cimg_mp_return(arg5); if (*ss>='i') @@ -15712,7 +15808,7 @@ namespace cimg_library_suffixed { arg5,arg1,arg2,arg3).move_to(code); else CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg5,arg1,arg2,arg3,_cimg_mp_vector_size(arg5)).move_to(code); + arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); } _cimg_mp_return(arg5); } @@ -15728,8 +15824,8 @@ namespace cimg_library_suffixed { if (is_sth && s0>ss) { variable_name[s0 - ss] = 0; // Remove brackets in variable name arg1 = ~0U; // Vector slot - arg2 = compile(++s0,ve1,depth1,0); // Index - arg3 = compile(s + 1,se,depth1,0); // Value to assign + arg2 = compile(++s0,ve1,depth1,0,is_single); // Index + arg3 = compile(s + 1,se,depth1,0,is_single); // Value to assign _cimg_mp_check_type(arg3,2,1,0); if (variable_name[1]) { // Multi-char variable @@ -15737,17 +15833,17 @@ namespace cimg_library_suffixed { arg1 = variable_pos[i]; break; } } else arg1 = reserved_label[*variable_name]; // Single-char variable - if (arg1==~0U) compile(ss,s0 - 1,depth1,0); // Variable does not exist -> error + if (arg1==~0U) compile(ss,s0 - 1,depth1,0,is_single); // Variable does not exist -> error else { // Variable already exists - if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0); // Variable is not a vector -> error + if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0,is_single); // Variable is not a vector -> error if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly nb = (int)mem[arg2]; - if (nb>=0 && nb<(int)_cimg_mp_vector_size(arg1)) { + if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) { arg1+=nb + 1; CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); _cimg_mp_return(arg1); } - compile(ss,s,depth1,0); // Out-of-bounds reference -> error + compile(ss,s,depth1,0,is_single); // Out-of-bounds reference -> error } // Case of non-constant index -> return assigned value + linked reference @@ -15758,7 +15854,7 @@ namespace cimg_library_suffixed { if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; // Prevent from being used in further optimization if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; } - CImg::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_vector_size(arg1), + CImg::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1), arg2,arg3). move_to(code); _cimg_mp_return(arg3); @@ -15790,7 +15886,7 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro " "definition '%s()', in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -15812,7 +15908,7 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: %s name specified for argument %u when defining " "macro '%s()', in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -15860,7 +15956,7 @@ namespace cimg_library_suffixed { // Detect parts of function body inside a string. is_inside_string(macro_body[0]).move_to(macro_body_is_string,0); - _cimg_mp_return(_cimg_mp_slot_nan); + _cimg_mp_return_nan(); } } @@ -15922,7 +16018,7 @@ namespace cimg_library_suffixed { else if (!std::strcmp(variable_name,"boundary")) arg3 = 30; // boundary arg1 = ~0U; - arg2 = compile(s + 1,se,depth1,0); + arg2 = compile(s + 1,se,depth1,0,is_single); if (is_const) _cimg_mp_check_constant(arg2,2,0); if (arg3!=~0U) // One-char variable, or variable in reserved_labels @@ -15956,7 +16052,7 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -15965,13 +16061,13 @@ namespace cimg_library_suffixed { !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"", s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); } - _cimg_mp_check_type(arg2,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_vector_size(arg1)); + _cimg_mp_check_type(arg2,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1)) { // Vector if (_cimg_mp_is_vector(arg2)) // From vector - CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_vector_size(arg1)). + CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)). move_to(code); else // From scalar - CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_vector_size(arg1),arg2). + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2). move_to(code); } else // Scalar CImg::vector((ulongT)mp_copy,arg1,arg2).move_to(code); @@ -15986,21 +16082,21 @@ namespace cimg_library_suffixed { if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { ref.assign(7); - arg1 = compile(ss,s,depth1,ref); // Lvalue slot - arg2 = compile(s + 1,se,depth1,0); // Value to assign + arg1 = compile(ss,s,depth1,ref,is_single); // Lvalue slot + arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign if (*ref==1) { // Vector value (scalar): V[k] = scalar _cimg_mp_check_type(arg2,2,1,0); arg3 = ref[1]; // Vector slot arg4 = ref[2]; // Index if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); - CImg::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_vector_size(arg3),arg4,arg2). + CImg::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg2). move_to(code); _cimg_mp_return(arg2); } if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar - is_parallelizable = false; + if (!is_single) is_parallelizable = false; _cimg_mp_check_type(arg2,2,1,0); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -16019,7 +16115,7 @@ namespace cimg_library_suffixed { } if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar - is_parallelizable = false; + if (!is_single) is_parallelizable = false; _cimg_mp_check_type(arg2,2,1,0); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -16041,8 +16137,8 @@ namespace cimg_library_suffixed { } if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value - is_parallelizable = false; - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + if (!is_single) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset @@ -16054,7 +16150,7 @@ namespace cimg_library_suffixed { arg2,p1,arg3).move_to(code); else CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg2,p1,arg3,_cimg_mp_vector_size(arg2)).move_to(code); + arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code); } else { if (!imgout) _cimg_mp_return(arg2); if (_cimg_mp_is_scalar(arg2)) @@ -16062,14 +16158,14 @@ namespace cimg_library_suffixed { arg2,arg3).move_to(code); else CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg2,arg3,_cimg_mp_vector_size(arg2)).move_to(code); + arg2,arg3,_cimg_mp_size(arg2)).move_to(code); } _cimg_mp_return(arg2); } if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value - is_parallelizable = false; - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + if (!is_single) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // X @@ -16083,7 +16179,7 @@ namespace cimg_library_suffixed { arg2,p1,arg3,arg4,arg5).move_to(code); else CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg2,p1,arg3,arg4,arg5,_cimg_mp_vector_size(arg2)).move_to(code); + arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); } else { if (!imgout) _cimg_mp_return(arg2); if (_cimg_mp_is_scalar(arg2)) @@ -16091,18 +16187,18 @@ namespace cimg_library_suffixed { arg2,arg3,arg4,arg5).move_to(code); else CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg2,arg3,arg4,arg5,_cimg_mp_vector_size(arg2)).move_to(code); + arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); } _cimg_mp_return(arg2); } if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg2)) // From vector - CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_vector_size(arg1)). + CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)). move_to(code); else // From scalar - CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_vector_size(arg1),arg2). + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2). move_to(code); _cimg_mp_return(arg1); } @@ -16119,7 +16215,7 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid %slvalue '%s', " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -16135,8 +16231,8 @@ namespace cimg_library_suffixed { _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='"); ref.assign(7); - arg1 = compile(ss,ns,depth1,ref); // Vector slot - arg2 = compile(s + 1,se,depth1,0); // Right operand + arg1 = compile(ss,ns,depth1,ref,is_single); // Vector slot + arg2 = compile(s + 1,se,depth1,0,is_single); // Right operand _cimg_mp_check_type(arg1,1,2,2); _cimg_mp_check_type(arg2,2,3,2); if (_cimg_mp_is_vector(arg2)) { // Complex **= complex @@ -16161,7 +16257,7 @@ namespace cimg_library_suffixed { // Write computed value back in image if necessary. if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value - is_parallelizable = false; + if (!is_single) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset @@ -16169,15 +16265,15 @@ namespace cimg_library_suffixed { if (p1!=~0U) { if (!listout) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); } else { if (!imgout) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); } } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value - is_parallelizable = false; + if (!is_single) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // X @@ -16187,11 +16283,11 @@ namespace cimg_library_suffixed { if (p1!=~0U) { if (!listout) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } else { if (!imgout) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } } @@ -16218,8 +16314,8 @@ namespace cimg_library_suffixed { s1 = *ps=='>' || *ps=='<'?ns:ps; ref.assign(7); - arg1 = compile(ss,s1,depth1,ref); // Variable slot - arg2 = compile(s + 1,se,depth1,0); // Value to apply + arg1 = compile(ss,s1,depth1,ref,is_single); // Variable slot + arg2 = compile(s + 1,se,depth1,0,is_single); // Value to apply // Check for particular case to be simplified. if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1); @@ -16237,13 +16333,13 @@ namespace cimg_library_suffixed { arg4 = ref[2]; // Index if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); CImg::vector((ulongT)op,arg1,arg2).move_to(code); - CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_vector_size(arg3),arg4,arg1). + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1). move_to(code); _cimg_mp_return(arg1); } if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar - is_parallelizable = false; + if (!is_single) is_parallelizable = false; _cimg_mp_check_type(arg2,2,1,0); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -16263,7 +16359,7 @@ namespace cimg_library_suffixed { } if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar - is_parallelizable = false; + if (!is_single) is_parallelizable = false; _cimg_mp_check_type(arg2,2,1,0); p1 = ref[1]; // Index is_relative = (bool)ref[2]; @@ -16286,8 +16382,8 @@ namespace cimg_library_suffixed { } if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value - is_parallelizable = false; - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + if (!is_single) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset @@ -16296,18 +16392,18 @@ namespace cimg_library_suffixed { if (p1!=~0U) { if (!listout) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); } else { if (!imgout) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); } _cimg_mp_return(arg1); } if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value - is_parallelizable = false; - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + if (!is_single) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // X @@ -16318,17 +16414,17 @@ namespace cimg_library_suffixed { if (p1!=~0U) { if (!listout) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } else { if (!imgout) _cimg_mp_return(arg1); CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } _cimg_mp_return(arg1); } if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector else self_vector_s(arg1,op,arg2); // Vector += scalar _cimg_mp_return(arg1); @@ -16345,7 +16441,7 @@ namespace cimg_library_suffixed { *se = saved_char; s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid %slvalue '%s', " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -16358,18 +16454,19 @@ namespace cimg_library_suffixed { if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2' _cimg_mp_op("Operator '?:'"); s1 = s + 1; while (s1::vector((ulongT)mp_if,pos,arg1,arg2,arg3, p3 - p2,code._width - p3,arg4).move_to(code,p2); @@ -16379,11 +16476,11 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||') _cimg_mp_op("Operator '||'"); - arg1 = compile(ss,s,depth1,0); + arg1 = compile(ss,s,depth1,0,is_single); _cimg_mp_check_type(arg1,1,1,0); if (arg1>0 && arg1<=16) _cimg_mp_return(1); p2 = code._width; - arg2 = compile(s + 2,se,depth1,0); + arg2 = compile(s + 2,se,depth1,0,is_single); _cimg_mp_check_type(arg2,2,1,0); if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] || mem[arg2]); @@ -16397,11 +16494,11 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&') _cimg_mp_op("Operator '&&'"); - arg1 = compile(ss,s,depth1,0); + arg1 = compile(ss,s,depth1,0,is_single); _cimg_mp_check_type(arg1,1,1,0); if (!arg1) _cimg_mp_return(0); p2 = code._width; - arg2 = compile(s + 2,se,depth1,0); + arg2 = compile(s + 2,se,depth1,0,is_single); _cimg_mp_check_type(arg2,2,1,0); if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] && mem[arg2]); @@ -16415,9 +16512,9 @@ namespace cimg_library_suffixed { for (s = se2; s>ss; --s) if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|') _cimg_mp_op("Operator '|'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { if (!arg2) _cimg_mp_return(arg1); @@ -16437,9 +16534,9 @@ namespace cimg_library_suffixed { for (s = se2; s>ss; --s) if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&') _cimg_mp_op("Operator '&'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2); @@ -16452,11 +16549,11 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=') _cimg_mp_op("Operator '!='"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); if (arg1==arg2) _cimg_mp_return(0); - p1 = _cimg_mp_vector_size(arg1); - p2 = _cimg_mp_vector_size(arg2); + p1 = _cimg_mp_size(arg1); + p2 = _cimg_mp_size(arg2); if (p1 || p2) { if (p1 && p2 && p1!=p2) _cimg_mp_return(1); _cimg_mp_scalar6(mp_vector_neq,arg1,p1,arg2,p2,11,1); @@ -16468,11 +16565,11 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==') _cimg_mp_op("Operator '=='"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); if (arg1==arg2) _cimg_mp_return(1); - p1 = _cimg_mp_vector_size(arg1); - p2 = _cimg_mp_vector_size(arg2); + p1 = _cimg_mp_size(arg1); + p2 = _cimg_mp_size(arg2); if (p1 || p2) { if (p1 && p2 && p1!=p2) _cimg_mp_return(0); _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,11,1); @@ -16484,9 +16581,9 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=') _cimg_mp_op("Operator '<='"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2); @@ -16498,9 +16595,9 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=') _cimg_mp_op("Operator '>='"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2); @@ -16512,9 +16609,9 @@ namespace cimg_library_suffixed { for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<') _cimg_mp_op("Operator '<'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2); @@ -16526,9 +16623,9 @@ namespace cimg_library_suffixed { for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than ('>') _cimg_mp_op("Operator '>'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2); @@ -16540,9 +16637,9 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<') _cimg_mp_op("Operator '<<'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { @@ -16561,9 +16658,9 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>') _cimg_mp_op("Operator '>>'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { @@ -16586,9 +16683,9 @@ namespace cimg_library_suffixed { *(ps - 1)<='9')))) && level[s - expr._data]==clevel) { // Addition ('+') _cimg_mp_op("Operator '+'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (!arg2) _cimg_mp_return(arg1); if (!arg1) _cimg_mp_return(arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2); @@ -16618,9 +16715,9 @@ namespace cimg_library_suffixed { *(ps - 1)<='9')))) && level[s - expr._data]==clevel) { // Subtraction ('-') _cimg_mp_op("Operator '-'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (!arg2) _cimg_mp_return(arg1); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2); @@ -16649,8 +16746,8 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**') _cimg_mp_op("Operator '**'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); _cimg_mp_check_type(arg1,1,3,2); _cimg_mp_check_type(arg2,2,3,2); if (arg2==1) _cimg_mp_return(arg1); @@ -16670,8 +16767,8 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//') _cimg_mp_op("Operator '//'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); _cimg_mp_check_type(arg1,1,3,2); _cimg_mp_check_type(arg2,2,3,2); if (arg2==1) _cimg_mp_return(arg1); @@ -16693,15 +16790,15 @@ namespace cimg_library_suffixed { for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*') _cimg_mp_op("Operator '*'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - p2 = _cimg_mp_vector_size(arg2); - if (p2>0 && _cimg_mp_vector_size(arg1)==p2*p2) { // Particular case of matrix multiplication + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + p2 = _cimg_mp_size(arg2); + if (p2>0 && _cimg_mp_size(arg1)==p2*p2) { // Particular case of matrix multiplication pos = vector(p2); CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code); _cimg_mp_return(pos); } - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (arg2==1) _cimg_mp_return(arg1); if (arg1==1) _cimg_mp_return(arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2); @@ -16726,9 +16823,9 @@ namespace cimg_library_suffixed { for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/') _cimg_mp_op("Operator '/'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (arg2==1) _cimg_mp_return(arg1); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); @@ -16741,9 +16838,9 @@ namespace cimg_library_suffixed { for (s = se2, ns = se1; s>ss; --s, --ns) if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%') _cimg_mp_op("Operator '%'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2); if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2); @@ -16755,12 +16852,12 @@ namespace cimg_library_suffixed { if (se1>ss) { if (*ss=='+' && (*ss1!='+' || (ss2='0' && *ss2<='9'))) { // Unary plus ('+') _cimg_mp_op("Operator '+'"); - _cimg_mp_return(compile(ss1,se,depth1,0)); + _cimg_mp_return(compile(ss1,se,depth1,0,is_single)); } if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus ('-') _cimg_mp_op("Operator '-'"); - arg1 = compile(ss1,se,depth1,0); + arg1 = compile(ss1,se,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]); _cimg_mp_scalar1(mp_minus,arg1); @@ -16769,12 +16866,12 @@ namespace cimg_library_suffixed { if (*ss=='!') { // Logical not ('!') _cimg_mp_op("Operator '!'"); if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)' - arg1 = compile(ss2,se,depth1,0); + arg1 = compile(ss2,se,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); _cimg_mp_scalar1(mp_bool,arg1); } - arg1 = compile(ss1,se,depth1,0); + arg1 = compile(ss1,se,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]); _cimg_mp_scalar1(mp_logical_not,arg1); @@ -16782,7 +16879,7 @@ namespace cimg_library_suffixed { if (*ss=='~') { // Bitwise not ('~') _cimg_mp_op("Operator '~'"); - arg1 = compile(ss1,se,depth1,0); + arg1 = compile(ss1,se,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]); _cimg_mp_scalar1(mp_bitwise_not,arg1); @@ -16792,8 +16889,8 @@ namespace cimg_library_suffixed { for (s = se3, ns = se2; s>ss; --s, --ns) if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^') _cimg_mp_op("Operator '^^'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 2,se,depth1,0); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 2,se,depth1,0,is_single); _cimg_mp_check_type(arg1,1,3,2); _cimg_mp_check_type(arg2,2,3,2); if (arg2==1) _cimg_mp_return(arg1); @@ -16817,9 +16914,9 @@ namespace cimg_library_suffixed { for (s = se2; s>ss; --s) if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^') _cimg_mp_op("Operator '^'"); - arg1 = compile(ss,s,depth1,0); - arg2 = compile(s + 1,se,depth1,0); - _cimg_mp_check_type(arg2,2,3,_cimg_mp_vector_size(arg1)); + arg1 = compile(ss,s,depth1,0,is_single); + arg2 = compile(s + 1,se,depth1,0,is_single); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); if (arg2==1) _cimg_mp_return(arg1); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2); if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2); @@ -16842,7 +16939,7 @@ namespace cimg_library_suffixed { // Percentage computation. if (*se1=='%') { - arg1 = compile(ss,se1,depth1,0); + arg1 = compile(ss,se1,depth1,0,is_single); arg2 = _cimg_mp_is_constant(arg1)?0:constant(100); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100); @@ -16859,7 +16956,8 @@ namespace cimg_library_suffixed { op = mp_self_decrement; } ref.assign(7); - arg1 = is_sth?compile(ss2,se,depth1,ref):compile(ss,se2,depth1,ref); // Variable slot + arg1 = is_sth?compile(ss2,se,depth1,ref,is_single): + compile(ss,se2,depth1,ref,is_single); // Variable slot // Apply operator on a copy to prevent modifying a constant or a variable. if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) { @@ -16878,13 +16976,13 @@ namespace cimg_library_suffixed { arg4 = ref[2]; // Index if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); CImg::vector((ulongT)op,arg1,1).move_to(code); - CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_vector_size(arg3),arg4,arg1). + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1). move_to(code); _cimg_mp_return(pos); } if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ - is_parallelizable = false; + if (!is_single) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset @@ -16903,7 +17001,7 @@ namespace cimg_library_suffixed { } if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++ - is_parallelizable = false; + if (!is_single) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // X @@ -16925,7 +17023,7 @@ namespace cimg_library_suffixed { } if (*ref==4) { // Image value (vector): I/J[_#ind,off]++ - is_parallelizable = false; + if (!is_single) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // Offset @@ -16934,17 +17032,17 @@ namespace cimg_library_suffixed { if (p1!=~0U) { if (!listout) _cimg_mp_return(pos); CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), - arg1,p1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); } else { if (!imgout) _cimg_mp_return(pos); CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), - arg1,arg3,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); } _cimg_mp_return(pos); } if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ - is_parallelizable = false; + if (!is_single) is_parallelizable = false; p1 = ref[1]; // Index is_relative = (bool)ref[2]; arg3 = ref[3]; // X @@ -16955,11 +17053,11 @@ namespace cimg_library_suffixed { if (p1!=~0U) { if (!listout) _cimg_mp_return(pos); CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), - arg1,p1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } else { if (!imgout) _cimg_mp_return(pos); CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), - arg1,arg3,arg4,arg5,_cimg_mp_vector_size(arg1)).move_to(code); + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); } _cimg_mp_return(pos); } @@ -16982,7 +17080,7 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Invalid %slvalue '%s', " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -16995,22 +17093,22 @@ namespace cimg_library_suffixed { if (*se1==']' && *ss!='[') { _cimg_mp_op("Value accessor '[]'"); is_relative = *ss=='j' || *ss=='J'; - s0 = s1 = std::strchr(ss,'['); do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); + s0 = s1 = std::strchr(ss,'['); if (s0) { do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); } if ((*ss=='I' || *ss=='J') && *ss1=='[' && (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a vector if (*ss2=='#') { // Index specified s0 = ss3; while (s0ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; if (s0>ss) { // Vector value - arg1 = compile(ss,s0,depth1,0); + arg1 = compile(ss,s0,depth1,0,is_single); if (_cimg_mp_is_scalar(arg1)) { variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; *se = saved_char; cimg::strellipsize(variable_name,64); s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -17089,9 +17187,9 @@ namespace cimg_library_suffixed { s1 = s0 + 1; while (s1 sub-vector extraction - p1 = _cimg_mp_vector_size(arg1); - arg2 = compile(++s0,s1,depth1,0); // Starting indice - arg3 = compile(++s1,se1,depth1,0); // Length + p1 = _cimg_mp_size(arg1); + arg2 = compile(++s0,s1,depth1,0,is_single); // Starting indice + arg3 = compile(++s1,se1,depth1,0,is_single); // Length _cimg_mp_check_constant(arg3,2,3); arg3 = (unsigned int)mem[arg3]; pos = vector(arg3); @@ -17100,22 +17198,22 @@ namespace cimg_library_suffixed { } // One argument -> vector value reference - arg2 = compile(++s0,se1,depth1,0); + arg2 = compile(++s0,se1,depth1,0,is_single); if (_cimg_mp_is_constant(arg2)) { // Constant index nb = (int)mem[arg2]; - if (nb>=0 && nb<(int)_cimg_mp_vector_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); + if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; *se = saved_char; cimg::strellipsize(variable_name,64); s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' " "(vector '%s' has dimension %u), " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function, variable_name._data,nb, - variable_name._data,_cimg_mp_vector_size(arg1), + variable_name._data,_cimg_mp_size(arg1), s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); } if (p_ref) { @@ -17124,7 +17222,7 @@ namespace cimg_library_suffixed { p_ref[2] = arg2; if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; // Prevent from being used in further optimization } - pos = scalar3(mp_vector_off,arg1,_cimg_mp_vector_size(arg1),arg2); + pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2); memtype[pos] = -2; // Prevent from being used in further optimization _cimg_mp_return(pos); } @@ -17132,16 +17230,16 @@ namespace cimg_library_suffixed { // Look for a function call, an access to image value, or a parenthesis. if (*se1==')') { - if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref)); // Simple parentheses + if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_single)); // Simple parentheses _cimg_mp_op("Value accessor '()'"); is_relative = *ss=='j' || *ss=='J'; - s0 = s1 = std::strchr(ss,'('); do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); + s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); } // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions) if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar if (*ss2=='#') { // Index specified s0 = ss3; while (s01) { arg2 = arg1 + 1; @@ -17160,19 +17258,19 @@ namespace cimg_library_suffixed { } if (s11) { arg2 = arg1 + 1; @@ -17238,22 +17336,22 @@ namespace cimg_library_suffixed { } if (s1::vector((ulongT)mp_arg,0,0,p2,arg1,arg2).move_to(_opcode); + CImg::vector((ulongT)mp_arg,0,0,p2,arg1,arg2).move_to(l_opcode); for (s = ++s2; s::vector(arg3).move_to(_opcode); + CImg::vector(arg3).move_to(l_opcode); ++p3; s = ns; } - (_opcode>'y').move_to(opcode); + (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; if (_cimg_mp_is_constant(arg1)) { p3-=1; // Number of args @@ -17355,7 +17453,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"asin(",5)) { // Arcsin _cimg_mp_op("Function 'asin()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::asin(mem[arg1])); _cimg_mp_scalar1(mp_asin,arg1); @@ -17363,7 +17461,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"atan(",5)) { // Arctan _cimg_mp_op("Function 'atan()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::atan(mem[arg1])); _cimg_mp_scalar1(mp_atan,arg1); @@ -17372,9 +17470,9 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"atan2(",6)) { // Arctan2 _cimg_mp_op("Function 'atan2()'"); s1 = ss6; while (s1::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code); - _cimg_mp_return(_cimg_mp_slot_nan); + _cimg_mp_return_nan(); } } @@ -17404,38 +17502,52 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'breakpoint()'"); if (pexpr[se2 - expr._data]=='(') { // no arguments? CImg::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code); - _cimg_mp_return(_cimg_mp_slot_nan); + _cimg_mp_return_nan(); } } break; case 'c' : - if ((*ss1=='m' || *ss1=='M') && *ss2=='(') { // cm(), cM() - _cimg_mp_op(*ss1=='m'?"Function 'cm()'":"Function 'cM()'"); - if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified - else { if (ss3!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)(*ss1=='m'?mp_image_cm:mp_image_cM),pos,p1).move_to(code); - _cimg_mp_return(pos); - } - if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value _cimg_mp_op("Function 'cabs()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); _cimg_mp_check_type(arg1,0,2,2); _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2); } if (!std::strncmp(ss,"carg(",5)) { // Complex argument _cimg_mp_op("Function 'carg()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); _cimg_mp_check_type(arg1,0,2,2); _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1); } + if (!std::strncmp(ss,"cats(",5)) { // Concatenate strings + _cimg_mp_op("Function 'cats()'"); + CImg::vector((ulongT)mp_cats,0,0,0).move_to(l_opcode); + arg1 = 0; + for (s = ss5; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + _cimg_mp_check_constant(arg1,1,3); // Last argument = output vector size + l_opcode.remove(); + (l_opcode>'y').move_to(opcode); + p1 = (unsigned int)mem[arg1]; + pos = vector(p1); + opcode[1] = pos; + opcode[2] = p1; + opcode[3] = opcode._height; + opcode.move_to(code); + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root _cimg_mp_op("Function 'cbrt()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1])); _cimg_mp_scalar1(mp_cbrt,arg1); @@ -17443,7 +17555,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate _cimg_mp_op("Function 'cconj()'"); - arg1 = compile(ss6,se1,depth1,0); + arg1 = compile(ss6,se1,depth1,0,is_single); _cimg_mp_check_type(arg1,0,2,2); pos = vector(2); CImg::vector((ulongT)mp_complex_conj,pos,arg1).move_to(code); @@ -17452,7 +17564,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"ceil(",5)) { // Ceil _cimg_mp_op("Function 'ceil()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1])); _cimg_mp_scalar1(mp_ceil,arg1); @@ -17460,7 +17572,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential _cimg_mp_op("Function 'cexp()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); _cimg_mp_check_type(arg1,0,2,2); pos = vector(2); CImg::vector((ulongT)mp_complex_exp,pos,arg1).move_to(code); @@ -17469,7 +17581,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm _cimg_mp_op("Function 'clog()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); _cimg_mp_check_type(arg1,0,2,2); pos = vector(2); CImg::vector((ulongT)mp_complex_log,pos,arg1).move_to(code); @@ -17479,7 +17591,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"continue(",9)) { // Complex absolute value if (pexpr[se2 - expr._data]=='(') { // no arguments? CImg::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code); - _cimg_mp_return(_cimg_mp_slot_nan); + _cimg_mp_return_nan(); } } @@ -17487,26 +17599,26 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'copy()'"); ref.assign(14); s1 = ss5; while (s1::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg1); + } + if (!std::strncmp(ss,"crop(",5)) { // Image crop _cimg_mp_op("Function 'crop()'"); if (*ss5=='#') { // Index specified s0 = ss6; while (s0::sequence(_cimg_mp_vector_size(arg1),arg1 + 1, - arg1 + (ulongT)_cimg_mp_vector_size(arg1)); - opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(_opcode); + opcode = CImg::sequence(_cimg_mp_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_size(arg1)); + opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode); is_sth = true; } else { _cimg_mp_check_type(arg1,pos + 1,1,0); - CImg::vector(arg1).move_to(_opcode); + CImg::vector(arg1).move_to(l_opcode); } s = ns; } - (_opcode>'y').move_to(opcode); + (l_opcode>'y').move_to(opcode); arg1 = 0; arg2 = (p1!=~0U); switch (opcode._height) { @@ -17607,7 +17727,7 @@ namespace cimg_library_suffixed { *se = saved_char; s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Too much arguments specified, " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -17647,7 +17767,7 @@ namespace cimg_library_suffixed { *se = saved_char; s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Cannot crop empty image when " "some xyzc-coordinates are unspecified, in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -17660,7 +17780,7 @@ namespace cimg_library_suffixed { } pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7])); - CImg::vector((ulongT)mp_image_crop, + CImg::vector((ulongT)mp_crop, pos,p1, *opcode,opcode[1],opcode[2],opcode[3], opcode[4],opcode[5],opcode[6],opcode[7], @@ -17671,8 +17791,8 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"cross(",6)) { // Cross product _cimg_mp_op("Function 'cross()'"); s1 = ss6; while (s1::vector((ulongT)mp_image_d,pos,p1).move_to(code); _cimg_mp_return(pos); } - if (!std::strncmp(ss,"date(",5)) { // Date and file date + if (!std::strncmp(ss,"date(",5)) { // Current date or file date _cimg_mp_op("Function 'date()'"); s1 = ss5; while (s1::string(s1,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + ((CImg::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)),variable_name)>'y'). + move_to(opcode); + *se1 = ')'; + } else + CImg::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)).move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return(pos); } if (!std::strncmp(ss,"debug(",6)) { // Print debug info _cimg_mp_op("Function 'debug()'"); p1 = code._width; - arg1 = compile(ss6,se1,depth1,p_ref); + arg1 = compile(ss6,se1,depth1,p_ref,is_single); *se1 = 0; variable_name.assign(CImg::string(ss6,true,true).unroll('y'),true); cimg::strpare(variable_name,false,true); @@ -17741,22 +17871,22 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'display()'"); if (pexpr[se2 - expr._data]=='(') { // no arguments? CImg::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code); - _cimg_mp_return(_cimg_mp_slot_nan); + _cimg_mp_return_nan(); } if (*ss8!='#') { // Vector s1 = ss8; while (s1::string(ss8,true,true).unroll('y'),true); cimg::strpare(variable_name,false,true); if (_cimg_mp_is_vector(arg1)) - ((CImg::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_vector_size(arg1)), + ((CImg::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0), variable_name)>'y').move_to(opcode); else - ((CImg::vector((ulongT)mp_print,arg1,0), + ((CImg::vector((ulongT)mp_print,arg1,0,0), variable_name)>'y').move_to(opcode); opcode[2] = opcode._height; opcode.move_to(code); - ((CImg::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_vector_size(arg1), + ((CImg::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1), arg2,arg3,arg4,arg5), variable_name)>'y').move_to(opcode); opcode[2] = opcode._height; @@ -17786,26 +17916,26 @@ namespace cimg_library_suffixed { _cimg_mp_return(arg1); } else { // Image - p1 = compile(ss8 + 1,se1,depth1,0); + p1 = compile(ss8 + 1,se1,depth1,0,is_single); _cimg_mp_check_list(true); CImg::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code); - _cimg_mp_return(_cimg_mp_slot_nan); + _cimg_mp_return_nan(); } } if (!std::strncmp(ss,"det(",4)) { // Matrix determinant _cimg_mp_op("Function 'det()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); _cimg_mp_check_matrix_square(arg1,1); - p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); _cimg_mp_scalar2(mp_det,arg1,p1); } if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix _cimg_mp_op("Function 'diag()'"); - arg1 = compile(ss5,se1,depth1,0); - _cimg_mp_check_type(arg1,1,2,0); - p1 = _cimg_mp_vector_size(arg1); + arg1 = compile(ss5,se1,depth1,0,is_single); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(arg1); + p1 = _cimg_mp_size(arg1); pos = vector(p1*p1); CImg::vector((ulongT)mp_diag,pos,arg1,p1).move_to(code); _cimg_mp_return(pos); @@ -17814,11 +17944,11 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"dot(",4)) { // Dot product _cimg_mp_op("Function 'dot()'"); s1 = ss4; while (s1::vector((ulongT)mp_dowhile,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_vector_size(p1), + CImg::vector((ulongT)mp_dowhile,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1), p1>=arg6 && !_cimg_mp_is_constant(p1), p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); _cimg_mp_return(p1); } if (!std::strncmp(ss,"draw(",5)) { // Draw image - is_parallelizable = false; + if (!is_single) is_parallelizable = false; _cimg_mp_op("Function 'draw()'"); if (*ss5=='#') { // Index specified s0 = ss6; while (s01) { arg3 = arg2 + 1; @@ -17872,15 +18002,15 @@ namespace cimg_library_suffixed { if (s0::vector((ulongT)mp_image_draw,arg1,(ulongT)_cimg_mp_vector_size(arg1),p1,arg2,arg3,arg4,arg5, - 0,0,0,0,1,(ulongT)~0U,0,1).move_to(opcode); + l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'! + CImg::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5, + 0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode); arg2 = arg3 = arg4 = arg5 = ~0U; p2 = p1!=~0U?0:1; if (s0::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode); + for (s = ss5; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector _cimg_mp_op("Function 'eig()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); _cimg_mp_check_matrix_square(arg1,1); - p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); pos = vector((p1 + 1)*p1); CImg::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code); _cimg_mp_return(pos); @@ -17957,14 +18105,61 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"end(",4)) { // End _cimg_mp_op("Function 'end()'"); code.swap(code_end); - arg1 = compile(ss4,se1,depth1,p_ref); + compile(ss4,se1,depth1,p_ref,true); code.swap(code_end); - _cimg_mp_return(arg1); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing + if (!is_single) is_parallelizable = false; + _cimg_mp_op("Function 'ellipse()'"); + if (*ss8=='#') { // Index specified + s0 = ss + 9; while (s0::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); + for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"ext(",4)) { // Extern + _cimg_mp_op("Function 'ext()'"); + if (!is_single) is_parallelizable = false; + CImg::vector((ulongT)mp_ext,0,0).move_to(l_opcode); + pos = 1; + for (s = ss4; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + pos = scalar(); + opcode[1] = pos; + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return(pos); } if (!std::strncmp(ss,"exp(",4)) { // Exponential _cimg_mp_op("Function 'exp()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1])); _cimg_mp_scalar1(mp_exp,arg1); @@ -17972,7 +18167,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"eye(",4)) { // Identity matrix _cimg_mp_op("Function 'eye()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); _cimg_mp_check_constant(arg1,1,3); p1 = (unsigned int)mem[arg1]; pos = vector(p1*p1); @@ -17984,17 +18179,17 @@ namespace cimg_library_suffixed { case 'f' : if (!std::strncmp(ss,"fact(",5)) { // Factorial _cimg_mp_op("Function 'fact()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial(mem[arg1])); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1])); _cimg_mp_scalar1(mp_factorial,arg1); } if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci _cimg_mp_op("Function 'fibo()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1); - if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci(mem[arg1])); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1])); _cimg_mp_scalar1(mp_fibonacci,arg1); } @@ -18004,38 +18199,38 @@ namespace cimg_library_suffixed { // First argument: data to look at. s0 = ss5; while (s0::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_vector_size(p3),p2,arg2 - arg1,arg3 - arg2, + arg5 = _cimg_mp_size(pos); + CImg::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2, arg4 - arg3,code._width - arg4, p3>=arg6 && !_cimg_mp_is_constant(p3), p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); @@ -18068,60 +18263,83 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"floor(",6)) { // Floor _cimg_mp_op("Function 'floor()'"); - arg1 = compile(ss6,se1,depth1,0); + arg1 = compile(ss6,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1])); _cimg_mp_scalar1(mp_floor,arg1); } + + if (!std::strncmp(ss,"fsize(",6)) { // File size + _cimg_mp_op("Function 'fsize()'"); + *se1 = 0; + variable_name.assign(CImg::string(ss6,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + pos = scalar(); + ((CImg::vector((ulongT)mp_fsize,pos,0),variable_name)>'y').move_to(opcode); + *se1 = ')'; + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return(pos); + } break; case 'g' : if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function _cimg_mp_op("Function 'gauss()'"); s1 = ss6; while (s1::vector((ulongT)mp_image_h,pos,p1).move_to(code); _cimg_mp_return(pos); } + break; case 'i' : - if ((*ss1=='m' || *ss1=='M' || *ss1=='a' || *ss1=='v' || *ss1=='s' || *ss1=='p' || *ss1=='c') && - *ss2=='(') { // im(), iM(), ia(), iv(), is(), ip(), ic() - _cimg_mp_op(*ss1=='m'?"Function 'im()'": - *ss1=='M'?"Function 'iM()'": - *ss1=='a'?"Function 'ia()'": - *ss1=='v'?"Function 'iv()'": - *ss1=='s'?"Function 'is()'": - *ss1=='p'?"Function 'ip()'": - "Function 'ic()"); - if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified - else { if (ss3!=se1) break; p1 = ~0U; } + if (*ss1=='c' && *ss2=='(') { // Image median + _cimg_mp_op("Function 'ic()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_single); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } pos = scalar(); - CImg::vector((ulongT)(*ss1=='m'?mp_image_im: - *ss1=='M'?mp_image_iM: - *ss1=='a'?mp_image_ia: - *ss1=='v'?mp_image_iv: - *ss1=='s'?mp_image_is: - *ss1=='p'?mp_image_ip: - mp_image_ic),pos,p1).move_to(code); + CImg::vector((ulongT)mp_image_median,pos,p1).move_to(code); _cimg_mp_return(pos); } @@ -18129,18 +18347,19 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'if()'"); s1 = ss3; while (s1::vector((ulongT)mp_if,pos,arg1,arg2,arg3, p3 - p2,code._width - p3,arg4).move_to(code,p2); @@ -18150,14 +18369,14 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"init(",5)) { // Init _cimg_mp_op("Function 'init()'"); code.swap(code_init); - arg1 = compile(ss5,se1,depth1,p_ref); + arg1 = compile(ss5,se1,depth1,p_ref,true); code.swap(code_init); _cimg_mp_return(arg1); } if (!std::strncmp(ss,"int(",4)) { // Integer cast _cimg_mp_op("Function 'int()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]); _cimg_mp_scalar1(mp_int,arg1); @@ -18165,10 +18384,10 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"inv(",4)) { // Matrix/scalar inversion _cimg_mp_op("Function 'inv()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) { _cimg_mp_check_matrix_square(arg1,1); - p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); pos = vector(p1*p1); CImg::vector((ulongT)mp_matrix_inv,pos,arg1,p1).move_to(code); _cimg_mp_return(pos); @@ -18182,7 +18401,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? _cimg_mp_op("Function 'isbool()'"); if (ss7==se1) _cimg_mp_return(0); - arg1 = compile(ss7,se1,depth1,0); + arg1 = compile(ss7,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0.0 || mem[arg1]==1.0); _cimg_mp_scalar1(mp_isbool,arg1); @@ -18208,19 +18427,19 @@ namespace cimg_library_suffixed { if (ss5>=se1) _cimg_mp_return(0); _cimg_mp_op("Function 'isin()'"); pos = scalar(); - CImg::vector((ulongT)mp_isin,pos,0).move_to(_opcode); + CImg::vector((ulongT)mp_isin,pos,0).move_to(l_opcode); for (s = ss5; s::sequence(_cimg_mp_vector_size(arg1),arg1 + 1, - arg1 + (ulongT)_cimg_mp_vector_size(arg1)). - move_to(_opcode); - else CImg::vector(arg1).move_to(_opcode); + CImg::sequence(_cimg_mp_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_size(arg1)). + move_to(l_opcode); + else CImg::vector(arg1).move_to(l_opcode); s = ns; } - (_opcode>'y').move_to(opcode); + (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; opcode.move_to(code); _cimg_mp_return(pos); @@ -18229,7 +18448,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? _cimg_mp_op("Function 'isinf()'"); if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0); + arg1 = compile(ss6,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_inf(mem[arg1])); _cimg_mp_scalar1(mp_isinf,arg1); @@ -18238,7 +18457,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"isint(",6)) { // Is integer? _cimg_mp_op("Function 'isint()'"); if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0); + arg1 = compile(ss6,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)(cimg::mod(mem[arg1],1.0)==0)); _cimg_mp_scalar1(mp_isint,arg1); @@ -18247,7 +18466,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"isnan(",6)) { // Is NaN? _cimg_mp_op("Function 'isnan()'"); if (ss6==se1) _cimg_mp_return(0); - arg1 = compile(ss6,se1,depth1,0); + arg1 = compile(ss6,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_nan(mem[arg1])); _cimg_mp_scalar1(mp_isnan,arg1); @@ -18264,9 +18483,15 @@ namespace cimg_library_suffixed { break; case 'l' : + if (*ss1=='(') { // Size of image list + _cimg_mp_op("Function 'l()'"); + if (ss2!=se1) break; + _cimg_mp_scalar0(mp_list_l); + } + if (!std::strncmp(ss,"log(",4)) { // Natural logarithm _cimg_mp_op("Function 'log()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1])); _cimg_mp_scalar1(mp_log,arg1); @@ -18274,7 +18499,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm _cimg_mp_op("Function 'log2()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1])); _cimg_mp_scalar1(mp_log2,arg1); @@ -18282,7 +18507,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm _cimg_mp_op("Function 'log10()'"); - arg1 = compile(ss6,se1,depth1,0); + arg1 = compile(ss6,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1])); _cimg_mp_scalar1(mp_log10,arg1); @@ -18290,7 +18515,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"lowercase(",10)) { // Lower case _cimg_mp_op("Function 'lowercase()'"); - arg1 = compile(ss + 10,se1,depth1,0); + arg1 = compile(ss + 10,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1])); _cimg_mp_scalar1(mp_lowercase,arg1); @@ -18301,15 +18526,15 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication _cimg_mp_op("Function 'mul()'"); s1 = ss4; while (s1expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') " "do not match with third argument 'nb_colsB=%u', " "in expression '%s%s%s'.", @@ -18351,37 +18576,37 @@ namespace cimg_library_suffixed { if (*ss4=='(') { arg1 = 2; s = ss5; } else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; } else if (arg1==~0U) { - arg1 = compile(ss4,s++,depth1,0); + arg1 = compile(ss4,s++,depth1,0,is_single); _cimg_mp_check_constant(arg1,0,2); arg1 = (unsigned int)mem[arg1]; } else s = std::strchr(ss4,'(') + 1; pos = scalar(); switch (arg1) { case 0 : - CImg::vector((ulongT)mp_norm0,pos,0).move_to(_opcode); break; + CImg::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break; case 1 : - CImg::vector((ulongT)mp_norm1,pos,0).move_to(_opcode); break; + CImg::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break; case 2 : - CImg::vector((ulongT)mp_norm2,pos,0).move_to(_opcode); break; + CImg::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break; case ~0U : - CImg::vector((ulongT)mp_norminf,pos,0).move_to(_opcode); break; + CImg::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break; default : CImg::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). - move_to(_opcode); + move_to(l_opcode); } for ( ; s::sequence(_cimg_mp_vector_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_vector_size(arg2)). - move_to(_opcode); - else CImg::vector(arg2).move_to(_opcode); + CImg::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); s = ns; } - (_opcode>'y').move_to(opcode); + (l_opcode>'y').move_to(opcode); if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1 _cimg_mp_scalar1(mp_abs,opcode[3]); opcode[2] = opcode._height; @@ -18395,32 +18620,90 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'permut()'"); s1 = ss7; while (s1::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); + for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions + is_sth = ss[5]=='s'; // is prints() + _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'"); + s0 = is_sth?ss7:ss6; + if (*s0!='#' || is_sth) { // Regular expression + for (s = s0; s::string(s,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(pos)) // Vector + ((CImg::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0), + variable_name)>'y').move_to(opcode); + else // Scalar + ((CImg::vector((ulongT)mp_print,pos,0,is_sth?1:0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + *ns = c1; s = ns; + } + _cimg_mp_return(pos); + } else { // Image + p1 = compile(ss7,se1,depth1,0,is_single); + _cimg_mp_check_list(true); + CImg::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code); + _cimg_mp_return_nan(); + } + } + if (!std::strncmp(ss,"pseudoinv(",10)) { // Matrix/scalar pseudo-inversion _cimg_mp_op("Function 'pseudoinv()'"); s1 = ss + 10; while (s1expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Type of first argument ('%s') " "does not match with second argument 'nb_colsA=%u', " "in expression '%s%s%s'.", @@ -18432,35 +18715,6 @@ namespace cimg_library_suffixed { CImg::vector((ulongT)mp_matrix_pseudoinv,pos,arg1,p2,p3).move_to(code); _cimg_mp_return(pos); } - - if (!std::strncmp(ss,"print(",6)) { // Print expressions - _cimg_mp_op("Function 'print()'"); - if (*ss6!='#') { // Regular expression - for (s = ss6; s::string(s,true,true).unroll('y'),true); - cimg::strpare(variable_name,false,true); - if (_cimg_mp_is_vector(pos)) // Vector - ((CImg::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_vector_size(pos)), - variable_name)>'y').move_to(opcode); - else // Scalar - ((CImg::vector((ulongT)mp_print,pos,0), - variable_name)>'y').move_to(opcode); - opcode[2] = opcode._height; - opcode.move_to(code); - *ns = c1; s = ns; - } - _cimg_mp_return(pos); - } else { // Image - p1 = compile(ss7,se1,depth1,0); - _cimg_mp_check_list(true); - CImg::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code); - _cimg_mp_return(_cimg_mp_slot_nan); - } - } break; case 'r' : @@ -18468,39 +18722,40 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'resize()'"); if (*ss7!='#') { // Vector s1 = ss7; while (s1::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_vector_size(arg1), + CImg::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1), arg3,arg4).move_to(code); _cimg_mp_return(pos); } else { // Image - is_parallelizable = false; + if (!is_single) is_parallelizable = false; s0 = ss8; while (s0::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0). - move_to(opcode); + move_to(l_opcode); pos = 0; for (s = s0; sexpr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, pos<1?"Missing":"Too much", s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); } - opcode.move_to(code); - _cimg_mp_return(_cimg_mp_slot_nan); + l_opcode[0].move_to(code); + _cimg_mp_return_nan(); } } if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse _cimg_mp_op("Function 'reverse()'"); - arg1 = compile(ss8,se1,depth1,0); + arg1 = compile(ss8,se1,depth1,0,is_single); if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1); - p1 = _cimg_mp_vector_size(arg1); + p1 = _cimg_mp_size(arg1); pos = vector(p1); CImg::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code); _cimg_mp_return(pos); @@ -18532,8 +18787,8 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'"); s1 = ss4; while (s11) { arg2 = arg1 + 1; if (p2>2) arg3 = arg2 + 1; } - arg4 = compile(++s1,se1,depth1,0); + arg4 = compile(++s1,se1,depth1,0,is_single); } else { s2 = s1 + 1; while (s2::vector((ulongT)mp_image_s,pos,p1).move_to(code); _cimg_mp_return(pos); @@ -18612,25 +18869,44 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values _cimg_mp_op("Function 'same()'"); s1 = ss5; while (s1::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code); + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"sign(",5)) { // Sign _cimg_mp_op("Function 'sign()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1])); _cimg_mp_scalar1(mp_sign,arg1); @@ -18638,7 +18914,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"sin(",4)) { // Sine _cimg_mp_op("Function 'sin()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1])); _cimg_mp_scalar1(mp_sin,arg1); @@ -18646,23 +18922,15 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal _cimg_mp_op("Function 'sinc()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1])); _cimg_mp_scalar1(mp_sinc,arg1); } - if (!std::strncmp(ss,"single(",7)) { // Force single thread execution - _cimg_mp_op("Function 'single()'"); - p1 = code._width; - arg1 = compile(ss7,se1,depth1,p_ref); - CImg::vector((ulongT)mp_single,arg1,code._width - p1).move_to(code,p1); - _cimg_mp_return(arg1); - } - if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine _cimg_mp_op("Function 'sinh()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1])); _cimg_mp_scalar1(mp_sinh,arg1); @@ -18670,22 +18938,22 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"size(",5)) { // Vector size. _cimg_mp_op("Function 'size()'"); - arg1 = compile(ss5,se1,depth1,0); - _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_vector_size(arg1)); + arg1 = compile(ss5,se1,depth1,0,is_single); + _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1)); } if (!std::strncmp(ss,"solve(",6)) { // Solve linear system _cimg_mp_op("Function 'solve()'"); s1 = ss6; while (s1expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') " "do not match with third argument 'nb_colsB=%u', " "in expression '%s%s%s'.", @@ -18708,38 +18976,56 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"sort(",5)) { // Sort vector _cimg_mp_op("Function 'sort()'"); - s1 = ss6; while (s1expr._data?ss - 4:expr._data; + cimg::strellipsize(s0,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid specified chunk size (%u) for first argument " + "('%s'), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + arg3,s_type(arg1)._data, + s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p1); + CImg::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3).move_to(code); + _cimg_mp_return(pos); + + } else { // Image + s1 = ss6; while (s1::vector((ulongT)mp_image_sort,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code); + _cimg_mp_return_nan(); } - _cimg_mp_check_type(arg1,1,2,0); - _cimg_mp_check_type(arg2,2,1,0); - _cimg_mp_check_constant(arg3,3,3); - arg3 = (unsigned int)mem[arg3]; - p1 = _cimg_mp_vector_size(arg1); - if (p1%arg3) { - *se = saved_char; - s0 = ss - 4>expr._data?ss - 4:expr._data; - cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s: Invalid specified chunk size (%u) for first argument " - "('%s'), in expression '%s%s%s'.", - pixel_type(),_cimg_mp_calling_function,s_op, - arg3,s_type(arg1)._data, - s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); - } - pos = vector(p1); - CImg::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3).move_to(code); - _cimg_mp_return(pos); } if (!std::strncmp(ss,"sqr(",4)) { // Square _cimg_mp_op("Function 'sqr()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1])); _cimg_mp_scalar1(mp_sqr,arg1); @@ -18747,37 +19033,61 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"sqrt(",5)) { // Square root _cimg_mp_op("Function 'sqrt()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1])); _cimg_mp_scalar1(mp_sqrt,arg1); } - if (!std::strncmp(ss,"stod(",5)) { // String to double - _cimg_mp_op("Function 'stod()'"); + if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed + _cimg_mp_op("Function 'srand()'"); + arg1 = ss6::vector((ulongT)mp_image_stats,pos,p1).move_to(code); + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"stov(",5)) { // String to double + _cimg_mp_op("Function 'stov()'"); s1 = ss5; while (s1expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Type of first argument ('%s') " "does not match with second argument 'nb_colsA=%u', " "in expression '%s%s%s'.", @@ -18794,7 +19104,7 @@ namespace cimg_library_suffixed { case 't' : if (!std::strncmp(ss,"tan(",4)) { // Tangent _cimg_mp_op("Function 'tan()'"); - arg1 = compile(ss4,se1,depth1,0); + arg1 = compile(ss4,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1])); _cimg_mp_scalar1(mp_tan,arg1); @@ -18802,7 +19112,7 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent _cimg_mp_op("Function 'tanh()'"); - arg1 = compile(ss5,se1,depth1,0); + arg1 = compile(ss5,se1,depth1,0,is_single); if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1); if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1])); _cimg_mp_scalar1(mp_tanh,arg1); @@ -18810,27 +19120,27 @@ namespace cimg_library_suffixed { if (!std::strncmp(ss,"trace(",6)) { // Matrix trace _cimg_mp_op("Function 'trace()'"); - arg1 = compile(ss6,se1,depth1,0); + arg1 = compile(ss6,se1,depth1,0,is_single); _cimg_mp_check_matrix_square(arg1,1); - p1 = (unsigned int)std::sqrt((float)_cimg_mp_vector_size(arg1)); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); _cimg_mp_scalar2(mp_trace,arg1,p1); } if (!std::strncmp(ss,"transp(",7)) { // Matrix transpose _cimg_mp_op("Function 'transp()'"); s1 = ss7; while (s1expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Size of first argument ('%s') does not match " "second argument 'nb_cols=%u', in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -18848,9 +19158,9 @@ namespace cimg_library_suffixed { _cimg_mp_op("Function 'u()'"); if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1); s1 = ss2; while (s1::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(_opcode); + arg4 = _cimg_mp_size(arg3); + CImg::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode); arg2+=arg4; - } else { CImg::vector(arg3).move_to(_opcode); ++arg2; } + } else { CImg::vector(arg3).move_to(l_opcode); ++arg2; } s = ns; } if (arg1==~0U) arg1 = arg2; _cimg_mp_check_vector0(arg1); pos = vector(arg1); - _opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); - (_opcode>'y').move_to(opcode); + l_opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; opcode.move_to(code); _cimg_mp_return(pos); } + + if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string + _cimg_mp_op("Function 'vtos()'"); + s1 = ss5; while (s1::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code); + _cimg_mp_return(pos); + } break; case 'w' : if (*ss1=='(') { // Image width _cimg_mp_op("Function 'w()'"); - if (*ss2=='#') { p1 = compile(ss3,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified - else { if (ss2!=se1) break; p1 = ~0U; } + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_single); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } pos = scalar(); CImg::vector((ulongT)mp_image_w,pos,p1).move_to(code); _cimg_mp_return(pos); @@ -18968,8 +19303,10 @@ namespace cimg_library_suffixed { if (*ss1=='h' && *ss2=='(') { // Image width*height _cimg_mp_op("Function 'wh()'"); - if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified - else { if (ss3!=se1) break; p1 = ~0U; } + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_single); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } pos = scalar(); CImg::vector((ulongT)mp_image_wh,pos,p1).move_to(code); _cimg_mp_return(pos); @@ -18977,8 +19314,10 @@ namespace cimg_library_suffixed { if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth _cimg_mp_op("Function 'whd()'"); - if (*ss4=='#') { p1 = compile(ss5,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified - else { if (ss4!=se1) break; p1 = ~0U; } + if (*ss4=='#') { // Index specified + p1 = compile(ss5,se1,depth1,0,is_single); + _cimg_mp_check_list(false); + } else { if (ss4!=se1) break; p1 = ~0U; } pos = scalar(); CImg::vector((ulongT)mp_image_whd,pos,p1).move_to(code); _cimg_mp_return(pos); @@ -18986,8 +19325,10 @@ namespace cimg_library_suffixed { if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum _cimg_mp_op("Function 'whds()'"); - if (*ss5=='#') { p1 = compile(ss6,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified - else { if (ss5!=se1) break; p1 = ~0U; } + if (*ss5=='#') { // Index specified + p1 = compile(ss6,se1,depth1,0,is_single); + _cimg_mp_check_list(false); + } else { if (ss5!=se1) break; p1 = ~0U; } pos = scalar(); CImg::vector((ulongT)mp_image_whds,pos,p1).move_to(code); _cimg_mp_return(pos); @@ -18998,12 +19339,12 @@ namespace cimg_library_suffixed { s0 = *ss5=='('?ss6:ss8; s1 = s0; while (s1::vector((ulongT)mp_whiledo,pos,arg1,p2 - p1,code._width - p2,arg2, pos>=arg6 && !_cimg_mp_is_constant(pos), arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1); @@ -19012,21 +19353,12 @@ namespace cimg_library_suffixed { break; case 'x' : - if ((*ss1=='m' || *ss1=='M') && *ss2=='(') { // xm(), xM() - _cimg_mp_op(*ss1=='m'?"Function 'xm()'":"Function 'xM()'"); - if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified - else { if (ss3!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)(*ss1=='m'?mp_image_xm:mp_image_xM),pos,p1).move_to(code); - _cimg_mp_return(pos); - } - if (!std::strncmp(ss,"xor(",4)) { // Xor _cimg_mp_op("Function 'xor()'"); s1 = ss4; while (s1::vector((ulongT)(*ss1=='m'?mp_image_ym:mp_image_yM),pos,p1).move_to(code); - _cimg_mp_return(pos); - } - break; - - case 'z' : - if ((*ss1=='m' || *ss1=='M') && *ss2=='(') { // zm(), zM() - _cimg_mp_op(*ss1=='m'?"Function 'zm()'":"Function 'zM()'"); - if (*ss3=='#') { p1 = compile(ss4,se1,depth1,0); _cimg_mp_check_list(false); } // Index specified - else { if (ss3!=se1) break; p1 = ~0U; } - pos = scalar(); - CImg::vector((ulongT)(*ss1=='m'?mp_image_zm:mp_image_zM),pos,p1).move_to(code); - _cimg_mp_return(pos); - } - break; - } if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) || !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) || - !std::strncmp(ss,"sum(",4) || - !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"variance(",9) || - !std::strncmp(ss,"prod(",5) || !std::strncmp(ss,"mean(",5) || - !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7)) { // Multi-argument functions - _cimg_mp_op(*ss=='a'?(ss[3]=='('?"Function 'arg()'":ss[4]=='i'?"Function 'argmin()'": + !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) || + !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) || + !std::strncmp(ss,"prod(",5) || + !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) || + !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions + _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'": + ss[3]=='k'?"Function 'argkth()'": + ss[4]=='i'?"Function 'argmin()'": "Function 'argmax()'"): *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"): *ss=='k'?"Function 'kth()'": *ss=='p'?"Function 'prod()'": - *ss=='v'?"Function 'variance()'": + *ss=='v'?"Function 'var()'": ss[1]=='i'?"Function 'min()'": - ss[1]=='a'?"Function 'max()'": - ss[2]=='a'?"Function 'mean()'":"Function 'med()'"); - op = *ss=='a'?(ss[3]=='('?mp_arg:ss[4]=='i'?mp_argmin:mp_argmax): + ss[1]=='a'?"Function 'max()'":"Function 'med()'"); + op = *ss=='a'?(ss[1]=='v'?mp_avg:ss[3]=='k'?mp_argkth:ss[4]=='i'?mp_argmin:mp_argmax): *ss=='s'?(ss[1]=='u'?mp_sum:mp_std): *ss=='k'?mp_kth: *ss=='p'?mp_prod: - *ss=='v'?mp_variance: + *ss=='v'?mp_var: ss[1]=='i'?mp_min: ss[1]=='a'?mp_max: - ss[2]=='a'?mp_mean: + ss[2]=='a'?mp_avg: mp_median; is_sth = true; // Tell if all arguments are constant pos = scalar(); - CImg::vector((ulongT)op,pos,0).move_to(_opcode); + CImg::vector((ulongT)op,pos,0).move_to(l_opcode); for (s = std::strchr(ss,'(') + 1; s::sequence(_cimg_mp_vector_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_vector_size(arg2)). - move_to(_opcode); - else CImg::vector(arg2).move_to(_opcode); + CImg::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); is_sth&=_cimg_mp_is_constant(arg2); s = ns; } - (_opcode>'y').move_to(opcode); + (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; if (is_sth) _cimg_mp_constant(op(*this)); opcode.move_to(code); @@ -19162,7 +19473,7 @@ namespace cimg_library_suffixed { level.swap(_level); s0 = user_macro; user_macro = macro_def[l]; - pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref); + pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_single); user_macro = s0; level.swap(_level); pexpr.swap(_pexpr); @@ -19183,7 +19494,7 @@ namespace cimg_library_suffixed { sig_nargs.sort(); arg1 = sig_nargs.back(); --sig_nargs._width; - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " "does not match macro declaration (defined for %s or %u arguments), " "in expression '%s%s%s'.", @@ -19191,7 +19502,7 @@ namespace cimg_library_suffixed { p1,sig_nargs.value_string()._data,arg1, s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); } else - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " "does not match macro declaration (defined for %u argument%s), " "in expression '%s%s%s'.", @@ -19221,7 +19532,7 @@ namespace cimg_library_suffixed { cimg::strellipsize(variable_name,64); s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s: Literal %s contains more than one character, " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op, @@ -19229,10 +19540,10 @@ namespace cimg_library_suffixed { s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); } pos = vector(arg1); - CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(_opcode); - CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(_opcode); - std::memcpy((char*)_opcode[1]._data,variable_name,arg1); - (_opcode>'y').move_to(code); + CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); + std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); + (l_opcode>'y').move_to(code); _cimg_mp_return(pos); } @@ -19250,27 +19561,27 @@ namespace cimg_library_suffixed { } if (!arg1) _cimg_mp_return(0); // Empty string -> 0 pos = vector(arg1); - CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(_opcode); - CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(_opcode); - std::memcpy((char*)_opcode[1]._data,variable_name,arg1); - (_opcode>'y').move_to(code); + CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); + std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); + (l_opcode>'y').move_to(code); } else { // Vector values provided as list of items arg1 = 0; // Number of specified values. if (*ss1!=']') for (s = ss1; s::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(_opcode); + arg3 = _cimg_mp_size(arg2); + CImg::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode); arg1+=arg3; - } else { CImg::vector(arg2).move_to(_opcode); ++arg1; } + } else { CImg::vector(arg2).move_to(l_opcode); ++arg1; } s = ns; } _cimg_mp_check_vector0(arg1); pos = vector(arg1); - _opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); - (_opcode>'y').move_to(opcode); + l_opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (l_opcode>'y').move_to(opcode); opcode[2] = opcode._height; opcode.move_to(code); } @@ -19279,7 +19590,7 @@ namespace cimg_library_suffixed { // Variables related to the input list of images. if (*ss1=='#' && ss2expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); if (is_sth) - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function, variable_name._data, s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); s1 = std::strchr(ss,'('); s_op = s1 && c1==')'?"function call":"item"; - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function, s_op,variable_name._data, @@ -19487,7 +19798,7 @@ namespace cimg_library_suffixed { CImg res; if (_cimg_mp_is_vector(arg)) { // Vector CImg::string("vectorXXXXXXXXXXXXXXXX").move_to(res); - std::sprintf(res._data + 6,"%u",_cimg_mp_vector_size(arg)); + std::sprintf(res._data + 6,"%u",_cimg_mp_size(arg)); } else CImg::string("scalar").move_to(res); return res; } @@ -19670,7 +19981,7 @@ namespace cimg_library_suffixed { // Insert code instructions for processing vectors. bool is_comp_vector(const unsigned int arg) const { - unsigned int siz = _cimg_mp_vector_size(arg); + unsigned int siz = _cimg_mp_size(arg); if (siz>8) return false; const int *ptr = memtype.data(arg + 1); bool is_tmp = true; @@ -19679,7 +19990,7 @@ namespace cimg_library_suffixed { } void set_variable_vector(const unsigned int arg) { - unsigned int siz = _cimg_mp_vector_size(arg); + unsigned int siz = _cimg_mp_size(arg); int *ptr = memtype.data(arg + 1); while (siz-->0) *(ptr++) = -1; } @@ -19705,14 +20016,14 @@ namespace cimg_library_suffixed { unsigned int vector_copy(const unsigned int arg) { // Insert new copy of specified vector in memory const unsigned int - siz = _cimg_mp_vector_size(arg), + siz = _cimg_mp_size(arg), pos = vector(siz); CImg::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code); return pos; } void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) { - const unsigned int siz = _cimg_mp_vector_size(pos); + const unsigned int siz = _cimg_mp_size(pos); if (siz>24) CImg::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code); else { code.insert(siz); @@ -19722,7 +20033,7 @@ namespace cimg_library_suffixed { } void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) { - const unsigned int siz = _cimg_mp_vector_size(pos); + const unsigned int siz = _cimg_mp_size(pos); if (siz>24) CImg::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code); else { code.insert(siz); @@ -19733,7 +20044,7 @@ namespace cimg_library_suffixed { unsigned int vector1_v(const mp_func op, const unsigned int arg1) { const unsigned int - siz = _cimg_mp_vector_size(arg1), + siz = _cimg_mp_size(arg1), pos = is_comp_vector(arg1)?arg1:vector(siz); if (siz>24) CImg::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code); else { @@ -19746,7 +20057,7 @@ namespace cimg_library_suffixed { unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { const unsigned int - siz = _cimg_mp_vector_size(arg1), + siz = _cimg_mp_size(arg1), pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:vector(siz); if (siz>24) CImg::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); else { @@ -19759,7 +20070,7 @@ namespace cimg_library_suffixed { unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) { const unsigned int - siz = _cimg_mp_vector_size(arg1), + siz = _cimg_mp_size(arg1), pos = is_comp_vector(arg1)?arg1:vector(siz); if (siz>24) CImg::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code); else { @@ -19772,7 +20083,7 @@ namespace cimg_library_suffixed { unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { const unsigned int - siz = _cimg_mp_vector_size(arg2), + siz = _cimg_mp_size(arg2), pos = is_comp_vector(arg2)?arg2:vector(siz); if (siz>24) CImg::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); else { @@ -19786,7 +20097,7 @@ namespace cimg_library_suffixed { unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { const unsigned int - siz = _cimg_mp_vector_size(arg1), + siz = _cimg_mp_size(arg1), pos = is_comp_vector(arg1)?arg1:vector(siz); if (siz>24) CImg::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code); else { @@ -19813,7 +20124,7 @@ namespace cimg_library_suffixed { *se = saved_char; char *const s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", @@ -19829,8 +20140,8 @@ namespace cimg_library_suffixed { char *const ss, char *const se, const char saved_char) { _cimg_mp_check_type(arg,n_arg,2,0); const unsigned int - siz = _cimg_mp_vector_size(arg), - n = (unsigned int)std::sqrt((float)siz); + siz = _cimg_mp_size(arg), + n = (unsigned int)cimg::round(std::sqrt((float)siz)); if (n*n!=siz) { const char *s_arg; if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand "; @@ -19838,7 +20149,7 @@ namespace cimg_library_suffixed { *se = saved_char; char *const s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s %s%s (of type '%s') " "cannot be considered as a square matrix, in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", @@ -19857,7 +20168,7 @@ namespace cimg_library_suffixed { char *const ss, char *const se, const char saved_char) { const bool is_scalar = _cimg_mp_is_scalar(arg), - is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_vector_size(arg)==N); + is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N); bool cond = false; if (mode&1) cond|=is_scalar; if (mode&2) cond|=is_vector; @@ -19879,7 +20190,7 @@ namespace cimg_library_suffixed { *se = saved_char; char *const s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", @@ -19896,7 +20207,7 @@ namespace cimg_library_suffixed { *se = saved_char; char *const s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s Invalid call with an empty image list, " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", @@ -19912,7 +20223,7 @@ namespace cimg_library_suffixed { *se = saved_char; s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " + throw CImgArgumentException("[" cimg_appname "_math_parser] " "CImg<%s>::%s: %s%s Invalid construction of a 0-dimensional vector, " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", @@ -19921,8 +20232,8 @@ namespace cimg_library_suffixed { *se = saved_char; s0 = ss - 4>expr._data?ss - 4:expr._data; cimg::strellipsize(s0,64); - throw CImgArgumentException("[_cimg_math_parser] " - "CImg<%s>::%s: %s%s Invalid construction of a vector with dynamic size, " + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s Invalid construction of a vector with possible dynamic size, " "in expression '%s%s%s'.", pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", s0!=expr._data?"...":"",s0,se<&expr.back()?"...":""); @@ -19964,6 +20275,13 @@ namespace cimg_library_suffixed { return _mp_arg(ind + 4); } + static double mp_argkth(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + const double val = mp_kth(mp); + for (unsigned int i = 4; i::nan(); } + static double mp_cats(_cimg_math_parser& mp) { + const double *ptrd = &_mp_arg(1) + 1; + const unsigned int + sizd = (unsigned int)mp.opcode[2], + nb_args = (unsigned int)(mp.opcode[3] - 4)/2; + CImgList _str; + for (unsigned int n = 0; n(ptrs,l,1,1,1,true).move_to(_str); + } else CImg::vector((char)_mp_arg(4 + 2*n)).move_to(_str); // Scalar argument + } + CImg(1,1,1,1,0).move_to(_str); + const CImg str = _str>'x'; + const unsigned int l = std::min(str._width,sizd); + CImg(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1); + return cimg::type::nan(); + } + static double mp_cbrt(_cimg_math_parser& mp) { return cimg::cbrt(_mp_arg(2)); } @@ -20183,6 +20531,41 @@ namespace cimg_library_suffixed { return std::cosh(_mp_arg(2)); } + static double mp_critical(_cimg_math_parser& mp) { + const double res = _mp_arg(1); + cimg_pragma_openmp(critical(mp_critical)) + { + for (const CImg *const p_end = ++mp.p_code + mp.opcode[2]; + mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + } + --mp.p_code; + return res; + } + + static double mp_crop(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6); + const unsigned int + dx = (unsigned int)mp.opcode[7], + dy = (unsigned int)mp.opcode[8], + dz = (unsigned int)mp.opcode[9], + dc = (unsigned int)mp.opcode[10]; + const bool boundary_conditions = (bool)_mp_arg(11); + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); + else CImg(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, + x + dx - 1,y + dy - 1, + z + dz - 1,c + dc - 1, + boundary_conditions); + return cimg::type::nan(); + } + static double mp_cross(_cimg_math_parser& mp) { CImg vout(&_mp_arg(1) + 1,1,3,1,1,true), @@ -20197,6 +20580,25 @@ namespace cimg_library_suffixed { return valcmax?cmax:val; } + static double mp_date(_cimg_math_parser& mp) { + const unsigned int + _arg = (unsigned int)mp.opcode[3], + _siz = (unsigned int)mp.opcode[4], + siz = _siz?_siz:1; + const double *const arg_in = _arg==~0U?0:&_mp_arg(3) + (_siz?1:0); + double *const arg_out = &_mp_arg(1) + (_siz?1:0); + if (arg_in) std::memcpy(arg_out,arg_in,siz*sizeof(double)); + else for (unsigned int i = 0; i filename(mp.opcode[2] - 5); + if (filename) { + const ulongT *ptrs = mp.opcode._data + 5; + cimg_for(filename,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::fdate(filename,arg_out,siz); + } else cimg::date(arg_out,siz); + return _siz?cimg::type::nan():*arg_out; + } + static double mp_debug(_cimg_math_parser& mp) { CImg expr(mp.opcode[2] - 4); const ulongT *ptrs = mp.opcode._data + 4; @@ -20209,10 +20611,10 @@ namespace cimg_library_suffixed { #else const unsigned int n_thread = omp_get_thread_num(); #endif - cimg_pragma_openmp(critical) + cimg_pragma_openmp(critical(mp_debug)) { std::fprintf(cimg::output(), - "\n[_cimg_math_parser] %p[thread #%u]:%*c" + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)", (void*)&mp,n_thread,mp.debug_indent,' ', expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width); @@ -20232,10 +20634,10 @@ namespace cimg_library_suffixed { const ulongT target = mp.opcode[1]; mp.mem[target] = _cimg_mp_defunc(mp); - cimg_pragma_openmp(critical) + cimg_pragma_openmp(critical(mp_debug)) { std::fprintf(cimg::output(), - "\n[_cimg_math_parser] %p[thread #%u]:%*c" + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" "Opcode %p = [ %p,%s ] -> mem[%u] = %g", (void*)&mp,n_thread,mp.debug_indent,' ', (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(), @@ -20243,11 +20645,11 @@ namespace cimg_library_suffixed { std::fflush(cimg::output()); } } - cimg_pragma_openmp(critical) + cimg_pragma_openmp(critical(mp_debug)) { mp.debug_indent-=3; std::fprintf(cimg::output(), - "\n[_cimg_math_parser] %p[thread #%u]:%*c" + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" "End debugging expression '%s' -> mem[%u] = %g (memsize: %u)", (void*)&mp,n_thread,mp.debug_indent,' ', expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width); @@ -20264,21 +20666,21 @@ namespace cimg_library_suffixed { static double mp_det(_cimg_math_parser& mp) { const double *ptrs = &_mp_arg(2) + 1; const unsigned int k = (unsigned int)mp.opcode[3]; - return CImg(ptrs,k,k,1,1,true).det(); + return CImg(ptrs,k,k,1,1,true).det(); } static double mp_diag(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const double *ptrs = &_mp_arg(2) + 1; const unsigned int k = (unsigned int)mp.opcode[3]; - CImg(ptrd,k,k,1,1,true) = CImg(ptrs,1,k,1,1,true).get_diagonal(); + CImg(ptrd,k,k,1,1,true) = CImg(ptrs,1,k,1,1,true).get_diagonal(); return cimg::type::nan(); } static double mp_display_memory(_cimg_math_parser& mp) { cimg::unused(mp); std::fputc('\n',cimg::output()); - mp.mem.display("[_cimg_math_parser] Memory snapshot"); + mp.mem.display("[" cimg_appname "_math_parser] Memory snapshot"); return cimg::type::nan(); } @@ -20292,7 +20694,7 @@ namespace cimg_library_suffixed { h = (int)_mp_arg(5), d = (int)_mp_arg(6), s = (int)_mp_arg(7); - CImg img; + CImg img; if (w>0 && h>0 && d>0 && s>0) { if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true); else img.assign(ptr,siz).resize(w,h,d,s,-1); @@ -20301,7 +20703,7 @@ namespace cimg_library_suffixed { CImg expr(mp.opcode[2] - 8); const ulongT *ptrs = mp.opcode._data + 8; cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); - ((CImg::string("[_cimg_math_parser] ",false,true),expr)>'x').move_to(expr); + ((CImg::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr); cimg::strellipsize(expr); std::fputc('\n',cimg::output()); img.display(expr._data); @@ -20354,10 +20756,151 @@ namespace cimg_library_suffixed { return mp.mem[mem_body]; } + static double mp_draw(_cimg_math_parser& mp) { + const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7); + unsigned int ind = (unsigned int)mp.opcode[3]; + + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + unsigned int + dx = (unsigned int)mp.opcode[8], + dy = (unsigned int)mp.opcode[9], + dz = (unsigned int)mp.opcode[10], + dc = (unsigned int)mp.opcode[11]; + dx = dx==~0U?img._width:(unsigned int)_mp_arg(8); + dy = dy==~0U?img._height:(unsigned int)_mp_arg(9); + dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10); + dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11); + + const ulongT sizS = mp.opcode[2]; + if (sizS<(ulongT)dx*dy*dz*dc) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + CImg S(&_mp_arg(1) + 1,dx,dy,dz,dc,true); + const float opacity = (float)_mp_arg(12); + + if (img._data) { + if (mp.opcode[13]!=~0U) { // Opacity mask specified + const ulongT sizM = mp.opcode[14]; + if (sizM<(ulongT)dx*dy*dz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + const CImg M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); + img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15)); + } else img.draw_image(x,y,z,c,S,opacity); + } + return cimg::type::nan(); + } + + static double mp_echo(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(256); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + const CImg str = _str>'x'; + std::fprintf(cimg::output(),"\n%s",str._data); + return cimg::type::nan(); + } + + static double mp_ellipse(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + CImg color(img._spectrum,1,1,1,0); + bool is_invalid_arguments = false; + unsigned int i = 4; + float r1, r2, angle = 0, opacity = 1; + int x0, y0; + if (i>=i_end) is_invalid_arguments = true; + else { + x0 = (int)cimg::round(_mp_arg(i++)); + if (i>=i_end) is_invalid_arguments = true; + else { + y0 = (int)cimg::round(_mp_arg(i++)); + if (i>=i_end) is_invalid_arguments = true; + else { + r1 = (float)_mp_arg(i++); + if (i>=i_end) r2 = r1; + else { + r2 = (float)_mp_arg(i++); + if (i args(i_end - 4); + cimg_forX(args,k) args[k] = _mp_arg(4 + k); + if (ind==~0U) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " + "Invalid arguments '%s'. ", + mp.imgin.pixel_type(),args.value_string()._data); + else + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " + "Invalid arguments '#%u%s%s'. ", + mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); + } + return cimg::type::nan(); + } + static double mp_eq(_cimg_math_parser& mp) { return (double)(_mp_arg(2)==_mp_arg(3)); } + static double mp_ext(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(256); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + CImg str = _str>'x'; +#ifdef cimg_mp_ext_function + cimg_mp_ext_function(str); +#endif + return cimg::type::nan(); + } + static double mp_exp(_cimg_math_parser& mp) { return std::exp(_mp_arg(2)); } @@ -20365,12 +20908,12 @@ namespace cimg_library_suffixed { static double mp_eye(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const unsigned int k = (unsigned int)mp.opcode[2]; - CImg(ptrd,k,k,1,1,true).identity_matrix(); + CImg(ptrd,k,k,1,1,true).identity_matrix(); return cimg::type::nan(); } static double mp_factorial(_cimg_math_parser& mp) { - return cimg::factorial(_mp_arg(2)); + return cimg::factorial((int)_mp_arg(2)); } static double mp_fibonacci(_cimg_math_parser& mp) { @@ -20497,6 +21040,11 @@ namespace cimg_library_suffixed { return mp.mem[mem_body]; } + static double mp_fsize(_cimg_math_parser& mp) { + const CImg filename(mp.opcode._data + 3,mp.opcode[2] - 3); + return (double)cimg::fsize(filename); + } + static double mp_g(_cimg_math_parser& mp) { cimg::unused(mp); return cimg::grand(); @@ -20504,7 +21052,11 @@ namespace cimg_library_suffixed { static double mp_gauss(_cimg_math_parser& mp) { const double x = _mp_arg(2), s = _mp_arg(3); - return std::exp(-x*x/(2*s*s))/std::sqrt(2*s*s*cimg::PI); + return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1); + } + + static double mp_gcd(_cimg_math_parser& mp) { + return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3)); } static double mp_gt(_cimg_math_parser& mp) { @@ -20545,30 +21097,10 @@ namespace cimg_library_suffixed { return mp.mem[is_cond?mem_left:mem_right]; } - static double mp_image_crop(_cimg_math_parser& mp) { - double *ptrd = &_mp_arg(1) + 1; - const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6); - const unsigned int - dx = (unsigned int)mp.opcode[7], - dy = (unsigned int)mp.opcode[8], - dz = (unsigned int)mp.opcode[9], - dc = (unsigned int)mp.opcode[10]; - const bool boundary_conditions = (bool)_mp_arg(11); - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); - else CImg(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, - x + dx - 1,y + dy - 1, - z + dz - 1,c + dc - 1, - boundary_conditions); - return cimg::type::nan(); - } - static double mp_image_d(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; return (double)img.depth(); } @@ -20584,99 +21116,17 @@ namespace cimg_library_suffixed { return cimg::type::nan(); } - static double mp_image_draw(_cimg_math_parser& mp) { - const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7); - unsigned int ind = (unsigned int)mp.opcode[3]; - - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); - CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; - unsigned int - dx = (unsigned int)mp.opcode[8], - dy = (unsigned int)mp.opcode[9], - dz = (unsigned int)mp.opcode[10], - dc = (unsigned int)mp.opcode[11]; - dx = dx==~0U?img._width:(unsigned int)_mp_arg(8); - dy = dy==~0U?img._height:(unsigned int)_mp_arg(9); - dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10); - dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11); - - const ulongT sizS = mp.opcode[2]; - if (sizS<(ulongT)dx*dy*dz*dc) - throw CImgArgumentException("[_cimg_math_parser] CImg<%s>: Function 'draw()': " - "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " - "(%lu values) do not match.", - mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); - CImg S(&_mp_arg(1) + 1,dx,dy,dz,dc,true); - const float opacity = (float)_mp_arg(12); - - if (img._data) { - if (mp.opcode[13]!=~0U) { // Opacity mask specified - const ulongT sizM = mp.opcode[14]; - if (sizM<(ulongT)dx*dy*dz) - throw CImgArgumentException("[_cimg_math_parser] CImg<%s>: Function 'draw()': " - "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " - "(%lu values) do not match.", - mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); - const CImg M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); - img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15)); - } else img.draw_image(x,y,z,c,S,opacity); - } - return cimg::type::nan(); - } - static double mp_image_h(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; return (double)img.height(); } - static double mp_image_im(_cimg_math_parser& mp) { + static double mp_image_median(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - return (double)img.min(); - } - - static double mp_image_iM(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - return (double)img.max(); - } - - static double mp_image_ia(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - return (double)img.mean(); - } - - static double mp_image_iv(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - return (double)img.variance(); - } - - static double mp_image_is(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - return (double)img.sum(); - } - - static double mp_image_ip(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - return (double)img.product(); - } - - static double mp_image_ic(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; return (double)img.median(); } @@ -20709,7 +21159,7 @@ namespace cimg_library_suffixed { interp = (int)_mp_arg(7); if (mp.is_fill && img._data==mp.imgout._data) { cimg::mutex(6,0); - throw CImgArgumentException("[_cimg_math_parser] CImg<%s>: Function 'resize()': " + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': " "Cannot both fill and resize image (%u,%u,%u,%u) " "to new dimensions (%u,%u,%u,%u).", img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s); @@ -20729,110 +21179,65 @@ namespace cimg_library_suffixed { static double mp_image_s(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; return (double)img.spectrum(); } + static double mp_image_sort(_cimg_math_parser& mp) { + const bool is_increasing = (bool)_mp_arg(3); + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()), + axis = (unsigned int)_mp_arg(4); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + img.sort(is_increasing, + axis==0 || axis=='x'?'x': + axis==1 || axis=='y'?'y': + axis==2 || axis=='z'?'z': + axis==3 || axis=='c'?'c':0); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_stats(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind==~0U) CImg(ptrd,14,1,1,1,true) = mp.imgout.get_stats(); + else { + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats(); + } + return cimg::type::nan(); + } + static double mp_image_w(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; return (double)img.width(); } static double mp_image_wh(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; return (double)img.width()*img.height(); } static double mp_image_whd(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; return (double)img.width()*img.height()*img.depth(); } static double mp_image_whds(_cimg_math_parser& mp) { unsigned int ind = (unsigned int)mp.opcode[2]; if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; return (double)img.width()*img.height()*img.depth()*img.spectrum(); } - static double mp_image_xm(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - double x = 0; - img.contains(img.min(),x); - return x; - } - - static double mp_image_xM(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - double x = 0; - img.contains(img.max(),x); - return x; - } - - static double mp_image_ym(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - double x = 0, y = 0; - img.contains(img.min(),x,y); - return y; - } - - static double mp_image_yM(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - double x = 0, y = 0; - img.contains(img.max(),x,y); - return y; - } - - static double mp_image_zm(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - double x = 0, y = 0, z = 0; - img.contains(img.min(),x,y,z); - return z; - } - - static double mp_image_zM(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - double x = 0, y = 0, z = 0; - img.contains(img.max(),x,y,z); - return z; - } - - static double mp_image_cm(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - double x = 0, y = 0, z = 0, c = 0; - img.contains(img.min(),x,y,z,c); - return c; - } - - static double mp_image_cM(_cimg_math_parser& mp) { - unsigned int ind = (unsigned int)mp.opcode[2]; - if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); - const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; - double x = 0, y = 0, z = 0, c = 0; - img.contains(img.max(),x,y,z,c); - return c; - } - static double mp_increment(_cimg_math_parser& mp) { return _mp_arg(2) + 1; } @@ -21280,6 +21685,10 @@ namespace cimg_library_suffixed { } } + static double mp_list_l(_cimg_math_parser& mp) { + return (double)mp.listout.width(); + } + static double mp_list_median(_cimg_math_parser& mp) { const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); if (!mp.list_median) mp.list_median.assign(mp.listin._width); @@ -21481,7 +21890,7 @@ namespace cimg_library_suffixed { static double mp_list_stats(_cimg_math_parser& mp) { const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), - k = (unsigned int)_mp_arg(3); + k = (unsigned int)mp.opcode[3]; if (!mp.list_stats) mp.list_stats.assign(mp.listin._width); if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false); return mp.list_stats(ind,k); @@ -21581,7 +21990,7 @@ namespace cimg_library_suffixed { cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } } break; default : // Dirichlet - if (img.containsXYZC(x,y,z)) { + if (img.containsXYZC((int)x,(int)y,(int)z)) { ptrs = &img((int)x,(int)y,(int)z); cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } } else std::memset(ptrd,0,vsiz*sizeof(double)); @@ -21689,7 +22098,7 @@ namespace cimg_library_suffixed { cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } } break; default : // Dirichlet - if (img.containsXYZC(x,y,z)) { + if (img.containsXYZC((int)x,(int)y,(int)z)) { ptrs = &img((int)x,(int)y,(int)z); cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } } else std::memset(ptrd,0,vsiz*sizeof(double)); @@ -21779,10 +22188,10 @@ namespace cimg_library_suffixed { double *ptrd = &_mp_arg(1) + 1; const double *ptr1 = &_mp_arg(2) + 1; const unsigned int k = (unsigned int)mp.opcode[3]; - CImg val, vec; - CImg(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); - CImg(ptrd,1,k,1,1,true) = val; - CImg(ptrd + k,k,k,1,1,true) = vec.get_transpose(); + CImg val, vec; + CImg(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); + CImg(ptrd,1,k,1,1,true) = val; + CImg(ptrd + k,k,k,1,1,true) = vec.get_transpose(); return cimg::type::nan(); } @@ -21790,7 +22199,7 @@ namespace cimg_library_suffixed { double *ptrd = &_mp_arg(1) + 1; const double *ptr1 = &_mp_arg(2) + 1; const unsigned int k = (unsigned int)mp.opcode[3]; - CImg(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(); + CImg(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(); return cimg::type::nan(); } @@ -21803,7 +22212,7 @@ namespace cimg_library_suffixed { k = (unsigned int)mp.opcode[4], l = (unsigned int)mp.opcode[5], m = (unsigned int)mp.opcode[6]; - CImg(ptrd,m,k,1,1,true) = CImg(ptr1,l,k,1,1,true)*CImg(ptr2,m,l,1,1,true); + CImg(ptrd,m,k,1,1,true) = CImg(ptr1,l,k,1,1,true)*CImg(ptr2,m,l,1,1,true); return cimg::type::nan(); } @@ -21813,7 +22222,7 @@ namespace cimg_library_suffixed { const unsigned int k = (unsigned int)mp.opcode[3], l = (unsigned int)mp.opcode[4]; - CImg(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_pseudoinvert(); + CImg(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_pseudoinvert(); return cimg::type::nan(); } @@ -21823,11 +22232,11 @@ namespace cimg_library_suffixed { const unsigned int k = (unsigned int)mp.opcode[3], l = (unsigned int)mp.opcode[4]; - CImg U, S, V; - CImg(ptr1,k,l,1,1,true).SVD(U,S,V); - CImg(ptrd,k,l,1,1,true) = U; - CImg(ptrd + k*l,1,k,1,1,true) = S; - CImg(ptrd + k*l + k,k,k,1,1,true) = V; + CImg U, S, V; + CImg(ptr1,k,l,1,1,true).SVD(U,S,V); + CImg(ptrd,k,l,1,1,true) = U; + CImg(ptrd + k*l,1,k,1,1,true) = S; + CImg(ptrd + k*l + k,k,k,1,1,true) = V; return cimg::type::nan(); } @@ -21844,7 +22253,7 @@ namespace cimg_library_suffixed { off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind, eoff = off + (siz - 1)*inc; if (off<0 || eoff>=mp.mem.width()) - throw CImgArgumentException("[_cimg_math_parser] CImg<%s>: Function 'copy()': " + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " "Out-of-bounds variable pointer " "(length: %ld, increment: %ld, offset start: %ld, " "offset end: %ld, offset max: %u).", @@ -21876,7 +22285,7 @@ namespace cimg_library_suffixed { } else off+=(longT)mp.mem[p_ref[3]]; const longT eoff = off + (siz - 1)*inc; if (off<0 || eoff>=(longT)img.size()) - throw CImgArgumentException("[_cimg_math_parser] CImg<%s>: Function 'copy()': " + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " "Out-of-bounds image pointer " "(length: %ld, increment: %ld, offset start: %ld, " "offset end: %ld, offset max: %lu).", @@ -21906,7 +22315,7 @@ namespace cimg_library_suffixed { if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } } else { // Overlapping buffers - CImg buf((unsigned int)siz); + CImg buf((unsigned int)siz); cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; } ptrs = buf; if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } @@ -21957,13 +22366,6 @@ namespace cimg_library_suffixed { return -_mp_arg(2); } - static double mp_mean(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - for (unsigned int i = 4; i &img = ind==~0U?mp.imgout:mp.listout[ind]; + bool is_invalid_arguments = i_end<=4; + if (!is_invalid_arguments) { + const int nbv = (int)_mp_arg(4); + if (nbv<=0) is_invalid_arguments = true; + else { + CImg points(nbv,2,1,1,0); + CImg color(img._spectrum,1,1,1,0); + float opacity = 1; + unsigned int i = 5; + cimg_foroff(points,k) if (i args(i_end - 4); + cimg_forX(args,k) args[k] = _mp_arg(4 + k); + if (ind==~0U) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " + "Invalid arguments '%s'. ", + mp.imgin.pixel_type(),args.value_string()._data); + else + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " + "Invalid arguments '#%u%s%s'. ", + mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); + } + return cimg::type::nan(); } static double mp_pow(_cimg_math_parser& mp) { @@ -22086,14 +22528,18 @@ namespace cimg_library_suffixed { static double mp_print(_cimg_math_parser& mp) { const double val = _mp_arg(1); - cimg_pragma_openmp(critical) + const bool print_char = (bool)mp.opcode[3]; + cimg_pragma_openmp(critical(mp_print)) { - CImg expr(mp.opcode[2] - 3); - const ulongT *ptrs = mp.opcode._data + 3; + CImg expr(mp.opcode[2] - 4); + const ulongT *ptrs = mp.opcode._data + 4; cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); cimg::strellipsize(expr); cimg::mutex(6); - std::fprintf(cimg::output(),"\n[_cimg_math_parser] %s = %g",expr._data,val); + if (print_char) + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g = '%c'",expr._data,val,(int)val); + else + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g",expr._data,val); std::fflush(cimg::output()); cimg::mutex(6,0); } @@ -22135,7 +22581,7 @@ namespace cimg_library_suffixed { static double mp_rot3d(_cimg_math_parser& mp) { double *ptrd = &_mp_arg(1) + 1; const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5); - CImg(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); + CImg(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); return cimg::type::nan(); } @@ -22399,6 +22845,17 @@ namespace cimg_library_suffixed { return cimg::type::nan(); } + static double mp_shift(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int siz = (unsigned int)mp.opcode[3]; + const int + shift = (int)_mp_arg(4), + boundary_conditions = (int)_mp_arg(5); + CImg(ptrd,siz,1,1,1,true) = CImg(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions); + return cimg::type::nan(); + } + static double mp_sign(_cimg_math_parser& mp) { return cimg::sign(_mp_arg(2)); } @@ -22411,21 +22868,6 @@ namespace cimg_library_suffixed { return cimg::sinc(_mp_arg(2)); } - static double mp_single(_cimg_math_parser& mp) { - const double res = _mp_arg(1); - cimg_pragma_openmp(critical) - { - for (const CImg *const p_end = ++mp.p_code + mp.opcode[2]; - mp.p_code_data; - const ulongT target = mp.opcode[1]; - mp.mem[target] = _cimg_mp_defunc(mp); - } - } - --mp.p_code; - return res; - } - static double mp_sinh(_cimg_math_parser& mp) { return std::sinh(_mp_arg(2)); } @@ -22439,7 +22881,7 @@ namespace cimg_library_suffixed { k = (unsigned int)mp.opcode[4], l = (unsigned int)mp.opcode[5], m = (unsigned int)mp.opcode[6]; - CImg(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,true).get_solve(CImg(ptr1,k,l,1,1,true)); + CImg(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,true).get_solve(CImg(ptr1,k,l,1,1,true)); return cimg::type::nan(); } @@ -22463,6 +22905,15 @@ namespace cimg_library_suffixed { return std::sqrt(_mp_arg(2)); } + static double mp_srand(_cimg_math_parser& mp) { + return cimg::srand((unsigned int)_mp_arg(2)); + } + + static double mp_srand0(_cimg_math_parser& mp) { + cimg::unused(mp); + return cimg::srand(); + } + static double mp_std(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; CImg vals(i_end - 3); @@ -22480,16 +22931,19 @@ namespace cimg_library_suffixed { return cimg::type::nan(); } - static double mp_stod(_cimg_math_parser& mp) { + static double mp_stov(_cimg_math_parser& mp) { const double *ptrs = &_mp_arg(2); - const unsigned int siz = (unsigned int)mp.opcode[3]; - const bool is_strict = (bool)_mp_arg(4); - if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':cimg::type::nan(); - CImg ss(siz + 1); + const ulongT siz = (ulongT)mp.opcode[3]; + longT ind = (longT)_mp_arg(4); + const bool is_strict = (bool)_mp_arg(5); double val = cimg::type::nan(); + if (ind<0 || ind>=(longT)siz) return val; + if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val; + + CImg ss(siz + 1 - ind); char sep; - for (unsigned i = 0; i(ptrs,k,k,1,1,true).trace(); + return CImg(ptrs,k,k,1,1,true).trace(); } static double mp_transp(_cimg_math_parser& mp) { @@ -22538,7 +22992,7 @@ namespace cimg_library_suffixed { const unsigned int k = (unsigned int)mp.opcode[3], l = (unsigned int)mp.opcode[4]; - CImg(ptrd,l,k,1,1,true) = CImg(ptrs,k,l,1,1,true).get_transpose(); + CImg(ptrd,l,k,1,1,true) = CImg(ptrs,k,l,1,1,true).get_transpose(); return cimg::type::nan(); } @@ -22550,7 +23004,7 @@ namespace cimg_library_suffixed { return cimg::uppercase(_mp_arg(2)); } - static double mp_variance(_cimg_math_parser& mp) { + static double mp_var(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; CImg vals(i_end - 3); double *p = vals.data(); @@ -22571,7 +23025,7 @@ namespace cimg_library_suffixed { start = (longT)_mp_arg(4), sublength = (longT)mp.opcode[5]; if (start<0 || start + sublength>length) - throw CImgArgumentException("[_cimg_math_parser] CImg<%s>: Value accessor '[]': " + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': " "Out-of-bounds sub-vector request " "(length: %ld, start: %ld, sub-length: %ld).", mp.imgin.pixel_type(),length,start,sublength); @@ -22742,10 +23196,11 @@ namespace cimg_library_suffixed { } static double mp_vector_print(_cimg_math_parser& mp) { - cimg_pragma_openmp(critical) + const bool print_string = (bool)mp.opcode[4]; + cimg_pragma_openmp(critical(mp_vector_print)) { - CImg expr(mp.opcode[2] - 4); - const ulongT *ptrs = mp.opcode._data + 4; + CImg expr(mp.opcode[2] - 5); + const ulongT *ptrs = mp.opcode._data + 5; cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); cimg::strellipsize(expr); unsigned int @@ -22753,7 +23208,7 @@ namespace cimg_library_suffixed { siz0 = (unsigned int)mp.opcode[3], siz = siz0; cimg::mutex(6); - std::fprintf(cimg::output(),"\n[_cimg_math_parser] %s = [ ",expr._data); + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",expr._data); unsigned int count = 0; while (siz-->0) { if (count>=64 && siz>=64) { @@ -22763,7 +23218,14 @@ namespace cimg_library_suffixed { } else std::fprintf(cimg::output(),"%g%s",mp.mem[ptr++],siz?",":""); ++count; } - std::fprintf(cimg::output()," ] (size: %u)",siz0); + if (print_string) { + CImg str(siz0 + 1); + ptr = (unsigned int)mp.opcode[1] + 1; + for (unsigned int k = 0; k format(8); + switch (nb_digits) { + case -1 : std::strcpy(format,"%g"); break; + case 0 : std::strcpy(format,"%.17g"); break; + default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits); + } + CImg str; + if (sizs) { // Vector expression + const double *ptrs = &_mp_arg(3) + 1; + CImg(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str); + } else { // Scalar expression + str.assign(sizd + 1); + cimg_snprintf(str,sizd + 1,format,_mp_arg(3)); + } + const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1); + CImg(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1); + return cimg::type::nan(); + } + static double mp_whiledo(_cimg_math_parser& mp) { const ulongT mem_body = mp.opcode[1], @@ -22916,7 +23403,7 @@ namespace cimg_library_suffixed { cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } } break; default : // Dirichlet - if (img.containsXYZC(x,y,z)) { + if (img.containsXYZC((int)x,(int)y,(int)z)) { ptrs = &img((int)x,(int)y,(int)z); cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } } else std::memset(ptrd,0,vsiz*sizeof(double)); @@ -23024,7 +23511,7 @@ namespace cimg_library_suffixed { cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } } break; default : // Dirichlet - if (img.containsXYZC(x,y,z)) { + if (img.containsXYZC((int)x,(int)y,(int)z)) { ptrs = &img((int)x,(int)y,(int)z); cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; } } else std::memset(ptrd,0,vsiz*sizeof(double)); @@ -23997,7 +24484,7 @@ namespace cimg_library_suffixed { throw CImgInstanceException(_cimg_instance "kth_smallest(): Empty instance.", cimg_instance); - CImg arr(*this); + CImg arr(*this,false); ulongT l = 0, ir = size() - 1; for ( ; ; ) { if (ir<=l + 1) { @@ -24167,7 +24654,7 @@ namespace cimg_library_suffixed { const ulongT siz = size(); if (!siz || !_data) return 0; if (variance_method>1) { // Compute a scaled version of the Laplacian. - CImg tmp(*this); + CImg tmp(*this,false); if (_depth==1) { const double cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=262144 && _spectrum>=2)) @@ -24278,7 +24765,7 @@ namespace cimg_library_suffixed { double _eval(CImg *const img_output, const char *const expression, const double x, const double y, const double z, const double c, const CImgList *const list_inputs, CImgList *const list_outputs) const { - if (!expression) return 0; + if (!expression || !*expression) return 0; if (!expression[1]) switch (*expression) { // Single-char optimization. case 'w' : return (double)_width; case 'h' : return (double)_height; @@ -24325,13 +24812,13 @@ namespace cimg_library_suffixed { void _eval(CImg& output, CImg *const img_output, const char *const expression, const double x, const double y, const double z, const double c, const CImgList *const list_inputs, CImgList *const list_outputs) const { - if (!expression) { output.assign(1); *output = 0; } + if (!expression || !*expression) { output.assign(1); *output = 0; } if (!expression[1]) switch (*expression) { // Single-char optimization. - case 'w' : output.assign(1); *output = (t)_width; - case 'h' : output.assign(1); *output = (t)_height; - case 'd' : output.assign(1); *output = (t)_depth; - case 's' : output.assign(1); *output = (t)_spectrum; - case 'r' : output.assign(1); *output = (t)_is_shared; + case 'w' : output.assign(1); *output = (t)_width; break; + case 'h' : output.assign(1); *output = (t)_height; break; + case 'd' : output.assign(1); *output = (t)_depth; break; + case 's' : output.assign(1); *output = (t)_spectrum; break; + case 'r' : output.assign(1); *output = (t)_is_shared; break; } _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='*' || *expression==':'),"eval", @@ -24365,7 +24852,7 @@ namespace cimg_library_suffixed { CImg _eval(CImg *const output, const char *const expression, const CImg& xyzc, const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { CImg res(1,xyzc.size()/4); - if (!expression) return res.fill(0); + if (!expression || !*expression) return res.fill(0); _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false); #ifdef cimg_use_openmp cimg_pragma_openmp(parallel if (res._height>=512)) @@ -24397,7 +24884,7 @@ namespace cimg_library_suffixed { /* \param variance_method Method used to compute the variance (see variance(const unsigned int) const). \return Statistics vector as - [min; max; mean; variance; xmin; ymin; zmin; cmin; xmax; ymax; zmax; cmax; sum; product]. + [min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]. **/ CImg get_stats(const unsigned int variance_method=1) const { if (is_empty()) return CImg(); @@ -24419,7 +24906,7 @@ namespace cimg_library_suffixed { S2+=_val*_val; P*=_val; } - cimg_pragma_openmp(critical) { + cimg_pragma_openmp(critical(get_stats)) { if (lmM || (lM==M && lpM lu(*this); + CImg lu(*this,false); CImg indx; bool d; lu._LU(indx,d); @@ -24571,7 +25059,7 @@ namespace cimg_library_suffixed { \note - The spectrum() of the image must be a square. **/ CImg get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { - const int n = (int)std::sqrt((double)_spectrum); + const int n = (int)cimg::round(std::sqrt((double)_spectrum)); const T *ptrs = data(x,y,z,0); const ulongT whd = (ulongT)_width*_height*_depth; CImg res(n,n); @@ -24881,12 +25369,12 @@ namespace cimg_library_suffixed { a = _data[0], d = _data[1], g = _data[2], b = _data[3], e = _data[4], h = _data[5], c = _data[6], f = _data[7], i = _data[8]; - _data[0] = (T)((i*e-f*h)/dete), _data[1] = (T)((g*f-i*d)/dete), _data[2] = (T)((d*h-g*e)/dete); - _data[3] = (T)((h*c-i*b)/dete), _data[4] = (T)((i*a-c*g)/dete), _data[5] = (T)((g*b-a*h)/dete); - _data[6] = (T)((b*f-e*c)/dete), _data[7] = (T)((d*c-a*f)/dete), _data[8] = (T)((a*e-d*b)/dete); + _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete); + _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete); + _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete); } else { if (use_LU) { // LU-based inverse computation - CImg A(*this), indx, col(1,_width); + CImg A(*this,false), indx, col(1,_width); bool d; A._LU(indx,d); cimg_forX(*this,j) { @@ -25188,7 +25676,7 @@ namespace cimg_library_suffixed { return *this; } CImg V(_width,_width); - Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1.0f,cimg::abs(m),cimg::abs(M)); + Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M)); (CImg(*this,false)/=maxabs).SVD(vec,val,V,false); if (maxabs!=1) val*=maxabs; @@ -26093,8 +26581,8 @@ namespace cimg_library_suffixed { for (ptrd = _data; ptrd is_error; bool is_value_sequence = false; + cimg_abort_init; if (allow_formula) { @@ -26536,7 +27025,7 @@ namespace cimg_library_suffixed { } // Try to fill values according to a formula. - cimg_abort_init; + _cimg_abort_init_omp; if (!is_value_sequence) try { CImg base = provides_copy?provides_copy->get_shared():get_shared(); _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || @@ -26544,7 +27033,7 @@ namespace cimg_library_suffixed { calling_function,base,this,list_inputs,list_outputs,true); if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && mp.need_input_copy) - base.assign().assign(*this); // Needs input copy + base.assign().assign(*this,false); // Needs input copy bool do_in_parallel = false; #ifdef cimg_use_openmp @@ -26559,7 +27048,7 @@ namespace cimg_library_suffixed { if (*expression=='<') { CImg res(1,mp.result_dim); cimg_rofYZ(*this,y,z) { - cimg_abort_test(); + cimg_abort_test; cimg_rofX(*this,x) { mp(x,y,z,0,res._data); const double *ptrs = res._data; @@ -26569,7 +27058,7 @@ namespace cimg_library_suffixed { } else if (*expression=='>' || !do_in_parallel) { CImg res(1,mp.result_dim); cimg_forYZ(*this,y,z) { - cimg_abort_test(); + cimg_abort_test; cimg_forX(*this,x) { mp(x,y,z,0,res._data); const double *ptrs = res._data; @@ -26585,8 +27074,8 @@ namespace cimg_library_suffixed { &lmp = omp_get_thread_num()?_mp:mp; lmp.is_fill = true; cimg_pragma_openmp(for collapse(2)) - cimg_forYZ(*this,y,z) cimg_abort_try { - cimg_abort_test(); + cimg_forYZ(*this,y,z) _cimg_abort_try_omp { + cimg_abort_test; CImg res(1,lmp.result_dim); T *ptrd = data(0,y,z,0); cimg_forX(*this,x) { @@ -26594,7 +27083,7 @@ namespace cimg_library_suffixed { const double *ptrs = res._data; T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } } - } cimg_abort_catch() cimg_abort_catch_fill() + } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp } #endif } @@ -26602,9 +27091,9 @@ namespace cimg_library_suffixed { } else { // Scalar-valued expression T *ptrd = *expression=='<'?end() - 1:_data; if (*expression=='<') - cimg_rofYZC(*this,y,z,c) { cimg_abort_test(); cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); } + cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); } else if (*expression=='>' || !do_in_parallel) - cimg_forYZC(*this,y,z,c) { cimg_abort_test(); cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } + cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } else { #ifdef cimg_use_openmp cimg_pragma_openmp(parallel) @@ -26614,11 +27103,11 @@ namespace cimg_library_suffixed { &lmp = omp_get_thread_num()?_mp:mp; lmp.is_fill = true; cimg_pragma_openmp(for collapse(3)) - cimg_forYZC(*this,y,z,c) cimg_abort_try { - cimg_abort_test(); + cimg_forYZC(*this,y,z,c) _cimg_abort_try_omp { + cimg_abort_test; T *ptrd = data(0,y,z,c); cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c); - } cimg_abort_catch() cimg_abort_catch_fill() + } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp } #endif } @@ -26655,7 +27144,7 @@ namespace cimg_library_suffixed { } cimg::exception_mode(omode); - cimg_abort_test(); + cimg_abort_test; return *this; } @@ -27889,8 +28378,8 @@ namespace cimg_library_suffixed { return _label(nb,dx,dy,dz,tolerance); } - CImg _label(const unsigned int nb, const int - *const dx, const int *const dy, const int *const dz, + CImg _label(const unsigned int nb, const int *const dx, + const int *const dy, const int *const dz, const Tfloat tolerance) const { CImg res(_width,_height,_depth,_spectrum); cimg_forC(*this,c) { @@ -27918,7 +28407,7 @@ namespace cimg_library_suffixed { for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z=width()) return fill((T)0); + if (delta_x<=-width() || delta_x>=width()) return fill((T)0); if (delta_x<0) cimg_forYZC(*this,y,z,c) { std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T)); std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T)); @@ -30242,7 +30730,7 @@ namespace cimg_library_suffixed { } break; default : // Dirichlet - if (cimg::abs(delta_y)>=height()) return fill((T)0); + if (delta_y<=-height() || delta_y>=height()) return fill((T)0); if (delta_y<0) cimg_forZC(*this,z,c) { std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T)); std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T)); @@ -30292,7 +30780,7 @@ namespace cimg_library_suffixed { } break; default : // Dirichlet - if (cimg::abs(delta_z)>=depth()) return fill((T)0); + if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0); if (delta_z<0) cimg_forC(*this,c) { std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T)); std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T)); @@ -30338,7 +30826,7 @@ namespace cimg_library_suffixed { } break; default : // Dirichlet - if (cimg::abs(delta_c)>=spectrum()) return fill((T)0); + if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0); if (delta_c<0) { std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T)); std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T)); @@ -30711,8 +31199,7 @@ namespace cimg_library_suffixed { const float xc = x - rw2, yc = y - rh2, mx = cimg::mod(w2 + xc*ca + yc*sa,ww), my = cimg::mod(h2 - xc*sa + yc*ca,hh); - const Tfloat val = _cubic_atXY(mx::cut(val); + res(x,y,z,c) = _cubic_cut_atXY(mx=2048)) cimg_forXYZC(res,x,y,z,c) { const float xc = x - rw2, yc = y - rh2; - const Tfloat val = _cubic_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()), + res(x,y,z,c) = _cubic_cut_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()), cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c); - res(x,y,z,c) = cimg::type::cut(val); } } break; case 1 : { // Linear interpolation @@ -30773,8 +31259,7 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048)) cimg_forXYZC(res,x,y,z,c) { const float xc = x - rw2, yc = y - rh2; - const Tfloat val = _cubic_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c); - res(x,y,z,c) = cimg::type::cut(val); + res(x,y,z,c) = _cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c); } } break; case 1 : { // Linear interpolation @@ -30800,8 +31285,7 @@ namespace cimg_library_suffixed { cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048)) cimg_forXYZC(res,x,y,z,c) { const float xc = x - rw2, yc = y - rh2; - const Tfloat val = cubic_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0); - res(x,y,z,c) = cimg::type::cut(val); + res(x,y,z,c) = cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0); } } break; case 1 : { // Linear interpolation @@ -30919,9 +31403,9 @@ namespace cimg_library_suffixed { X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww), Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh), Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd); - cimg_forC(res,c) res(x,y,z,c) = cimg::type::cut(_cubic_atXYZ(X::cut(_cubic_atXYZ(X,Y,Z,c)); + cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c); } } break; case 1 : { // Linear interpolation @@ -31001,7 +31485,7 @@ namespace cimg_library_suffixed { X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc, Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc, Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc; - cimg_forC(res,c) res(x,y,z,c) = cimg::type::cut(_cubic_atXYZ(X,Y,Z,c)); + cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c); } } break; case 1 : { // Linear interpolation @@ -31038,7 +31522,7 @@ namespace cimg_library_suffixed { X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc, Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc, Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc; - cimg_forC(res,c) res(x,y,z,c) = cimg::type::cut(cubic_atXYZ(X,Y,Z,c,(T)0)); + cimg_forC(res,c) res(x,y,z,c) = cubic_cut_atXYZ(X,Y,Z,c,(T)0); } } break; case 1 : { // Linear interpolation @@ -31137,7 +31621,7 @@ namespace cimg_library_suffixed { const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); cimg_forX(res,x) { const float mx = cimg::mod(x - (float)*(ptrs0++),w2); - *(ptrd++) = (T)_cubic_atX(mx=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); + cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c); } break; case 1 : // Neumann cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(x - (float)*(ptrs0++),y,z,c); + cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(x - (float)*(ptrs0++),y,z,c); } break; default : // Dirichlet cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atX(x - (float)*(ptrs0++),y,z,c,(T)0); + cimg_forX(res,x) *(ptrd++) = cubic_cut_atX(x - (float)*(ptrs0++),y,z,c,(T)0); } } else if (interpolation==1) // Linear interpolation @@ -31238,7 +31722,7 @@ namespace cimg_library_suffixed { const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); cimg_forX(res,x) { const float mx = cimg::mod((float)*(ptrs0++),w2); - *(ptrd++) = (T)_cubic_atX(mx=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); + cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c); } break; case 1 : // Neumann cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atX((float)*(ptrs0++),0,0,c); + cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX((float)*(ptrs0++),0,0,c); } break; default : // Dirichlet cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atX((float)*(ptrs0++),0,0,c,(T)0); + cimg_forX(res,x) *(ptrd++) = cubic_cut_atX((float)*(ptrs0++),0,0,c,(T)0); } } else if (interpolation==1) // Linear interpolation @@ -31375,7 +31859,7 @@ namespace cimg_library_suffixed { const float mx = cimg::mod(x - (float)*(ptrs0++),w2), my = cimg::mod(y - (float)*(ptrs1++),h2); - *(ptrd++) = (T)_cubic_atXY(mx=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width), - cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c); + cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width), + cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c); } break; case 1 : // Neumann cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); + cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c); } break; default : // Dirichlet cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0); + cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0); } } else if (interpolation==1) // Linear interpolation @@ -31484,7 +31968,7 @@ namespace cimg_library_suffixed { const float mx = cimg::mod((float)*(ptrs0++),w2), my = cimg::mod((float)*(ptrs1++),h2); - *(ptrd++) = (T)_cubic_atXY(mx=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY(cimg::mod((float)*(ptrs0++),(float)_width), - cimg::mod((float)*(ptrs1++),(float)_height),0,c); + cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod((float)*(ptrs0++),(float)_width), + cimg::mod((float)*(ptrs1++),(float)_height),0,c); } break; case 1 : // Neumann cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)_cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); + cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c); } break; default : // Dirichlet cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096)) cimg_forYZC(res,y,z,c) { const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c); - cimg_forX(res,x) *(ptrd++) = (T)cubic_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0); + cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0); } } else if (interpolation==1) // Linear interpolation @@ -31641,9 +32125,9 @@ namespace cimg_library_suffixed { mx = cimg::mod(x - (float)*(ptrs0++),w2), my = cimg::mod(y - (float)*(ptrs1++),h2), mz = cimg::mod(z - (float)*(ptrs2++),d2); - *(ptrd++) = (T)_cubic_atXYZ(mx res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + CImg res; + const ulongT + res_whd = (ulongT)_width*_height*_depth, + res_size = res_whd*std::max(_spectrum,kernel._spectrum); + const bool + is_inner_parallel = _width*_height*_depth>=32768, + is_outer_parallel = res_size>=32768; + _cimg_abort_init_omp; cimg_abort_init; - if (boundary_conditions && kernel._width==kernel._height && - ((kernel._depth==1 && kernel._width<=5) || (kernel._depth==kernel._width && kernel._width<=3))) { - // Special optimization done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 kernel (with boundary_conditions=true). - CImg _kernel; - if (is_convolution) { // Add empty column/row/slice to shift kernel center in case of convolution - const int dw = !(kernel.width()%2), dh = !(kernel.height()%2), dd = !(kernel.depth()%2); - if (dw || dh || dd) - kernel.get_resize(kernel.width() + dw,kernel.height() + dh,kernel.depth() + dd,-100,0,0). - move_to(_kernel); - } - if (!_kernel) _kernel = kernel.get_shared(); - Ttfloat *ptrd = res._data; - CImg I; - switch (_kernel._depth) { - case 3 : { - I.assign(27); - cimg_forC(res,c) { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg _K = _kernel.get_shared_channel(c%_kernel._spectrum); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)_K.magnitude(2), M = _M*_M; - cimg_for3x3x3(_img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + - I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + - I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + - I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + - I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + - I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + - I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + - I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + - I[24]*I[24] + I[25]*I[25] + I[26]*I[26]); - *(ptrd++) = (Ttfloat)(N?(I[ 0]*_K[ 0] + I[ 1]*_K[ 1] + I[ 2]*_K[ 2] + - I[ 3]*_K[ 3] + I[ 4]*_K[ 4] + I[ 5]*_K[ 5] + - I[ 6]*_K[ 6] + I[ 7]*_K[ 7] + I[ 8]*_K[ 8] + - I[ 9]*_K[ 9] + I[10]*_K[10] + I[11]*_K[11] + - I[12]*_K[12] + I[13]*_K[13] + I[14]*_K[14] + - I[15]*_K[15] + I[16]*_K[16] + I[17]*_K[17] + - I[18]*_K[18] + I[19]*_K[19] + I[20]*_K[20] + - I[21]*_K[21] + I[22]*_K[22] + I[23]*_K[23] + - I[24]*_K[24] + I[25]*_K[25] + I[26]*_K[26])/std::sqrt(N):0); - } - } else cimg_for3x3x3(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*_K[ 0] + I[ 1]*_K[ 1] + I[ 2]*_K[ 2] + - I[ 3]*_K[ 3] + I[ 4]*_K[ 4] + I[ 5]*_K[ 5] + - I[ 6]*_K[ 6] + I[ 7]*_K[ 7] + I[ 8]*_K[ 8] + - I[ 9]*_K[ 9] + I[10]*_K[10] + I[11]*_K[11] + - I[12]*_K[12] + I[13]*_K[13] + I[14]*_K[14] + - I[15]*_K[15] + I[16]*_K[16] + I[17]*_K[17] + - I[18]*_K[18] + I[19]*_K[19] + I[20]*_K[20] + - I[21]*_K[21] + I[22]*_K[22] + I[23]*_K[23] + - I[24]*_K[24] + I[25]*_K[25] + I[26]*_K[26]); + if (kernel._width==kernel._height && + ((kernel._depth==1 && kernel._width<=6) || (kernel._depth==kernel._width && kernel._width<=3))) { + + // Special optimization done for 2x2, 3x3, 4x4, 5x5, 6x6, 2x2x2 and 3x3x3 kernel. + if (!boundary_conditions && res_whd<=3000*3000) { // Dirichlet boundaries + // For relatively small images, adding a zero border then use optimized NxN convolution loops is faster. + res = (kernel._depth==1?get_crop(-1,-1,_width,_height):get_crop(-1,-1,-1,_width,_height,_depth)). + _correlate(kernel,true,is_normalized,is_convolution); + if (kernel._depth==1) res.crop(1,1,res._width - 2,res._height - 2); + else res.crop(1,1,1,res._width - 2,res._height - 2,res._depth - 2); + + } else { // Neumann boundaries + res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + cimg::unused(is_inner_parallel,is_outer_parallel); + CImg _kernel; + if (is_convolution) { // Add empty column/row/slice to shift kernel center in case of convolution + const int dw = !(kernel.width()%2), dh = !(kernel.height()%2), dd = !(kernel.depth()%2); + if (dw || dh || dd) + kernel.get_resize(kernel.width() + dw,kernel.height() + dh,kernel.depth() + dd,-100,0,0). + move_to(_kernel); } - } break; - case 2 : { - I.assign(8); - cimg_forC(res,c) { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_for2x2x2(_img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + - I[2]*I[2] + I[3]*I[3] + - I[4]*I[4] + I[5]*I[5] + - I[6]*I[6] + I[7]*I[7]); - *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + - I[2]*K[2] + I[3]*K[3] + - I[4]*K[4] + I[5]*K[5] + - I[6]*K[6] + I[7]*K[7])/std::sqrt(N):0); - } - } else cimg_for2x2x2(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + - I[2]*K[2] + I[3]*K[3] + - I[4]*K[4] + I[5]*K[5] + - I[6]*K[6] + I[7]*K[7]); - } - } break; - default : - case 1 : - switch (_kernel._width) { - case 6 : { - I.assign(36); - cimg_forC(res,c) { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); + if (!_kernel) _kernel = kernel.get_shared(); + + switch (_kernel._depth) { + case 3 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + cimg_forC(res,c) { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg I(27); + Ttfloat *ptrd = res.data(0,0,0,c); if (is_normalized) { const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + - I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + - I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + - I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + - I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24] + - I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] + - I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + - I[35]*I[35]); - *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + - I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + - I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] + - I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] + - I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35])/ - std::sqrt(N):0); + cimg_for3x3x3(img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + + I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + + I[24]*I[24] + I[25]*I[25] + I[26]*I[26]); + *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + + I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] + + I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] + + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + + I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + + I[15]*K[15] + I[16]*K[16] + I[17]*K[17] + + I[18]*K[18] + I[19]*K[19] + I[20]*K[20] + + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + + I[24]*K[24] + I[25]*K[25] + I[26]*K[26])/std::sqrt(N):0); } - } else cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + + } else cimg_for3x3x3(img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + + I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] + + I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] + + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + + I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + + I[15]*K[15] + I[16]*K[16] + I[17]*K[17] + + I[18]*K[18] + I[19]*K[19] + I[20]*K[20] + + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + + I[24]*K[24] + I[25]*K[25] + I[26]*K[26]); + } + } break; + case 2 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + cimg_forC(res,c) { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg I(8); + Ttfloat *ptrd = res.data(0,0,0,c); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; + cimg_for2x2x2(img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + + I[2]*I[2] + I[3]*I[3] + + I[4]*I[4] + I[5]*I[5] + + I[6]*I[6] + I[7]*I[7]); + *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + + I[2]*K[2] + I[3]*K[3] + + I[4]*K[4] + I[5]*K[5] + + I[6]*K[6] + I[7]*K[7])/std::sqrt(N):0); + } + } else cimg_for2x2x2(img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + + I[2]*K[2] + I[3]*K[3] + + I[4]*K[4] + I[5]*K[5] + + I[6]*K[6] + I[7]*K[7]); + } + } break; + default : + case 1 : + switch (_kernel._width) { + case 6 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + cimg_forC(res,c) { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg I(36); + Ttfloat *ptrd = res.data(0,0,0,c); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; + cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + + I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24] + + I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] + + I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + + I[35]*I[35]); + *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + @@ -33256,229 +33751,254 @@ namespace cimg_library_suffixed { I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] + I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] + - I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35]); - } - } break; - case 5 : { - I.assign(25); - cimg_forC(res,c) { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + - I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + - I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + - I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + - I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]); - *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + - I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + - I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24])/std::sqrt(N):0); - } - } else cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + + I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35])/ + std::sqrt(N):0); + } + } else cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + + I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + + I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + + I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + + I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + + I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + + I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] + + I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] + + I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35]); + } + } break; + case 5 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + cimg_forC(res,c) { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg I(25); + Ttfloat *ptrd = res.data(0,0,0,c); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; + cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + + I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]); + *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + - I[24]*K[24]); - } - } break; - case 4 : { - I.assign(16); - cimg_forC(res,c) { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + - I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + - I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + - I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]); - *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + - I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + - I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15])/ - std::sqrt(N):0); - } - } else cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + + I[24]*K[24])/std::sqrt(N):0); + } + } else cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + + I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + + I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + + I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] + + I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] + + I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] + + I[24]*K[24]); + } + } break; + case 4 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + cimg_forC(res,c) { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg I(16); + Ttfloat *ptrd = res.data(0,0,0,c); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; + cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + + I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]); + *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + - I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15]); - } - } break; - case 3 : { - I.assign(9); - cimg_forC(res,c) { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + - I[3]*I[3] + I[4]*I[4] + I[5]*I[5] + - I[6]*I[6] + I[7]*I[7] + I[8]*I[8]); - *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] + - I[3]*K[3] + I[4]*K[4] + I[5]*K[5] + - I[6]*K[6] + I[7]*K[7] + I[8]*K[8])/std::sqrt(N):0); - } - } else cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] + - I[3]*K[3] + I[4]*K[4] + I[5]*K[5] + - I[6]*K[6] + I[7]*K[7] + I[8]*K[8]); - } - } break; - case 2 : { - I.assign(4); - cimg_forC(res,c) { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); - if (is_normalized) { - const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T) { - const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + - I[2]*I[2] + I[3]*I[3]); - *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + - I[2]*K[2] + I[3]*K[3])/std::sqrt(N):0); - } - } else cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T) - *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + - I[2]*K[2] + I[3]*K[3]); - } - } break; - case 1 : - if (is_normalized) res.fill(1); - else cimg_forC(res,c) { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); - const CImg K = _kernel.get_shared_channel(c%_kernel._spectrum); - res.get_shared_channel(c).assign(_img)*=K[0]; + I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15])/ + std::sqrt(N):0); + } + } else cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] + + I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] + + I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] + + I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15]); } - break; + } break; + case 3 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + cimg_forC(res,c) { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg I(9); + Ttfloat *ptrd = res.data(0,0,0,c); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; + cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + + I[3]*I[3] + I[4]*I[4] + I[5]*I[5] + + I[6]*I[6] + I[7]*I[7] + I[8]*I[8]); + *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] + + I[3]*K[3] + I[4]*K[4] + I[5]*K[5] + + I[6]*K[6] + I[7]*K[7] + I[8]*K[8])/std::sqrt(N):0); + } + } else cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] + + I[3]*K[3] + I[4]*K[4] + I[5]*K[5] + + I[6]*K[6] + I[7]*K[7] + I[8]*K[8]); + } + } break; + case 2 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel)) + cimg_forC(res,c) { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg I(4); + Ttfloat *ptrd = res.data(0,0,0,c); + if (is_normalized) { + const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; + cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T) { + const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + + I[2]*I[2] + I[3]*I[3]); + *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + + I[2]*K[2] + I[3]*K[3])/std::sqrt(N):0); + } + } else cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T) + *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + + I[2]*K[2] + I[3]*K[3]); + } + } break; + case 1 : + if (is_normalized) res.fill(1); + else cimg_forC(res,c) { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + res.get_shared_channel(c).assign(img)*=K[0]; + } + break; + } } } - } else { // Generic version for other kernels and boundary conditions. + } + + if (!res) { // Generic version for other kernels and boundary conditions. + res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); int mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2, mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1; if (is_convolution) cimg::swap(mx1,mx2,my1,my2,mz1,mz2); // Shift kernel center in case of convolution const int mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; - cimg_forC(res,c) cimg_abort_try { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_omp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); const CImg K = kernel.get_shared_channel(c%kernel._spectrum); if (is_normalized) { // Normalized correlation. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M; - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=32768)) + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) for (int z = mz1; z=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Ttfloat val = 0, N = 0; for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { - const Ttfloat _val = (Ttfloat)_img._atXYZ(x + xm,y + ym,z + zm); + const Ttfloat _val = (Ttfloat)img._atXYZ(x + xm,y + ym,z + zm); val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm); N+=_val*_val; } N*=M; res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0); } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Ttfloat val = 0, N = 0; for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { - const Ttfloat _val = (Ttfloat)_img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + const Ttfloat _val = (Ttfloat)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm); N+=_val*_val; } N*=M; res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0); } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 } else { // Classical correlation. - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=32768)) + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) for (int z = mz1; z=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Ttfloat val = 0; for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) - val+=_img._atXYZ(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm); + val+=img._atXYZ(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm); res(x,y,z,c) = (Ttfloat)val; } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Ttfloat val = 0; for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) - val+=_img.atXYZ(x + xm,y + ym,z + zm,0,(T)0)*K(mx1 + xm,my1 + ym,mz1 + zm); + val+=img.atXYZ(x + xm,y + ym,z + zm,0,(T)0)*K(mx1 + xm,my1 + ym,mz1 + zm); res(x,y,z,c) = (Ttfloat)val; } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 } - } cimg_abort_catch() + } _cimg_abort_catch_omp } - cimg_abort_test(); + cimg_abort_test; return res; } @@ -33501,8 +34021,8 @@ namespace cimg_library_suffixed { template CImg<_cimg_Ttfloat> get_convolve(const CImg& kernel, const bool boundary_conditions=true, const bool is_normalized=false) const { - return _correlate(CImg(kernel._data,kernel.size(),1,1,1,true).get_mirror('x'). - resize(kernel,-1),boundary_conditions,is_normalized,true); + return _correlate(CImg(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true). + get_mirror('x').resize(kernel,-1),boundary_conditions,is_normalized,true); } //! Cumulate image values, optionally along specified axis. @@ -33599,111 +34119,117 @@ namespace cimg_library_suffixed { mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2, mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1, mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + const bool + is_inner_parallel = _width*_height*_depth>=32768, + is_outer_parallel = res.size()>=32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + _cimg_abort_init_omp; cimg_abort_init; - cimg_forC(*this,c) cimg_abort_try { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_omp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); const CImg K = kernel.get_shared_channel(c%kernel._spectrum); if (is_real) { // Real erosion - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=32768)) + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) for (int z = mz1; z::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); - const Tt cval = (Tt)(_img(x + xm,y + ym,z + zm) - mval); + const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval); if (cval=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); - const Tt cval = (Tt)(_img._atXYZ(x + xm,y + ym,z + zm) - mval); + const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); if (cval=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); - const Tt cval = (Tt)(_img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); + const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); if (cval=32768)) + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) for (int z = mz1; z::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) if (K(mx1 + xm,my1 + ym,mz1 + zm)) { - const Tt cval = (Tt)_img(x + xm,y + ym,z + zm); + const Tt cval = (Tt)img(x + xm,y + ym,z + zm); if (cval=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) if (K(mx1 + xm,my1 + ym,mz1 + zm)) { - const T cval = (Tt)_img._atXYZ(x + xm,y + ym,z + zm); + const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); if (cval=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt min_val = cimg::type::max(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) if (K(mx1 + xm,my1 + ym,mz1 + zm)) { - const T cval = (Tt)_img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + const T cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); if (cval res(_width,_height,_depth,_spectrum); + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); const int mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2, mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1, mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + const bool + is_inner_parallel = _width*_height*_depth>=32768, + is_outer_parallel = res.size()>=32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + _cimg_abort_init_omp; cimg_abort_init; - cimg_forC(*this,c) cimg_abort_try { - cimg_abort_test(); - const CImg _img = get_shared_channel(c%_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_omp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); const CImg K = kernel.get_shared_channel(c%kernel._spectrum); if (is_real) { // Real dilation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=32768)) + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) for (int z = mz1; z::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); - const Tt cval = (Tt)(_img(x + xm,y + ym,z + zm) + mval); + const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval); if (cval>max_val) max_val = cval; } res(x,y,z,c) = max_val; - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 if (boundary_conditions) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt max_val = cimg::type::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); - const Tt cval = (Tt)(_img._atXYZ(x + xm,y + ym,z + zm) + mval); + const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); if (cval>max_val) max_val = cval; } res(x,y,z,c) = max_val; } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) - cimg_forYZ(*this,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt max_val = cimg::type::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); - const Tt cval = (Tt)(_img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); + const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); if (cval>max_val) max_val = cval; } res(x,y,z,c) = max_val; } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 } else { // Binary dilation - cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth>=128)) + cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel)) for (int z = mz1; z::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) if (K(mx2 - xm,my2 - ym,mz2 - zm)) { - const Tt cval = (Tt)_img(x + xm,y + ym,z + zm); + const Tt cval = (Tt)img(x + xm,y + ym,z + zm); if (cval>max_val) max_val = cval; } res(x,y,z,c) = max_val; - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 if (boundary_conditions) - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt max_val = cimg::type::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) if (K(mx2 - xm,my2 - ym,mz2 - zm)) { - const T cval = (Tt)_img._atXYZ(x + xm,y + ym,z + zm); + const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); if (cval>max_val) max_val = cval; } res(x,y,z,c) = max_val; } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 else - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=128)) - cimg_forYZ(res,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { Tt max_val = cimg::type::min(); for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) if (K(mx2 - xm,my2 - ym,mz2 - zm)) { - const T cval = (Tt)_img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + const T cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); if (cval>max_val) max_val = cval; } res(x,y,z,c) = max_val; } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 } - } cimg_abort_catch() - cimg_abort_test(); + } _cimg_abort_catch_omp + cimg_abort_test; return res; } @@ -34628,6 +35160,8 @@ namespace cimg_library_suffixed { CImg& vanvliet(const float sigma, const unsigned int order, const char axis='x', const bool boundary_conditions=true) { if (is_empty()) return *this; + if (!cimg::type::is_float()) + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this); const char naxis = cimg::lowercase(axis); const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; if (is_empty() || (nsigma<0.5f && !order)) return *this; @@ -34750,19 +35284,21 @@ namespace cimg_library_suffixed { "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).", cimg_instance, G._width,G._height,G._depth,G._spectrum,G._data); - - if (is_empty() || amplitude<=0 || dl<0) return *this; + if (is_empty() || dl<0) return *this; + const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100; + unsigned int iamplitude = cimg::round(namplitude); const bool is_3d = (G._spectrum==6); T val_min, val_max = max_min(val_min); + _cimg_abort_init_omp; cimg_abort_init; if (da<=0) { // Iterated oriented Laplacians CImg velocity(_width,_height,_depth,_spectrum); - for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) { + for (unsigned int iteration = 0; iteration res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0); int N = 0; @@ -34828,11 +35364,11 @@ namespace cimg_library_suffixed { *(pd3++) = (Tfloat)n; } - cimg_abort_test(); + cimg_abort_test; cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=2) firstprivate(val)) - cimg_forYZ(*this,y,z) cimg_abort_try2 { - cimg_abort_test2(); + cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 { + cimg_abort_test2; cimg_forX(*this,x) { val.fill(0); const float @@ -34903,7 +35439,7 @@ namespace cimg_library_suffixed { if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; } } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 } } } else { // 2d LIC algorithm @@ -34924,10 +35460,10 @@ namespace cimg_library_suffixed { *(pd2++) = (Tfloat)n; } - cimg_abort_test(); + cimg_abort_test; cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256 && _height>=2) firstprivate(val)) - cimg_forY(*this,y) cimg_abort_try2 { - cimg_abort_test2(); + cimg_forY(*this,y) _cimg_abort_try_omp2 { + cimg_abort_test2; cimg_forX(*this,x) { val.fill(0); const float @@ -34992,7 +35528,7 @@ namespace cimg_library_suffixed { if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; } } - } cimg_abort_catch2() + } _cimg_abort_catch_omp2 } } const Tfloat *ptrs = res._data; @@ -35001,7 +35537,7 @@ namespace cimg_library_suffixed { *ptrd = valval_max?val_max:(T)val); } } - cimg_abort_test(); + cimg_abort_test; return *this; } @@ -35032,7 +35568,9 @@ namespace cimg_library_suffixed { const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0, const bool is_fast_approx=true) { - return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3), + const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100; + const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; + return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3), amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); } @@ -35412,37 +35950,18 @@ namespace cimg_library_suffixed { } _regularization = std::max(_regularization,0.01f); const unsigned int psize = (unsigned int)(1 + 2*_radius); - const CImg N = CImg(_width,_height,_depth,1,1)._blur_guided(psize); CImg - mean_I = CImg(guide,false)._blur_guided(psize).div(N), - mean_p = CImg(*this,false)._blur_guided(psize).div(N), - cov_Ip = CImg(*this,false).mul(guide)._blur_guided(psize).div(N)-=mean_p.get_mul(mean_I), - var_I = CImg(guide,false).sqr()._blur_guided(psize).div(N)-=mean_I.get_sqr(), + mean_I = guide.get_blur_box(psize,true), + mean_p = get_blur_box(psize,true), + cov_Ip = (guide.get_mul(*this)).blur_box(psize,true)-=mean_I.get_mul(mean_p), + var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(), &a = cov_Ip.div(var_I+=_regularization), &b = mean_p-=a.get_mul(mean_I); - a._blur_guided(psize).div(N); - b._blur_guided(psize).div(N); + a.blur_box(psize,true); + b.blur_box(psize,true); return a.mul(guide)+=b; } - // [internal] Perform box filter with dirichlet boundary conditions. - CImg& _blur_guided(const unsigned int psize) { - const int p1 = (int)psize/2, p2 = (int)psize - p1; - if (_depth!=1) { - CImg cumul = get_cumulate('z'), cumul2 = cumul.get_shift(0,0,p2,0,1); - (cumul.shift(0,0,-p1,0,1)-=cumul2).move_to(*this); - } - if (_height!=1) { - CImg cumul = get_cumulate('y'), cumul2 = cumul.get_shift(0,p2,0,0,1); - (cumul.shift(0,-p1,0,0,1)-=cumul2).move_to(*this); - } - if (_width!=1) { - CImg cumul = get_cumulate('x'), cumul2 = cumul.get_shift(p2,0,0,0,1); - (cumul.shift(-p1,0,0,0,1)-=cumul2).move_to(*this); - } - return *this; - } - //! Blur image using patch-based space. /** \param sigma_s Amount of blur along the XYZ-axes. @@ -35469,16 +35988,17 @@ namespace cimg_library_suffixed { const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ float sum_weights = 0; \ - cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0) - img(p,q,r,0))3?0.0f:1.0f; \ - sum_weights+=weight; \ - cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \ - } \ + cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \ + if (cimg::abs((Tfloat)img(x,y,z,0) - (Tfloat)img(p,q,r,0))3?0.0f:1.0f; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \ + } \ if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \ else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ } @@ -35510,16 +36030,17 @@ namespace cimg_library_suffixed { T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \ const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ float sum_weights = 0; \ - cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0,0) - img(p,q,0,0))3?0.0f:1.0f; \ - sum_weights+=weight; \ - cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \ - } \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \ + if (cimg::abs((Tfloat)img(x,y,0,0) - (Tfloat)img(p,q,0,0))3?0.0f:1.0f; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \ + } \ if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \ else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ } @@ -35569,15 +36090,16 @@ namespace cimg_library_suffixed { const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; float sum_weights = 0; - cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0)-img(p,q,r,0))3?0.0f:1.0f; - sum_weights+=weight; - cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); - } + cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) + if (cimg::abs((Tfloat)img(x,y,z,0) - (Tfloat)img(p,q,r,0))3?0.0f:1.0f; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); + } if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); } else @@ -35620,15 +36142,16 @@ namespace cimg_library_suffixed { P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; float sum_weights = 0; - cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0)-img(p,q,0))3?0.0f:1.0f; - sum_weights+=weight; - cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); - } + cimg_for_inXY(res,x0,y0,x1,y1,p,q) + if ((Tfloat)cimg::abs(img(x,y,0) - (Tfloat)img(p,q,0))3?0.0f:1.0f; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); + } if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); } else @@ -35681,13 +36204,13 @@ namespace cimg_library_suffixed { x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; - const float val0 = (float)(*this)(x,y,z,c); + const Tfloat val0 = (Tfloat)(*this)(x,y,z,c); CImg values(n*n*n); unsigned int nb_values = 0; T *ptrd = values.data(); cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r) - if (cimg::abs((float)(*this)(p,q,r,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,q,r,c); ++nb_values; } - res(x,y,z,c) = values.get_shared_points(0,nb_values - 1).median(); + if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(ptrd++) = (*this)(p,q,r,c); ++nb_values; } + res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c); } else cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4)) @@ -35699,112 +36222,82 @@ namespace cimg_library_suffixed { res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median(); } } else { -#define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b) - if (res._height!=1) { // 2d - if (threshold>0) + if (threshold>0) + cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=16 && _height*_spectrum>=4)) + cimg_forXYC(*this,x,y,c) { // With threshold. + const int + x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; + const Tfloat val0 = (Tfloat)(*this)(x,y,c); + CImg values(n*n); + unsigned int nb_values = 0; + T *ptrd = values.data(); + cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q) + if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(ptrd++) = (*this)(p,q,c); ++nb_values; } + res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c); + } + else { + const int + w1 = width() - 1, h1 = height() - 1, + w2 = width() - 2, h2 = height() - 2, + w3 = width() - 3, h3 = height() - 3, + w4 = width() - 4, h4 = height() - 4; + switch (n) { // Without threshold. + case 3 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(9); + cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]); + cimg_for_borderXY(*this,x,y,1) + res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c, + std::min(w1,x + 1),std::min(h1,y + 1),0,c).median(); + } + } break; + case 5 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(25); + cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4], + I[5],I[6],I[7],I[8],I[9], + I[10],I[11],I[12],I[13],I[14], + I[15],I[16],I[17],I[18],I[19], + I[20],I[21],I[22],I[23],I[24]); + cimg_for_borderXY(*this,x,y,2) + res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c, + std::min(w1,x + 2),std::min(h1,y + 2),0,c).median(); + } + } break; + case 7 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(49); + cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6], + I[7],I[8],I[9],I[10],I[11],I[12],I[13], + I[14],I[15],I[16],I[17],I[18],I[19],I[20], + I[21],I[22],I[23],I[24],I[25],I[26],I[27], + I[28],I[29],I[30],I[31],I[32],I[33],I[34], + I[35],I[36],I[37],I[38],I[39],I[40],I[41], + I[42],I[43],I[44],I[45],I[46],I[47],I[48]); + cimg_for_borderXY(*this,x,y,3) + res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c, + std::min(w1,x + 3),std::min(h1,y + 3),0,c).median(); + } + } break; + default : { cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=16 && _height*_spectrum>=4)) - cimg_forXYC(*this,x,y,c) { // With threshold. + cimg_forXYC(*this,x,y,c) { const int x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, - nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; - const float val0 = (float)(*this)(x,y,c); - CImg values(n*n); - unsigned int nb_values = 0; - T *ptrd = values.data(); - cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q) - if (cimg::abs((float)(*this)(p,q,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,q,c); ++nb_values; } - res(x,y,c) = values.get_shared_points(0,nb_values - 1).median(); - } - else switch (n) { // Without threshold. - case 3 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - T I[9] = { (T)0 }; - cimg_for3x3(*this,x,y,0,c,I,T) - res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]); - } - } break; - case 5 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - T I[25] = { (T)0 }; - cimg_for5x5(*this,x,y,0,c,I,T) - res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4], - I[5],I[6],I[7],I[8],I[9], - I[10],I[11],I[12],I[13],I[14], - I[15],I[16],I[17],I[18],I[19], - I[20],I[21],I[22],I[23],I[24]); - } - } break; - case 7 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - T I[49] = { (T)0 }; - cimg_for7x7(*this,x,y,0,c,I,T) - res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6], - I[7],I[8],I[9],I[10],I[11],I[12],I[13], - I[14],I[15],I[16],I[17],I[18],I[19],I[20], - I[21],I[22],I[23],I[24],I[25],I[26],I[27], - I[28],I[29],I[30],I[31],I[32],I[33],I[34], - I[35],I[36],I[37],I[38],I[39],I[40],I[41], - I[42],I[43],I[44],I[45],I[46],I[47],I[48]); - } - } break; - default : { - cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=16 && _height*_spectrum>=4)) - cimg_forXYC(*this,x,y,c) { - const int - x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, - nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, - nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; - res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); - } - } - } - } else { // 1d - - CImg I; - if (threshold>0) - cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=16 && _spectrum>=2)) - cimg_forXC(*this,x,c) { // With threshold. - const int - x0 = x - hl, x1 = x + hr, - nx0 = x0<0?0:x0, nx1 = x1>=width()?width() - 1:x1; - const float val0 = (float)(*this)(x,c); - CImg values(n); - unsigned int nb_values = 0; - T *ptrd = values.data(); - cimg_for_inX(*this,nx0,nx1,p) - if (cimg::abs((float)(*this)(p,c)-val0)<=threshold) { *(ptrd++) = (*this)(p,c); ++nb_values; } - res(x,c) = values.get_shared_points(0,nb_values - 1).median(); - } - else switch (n) { // Without threshold. - case 2 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - I.assign(4); - cimg_for2x2(*this,x,y,0,c,I,T) res(x,c) = (T)(0.5f*(I[0] + I[1])); - } - } break; - case 3 : { - cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) - cimg_forC(*this,c) { - I.assign(9); - cimg_for3x3(*this,x,y,0,c,I,T) - res(x,c) = I[3]=16 && _spectrum>=2)) - cimg_forXC(*this,x,c) { - const int - x0 = x - hl, x1 = x + hr, - nx0 = x0<0?0:x0, nx1 = x1>=width()?width() - 1:x1; - res(x,c) = get_crop(nx0,0,0,c,nx1,0,0,c).median(); - } - } + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; + res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); } + } + } } } return res; @@ -36210,7 +36703,7 @@ namespace cimg_library_suffixed { cimg_forC(*this,c) { Tfloat *ptrd = res[l2].data(0,0,0,c); CImg_3x3x3(I,Tfloat); - cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4; + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4; } } else if (axis1=='z' && axis2=='z') { // Izz @@ -36510,9 +37003,10 @@ namespace cimg_library_suffixed { float dt = 2, energy = cimg::type::max(); const CImgList dI = is_backward?I1.get_gradient():I2.get_gradient(); + cimg_abort_init; for (unsigned int iteration = 0; iteration64 && iter& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) { if (is_empty()) return *this; - CImg velocity(*this); + CImg velocity(*this,false); for (unsigned int iteration = 0; iteration1) { // 3d @@ -37897,7 +38392,7 @@ namespace cimg_library_suffixed { } } else { // Multi-scale version if (invert) { - res.assign(*this); + res.assign(*this,false); switch (cimg::lowercase(axis)) { case 'x' : { unsigned int w = _width; @@ -37968,7 +38463,7 @@ namespace cimg_library_suffixed { if (res) return res; } else { // Multi-scale transform if (invert) { // Inverse transform - res.assign(*this); + res.assign(*this,false); if (_width>1) { if (_height>1) { if (_depth>1) { @@ -43838,8 +44333,8 @@ namespace cimg_library_suffixed { cimg_instance); if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity); if (r1==r2 && (float)(int)r1==r1) { - if (pattern) return draw_circle(x0,y0,r1,color,opacity,pattern); - else return draw_circle(x0,y0,r1,color,opacity); + if (pattern) return draw_circle(x0,y0,(int)cimg::round(r1),color,opacity,pattern); + else return draw_circle(x0,y0,(int)cimg::round(r1),color,opacity); } cimg_init_scanline(color,opacity); const float @@ -43854,7 +44349,7 @@ namespace cimg_library_suffixed { b = u*v*(l1 - l2), c = l1*v*v + l2*u*u; const int - yb = (int)std::sqrt(a*rmax*rmax/(a*c - b*b)), + yb = (int)cimg::round(std::sqrt(a*rmax*rmax/(a*c - b*b))), tymin = y0 - yb - 1, tymax = y0 + yb + 1, ymin = tymin<0?0:tymin, @@ -44597,7 +45092,7 @@ namespace cimg_library_suffixed { if (is_empty()) return *this; const bool allow_zero = (x0*x1>0) || (y0*y1>0); const float - dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0), + dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0), px = dx<=0?1:precisionx==0?(float)std::pow(10.0,(int)std::log10(dx) - 2.0):precisionx, py = dy<=0?1:precisiony==0?(float)std::pow(10.0,(int)std::log10(dy) - 2.0):precisiony; if (x0!=x1 && y0!=y1) @@ -46699,30 +47194,34 @@ namespace cimg_library_suffixed { **/ CImg& select(CImgDisplay &disp, const unsigned int feature_type=2, unsigned int *const XYZ=0, - const bool exit_on_anykey=false) { - return get_select(disp,feature_type,XYZ,exit_on_anykey).move_to(*this); + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) { + return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); } //! Simple interface to select a shape from an image \overloading. CImg& select(const char *const title, const unsigned int feature_type=2, unsigned int *const XYZ=0, - const bool exit_on_anykey=false) { - return get_select(title,feature_type,XYZ,exit_on_anykey).move_to(*this); + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) { + return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); } //! Simple interface to select a shape from an image \newinstance. CImg get_select(CImgDisplay &disp, const unsigned int feature_type=2, unsigned int *const XYZ=0, - const bool exit_on_anykey=false) const { - return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false); + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) const { + return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); } //! Simple interface to select a shape from an image \newinstance. CImg get_select(const char *const title, const unsigned int feature_type=2, unsigned int *const XYZ=0, - const bool exit_on_anykey=false) const { + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) const { CImgDisplay disp; - return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false); + return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); } CImg _select(CImgDisplay &disp, const char *const title, @@ -46730,7 +47229,8 @@ namespace cimg_library_suffixed { const int origX, const int origY, const int origZ, const bool exit_on_anykey, const bool reset_view3d, - const bool force_display_z_coord) const { + const bool force_display_z_coord, + const bool is_deep_selection_default) const { if (is_empty()) return CImg(1,feature_type==0?3:6,1,1,-1); if (!disp) { disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); @@ -46739,7 +47239,7 @@ namespace cimg_library_suffixed { CImg thumb; if (width()>disp.screen_width() || height()>disp.screen_height()) - get_resize(cimg_fitscreen(width(),height(),1),1,-100).move_to(thumb); + get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb); const unsigned int old_normalization = disp.normalization(); bool old_is_resized = disp.is_resized(); @@ -46748,7 +47248,7 @@ namespace cimg_library_suffixed { static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; - int area = 0, starting_area = 0, clicked_area = 0, phase = 0, + int area = 0, area_started = 0, area_clicked = 0, phase = 0, X0 = (int)((XYZ?XYZ[0]:(_width - 1)/2)%_width), Y0 = (int)((XYZ?XYZ[1]:(_height - 1)/2)%_height), Z0 = (int)((XYZ?XYZ[2]:(_depth - 1)/2)%_depth), @@ -46757,9 +47257,10 @@ namespace cimg_library_suffixed { oX3d = X3d, oY3d = -1, omx = -1, omy = -1; float X = -1, Y = -1, Z = -1; - unsigned int old_button = 0, key = 0; + unsigned int key = 0; - bool shape_selected = false, text_down = false, visible_cursor = true; + bool is_deep_selection = is_deep_selection_default, + shape_selected = false, text_down = false, visible_cursor = true; static CImg pose3d; static bool is_view3d = false, is_axes = true; if (reset_view3d) { pose3d.assign(); is_view3d = false; } @@ -46785,7 +47286,7 @@ namespace cimg_library_suffixed { if (mX>=0 && mX=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); } if (mY>=0 && mX>=width() && mY=width() && mY>=height()) area = 4; - if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0; + if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0; CImg filename(32); @@ -46864,19 +47365,22 @@ namespace cimg_library_suffixed { mx = my = -1; X = Y = Z = -1; break; - case 1 : case 2 : case 3 : // When mouse is over the XY,XZ or YZ projections. - if (disp.button()&1 && phase<2 && clicked_area==area) { // When selection has been started (1st step). + case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections. + const unsigned int but = disp.button(); + const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4); + + if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step). if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; } - if (!(disp.button()&1) && phase>=2 && clicked_area!=area) { // When selection is at 2nd step (for volumes). - switch (starting_area) { + if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes). + switch (area_started) { case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break; case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break; case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break; } } - if (disp.button()&2 && clicked_area==area) { // When moving through the image/volume. + if (b2 && area_clicked==area) { // When moving through the image/volume. if (phase) { if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; @@ -46885,14 +47389,13 @@ namespace cimg_library_suffixed { X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; } } - if (disp.button()&4) { - X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = clicked_area = starting_area = 0; + if (b3) { // Reset selection + X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0; visu0.assign(); } if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel). if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && - !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT() && - !disp.is_keyALT() && !disp.is_keyALTGR()) { + !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) { switch (area) { case 1 : if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel()); @@ -46907,22 +47410,26 @@ namespace cimg_library_suffixed { disp.set_wheel(); } else key = ~0U; } - if ((disp.button()&1)!=old_button) { // When left button has just been pressed or released. - switch (phase) { + + if ((phase==0 && b1) || + (phase==1 && !b1) || + (phase==2 && b1)) switch (phase) { // Detect change of phase case 0 : - if (area==clicked_area) { - X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; starting_area = area; ++phase; + if (area==area_clicked) { + X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase; } break; case 1 : - if (area==starting_area) { + if (area==area_started) { X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase; - } else if (!(disp.button()&1)) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); } + if (_depth>1) { + if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default; + if (is_deep_selection) ++phase; + } + } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); } break; case 2 : ++phase; break; } - old_button = disp.button()&1; - } - break; + } break; case 4 : // When mouse is over the 3d view. if (is_view3d && points3d) { @@ -46955,7 +47462,7 @@ namespace cimg_library_suffixed { pose3d.draw_image(CImg::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2)); view3d.assign(); } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom. - pose3d(3,2)-=(oY3d - Y3d)*1.5f; view3d.assign(); + pose3d(3,2)+=(oY3d - Y3d)*1.5f; view3d.assign(); } if (disp.wheel()) { // Wheel: zoom pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel(); @@ -47084,8 +47591,9 @@ namespace cimg_library_suffixed { if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }} else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} const int d = (depth()>1)?depth():0; + int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z; + if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; } int - _vX = (int)X, _vY = (int)Y, _vZ = (int)Z, w = disp.width(), W = width() + d, h = disp.height(), H = height() + d, _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX), @@ -47122,20 +47630,23 @@ namespace cimg_library_suffixed { } // Draw box cursor. - if (xn - xp>=4 && yn - yp>=4) visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f). - draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555); + if (xn - xp>=4 && yn - yp>=4) + visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f). + draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555); if (_depth>1) { - if (yn - yp>=4 && zxn - zxp>=4) visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f). - draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555); - if (xn - xp>=4 && zyn - zyp>=4) visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f). - draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA). - draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555); + if (yn - yp>=4 && zxn - zxp>=4) + visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f). + draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555); + if (xn - xp>=4 && zyn - zyp>=4) + visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f). + draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555); } // Draw selection. - if (phase) { + if (phase && (phase!=1 || area_started==area)) { const int _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0), _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0), @@ -47165,20 +47676,26 @@ namespace cimg_library_suffixed { } break; case 2 : { visu.draw_rectangle(X01 || force_display_z_coord) cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z); else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y); - CImg values = get_vector_at(X,Y,Z); + CImg values = get_vector_at((int)X,(int)Y,(int)Z); const bool is_large_spectrum = values._height>16; - if (is_large_spectrum) values.draw_image(0,8,values.get_rows(values._height - 8,values._height - 1)).resize(1,16,1,1,0); + if (is_large_spectrum) + values.draw_image(0,8,values.get_rows(values._height - 8,values._height - 1)).resize(1,16,1,1,0); char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512; for (unsigned int c = 0; c::format_s(), @@ -47231,24 +47749,30 @@ namespace cimg_library_suffixed { } else switch (feature_type) { case 1 : { const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), - norm = cimg::hypot(dX,dY,dZ); + length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); if (_depth>1 || force_display_z_coord) - cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g ", - origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,norm); - else cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Norm = %g ", - origX + X0,origY + Y0,origX + X1,origY + Y1,norm); + cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ", + origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length); + else cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ", + origX + X0,origY + Y0,origX + X1,origY + Y1,length, + cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1)); } break; - case 2 : + case 2 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), + length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); if (_depth>1 || force_display_z_coord) - cimg_snprintf(text,text._width," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ", + cimg_snprintf(text,text._width, + " Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d), Length = %g ", origX + (X01 || force_display_z_coord) cimg_snprintf(text,text._width," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ", @@ -47262,8 +47786,9 @@ namespace cimg_library_suffixed { visu.draw_text(0,text_down?visu.height() - 13:0,text,foreground_color,background_color,0.7f,13); } - disp.display(visu).wait(); - } else if (!shape_selected) disp.wait(); + disp.display(visu); + } + if (!shape_selected) disp.wait(); if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); } omx = mx; omy = my; if (!exit_on_anykey && key && key!=cimg::keyESC && @@ -47277,6 +47802,11 @@ namespace cimg_library_suffixed { if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } if (shape_selected) { if (feature_type==2) { + if (is_deep_selection) switch (area_started) { + case 1 : Z0 = 0; Z1 = _depth - 1; break; + case 2 : Y0 = 0; Y1 = _height - 1; break; + case 3 : X0 = 0; X1 = _width - 1; break; + } if (X0>X1) cimg::swap(X0,X1); if (Y0>Y1) cimg::swap(Y0,Y1); if (Z0>Z1) cimg::swap(Z0,Z1); @@ -47285,7 +47815,9 @@ namespace cimg_library_suffixed { switch (feature_type) { case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break; case 3 : - res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0); // keep no break here! + res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0); + res[0] = X0; res[1] = Y0; res[2] = Z0; + break; default : res[0] = X0; res[1] = Y0; res[2] = Z0; } } @@ -47340,19 +47872,18 @@ namespace cimg_library_suffixed { } switch (normalization) { - case 1 : img2d.normalize((T)0,(T)255); break; + case 1 : img2d.normalize((ucharT)0,(ucharT)255); break; case 2 : { const float m = disp._min, M = disp._max; (img2d-=m)*=255.0f/(M - m>0?M - m:1); } break; case 3 : - if (cimg::type::is_float()) img2d.normalize((T)0,(T)255); + if (cimg::type::is_float()) img2d.normalize((ucharT)0,(ucharT)255); else { const float m = (float)cimg::type::min(), M = (float)cimg::type::max(); (img2d-=m)*=255.0f/(M - m>0?M - m:1); } break; } - if (img2d.spectrum()==2) img2d.channels(0,2); return img2d; } @@ -47970,11 +48501,11 @@ namespace cimg_library_suffixed { if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR); const int - dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(dx*bpp/8)), + dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)), align_bytes = (4 - dx_bytes%4)%4; - const longT - cimg_iobuffer = (longT)24*1024*1024, - buf_size = std::min((longT)cimg::abs(dy)*(dx_bytes + align_bytes),(longT)file_size - offset); + const ulongT + cimg_iobuffer = (ulongT)24*1024*1024, + buf_size = std::min((ulongT)cimg::abs(dy)*(dx_bytes + align_bytes),(ulongT)file_size - offset); CImg colormap; if (bpp<16) { if (!nb_colors) nb_colors = 1<=0; --y) { + if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) { if (buf_size>=cimg_iobuffer) { - cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; cimg::fseek(nfile,align_bytes,SEEK_CUR); } unsigned char mask = 0x80, val = 0; @@ -48023,9 +48554,9 @@ namespace cimg_library_suffixed { } } break; case 4 : { // 16 colors - for (int y = height() - 1; y>=0; --y) { + if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) { if (buf_size>=cimg_iobuffer) { - cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; cimg::fseek(nfile,align_bytes,SEEK_CUR); } unsigned char mask = 0xF0, val = 0; @@ -48041,10 +48572,10 @@ namespace cimg_library_suffixed { ptrs+=align_bytes; } } break; - case 8 : { // 256 colors - for (int y = height() - 1; y>=0; --y) { + case 8 : { // 256 colors + if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) { if (buf_size>=cimg_iobuffer) { - cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; cimg::fseek(nfile,align_bytes,SEEK_CUR); } cimg_forX(*this,x) { @@ -48059,7 +48590,7 @@ namespace cimg_library_suffixed { case 16 : { // 16 bits colors for (int y = height() - 1; y>=0; --y) { if (buf_size>=cimg_iobuffer) { - cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; cimg::fseek(nfile,align_bytes,SEEK_CUR); } cimg_forX(*this,x) { @@ -48075,7 +48606,7 @@ namespace cimg_library_suffixed { case 24 : { // 24 bits colors for (int y = height() - 1; y>=0; --y) { if (buf_size>=cimg_iobuffer) { - cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; cimg::fseek(nfile,align_bytes,SEEK_CUR); } cimg_forX(*this,x) { @@ -48089,7 +48620,7 @@ namespace cimg_library_suffixed { case 32 : { // 32 bits colors for (int y = height() - 1; y>=0; --y) { if (buf_size>=cimg_iobuffer) { - cimg::fread(ptrs=buffer._data,dx_bytes,nfile); + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; cimg::fseek(nfile,align_bytes,SEEK_CUR); } cimg_forX(*this,x) { @@ -49298,15 +49829,15 @@ namespace cimg_library_suffixed { rdr.ndim(2)?rdr.ndim(2):1, rdr.ndim(3)?rdr.ndim(3):1, rdr.ndim(4)?rdr.ndim(4):1); - if (typeid(T)==typeid(unsigned char)) + if (cimg::type::string()==cimg::type::string()) rdr.setup_read_byte(); - else if (typeid(T)==typeid(int)) + else if (cimg::type::string()==cimg::type::string()) rdr.setup_read_int(); - else if (typeid(T)==typeid(double)) + else if (cimg::type::string()==cimg::type::string()) rdr.setup_read_double(); else rdr.setup_read_float(); - minc::load_standard_volume(rdr, this->_data); + minc::load_standard_volume(rdr,this->_data); return *this; #endif } @@ -49611,12 +50142,14 @@ namespace cimg_library_suffixed { if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1; switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) { case 0 : break; - case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,tmp1._width - 1); + case 2 : + out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; + std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough case 1 : if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; - if (out[4]>=0) break; + if (out[4]>=0) break; // fallthrough default : throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.", pixel_type(), @@ -49776,7 +50309,6 @@ namespace cimg_library_suffixed { "load_pandore(): Unknown pixel datatype in file '%s'.", \ cimg_instance, \ filename?filename:"(FILE*)"); } - if (!file && !filename) throw CImgArgumentException(_cimg_instance "load_pandore(): Specified filename is (null).", @@ -49915,7 +50447,7 @@ namespace cimg_library_suffixed { case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break; case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break; case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break; - case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); + case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break; case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break; case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break; case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break; @@ -50072,6 +50604,7 @@ namespace cimg_library_suffixed { \param filename Filename, as a C-string. \param size_x Width of the frames. \param size_y Height of the frames. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. \param first_frame Index of the first frame to read. \param last_frame Index of the last frame to read. \param step_frame Step value for frame reading. @@ -50080,33 +50613,41 @@ namespace cimg_library_suffixed { **/ CImg& load_yuv(const char *const filename, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { - return get_load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + return get_load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); } //! Load image sequence from a YUV file \newinstance. static CImg get_load_yuv(const char *const filename, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { - return CImgList().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + return CImgList().load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); } //! Load image sequence from a YUV file \overloading. CImg& load_yuv(std::FILE *const file, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { - return get_load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + return get_load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); } //! Load image sequence from a YUV file \newinstance. static CImg get_load_yuv(std::FILE *const file, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { - return CImgList().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + return CImgList().load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); } //! Load 3d object from a .OFF file. @@ -50407,22 +50948,25 @@ namespace cimg_library_suffixed { std::FILE *file = 0; const CImg s_filename = CImg::string(filename)._system_strescape(); #if cimg_OS==1 - cimg_snprintf(command,command._width,"%s convert \"%s\" pnm:-", - cimg::graphicsmagick_path(),s_filename.data()); - file = popen(command,"r"); - if (file) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { load_pnm(file); } catch (...) { + if (!cimg::system("which gm")) { + cimg_snprintf(command,command._width,"%s convert \"%s\" pnm:-", + cimg::graphicsmagick_path(),s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' " + "with external command 'gm'.", + cimg_instance, + filename); + } pclose(file); - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", - cimg_instance, - filename); + return *this; } - pclose(file); - return *this; } #endif do { @@ -50519,25 +51063,27 @@ namespace cimg_library_suffixed { std::FILE *file = 0; const CImg s_filename = CImg::string(filename)._system_strescape(); #if cimg_OS==1 - cimg_snprintf(command,command._width,"%s%s \"%s\" pnm:-", - cimg::imagemagick_path(), - !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", - s_filename.data()); - file = popen(command,"r"); - if (file) { - const unsigned int omode = cimg::exception_mode(); - cimg::exception_mode(0); - try { load_pnm(file); } catch (...) { + if (!cimg::system("which convert")) { + cimg_snprintf(command,command._width,"%s%s \"%s\" pnm:-", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + } pclose(file); - cimg::exception_mode(omode); - throw CImgIOException(_cimg_instance - "load_imagemagick_external(): Failed to load file '%s' with " - "external command 'magick/convert'.", - cimg_instance, - filename); + return *this; } - pclose(file); - return *this; } #endif do { @@ -50958,24 +51504,18 @@ namespace cimg_library_suffixed { if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0); if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0); - if (!is_first_select) { - _XYZ[0] = (unsigned int)(x1 - x0)/2; - _XYZ[1] = (unsigned int)(y1 - y0)/2; - _XYZ[2] = (unsigned int)(z1 - z0)/2; - } - disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; - const CImg selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1); + CImg selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true); old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y; is_first_select = false; if (disp.wheel()) { - if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { - go_down = !(go_up = disp.wheel()>0); - } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { + if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) && + (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) { go_left = !(go_right = disp.wheel()>0); - } - else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { + } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { + go_down = !(go_up = disp.wheel()>0); + } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { go_out = !(go_in = disp.wheel()>0); go_in_center = false; } disp.set_wheel(); @@ -50987,7 +51527,7 @@ namespace cimg_library_suffixed { if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) { x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; x0+=sx0; y0+=sy0; z0+=sz0; - if (sx0==sx1 && sy0==sy1 && sz0==sz1) { + if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) { if (exit_on_simpleclick && (!zoom || is_empty())) break; else reset_view = true; } resize_disp = true; @@ -50995,11 +51535,7 @@ namespace cimg_library_suffixed { #if cimg_OS!=2 case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : #endif - case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT : -#if cimg_OS!=2 - case cimg::keyALTGR : -#endif - case cimg::keyALT : key = 0; break; + case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break; case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { // Special mode: play stack of frames const unsigned int @@ -51077,13 +51613,13 @@ namespace cimg_library_suffixed { mY = my*(height() + (depth()>1?depth():0))/disp.height(); int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2]; if (mX=height()) { - X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth(); Y = (int)_XYZ[1]; + X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth(); } if (mX>=width() && mY4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; } if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; } @@ -51410,7 +51946,7 @@ namespace cimg_library_suffixed { axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3); // Begin user interaction loop - CImg visu0(*this), visu; + CImg visu0(*this,false), visu; CImg zbuffer(visu0.width(),visu0.height(),1,1,0); bool init_pose = true, clicked = false, redraw = true; unsigned int key = 0; @@ -51996,7 +52532,7 @@ namespace cimg_library_suffixed { else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); // Image sequences - else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true); + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); else if (!cimg::strcasecmp(ext,"avi") || !cimg::strcasecmp(ext,"mov") || !cimg::strcasecmp(ext,"asf") || @@ -53178,7 +53714,7 @@ namespace cimg_library_suffixed { _TIFFfree(buf); } TIFFWriteDirectory(tif); - return (*this); + return *this; } const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, @@ -53232,11 +53768,11 @@ namespace cimg_library_suffixed { if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME)); wtr.open(filename,di,1,NC_FLOAT,0); } - if (typeid(T)==typeid(unsigned char)) + if (cimg::type::string()==cimg::type::string()) wtr.setup_write_byte(); - else if (typeid(T)==typeid(int)) + else if (cimg::type::string()==cimg::type::string()) wtr.setup_write_int(); - else if (typeid(T)==typeid(double)) + else if (cimg::type::string()==cimg::type::string()) wtr.setup_write_double(); else wtr.setup_write_float(); @@ -53778,21 +54314,26 @@ namespace cimg_library_suffixed { //! Save image as a .yuv video file. /** \param filename Filename, as a C-string. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false). \note Each slice of the instance image is considered to be a single frame of the output video file. **/ - const CImg& save_yuv(const char *const filename, const bool is_rgb=true) const { - get_split('z').save_yuv(filename,is_rgb); + const CImg& save_yuv(const char *const filename, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + CImgList(*this,true).save_yuv(filename,chroma_subsampling,is_rgb); return *this; } //! Save image as a .yuv video file \overloading. /** - Same as save_yuv(const char*,bool) const + Same as save_yuv(const char*,const unsigned int,const bool) const with a file stream argument instead of a filename string. **/ - const CImg& save_yuv(std::FILE *const file, const bool is_rgb=true) const { - get_split('z').save_yuv(file,is_rgb); + const CImg& save_yuv(std::FILE *const file, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + CImgList(*this,true).save_yuv(file,chroma_subsampling,is_rgb); return *this; } @@ -55547,26 +56088,6 @@ namespace cimg_library_suffixed { return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); } - //! Return a C-string containing the values of all images in the instance list. - /** - \param separator Character separator set between consecutive pixel values. - \param max_size Maximum size of the returned string. - \note The result is returne as a CImg image whose pixel buffer contains the desired C-string. - **/ - CImg value_string(const char separator=',', const unsigned int max_size=0) const { - if (is_empty()) return CImg(1,1,1,1,0); - CImgList items; - for (unsigned int l = 0; l<_width - 1; ++l) { - CImg item = _data[l].value_string(separator,0); - item.back() = separator; - item.move_to(items); - } - _data[_width - 1].value_string(separator,0).move_to(items); - CImg res; (items>'x').move_to(res); - if (max_size) { res.crop(0,max_size); res(max_size) = 0; } - return res; - } - //@} //------------------------------------- // @@ -56023,13 +56544,14 @@ namespace cimg_library_suffixed { *_data = img; } else { if (new_data) { // Insert with re-allocation. - if (npos) std::memcpy(new_data,_data,sizeof(CImg)*npos); - if (npos!=_width - 1) std::memcpy(new_data + npos + 1,_data + npos,sizeof(CImg)*(_width - 1 - npos)); - std::memset(_data,0,sizeof(CImg)*(_width - 1)); + if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos); + if (npos!=_width - 1) + std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + std::memset((void*)_data,0,sizeof(CImg)*(_width - 1)); delete[] _data; _data = new_data; } else if (npos!=_width - 1) // Insert without re-allocation. - std::memmove(_data + npos + 1,_data + npos,sizeof(CImg)*(_width - 1 - npos)); + std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0; _data[npos] = img; @@ -56061,8 +56583,9 @@ namespace cimg_library_suffixed { } else { if (new_data) { // Insert with re-allocation. - if (npos) std::memcpy(new_data,_data,sizeof(CImg)*npos); - if (npos!=_width - 1) std::memcpy(new_data + npos + 1,_data + npos,sizeof(CImg)*(_width - 1 - npos)); + if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos); + if (npos!=_width - 1) + std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); if (is_shared && img) { new_data[npos]._width = img._width; new_data[npos]._height = img._height; @@ -56075,11 +56598,12 @@ namespace cimg_library_suffixed { new_data[npos]._data = 0; new_data[npos] = img; } - std::memset(_data,0,sizeof(CImg)*(_width - 1)); + std::memset((void*)_data,0,sizeof(CImg)*(_width - 1)); delete[] _data; _data = new_data; } else { // Insert without re-allocation. - if (npos!=_width - 1) std::memmove(_data + npos + 1,_data + npos,sizeof(CImg)*(_width - 1 - npos)); + if (npos!=_width - 1) + std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); if (is_shared && img) { _data[npos]._width = img._width; _data[npos]._height = img._height; @@ -56214,16 +56738,19 @@ namespace cimg_library_suffixed { const unsigned int nb = 1 + npos2 - npos1; if (!(_width-=nb)) return assign(); if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation. - if (npos1!=_width) std::memmove(_data + npos1,_data + npos2 + 1,sizeof(CImg)*(_width - npos1)); - std::memset(_data + _width,0,sizeof(CImg)*nb); + if (npos1!=_width) + std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg)*(_width - npos1)); + std::memset((void*)(_data + _width),0,sizeof(CImg)*nb); } else { // Removing items with reallocation. _allocated_width>>=2; while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1; CImg *const new_data = new CImg[_allocated_width]; - if (npos1) std::memcpy(new_data,_data,sizeof(CImg)*npos1); - if (npos1!=_width) std::memcpy(new_data + npos1,_data + npos2 + 1,sizeof(CImg)*(_width - npos1)); - if (_width!=_allocated_width) std::memset(new_data + _width,0,sizeof(CImg)*(_allocated_width - _width)); - std::memset(_data,0,sizeof(CImg)*(_width + nb)); + if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos1); + if (npos1!=_width) + std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg)*(_width - npos1)); + if (_width!=_allocated_width) + std::memset((void*)(new_data + _width),0,sizeof(CImg)*(_allocated_width - _width)); + std::memset((void*)_data,0,sizeof(CImg)*(_width + nb)); delete[] _data; _data = new_data; } @@ -57341,6 +57868,7 @@ namespace cimg_library_suffixed { \param filename Filename to read data from. \param size_x Width of the images. \param size_y Height of the images. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. \param first_frame Index of first image frame to read. \param last_frame Index of last image frame to read. \param step_frame Step applied between each frame. @@ -57348,65 +57876,76 @@ namespace cimg_library_suffixed { **/ CImgList& load_yuv(const char *const filename, const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling=444, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { - return _load_yuv(0,filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); + return _load_yuv(0,filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); } //! Load a list from a YUV image sequence file \newinstance. static CImgList get_load_yuv(const char *const filename, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { - return CImgList().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); + return CImgList().load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); } //! Load a list from an image sequence YUV file \overloading. CImgList& load_yuv(std::FILE *const file, const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling=444, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { - return _load_yuv(file,0,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); + return _load_yuv(file,0,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); } //! Load a list from an image sequence YUV file \newinstance. static CImgList get_load_yuv(std::FILE *const file, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, const unsigned int first_frame=0, const unsigned int last_frame=~0U, const unsigned int step_frame=1, const bool yuv2rgb=true) { - return CImgList().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb); + return CImgList().load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); } CImgList& _load_yuv(std::FILE *const file, const char *const filename, const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling, const unsigned int first_frame, const unsigned int last_frame, const unsigned int step_frame, const bool yuv2rgb) { if (!filename && !file) throw CImgArgumentException(_cimglist_instance "load_yuv(): Specified filename is (null).", cimglist_instance); - if (size_x%2 || size_y%2) + if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) throw CImgArgumentException(_cimglist_instance - "load_yuv(): Invalid odd XY dimensions %ux%u in file '%s'.", + "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.", cimglist_instance, - size_x,size_y,filename?filename:"(FILE*)"); - if (!size_x || !size_y) - throw CImgArgumentException(_cimglist_instance - "load_yuv(): Invalid sequence size (%u,%u) in file '%s'.", - cimglist_instance, - size_x,size_y,filename?filename:"(FILE*)"); - + chroma_subsampling,filename?filename:"(FILE*)"); const unsigned int + cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, + cfy = chroma_subsampling==420?2:1, nfirst_frame = first_frame tmp(size_x,size_y,1,3), UV(size_x/2,size_y/2,1,2); + if (!size_x || !size_y || size_x%cfx || size_y%cfy) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.", + cimglist_instance, + size_x,size_y,filename?filename:"(FILE*)"); + + CImg YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); bool stop_flag = false; int err; if (nfirst_frame) { - err = cimg::fseek(nfile,nfirst_frame*(size_x*size_y + size_x*size_y/2),SEEK_CUR); + err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR); if (err) { if (!file) cimg::fclose(nfile); throw CImgIOException(_cimglist_instance @@ -57417,10 +57956,10 @@ namespace cimg_library_suffixed { } unsigned int frame; for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) { - tmp.fill(0); + YUV.get_shared_channel(0).fill(0); // *TRY* to read the luminance part, do not replace by cimg::fread! - err = (int)std::fread((void*)(tmp._data),1,(ulongT)tmp._width*tmp._height,nfile); - if (err!=(int)(tmp._width*tmp._height)) { + err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile); + if (err!=(int)(YUV._width*YUV._height)) { stop_flag = true; if (err>0) cimg::warn(_cimglist_instance @@ -57431,27 +57970,53 @@ namespace cimg_library_suffixed { } else { UV.fill(0); // *TRY* to read the luminance part, do not replace by cimg::fread! - err = (int)std::fread((void*)(UV._data),1,(size_t)(UV.size()),nfile); + err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile); if (err!=(int)(UV.size())) { stop_flag = true; if (err>0) cimg::warn(_cimglist_instance - "load_yuv(): File '%s' contains incomplete data or given image dimensions (%u,%u) " - "are incorrect.", + "load_yuv(): File '%s' contains incomplete data or given image dimensions " + "(%u,%u) are incorrect.", cimglist_instance, filename?filename:"(FILE*)",size_x,size_y); } else { - cimg_forXY(UV,x,y) { - const int x2 = x*2, y2 = y*2; - tmp(x2,y2,1) = tmp(x2 + 1,y2,1) = tmp(x2,y2 + 1,1) = tmp(x2 + 1,y2 + 1,1) = UV(x,y,0); - tmp(x2,y2,2) = tmp(x2 + 1,y2,2) = tmp(x2,y2 + 1,2) = tmp(x2 + 1,y2 + 1,2) = UV(x,y,1); + const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1); + ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2); + const unsigned int wd = YUV._width; + switch (chroma_subsampling) { + case 420 : + cimg_forY(UV,y) { + cimg_forX(UV,x) { + const ucharT U = *(ptrs1++), V = *(ptrs2++); + ptrd1[wd] = U; *(ptrd1)++ = U; + ptrd1[wd] = U; *(ptrd1)++ = U; + ptrd2[wd] = V; *(ptrd2)++ = V; + ptrd2[wd] = V; *(ptrd2)++ = V; + } + ptrd1+=wd; ptrd2+=wd; + } + break; + case 422 : + cimg_forXY(UV,x,y) { + const ucharT U = *(ptrs1++), V = *(ptrs2++); + *(ptrd1++) = U; *(ptrd1++) = U; + *(ptrd2++) = V; *(ptrd2++) = V; + } + break; + default : + YUV.draw_image(0,0,0,1,UV); } - if (yuv2rgb) tmp.YCbCrtoRGB(); - insert(tmp); - if (nstep_frame>1) cimg::fseek(nfile,(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); + if (yuv2rgb) YUV.YCbCrtoRGB(); + insert(YUV); + if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); } } } + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_yuv() : Missing data in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame) cimg::warn(_cimglist_instance "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.", @@ -57642,17 +58207,10 @@ namespace cimg_library_suffixed { if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); } while (file); cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data); -#if cimg_OS!=2 - cimg_snprintf(command,command._width,"%s -i \"%s\" \"%s\" >/dev/null 2>&1", + cimg_snprintf(command,command._width,"%s -i \"%s\" \"%s\"", cimg::ffmpeg_path(), CImg::string(filename)._system_strescape().data(), CImg::string(filename_tmp2)._system_strescape().data()); -#else - cimg_snprintf(command,command._width,"\"%s -i \"%s\" \"%s\"\" >NUL 2>&1", - cimg::ffmpeg_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp2)._system_strescape().data()); -#endif cimg::system(command,0); const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); @@ -57709,25 +58267,14 @@ namespace cimg_library_suffixed { else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data); if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); } while (file); -#if cimg_OS!=2 - if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\" >/dev/null 2>&1", + if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"", cimg::graphicsmagick_path(), CImg::string(filename)._system_strescape().data(), CImg::string(filename_tmp)._system_strescape().data()); - else cimg_snprintf(command,command._width,"%s \"%s\" \"%s.png\" >/dev/null 2>&1", + else cimg_snprintf(command,command._width,"%s \"%s\" \"%s.png\"", cimg::imagemagick_path(), CImg::string(filename)._system_strescape().data(), CImg::string(filename_tmp)._system_strescape().data()); -#else - if (use_graphicsmagick) cimg_snprintf(command,command._width,"\"%s convert \"%s\" \"%s.png\"\" >NUL 2>&1", - cimg::graphicsmagick_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._system_strescape().data()); - else cimg_snprintf(command,command._width,"\"%s \"%s\" \"%s.png\"\" >NUL 2>&1", - cimg::imagemagick_path(), - CImg::string(filename)._system_strescape().data(), - CImg::string(filename_tmp)._system_strescape().data()); -#endif cimg::system(command,0); const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); @@ -58144,7 +58691,7 @@ namespace cimg_library_suffixed { #endif if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false); - else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true); + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); else if (!cimg::strcasecmp(ext,"avi") || !cimg::strcasecmp(ext,"mov") || !cimg::strcasecmp(ext,"asf") || @@ -58249,22 +58796,12 @@ namespace cimg_library_suffixed { if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2); else _data[l].save(filename_tmp2); } - -#if cimg_OS!=2 cimg_snprintf(command,command._width,"%s -delay %u -loop %u", cimg::imagemagick_path(),(unsigned int)std::max(0.0f,cimg::round(100/fps)),nb_loops); CImg::string(command).move_to(filenames,0); - cimg_snprintf(command,command._width,"\"%s\" >/dev/null 2>&1", + cimg_snprintf(command,command._width,"\"%s\"", CImg::string(filename)._system_strescape().data()); CImg::string(command).move_to(filenames); -#else - cimg_snprintf(command,command._width,"\"%s -delay %u -loop %u", - cimg::imagemagick_path(),(unsigned int)std::max(0.0f,cimg::round(100/fps)),nb_loops); - CImg::string(command).move_to(filenames,0); - cimg_snprintf(command,command._width,"\"%s\"\" >NUL 2>&1", - CImg::string(filename)._system_strescape().data()); - CImg::string(command).move_to(filenames); -#endif CImg _command = filenames>'x'; cimg_for(_command,p,char) if (!*p) *p = ' '; _command.back() = 0; @@ -58281,47 +58818,83 @@ namespace cimg_library_suffixed { return *this; } - const CImgList& _save_yuv(std::FILE *const file, const char *const filename, const bool is_rgb) const { - if (!file && !filename) - throw CImgArgumentException(_cimglist_instance - "save_yuv(): Specified filename is (null).", - cimglist_instance); - if (is_empty()) { cimg::fempty(file,filename); return *this; } - if ((*this)[0].width()%2 || (*this)[0].height()%2) - throw CImgInstanceException(_cimglist_instance - "save_yuv(): Invalid odd instance dimensions (%u,%u) for file '%s'.", - cimglist_instance, - (*this)[0].width(),(*this)[0].height(), - filename?filename:"(FILE*)"); - - std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); - cimglist_for(*this,l) { - CImg YCbCr((*this)[l]); - if (is_rgb) YCbCr.RGBtoYCbCr(); - cimg::fwrite(YCbCr._data,(size_t)YCbCr._width*YCbCr._height,nfile); - cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1), - (size_t)YCbCr._width*YCbCr._height/2,nfile); - } - if (!file) cimg::fclose(nfile); - return *this; - } - //! Save list as a YUV image sequence file. /** \param filename Filename to write data to. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. \param is_rgb Tells if the RGB to YUV conversion must be done for saving. **/ - const CImgList& save_yuv(const char *const filename=0, const bool is_rgb=true) const { - return _save_yuv(0,filename,is_rgb); + const CImgList& save_yuv(const char *const filename=0, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + return _save_yuv(0,filename,chroma_subsampling,is_rgb); } //! Save image sequence into a YUV file. /** \param file File to write data to. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. \param is_rgb Tells if the RGB to YUV conversion must be done for saving. **/ - const CImgList& save_yuv(std::FILE *const file, const bool is_rgb=true) const { - return _save_yuv(file,0,is_rgb); + const CImgList& save_yuv(std::FILE *const file, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + return _save_yuv(file,0,chroma_subsampling,is_rgb); + } + + const CImgList& _save_yuv(std::FILE *const file, const char *const filename, + const unsigned int chroma_subsampling, + const bool is_rgb) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified filename is (null).", + cimglist_instance); + if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.", + cimglist_instance, + chroma_subsampling,filename?filename:"(FILE*)"); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + const unsigned int + cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, + cfy = chroma_subsampling==420?2:1, + w0 = (*this)[0]._width, h0 = (*this)[0]._height, + width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + cimglist_for(*this,l) { + const CImg &frame = (*this)[l]; + cimg_forZ(frame,z) { + CImg YUV; + if (sizeof(T)==1 && !is_rgb && + frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3) + YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true); + else { + YUV = frame.get_slice(z); + if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0); + if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0); + if (is_rgb) YUV.RGBtoYCbCr(); + } + if (chroma_subsampling==444) + cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile); + else { + cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile); + CImg UV = YUV.get_channels(1,2); + UV.resize(UV._width/cfx,UV._height/cfy,1,2,2); + cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list into a .cimg file. + /** + \param filename Filename to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(const char *const filename, const bool is_compressed=false) const { + return _save_cimg(0,filename,is_compressed); } const CImgList& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const { @@ -58377,15 +58950,6 @@ namespace cimg_library_suffixed { return *this; } - //! Save list into a .cimg file. - /** - \param filename Filename to write data to. - \param is_compressed Tells if data compression must be enabled. - **/ - const CImgList& save_cimg(const char *const filename, const bool is_compressed=false) const { - return _save_cimg(0,filename,is_compressed); - } - //! Save list into a .cimg file. /** \param file File to write data to. @@ -58742,8 +59306,9 @@ namespace cimg_library_suffixed { cimglist_instance); #define _cimg_docase(x) ((x)>='a'&&(x)<='z'?(x) + 'A' - 'a':(x)) + const char - *const _codec = codec && *codec?codec:"mp4v", + *const _codec = codec && *codec?codec:cimg_OS==2?"mpeg":"mp4v", codec0 = _cimg_docase(_codec[0]), codec1 = _codec[0]?_cimg_docase(_codec[1]):0, codec2 = _codec[1]?_cimg_docase(_codec[2]):0, @@ -58851,19 +59416,11 @@ namespace cimg_library_suffixed { if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filename_tmp2); else _data[l].save_pnm(filename_tmp2); } -#if cimg_OS!=2 - cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1", + cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"", cimg::ffmpeg_path(), CImg::string(filename_tmp)._system_strescape().data(), _codec,bitrate,fps, CImg::string(filename)._system_strescape().data()); -#else - cimg_snprintf(command,command._width,"\"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1", - cimg::ffmpeg_path(), - CImg::string(filename_tmp)._system_strescape().data(), - _codec,bitrate,fps, - CImg::string(filename)._system_strescape().data()); -#endif cimg::system(command); file = std_fopen(filename,"rb"); if (!file) @@ -58972,7 +59529,7 @@ namespace cimg_library_suffixed { CImg raw; \ CImg &img = res._data[l]; \ if (err==5) _cimgz_unserialize_case(Tss) \ - else if (sizeof(Tss)==1) { \ + else if (sizeof(Tss)==sizeof(t) && cimg::type::is_float()==cimg::type::is_float()) { \ raw.assign((Tss*)stream,W,H,D,C,true); \ stream+=raw.size(); \ } else { \ @@ -59118,7 +59675,7 @@ namespace cimg_library_suffixed { fonts->assign(); std::memmove(fonts,fonts + 1,15*sizeof(CImgList)); std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool)); - std::memset(fonts + (ind=15),0,sizeof(CImgList)); // Free a slot in cache for new font. + std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList)); // Free a slot in cache for new font. } CImgList &font = fonts[ind]; @@ -60173,14 +60730,16 @@ namespace cimg { static CImg times(64); static unsigned int pos = 0; const cimg_ulong t1 = cimg::time(); - if (is_tic) { // Tic. + if (is_tic) { + // Tic times[pos++] = t1; if (pos>=times._width) throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'."); cimg::mutex(2,0); return t1; } - // Toc. + + // Toc if (!pos) throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made."); const cimg_ulong