From 2016fd6f5c3d0576722ff49e1aa34fdb69eaf8a9 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 5 Aug 2023 10:49:38 +0200 Subject: [PATCH] Squashed 'external/CImg/' changes from 4d061dcd6..049267da1 049267da1 . d974f83e1 . 143c6f781 . 8b641fa30 . 79ab52e6f . 593667dcf Auto-commit for release 3.2.6_pre a5305a976 . 6fa06681b . cdc9ee87a . 5f40398d1 Start work on next version 3.2.6. 78d1f0d7d Final release 3.2.5 620e4155c . be214a006 . 1f9896c3a Add support for 'id#ind' in math parser. 44800102a Add support for 'id#ind' in math parser. a6657d3d1 Auto-commit for release 3.2.5_pre 70e9a2cc3 . a77287fe0 . 05fd7a869 . 81c22bb46 . 1f14f5eff . bd1228f0f . 19e44e564 . 3f19980fc Start work on v.3.2.5. 504f5c653 . 26862961d Fix line width. 860b3747d Optimize cimg_math_parser::mp_vector_norm() by avoiding copies. 44eb192e9 . c8d8710ee . 1cf8f5c27 . 7b6ec0809 . 99871e252 Simplify CImg::draw_polygon() and CImg::draw_line(). 30ec22fde . a2c50471e . d2f63a9f8 Fix possible issue in 'CImg::draw_polygon()' (better fix). e0675260c Fix possible issue in 'CImg::draw_polygon()'. bb9980723 . c431612a1 . 3606c5f5b Auto-commit for release 3.2.4_pre d5d63e21f . acea91b06 . 9a3c772fa . ccb519bc0 . 9bf62d16b . 0936bc4b7 math evaluator: Add function 'unitnorm(V,p). 5236bd690 . 95ff538e1 . 3285740e9 . 7d01d9b70 Start work on 3.2.4 c7868e298 . 903cc805d . f03139316 math_parser(): Add function 'c2o()' (coordinates to offset). ff49ec20f math_parser(): Add function 'o2c()' (offset to coordinates). 6cbef2c1d Start working on version 3.2.3 ba9bb02a8 Final release 3.2.2 998d0157c . 1bb2d4f00 . 722646c42 Attempt to fix compilation on Windows. 2f3c64511 . 113a16334 . e67bdf5d9 . 31fe9269c . 64839222d Better support of 'CImg' and 'CImg'. 32b3bb6ff Add specific cimg::type() traits for 'unsigned long long' and 'long long'. 806ef2861 Start work on 3.2.2 b558be730 Final release 3.2.1 85aa2ac2e . bcafdc4d9 . a6961a5bd . 33b78048e . 3c063517e . 9c76c1842 . 87863667a . d60290f8d . 3b75fa436 . f1815c88a . 6f575e662 . 5d6be92b4 Allow dynamic arrays to have larger size (32bits integers, i.e 2G elements). c7d1772b8 . 977a1bb81 . 2cf046326 . 54d2b753c . f4a0f772d . d11a0380e . 4828190aa math_parser: Check that specified '#index' of functions are not NaN. 3b71fa0ce Function 'v2s()': Add option to 0-pad integer values. 652e6604b . ea3197ce4 Fix #374 c88e03819 math_parser: Add function that computes the square root of complex numbers. b58775209 Add detection of the not operator in fast pre-evaluation of math expressions. 5e3585553 Add detection of the not operator in fast pre-evaluation of math expressions. 209c38da9 . 6ba3d0a11 Fix #319. 4e9412e8d Start work on next version 3.2.1 57028920e . 9772e0204 . 446c39411 Math evaluator: Add new function 'abort()'. 3c2ecee1f Make fast string comparisons more robust when expressions contain blank characters. af9ecf8d0 Optimize evaluation of string comparisons. edfcda038 . 00cab3cea . a0af5312c . 6a92e46f2 . 7ae52c28d cimg_math_parser(): Add 'gamma()' function. ad516d976 . b71e2db76 . a9633d4ed . 9d8406e57 . 103d23625 . 6349c771f . bef50f9d8 . 391a24013 . d4669fbbc . 5e77a9c7e . 86440098e . 6b9eee37c . 616484469 . e79edfdb9 . e616c2443 . 772fa4dca . git-subtree-dir: external/CImg git-subtree-split: 049267da171c50ab48f93c7285f096d7d686e8f3 --- CImg.h | 1667 ++++++++++++++++++--------- examples/CImg_demo.cpp | 2 +- examples/image_surface3d.cpp | 4 +- html/header.html | 8 +- html/header_doxygen.html | 8 +- html/img/book_cimg_en.jpg | Bin 0 -> 36326 bytes html/index.html | 15 +- resources/compile_win_visualcpp.bat | 2 +- 8 files changed, 1123 insertions(+), 583 deletions(-) create mode 100644 html/img/book_cimg_en.jpg diff --git a/CImg.h b/CImg.h index 9ac9175b0..3d07e7834 100644 --- a/CImg.h +++ b/CImg.h @@ -54,7 +54,7 @@ // Set version number of the library. #ifndef cimg_version -#define cimg_version 320 +#define cimg_version 326 /*----------------------------------------------------------- # @@ -2693,6 +2693,7 @@ namespace cimg_library { static T min() { return ~max(); } static T max() { return (T)1<<(8*sizeof(T) - 1); } static T inf() { return max(); } + static T nan() { return inf(); } static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; } static const char* format() { return "%s"; } static const char* format_s() { return "%s"; } @@ -2711,6 +2712,7 @@ namespace cimg_library { static bool min() { return false; } static bool max() { return true; } static bool inf() { return max(); } + static bool nan() { return inf(); } static bool is_inf() { return false; } static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; } static const char* format() { return "%s"; } @@ -2727,6 +2729,7 @@ namespace cimg_library { static unsigned char min() { return 0; } static unsigned char max() { return (unsigned char)-1; } static unsigned char inf() { return max(); } + static unsigned char nan() { return inf(); } static unsigned char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } static const char* format() { return "%u"; } @@ -2744,6 +2747,7 @@ namespace cimg_library { static char min() { return 0; } static char max() { return (char)-1; } static char inf() { return max(); } + static char nan() { return inf(); } static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } static const char* format() { return "%u"; } @@ -2760,6 +2764,7 @@ namespace cimg_library { static char min() { return ~max(); } static char max() { return (char)((unsigned char)-1>>1); } static char inf() { return max(); } + static char nan() { return inf(); } static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; } static const char* format() { return "%d"; } static const char* format_s() { return "%d"; } @@ -2776,6 +2781,7 @@ namespace cimg_library { static signed char min() { return ~max(); } static signed char max() { return (signed char)((unsigned char)-1>>1); } static signed char inf() { return max(); } + static signed char nan() { return inf(); } static signed char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(signed char)val; } static const char* format() { return "%d"; } @@ -2792,6 +2798,7 @@ namespace cimg_library { static unsigned short min() { return 0; } static unsigned short max() { return (unsigned short)-1; } static unsigned short inf() { return max(); } + static unsigned short nan() { return inf(); } static unsigned short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; } static const char* format() { return "%u"; } @@ -2808,6 +2815,7 @@ namespace cimg_library { static short min() { return ~max(); } static short max() { return (short)((unsigned short)-1>>1); } static short inf() { return max(); } + static short nan() { return inf(); } static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; } static const char* format() { return "%d"; } static const char* format_s() { return "%d"; } @@ -2823,6 +2831,7 @@ namespace cimg_library { static unsigned int min() { return 0; } static unsigned int max() { return (unsigned int)-1; } static unsigned int inf() { return max(); } + static unsigned int nan() { return inf(); } static unsigned int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; } static const char* format() { return "%u"; } @@ -2839,6 +2848,7 @@ namespace cimg_library { static int min() { return ~max(); } static int max() { return (int)(~0U>>1); } static int inf() { return max(); } + static int nan() { return inf(); } static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; } static const char* format() { return "%d"; } static const char* format_s() { return "%d"; } @@ -2854,6 +2864,7 @@ namespace cimg_library { static cimg_uint64 min() { return 0; } static cimg_uint64 max() { return (cimg_uint64)-1; } static cimg_uint64 inf() { return max(); } + static cimg_uint64 nan() { return inf(); } static cimg_uint64 cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } static const char* format() { return cimg_fuint64; } @@ -2870,6 +2881,7 @@ namespace cimg_library { static cimg_int64 min() { return ~max(); } static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); } static cimg_int64 inf() { return max(); } + static cimg_int64 nan() { return inf(); } static cimg_int64 cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val; } @@ -2878,6 +2890,43 @@ namespace cimg_library { static long format(const long val) { return (long)val; } }; +#if !(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))) + template<> struct type { + static const char* string() { static const char *const s = "uint64"; return s; } + static bool is_float() { return false; } + static bool is_inf(const cimg_uint64) { return false; } + static bool is_nan(const cimg_uint64) { return false; } + static bool is_finite(const cimg_uint64) { return true; } + static cimg_uint64 min() { return 0; } + static cimg_uint64 max() { return (cimg_uint64)-1; } + static cimg_uint64 inf() { return max(); } + static cimg_uint64 nan() { return inf(); } + static cimg_uint64 cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } + static const char* format() { return cimg_fuint64; } + static const char* format_s() { return cimg_fuint64; } + static cimg_uint64 format(const cimg_uint64 val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int64"; return s; } + static bool is_float() { return false; } + static bool is_inf(const cimg_int64) { return false; } + static bool is_nan(const cimg_int64) { return false; } + static bool is_finite(const cimg_int64) { return true; } + static long long min() { return ~max(); } + static long long max() { return (cimg_int64)((cimg_uint64)-1>>1); } + static long long inf() { return max(); } + static long long nan() { return max(); } + static long long cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val; + } + static const char* format() { return cimg_fint64; } + static const char* format_s() { return cimg_fint64; } + static long format(const long val) { return (long)val; } + }; +#endif + template<> struct type { static const char* string() { static const char *const s = "float64"; return s; } static bool is_float() { return true; } @@ -3120,6 +3169,17 @@ namespace cimg_library { template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; +#if !(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))) + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; +#endif template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; @@ -5883,21 +5943,21 @@ namespace cimg_library { return a; } - // Conversion functions to get more precision when trying to store unsigned ints values as floats. - inline unsigned int float2uint(const float f) { + // Conversion functions to get more precision when trying to store 'unsigned int' values as 'float'. + inline unsigned int float2uint(const float value) { int tmp = 0; - std::memcpy(&tmp,&f,sizeof(float)); - if (tmp>=0) return (unsigned int)f; + std::memcpy(&tmp,&value,sizeof(float)); + if (tmp>=0) return (unsigned int)value; unsigned int u; // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler. - std::memcpy(&u,&f,sizeof(float)); + std::memcpy(&u,&value,sizeof(float)); return ((u)<<2)>>2; // set sign & exponent bit to 0 } - inline float uint2float(const unsigned int u) { - if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287) + inline float uint2float(const unsigned int value) { + if (value<(1U<<19)) return (float)value; // Consider 'uint32' safely stored as floats until 19bits (i.e 524287) float f; - const unsigned int v = u|(3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1 + const unsigned int v = value | (3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1 // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler. std::memcpy(&f,&v,sizeof(float)); return f; @@ -6005,7 +6065,7 @@ namespace cimg_library { } inline void srand(cimg_uint64 *const p_rng) { -#if cimg_OS==1 +#if cimg_OS==1 || defined(__BORLANDC__) *p_rng = cimg::time() + (cimg_uint64)getpid(); #elif cimg_OS==2 *p_rng = cimg::time() + (cimg_uint64)_getpid(); @@ -6312,7 +6372,10 @@ namespace cimg_library { **/ template inline T mod(const T& x, const T& m) { - if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + if (!m) { + if (cimg::type::is_float()) return cimg::type::nan(); + else throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + } const double dx = (double)x, dm = (double)m; if (!cimg::type::is_finite(dm)) return x; if (cimg::type::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm)); @@ -6937,7 +7000,7 @@ namespace cimg_library { //! Ellipsize a string. /** \param str C-string. - \param l Max number of characters. + \param l Max number of printed characters. \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string. **/ inline char *strellipsize(char *const str, const unsigned int l=64, @@ -6959,7 +7022,7 @@ namespace cimg_library { /** \param str C-string. \param res output C-string. - \param l Max number of characters. + \param l Max number of printed characters. String 'res' must be a size of at least 'l+1'. \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string. **/ inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64, @@ -13096,7 +13159,7 @@ namespace cimg_library { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - _fill(expression,true,1,0,0,"operator=",0); + _fill(expression,true,3,0,0,"operator=",0,0); } catch (CImgException&) { cimg::exception_mode(omode); load(expression); @@ -13171,7 +13234,7 @@ namespace cimg_library { instead of assigning them. **/ CImg& operator+=(const char *const expression) { - return *this+=(+*this)._fill(expression,true,1,0,0,"operator+=",this); + return *this+=(+*this)._fill(expression,true,3,0,0,"operator+=",this,0); } //! In-place addition operator. @@ -13292,7 +13355,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a subtraction instead of an addition. **/ CImg& operator-=(const char *const expression) { - return *this-=(+*this)._fill(expression,true,1,0,0,"operator-=",this); + return *this-=(+*this)._fill(expression,true,3,0,0,"operator-=",this,0); } //! In-place subtraction operator. @@ -13396,7 +13459,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. **/ CImg& operator*=(const char *const expression) { - return mul((+*this)._fill(expression,true,1,0,0,"operator*=",this)); + return mul((+*this)._fill(expression,true,3,0,0,"operator*=",this,0)); } //! In-place multiplication operator. @@ -13661,7 +13724,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a division instead of an addition. **/ CImg& operator/=(const char *const expression) { - return div((+*this)._fill(expression,true,1,0,0,"operator/=",this)); + return div((+*this)._fill(expression,true,3,0,0,"operator/=",this,0)); } //! In-place division operator. @@ -13725,7 +13788,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. **/ CImg& operator%=(const char *const expression) { - return *this%=(+*this)._fill(expression,true,1,0,0,"operator%=",this); + return *this%=(+*this)._fill(expression,true,3,0,0,"operator%=",this,0); } //! In-place modulo operator. @@ -13791,7 +13854,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. **/ CImg& operator&=(const char *const expression) { - return *this&=(+*this)._fill(expression,true,1,0,0,"operator&=",this); + return *this&=(+*this)._fill(expression,true,3,0,0,"operator&=",this,0); } //! In-place bitwise AND operator. @@ -13857,7 +13920,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. **/ CImg& operator|=(const char *const expression) { - return *this|=(+*this)._fill(expression,true,1,0,0,"operator|=",this); + return *this|=(+*this)._fill(expression,true,3,0,0,"operator|=",this,0); } //! In-place bitwise OR operator. @@ -13927,7 +13990,7 @@ namespace cimg_library { - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. **/ CImg& operator^=(const char *const expression) { - return *this^=(+*this)._fill(expression,true,1,0,0,"operator^=",this); + return *this^=(+*this)._fill(expression,true,3,0,0,"operator^=",this,0); } //! In-place bitwise XOR operator. @@ -13995,7 +14058,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. **/ CImg& operator<<=(const char *const expression) { - return *this<<=(+*this)._fill(expression,true,1,0,0,"operator<<=",this); + return *this<<=(+*this)._fill(expression,true,3,0,0,"operator<<=",this,0); } //! In-place bitwise left shift operator. @@ -14062,7 +14125,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. **/ CImg& operator>>=(const char *const expression) { - return *this>>=(+*this)._fill(expression,true,1,0,0,"operator>>=",this); + return *this>>=(+*this)._fill(expression,true,3,0,0,"operator>>=",this,0); } //! In-place bitwise right shift operator. @@ -14144,7 +14207,7 @@ namespace cimg_library { \param expression Value string describing the way pixel values are compared. **/ bool operator==(const char *const expression) const { - return *this==(+*this)._fill(expression,true,1,0,0,"operator==",this); + return *this==(+*this)._fill(expression,true,3,0,0,"operator==",this,0); } //! Test if two images have the same size and values. @@ -16788,7 +16851,7 @@ namespace cimg_library { if (*(ptrs++)!=(T)-128) ptrs+=2; else if ((ptrs+=3) variable_def, macro_def, macro_body; char *user_macro; - unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, break_type, - constcache_size; + unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, + result_dim, result_end_dim, break_type, constcache_size; bool is_parallelizable, is_noncritical_run, is_end_code, is_fill, return_new_comp, need_input_copy; - double *result; + double *result, *result_end; cimg_uint64 rng; const char *const calling_function, *s_op, *ss_op; typedef double (*mp_func)(_cimg_math_parser&); @@ -16901,11 +16964,13 @@ namespace cimg_library { #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 s_calling_function()._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) #define _cimg_mp_check_const_scalar(arg,n_arg,mode) check_const_scalar(arg,n_arg,mode,ss,se,saved_char) #define _cimg_mp_check_const_index(arg) check_const_index(arg,ss,se,saved_char) -#define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char) +#define _cimg_mp_check_notnan_index(arg) check_notnan_index(arg,ss,se,saved_char) #define _cimg_mp_check_list() check_list(ss,se,saved_char) +#define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char) +#define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,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) @@ -16945,9 +17010,10 @@ namespace cimg_library { p_break((CImg*)(cimg_ulong)-2),imgin(img_input), imgout(img_output?*img_output:CImg::empty()),imglist(list_images?*list_images:CImgList::empty()), img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0), - mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),break_type(0), - constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(_is_fill),need_input_copy(false), - rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") { + mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),result_end_dim(0), + break_type(0),constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(_is_fill), + need_input_copy(false),result_end(0),rng((cimg::_rand(),cimg::rng())), + calling_function(funcname?funcname:"cimg_math_parser") { #if cimg_use_openmp!=0 rng+=omp_get_thread_num(); @@ -17026,11 +17092,11 @@ namespace cimg_library { fill(cimg::type::nan()); else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type::nan(); } + if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1); + result_dim = _cimg_mp_size(ind_result); + result = mem._data + ind_result; // Free resources used for compiling expression and prepare evaluation. - 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(); constcache_inds.assign(); @@ -17060,8 +17126,8 @@ namespace cimg_library { p_code_end(0),p_break((CImg*)(cimg_ulong)-2), imgin(CImg::const_empty()),imgout(CImg::empty()),imglist(CImgList::empty()), img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0), - result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(false), - need_input_copy(false),rng(0),calling_function(0) { + result_dim(0),result_end_dim(0),break_type(0),constcache_size(0),is_parallelizable(true), + is_noncritical_run(false),is_fill(false),need_input_copy(false),result_end(0),rng(0),calling_function(0) { mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() result = mem._data; } @@ -17071,9 +17137,11 @@ namespace cimg_library { p_code_end(mp.p_code_end),p_break(mp.p_break), imgin(mp.imgin),imgout(mp.imgout),imglist(mp.imglist), img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm), - debug_indent(0),result_dim(mp.result_dim),break_type(0),constcache_size(0), + debug_indent(0),result_dim(mp.result_dim),result_end_dim(mp.result_end_dim),break_type(0),constcache_size(0), is_parallelizable(mp.is_parallelizable),is_noncritical_run(mp.is_noncritical_run),is_fill(mp.is_fill), - need_input_copy(mp.need_input_copy),result(mem._data + (mp.result - mp.mem._data)), + need_input_copy(mp.need_input_copy), + result(mem._data + (mp.result - mp.mem._data)), + result_end(mp.result_end?mem._data + (mp.result_end - mp.mem._data):0), rng((cimg::_rand(),cimg::rng())),calling_function(0) { #if cimg_use_openmp!=0 @@ -17261,22 +17329,23 @@ namespace cimg_library { _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 21,0,0); } switch (*ss1) { - case 'm' : arg1 = 4; arg2 = 0; break; // im - case 'M' : arg1 = 5; arg2 = 1; break; // iM case 'a' : arg1 = 6; arg2 = 2; break; // ia - case 'v' : arg1 = 7; arg2 = 3; break; // iv - case 'd' : arg1 = 8; arg2 = 3; break; // id - case 's' : arg1 = 9; arg2 = 12; break; // is - case 'p' : arg1 = 10; arg2 = 13; break; // ip case 'c' : // ic if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]); if (mem_img_median==~0U) mem_img_median = imgin?const_scalar(imgin.median()):0; _cimg_mp_return(mem_img_median); break; + case 'd' : arg1 = 8; arg2 = 3; break; // id + case 'm' : arg1 = 4; arg2 = 0; break; // im + case 'M' : arg1 = 5; arg2 = 1; break; // iM case 'n' : // in if (reserved_label[12]!=~0U) _cimg_mp_return(reserved_label[12]); - if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude()):0; + if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude(2)):0; _cimg_mp_return(mem_img_norm); + break; + case 'p' : arg1 = 10; arg2 = 13; break; // ip + case 's' : arg1 = 9; arg2 = 12; break; // is + case 'v' : arg1 = 7; arg2 = 3; break; // iv } } else if (*ss1=='m') switch (*ss) { @@ -17350,6 +17419,7 @@ namespace cimg_library { if (*ss2=='#') { // Index specified s0 = ss3; while (s0::vector((ulongT)mp_abort,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } +#endif + if (!std::strncmp(ss,"abs(",4)) { // Absolute value _cimg_mp_op("Function 'abs()'"); arg1 = compile(ss4,se1,depth1,0,block_flags); @@ -19219,6 +19305,45 @@ namespace cimg_library { break; case 'c' : + if (!std::strncmp(ss,"c2o(",4)) { // Coordinates to offset + _cimg_mp_op("Function 'c2o()'"); + if (*ss4=='#') { // Index specified + s0 = ss5; while (s0 Error too much arguments + if (p2>4) { + *s1 = 0; s1 = s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Argument '%s' is a vector of size %u (should be <=4), " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,s1,p2,s0); + } + arg1 = pos + 1; + arg2 = p2>1?pos + 2:0; + arg3 = p2>2?pos + 3:0; + arg4 = p2>3?pos + 4:0; + } else { // Coordinates specified as scalars + arg1 = pos; arg2 = arg3 = arg4 = 0; + if (s1::vector((ulongT)mp_complex_sqrt,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_sqrt,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent _cimg_mp_op("Function 'ctan()'"); arg1 = compile(ss5,se1,depth1,0,block_flags); @@ -19446,12 +19582,14 @@ namespace cimg_library { if (!std::strncmp(ss,"crop(",5)) { // Image or vector crop _cimg_mp_op("Function 'crop()'"); is_sth = false; // is image crop ? + arg1 = 0; if (*ss5=='#') { // Index specified s0 = ss6; while (s0::vector((ulongT)mp_da_freeze,_cimg_mp_slot_nan,p1).move_to(code); @@ -19908,6 +20050,7 @@ namespace cimg_library { if (ss[10]=='#') { // Index specified s0 = ss + 11; while (s0::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); - for (s = s0; s::vector((ulongT)op,pos,0).move_to(l_opcode); break; - case 1 : op = mp_norm1; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; - case 2 : op = mp_norm2; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; - case ~0U : op = mp_norminf; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; - default : op = mp_normp; CImg::vector((ulongT)op,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). - move_to(l_opcode); - } - for ( ; s::vector(0,0,0,arg1).move_to(l_opcode); + for (++s; s::sequence(_cimg_mp_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_size(arg2)). + 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_const_scalar(arg2); s = ns; } - (l_opcode>'y').move_to(opcode); + op = val==2?_mp_vector_norm2:val==1?_mp_vector_norm1:!val?_mp_vector_norm0: + cimg::type::is_inf(val)?_mp_vector_norminf:_mp_vector_normp; + opcode[0] = (ulongT)op; opcode[2] = opcode._height; if (is_sth) _cimg_mp_const_scalar(op(*this)); - if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1 - _cimg_mp_scalar1(mp_abs,opcode[3]); + if (opcode._height==5) { // Single argument + if (arg1) { _cimg_mp_scalar1(mp_abs,opcode[4]); } + else { _cimg_mp_scalar2(mp_neq,opcode[4],0); } + } + opcode[1] = pos = scalar(); opcode.move_to(code); return_new_comp = true; _cimg_mp_return(pos); } break; + case 'o' : + if (!std::strncmp(ss,"o2c(",4)) { // Offset to coordinates + _cimg_mp_op("Function 'o2c()'"); + if (*ss4=='#') { // Index specified + s0 = ss5; while (s0::vector((ulongT)mp_o2c,pos,p1,arg1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + case 'p' : if (!std::strncmp(ss,"permut(",7)) { // Number of permutations _cimg_mp_op("Function 'permut()'"); @@ -21359,8 +21539,9 @@ namespace cimg_library { if (*ss8=='#') { // Index specified s0 = ss + 9; while (s0::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); @@ -21391,6 +21572,7 @@ namespace cimg_library { _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'"); if (!is_sth && *s0=='#') { // Image p1 = compile(ss7,se1,depth1,0,block_flags); + _cimg_mp_check_notnan_index(p1); _cimg_mp_check_list(); CImg::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code); _cimg_mp_return_nan(); @@ -21735,6 +21917,7 @@ namespace cimg_library { _cimg_mp_op("Function 's()'"); if (*ss2=='#') { // Index specified p1 = compile(ss3,se1,depth1,0,block_flags); + _cimg_mp_check_notnan_index(p1); _cimg_mp_check_list(); } else { if (ss2!=se1) break; p1 = ~0U; } _cimg_mp_scalar1(mp_image_s,p1); @@ -21921,6 +22104,7 @@ namespace cimg_library { _cimg_mp_op("Function 'stats()'"); if (*ss6=='#') { // Index specified p1 = compile(ss7,se1,depth1,0,block_flags); + _cimg_mp_check_notnan_index(p1); _cimg_mp_check_list(); } else { if (ss6!=se1) break; p1 = ~0U; } pos = vector(14); @@ -22280,6 +22464,26 @@ namespace cimg_library { _cimg_mp_scalar1(mp_ui2f,arg1); } + if (!std::strncmp(ss,"unitnorm(",9)) { // Normalize vector to unit norm + _cimg_mp_op("Function 'unitnorm()'"); + s0 = ss + 9; + s1 = s0; while (s10) pos = is_comp_vector(arg1)?arg1:((return_new_comp = true), vector(p1)); + else { + pos = scalar(); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) { + val = mem[arg1]; + _cimg_mp_const_scalar(val?(mem[arg2]?1:val):0); + } + } + CImg::vector((ulongT)mp_vector_unitnorm,pos,arg1,p1,arg2).move_to(code); + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable _cimg_mp_op("Function 'unref()'"); arg1=~0U; @@ -22445,6 +22649,7 @@ namespace cimg_library { _cimg_mp_op("Function 'w()'"); if (*ss2=='#') { // Index specified p1 = compile(ss3,se1,depth1,0,block_flags); + _cimg_mp_check_notnan_index(p1); _cimg_mp_check_list(); } else { if (ss2!=se1) break; p1 = ~0U; } _cimg_mp_scalar1(mp_image_w,p1); @@ -22454,6 +22659,7 @@ namespace cimg_library { _cimg_mp_op("Function 'wh()'"); if (*ss3=='#') { // Index specified p1 = compile(ss4,se1,depth1,0,block_flags); + _cimg_mp_check_notnan_index(p1); _cimg_mp_check_list(); } else { if (ss3!=se1) break; p1 = ~0U; } _cimg_mp_scalar1(mp_image_wh,p1); @@ -22463,6 +22669,7 @@ namespace cimg_library { _cimg_mp_op("Function 'whd()'"); if (*ss4=='#') { // Index specified p1 = compile(ss5,se1,depth1,0,block_flags); + _cimg_mp_check_notnan_index(p1); _cimg_mp_check_list(); } else { if (ss4!=se1) break; p1 = ~0U; } _cimg_mp_scalar1(mp_image_whd,p1); @@ -22472,6 +22679,7 @@ namespace cimg_library { _cimg_mp_op("Function 'whds()'"); if (*ss5=='#') { // Index specified p1 = compile(ss6,se1,depth1,0,block_flags); + _cimg_mp_check_notnan_index(p1); _cimg_mp_check_list(); } else { if (ss5!=se1) break; p1 = ~0U; } _cimg_mp_scalar1(mp_image_whds,p1); @@ -22547,7 +22755,7 @@ namespace cimg_library { *ss=='v'?mp_var: ss[1]=='i'?(ss[3]=='('?mp_min:mp_minabs): ss[1]=='a'?(ss[3]=='('?mp_max:mp_maxabs): - mp_median; + mp_med; is_sth = true; // Tell if all arguments are constant pos = scalar(); CImg::vector((ulongT)op,pos,0).move_to(l_opcode); @@ -22555,11 +22763,8 @@ namespace cimg_library { ns = s; while (ns::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); + if (_cimg_mp_is_vector(arg2)) CImg::vector(arg2 + 1,_cimg_mp_size(arg2)).move_to(l_opcode); + else CImg::vector(arg2,1).move_to(l_opcode); is_sth&=_cimg_mp_is_const_scalar(arg2); s = ns; } @@ -22752,6 +22957,7 @@ namespace cimg_library { // Variables related to the input list of images. if (*ss1=='#' && ss2::vector(imglist[p1].median()).move_to(list_median[p1]); _cimg_mp_const_scalar(*list_median[p1]); } - _cimg_mp_scalar1(mp_list_median,arg1); + _cimg_mp_scalar1(mp_list_id,arg1); + } + + if (*ss1=='d') { // id#ind + if (!imglist) _cimg_mp_return(0); + if (_cimg_mp_is_const_scalar(arg1)) { + if (!list_stats) list_stats.assign(imglist._width); + if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(imglist[p1].get_stats(),false); + _cimg_mp_const_scalar(std::sqrt(list_stats(p1,3))); + } + _cimg_mp_scalar1(mp_list_id,arg1); } if (*ss1=='n') { // in#ind if (!imglist) _cimg_mp_return(0); if (_cimg_mp_is_const_scalar(arg1)) { if (!list_norm) list_norm.assign(imglist._width); - if (!list_norm[p1]) CImg::vector(imglist[p1].magnitude()).move_to(list_norm[p1]); + if (!list_norm[p1]) CImg::vector(imglist[p1].magnitude(2)).move_to(list_norm[p1]); _cimg_mp_const_scalar(*list_norm[p1]); } _cimg_mp_scalar1(mp_list_norm,arg1); } switch (*ss1) { + case 'a' : arg2 = 2; break; // ia#ind case 'm' : arg2 = 0; break; // im#ind case 'M' : arg2 = 1; break; // iM#ind - case 'a' : arg2 = 2; break; // ia#ind - case 'v' : arg2 = 3; break; // iv#ind - case 's' : arg2 = 12; break; // is#ind case 'p' : arg2 = 13; break; // ip#ind + case 's' : arg2 = 12; break; // is#ind + case 'v' : arg2 = 3; break; // iv#ind } } else if (*ss1=='m') switch (*ss) { case 'x' : arg2 = 4; break; // xm#ind @@ -22875,6 +23092,7 @@ namespace cimg_library { if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4::is_nan(mem[arg])))) { + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s Specified index is NaN.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":""); + } + } + // Check a matrix is square. void check_matrix_square(const unsigned int arg, const unsigned int n_arg, char *const ss, char *const se, const char saved_char) { @@ -23736,6 +23967,14 @@ namespace cimg_library { #endif #define _mp_arg(x) mp.mem[mp.opcode[x]] +#ifdef cimg_mp_func_abort + static double mp_abort(_cimg_math_parser& mp) { + cimg::unused(mp); + cimg_mp_func_abort(); + return cimg::type::nan(); + } +#endif + static double mp_abs(_cimg_math_parser& mp) { return cimg::abs(_mp_arg(2)); } @@ -23792,53 +24031,105 @@ namespace cimg_library { 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 values; + if (i_end==5) values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Only a single argument + else { + unsigned int siz = 0; + for (unsigned int i = 4; i1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double)); + else *ptr = _mp_arg(i); + ptr+=len; + } + } + longT ind = (longT)cimg::round(_mp_arg(3)); + ++values._data; --values._width; // Skip first value + if (ind<0) ind+=values.width() + 1; + ind = cimg::cut(ind,(longT)1,(longT)values.width()); + const double kth = values.kth_smallest((ulongT)(ind - 1)); + --values._data; ++values._width; + for (unsigned int argkth = 1; argkth::nan(); } static double mp_argmin(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - unsigned int argval = 0; - for (unsigned int i = 4; i::inf(); + unsigned int siz = 0, argmin = 0; + for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; k::inf(); + unsigned int siz = 0, argminabs = 0; + for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; kval) { val = _val; argval = i - 3; } + double val, valmax = -cimg::type::inf(); + unsigned int siz = 0, argmax = 0; + for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; kvalmax) { valmax = val; argmax = siz + k; } } + } else { val = _mp_arg(i); if (val>valmax) { valmax = val; argmax = siz; } } + siz+=len; } - return (double)argval; + return (double)argmax; } static double mp_argmaxabs(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3), absval = cimg::abs(val); - unsigned int argval = 0; - for (unsigned int i = 4; iabsval) { val = _val; absval = _absval; argval = i - 3; } + double val, abs_val, abs_valmaxabs = 0; + unsigned int siz = 0, argmaxabs = 0; + for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; kabs_valmaxabs) { abs_valmaxabs = abs_val; argmaxabs = siz + k; } + } + } else { + val = _mp_arg(i); + abs_val = cimg::abs(val); + if (abs_val>abs_valmaxabs) { abs_valmaxabs = abs_val; argmaxabs = siz; } + } + siz+=len; } - return (double)argval; + return (double)argmaxabs; } static double mp_asin(_cimg_math_parser& mp) { @@ -23855,9 +24146,17 @@ namespace cimg_library { static double mp_avg(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - for (unsigned int i = 4; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; k::nan(); } -#ifdef cimg_mp_func_run - static double mp_run(_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(24); - 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'; - cimg_mp_func_run(str._data); - return cimg::type::nan(); + static double mp_c2o(_cimg_math_parser& mp) { + mp_check_list(mp,"c2o"); + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + const CImg &img = ind==~0U?mp.imgin:mp.imglist[ind]; + const int + x = (int)_mp_arg(3), + y = (int)_mp_arg(4), + z = (int)_mp_arg(5), + c = (int)_mp_arg(6); + return (double)img.offset(x,y,z,c); } -#endif static double mp_cbrt(_cimg_math_parser& mp) { return cimg::cbrt(_mp_arg(2)); @@ -24073,6 +24360,17 @@ namespace cimg_library { return cimg::type::nan(); } + static double mp_complex_sqrt(_cimg_math_parser& mp) { + const double + real = _mp_arg(2), imag = _mp_arg(3), + r = std::sqrt(cimg::_hypot(real,imag)), + theta = std::atan2(imag,real)/2; + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = r*std::cos(theta); + ptrd[1] = r*std::sin(theta); + return cimg::type::nan(); + } + static double mp_complex_tan(_cimg_math_parser& mp) { const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag); double *ptrd = &_mp_arg(1) + 1; @@ -24292,7 +24590,7 @@ namespace cimg_library { mp_check_list(mp,s_op); const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); CImg &img = mp.imglist[ind]; - int siz = img?(int)img[img._height - 1]:0; + int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0; if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1)) throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': " "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.", @@ -24313,7 +24611,7 @@ namespace cimg_library { ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); CImg &img = mp.imglist[ind]; const int - siz = img?(int)img[img._height - 1]:0, + siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0, pos0 = mp.opcode[3]==~0U?siz:(int)_mp_arg(3), pos = pos0<0?pos0 + siz:pos0; @@ -24345,7 +24643,7 @@ namespace cimg_library { double *ptrs = &_mp_arg(6 + k) + 1; cimg_forC(img,c) img(0,pos + k,0,c) = ptrs[c]; } - img[img._height - 1] = (T)(siz + nb_elts); + img[img._height - 1] = (T)cimg::uint2float(siz + nb_elts); return cimg::type::nan(); } @@ -24353,7 +24651,7 @@ namespace cimg_library { mp_check_list(mp,"da_remove"); const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); CImg &img = mp.imglist[ind]; - int siz = img?(int)img[img._height - 1]:0; + int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0; if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1)) throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': " "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.", @@ -24379,7 +24677,7 @@ namespace cimg_library { siz-=end - start + 1; if (img.height()>32 && siz<2*img.height()/3) // Reduce size of dynamic array img.resize(1,std::max(2*siz + 1,32),1,-100,0); - img[img._height - 1] = (T)siz; + img[img._height - 1] = (T)cimg::uint2float(siz); return cimg::type::nan(); } @@ -24387,7 +24685,7 @@ namespace cimg_library { mp_check_list(mp,"da_size"); const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); CImg &img = mp.imglist[ind]; - const int siz = img?(int)img[img._height - 1]:0; + const int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0; if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1)) throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_size()': " "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.", @@ -24618,10 +24916,7 @@ namespace cimg_library { mp_check_list(mp,"ellipse"); const unsigned int i_end = (unsigned int)mp.opcode[2]; unsigned int ind = (unsigned int)mp.opcode[3]; - if (ind!=~0U) { - if (!mp.imglist.width()) return cimg::type::nan(); - ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width()); - } + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width()); CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; CImg color(img._spectrum,1,1,1,0); bool is_invalid_arguments = false, is_outlined = false; @@ -24911,6 +25206,12 @@ namespace cimg_library { return cimg::grand(&mp.rng); } +#if cimg_use_cpp11==1 + static double mp_gamma(_cimg_math_parser& mp) { + return std::tgamma(_mp_arg(2)); + } +#endif + 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))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1); @@ -25081,7 +25382,7 @@ namespace cimg_library { ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); } const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; - return (double)img.magnitude(); + return (double)img.magnitude(2); } static double mp_image_print(_cimg_math_parser& mp) { @@ -25538,13 +25839,27 @@ namespace cimg_library { static double mp_kth(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; - CImg vals(i_end - 4); - double *p = vals.data(); - for (unsigned int i = 4; i values; + if (i_end==5) values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Only a single argument + else { + unsigned int siz = 0; + for (unsigned int i = 4; i1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double)); + else *ptr = _mp_arg(i); + ptr+=len; + } + } + longT ind = (longT)values[0]; + ++values._data; --values._width; // Skip first value + if (ind<0) ind+=values.width() + 1; + ind = cimg::cut(ind,(longT)1,(longT)values.width()); + const double &kth = values.kth_smallest((ulongT)(ind - 1)); + --values._data; ++values._width; + return kth; } static double mp_lerp(_cimg_math_parser& mp) { @@ -25872,10 +26187,27 @@ namespace cimg_library { static double mp_list_norm(_cimg_math_parser& mp) { const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); if (!mp.list_norm) mp.list_norm.assign(mp.imglist._width); - if (!mp.list_norm[ind]) CImg::vector(mp.imglist[ind].magnitude()).move_to(mp.list_norm[ind]); + if (!mp.list_norm[ind]) CImg::vector(mp.imglist[ind].magnitude(2)).move_to(mp.list_norm[ind]); return *mp.list_norm[ind]; } + static double mp_list_id(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + bool get_stats = false; + cimg::mutex(13); + if (!mp.list_stats || mp.list_stats.size()!=mp.imglist._width) mp.list_stats.assign(mp.imglist._width); + if (!mp.list_stats[ind]) get_stats = true; + cimg::mutex(13,0); + + if (get_stats) { + CImg st = mp.imglist[ind].get_stats(); + cimg::mutex(13); + st.move_to(mp.list_stats[ind]); + cimg::mutex(13,0); + } + return std::sqrt(mp.list_stats(ind,3)); + } + static double mp_list_set_ioff(_cimg_math_parser& mp) { if (!mp.imglist.width()) return cimg::type::nan(); const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); @@ -26497,19 +26829,36 @@ namespace cimg_library { static double mp_max(_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::inf(); + for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; kvalmax) valmax = val; } + } else { val = _mp_arg(i); if (val>valmax) valmax = val; } + } + return valmax; } static double mp_maxabs(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3), absval = cimg::abs(val); - for (unsigned int i = 4; iabsval) { val = _val; absval = _absval; } + double val, abs_val, valmaxabs = 0, abs_valmaxabs = 0; + for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; kabs_valmaxabs) { valmaxabs = val; abs_valmaxabs = abs_val; } + } + } else { + val = _mp_arg(i); + abs_val = cimg::abs(val); + if (abs_val>abs_valmaxabs) { valmaxabs = val; abs_valmaxabs = abs_val; } + } } - return val; + return valmaxabs; } static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref, @@ -26624,42 +26973,61 @@ namespace cimg_library { static double mp_min(_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::inf(); + for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; k::inf(), abs_valminabs = cimg::type::inf(); + for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; k values; + if (i_end==5) { // Only a single argument + if ((unsigned)mp.opcode[4]==1) return _mp_arg(3); // Real value + else values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Vector value + } else { + unsigned int siz = 0; + for (unsigned int i = 4; i1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double)); + else *ptr = _mp_arg(i); + ptr+=len; + } } - CImg vals(i_end - 3); - double *p = vals.data(); - for (unsigned int i = 3; i &img = ind==~0U?mp.imgin:mp.imglist[ind]; + longT offset = (longT)_mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + if (!img) + ptrd[0] = ptrd[1] = ptrd[2] = ptrd[3] = cimg::type::nan(); + else { + *(ptrd++) = (double)(offset%img.width()); + offset/=img.width(); + *(ptrd++) = (double)(offset%img.height()); + offset/=img.height(); + *(ptrd++) = (double)(offset%img.depth()); + offset/=img.depth(); + *ptrd = (double)(offset%img.spectrum()); } - double res = 0; - for (unsigned int i = 3; ires) res = val; - } - return res; - } - - static double mp_normp(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - if (i_end==4) return cimg::abs(_mp_arg(3)); - const double p = (double)mp.opcode[3]; - double res = 0; - for (unsigned int i = 4; i0?res:0.; + return cimg::type::nan(); } static double mp_permutations(_cimg_math_parser& mp) { @@ -26862,9 +27190,15 @@ namespace cimg_library { static double mp_prod(_cimg_math_parser& mp) { const unsigned int i_end = (unsigned int)mp.opcode[2]; - double val = _mp_arg(3); - for (unsigned int i = 4; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; k0) { + if (nb_it>=1) { const unsigned int _break_type = mp.break_type; mp.break_type = 0; double it = 0; if (ptrc) { // Version with loop variable (3 arguments) - while (it_data; @@ -26898,7 +27232,7 @@ namespace cimg_library { } *ptrc = it; } else // Version without loop variable (2 arguments) - while (it_data; const ulongT target = mp.opcode[1]; @@ -26950,6 +27284,31 @@ namespace cimg_library { return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); } +#ifdef cimg_mp_func_run + static double mp_run(_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(24); + 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'; + cimg_mp_func_run(str._data); + return cimg::type::nan(); + } +#endif + static double mp_self_add(_cimg_math_parser& mp) { return _mp_arg(1)+=_mp_arg(2); } @@ -27307,11 +27666,7 @@ namespace cimg_library { } static double mp_std(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - CImg vals(i_end - 3); - double *p = vals.data(); - for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; k vals(i_end - 3); - double *p = vals.data(); - for (unsigned int i = 3; i1) { + const double *ptr = &_mp_arg(i); + for (unsigned int k = 0; k3; --i) res+=(double)(_mp_arg(i)?1:0); + return res; + } + + static double _mp_vector_norm1(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::abs(_mp_arg(i)); + return res; + } + + static double _mp_vector_norm2(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::sqr(_mp_arg(i)); + return (double)std::sqrt(res); + } + + static double _mp_vector_norminf(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) { + const double val = (double)cimg::abs(_mp_arg(i)); + if (val>res) res = val; + } + return res; + } + + static double _mp_vector_normp(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + const double p = _mp_arg(3); + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) res+=(double)std::pow(cimg::abs(_mp_arg(i)),p); + res = (double)std::pow(res,1.0/p); + return res; + } + + static double mp_vector_normp(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[3]; + const double p = _mp_arg(4); + if (siz>0) { // Vector-valued argument + const double *ptrs = &_mp_arg(2) + 1; + double res = 0; + if (p==2) { // L2 + for (unsigned int i = 0; i::is_inf(p)) { // L-inf + for (unsigned int i = 0; ires) res = val; + } + } else { // L-p + for (unsigned int i = 0; i0?res:0; + } + // Scalar-valued argument. + const double val = _mp_arg(2); + return p?cimg::abs(val):(val!=0); + } + static double mp_vector_print(_cimg_math_parser& mp) { const bool print_string = (bool)mp.opcode[4]; cimg_pragma_openmp(critical(mp_vector_print)) @@ -27847,6 +28284,23 @@ namespace cimg_library { return _mp_arg(1); } + static double mp_vector_unitnorm(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[3]; + const double p = _mp_arg(4); + if (siz>0) { // Vector-valued argument + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + if (ptrd!=ptrs) std::memcpy(ptrd,ptrs,siz*sizeof(double)); + CImg vec(ptrd,siz,1,1,1,true); + const double mag = vec.magnitude(p); + if (mag>0) vec/=mag; + return cimg::type::nan(); + } + // Scalar-valued argument. + const double val = _mp_arg(2); + return val?(_mp_arg(2)?1:val):0; + } + #define _cimg_mp_vfunc(func) \ const longT sizd = (longT)mp.opcode[2];\ const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \ @@ -27942,15 +28396,19 @@ namespace cimg_library { 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); + default : + if (nb_digits>=-1) cimg_snprintf(format,format._width,"%%.%dg",nb_digits); + else cimg_snprintf(format,format._width,"%%.%dld",-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); + if (nb_digits>=-1) CImg(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str); + else CImg(ptrs,sizs,1,1,1).value_string(',',sizd + 1,format).move_to(str); } else { // Scalar expression str.assign(sizd + 1); - cimg_snprintf(str,sizd + 1,format,_mp_arg(3)); + if (nb_digits>=-1) cimg_snprintf(str,sizd + 1,format,_mp_arg(3)); + else cimg_snprintf(str,sizd + 1,format,(long)_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); @@ -28635,7 +29093,7 @@ namespace cimg_library { Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition. **/ CImg& pow(const char *const expression) { - return pow((+*this)._fill(expression,true,1,0,0,"pow",this)); + return pow((+*this)._fill(expression,true,3,0,0,"pow",this,0)); } //! Raise each pixel value to a power, specified from an expression \newinstance. @@ -28687,7 +29145,7 @@ namespace cimg_library { Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift. **/ CImg& rol(const char *const expression) { - return rol((+*this)._fill(expression,true,1,0,0,"rol",this)); + return rol((+*this)._fill(expression,true,3,0,0,"rol",this,0)); } //! Compute the bitwise left rotation of each pixel value \newinstance. @@ -28739,7 +29197,7 @@ namespace cimg_library { Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift. **/ CImg& ror(const char *const expression) { - return ror((+*this)._fill(expression,true,1,0,0,"ror",this)); + return ror((+*this)._fill(expression,true,3,0,0,"ror",this,0)); } //! Compute the bitwise right rotation of each pixel value \newinstance. @@ -28821,7 +29279,7 @@ namespace cimg_library { \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& min(const char *const expression) { - return min((+*this)._fill(expression,true,1,0,0,"min",this)); + return min((+*this)._fill(expression,true,3,0,0,"min",this,0)); } //! Pointwise min operator between an image and an expression \newinstance. @@ -28879,7 +29337,7 @@ namespace cimg_library { \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& max(const char *const expression) { - return max((+*this)._fill(expression,true,1,0,0,"max",this)); + return max((+*this)._fill(expression,true,3,0,0,"max",this,0)); } //! Pointwise max operator between an image and an expression \newinstance. @@ -28938,7 +29396,7 @@ namespace cimg_library { \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& minabs(const char *const expression) { - return minabs((+*this)._fill(expression,true,1,0,0,"minabs",this)); + return minabs((+*this)._fill(expression,true,3,0,0,"minabs",this,0)); } //! Pointwise minabs operator between an image and an expression \newinstance. @@ -28997,7 +29455,7 @@ namespace cimg_library { \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& maxabs(const char *const expression) { - return maxabs((+*this)._fill(expression,true,1,0,0,"maxabs",this)); + return maxabs((+*this)._fill(expression,true,3,0,0,"maxabs",this,0)); } //! Pointwise maxabs operator between an image and an expression \newinstance. @@ -29492,67 +29950,98 @@ namespace cimg_library { // (return 'true' in case of success, and set value of 'res'). template bool __eval(const char *const expression, t &res) const { - if (!expression || !*expression) { res = (t)0; return true; } - const char c = *expression; - bool is_success = false; - char sep, end; - double val,val2; - int err; - if ((c>='0' && c<='9') || c=='.') { // Possible value - if (!expression[1]) { // Single digit - res = (t)(c - '0'); - is_success = true; - } else if ((err = std::sscanf(expression,"%lf %c%lf %c",&val,&sep,&val2,&end))==1) { // Single value - res = (t)val; - is_success = true; - } else if (err==3) { // Value1 Operator Value2 - switch (sep) { - case '+' : res = (t)(val + val2); is_success = true; break; - case '-' : res = (t)(val - val2); is_success = true; break; - case '*' : res = (t)(val*val2); is_success = true; break; - case '/' : res = (t)(val/val2); is_success = true; break; - case '%' : res = (t)cimg::mod(val,val2); is_success = true; break; - case '&' : res = (t)((long)val & (long)val2); is_success = true; break; - case '|' : res = (t)((long)val | (long)val2); is_success = true; break; - case '>' : res = (t)(val>val2); is_success = true; break; - case '<' : res = (t)(val='0' && *expression<='9') { res = (t)(*expression - '0'); return true; } + } + if (*expression=='w' && expression[1]=='h') { + if (!expression[2]) { res = (t)(_width*_height); return true; } + if (expression[2]=='d') { + if (!expression[3]) { res = (t)(_width*_height*_depth); return true; } + if (expression[3]=='s' && !expression[4]) { res = (t)(_width*_height*_depth*_spectrum); return true; } + } + if (expression[2]=='s' && !expression[3]) { res = (t)(_width*_height*_spectrum); return true; } + } + const char *ptr = expression; + while (*ptr && cimg::is_blank(*ptr)) ++ptr; + if (*ptr=='\'' && *(++ptr)) { // Detect 'stringA' op 'stringB' (op='==' or '!=') + const char *ptr2 = std::strchr(ptr,'\''); + if (ptr2) { + const char *ptr3 = ptr2 + 1; + while (*ptr3 && cimg::is_blank(*ptr3)) ++ptr3; + const char *ptr4 = ptr3; + if ((*ptr3=='!' || *ptr3=='=') && *(++ptr4)=='=') { + ++ptr4; + while (*ptr4 && cimg::is_blank(*ptr4)) ++ptr4; + if (*ptr4=='\'' && *(++ptr4)) { + const char *const ptr5 = std::strchr(ptr4,'\''); + if (ptr5) { + const char *ptr6 = ptr5 + 1; + while (*ptr6 && cimg::is_blank(*ptr6)) ++ptr6; + if (!*ptr6) { + CImg str1(ptr,ptr2 - ptr,1,1,1,true), str2(ptr4,ptr5 - ptr4,1,1,1,true); + if (*ptr3=='!') res = (t)!(str1==str2); else res = (t)(str1==str2); + return true; + } + } + } } } - } else if ((c=='+' || c=='-' || c=='!') && // +Value, -Value or !Value - (((sep = expression[1])>='0' && sep<='9') || sep=='.')) { - if (!expression[2]) { // [+-!] + Single digit - const int ival = sep - '0'; - res = (t)(c=='+'?ival:c=='-'?-ival:!ival); - is_success = true; - } else if ((err = std::sscanf(expression + 1,"%lf %c%lf %c",&val,&sep,&val2,&end))==1) { // [+-!] Single value - res = (t)(c=='+'?val:c=='-'?-val:(double)!val); - is_success = true; - } else if (err==3) { // [+-!] Value1 Operator Value2 - const double val1 = c=='+'?val:c=='-'?-val:(double)!val; - switch (sep) { - case '+' : res = (t)(val1 + val2); is_success = true; break; - case '-' : res = (t)(val1 - val2); is_success = true; break; - case '*' : res = (t)(val1*val2); is_success = true; break; - case '/' : res = (t)(val1/val2); is_success = true; break; - case '%' : res = (t)cimg::mod(val1,val2); is_success = true; break; - case '&' : res = (t)((long)val1 & (long)val2); is_success = true; break; - case '|' : res = (t)((long)val1 | (long)val2); is_success = true; break; - case '>' : res = (t)(val1>val2); is_success = true; break; - case '<' : res = (t)(val1' : if (ptr[1]=='=') { ++ptr; __eval_op(val1>=val2); } else { __eval_op(val1>val2); } + case '<' : if (ptr[1]=='=') { ++ptr; __eval_op(val1<=val2); } else { __eval_op(val1 *const img_output, const char *const expression, @@ -29737,32 +30226,36 @@ namespace cimg_library { //! Compute norm of the image, viewed as a matrix. /** - \param magnitude_type Norm type. Can be: - - \c -1: Linf-norm + \param magnitude_type Can be: - \c 0: L0-norm - \c 1: L1-norm - \c 2: L2-norm + - \c p>2 : Lp-norm + - \c ~0U: Linf-norm **/ - double magnitude(const int magnitude_type=2) const { + double magnitude(const float magnitude_type=2) const { if (is_empty()) throw CImgInstanceException(_cimg_instance "magnitude(): Empty instance.", cimg_instance); const ulongT siz = size(); double res = 0; - switch (magnitude_type) { - case -1 : { - cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } - } break; - case 1 : { - cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) - for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]); - } break; - default : { + if (magnitude_type==2) { // L2 cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]); res = (double)std::sqrt(res); - } + } else if (magnitude_type==1) { // L1 + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]); + } else if (!magnitude_type) { // L0 + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)(_data[off]?1:0); + } else if (cimg::type::is_inf(magnitude_type)) { // L-inf + cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } + } else { // L-p + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)std::pow(cimg::abs(_data[off]),magnitude_type); + res = (double)std::pow(res,1.0/magnitude_type); } return res; } @@ -30033,7 +30526,7 @@ namespace cimg_library { /** If the instance matrix is not square, the Moore-Penrose pseudo-inverse is computed instead. \param use_LU Choose the inverting algorithm. Can be: - - \c true: LU solver (faster but less precise). + - \c true: LU solver (faster but sometimes less precise). - \c false: SVD solver (more precise but slower). \param lambda is used only in the Moore-Penrose pseudoinverse for estimating A^t.(A^t.A + lambda.Id)^-1. **/ @@ -30972,7 +31465,7 @@ namespace cimg_library { cimg_forX(*this,x) { CImg S = get_column(x); const CImg S0 = method<2?CImg():S; - Tfloat residual = S.magnitude()/S._height; + Tfloat residual = S.magnitude(2)/S._height; const unsigned int nmax = max_iter?max_iter:D._width; for (unsigned int n = 0; nmax_residual; ++n) { @@ -31027,7 +31520,7 @@ namespace cimg_library { W(x,ind) = weight; cimg_forY(S,y) S[y]-=weight*D(ind,y); } - residual = S.magnitude()/S._height; + residual = S.magnitude(2)/S._height; is_orthoproj = true; } } @@ -32187,103 +32680,108 @@ namespace cimg_library { **/ CImg& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, CImgList *const list_images=0) { - return _fill(expression,repeat_values,allow_formula?1:0,list_images,"fill",0); + return _fill(expression,repeat_values,allow_formula?3:1,list_images,"fill",0,0); } - // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula | - // 2 = allow formula and do not fill image values }. - CImg& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode, - CImgList *const list_images, const char *const calling_function, const CImg *provides_copy) { + // bits of 'mode' can enable/disable these properties: + // . 1 = Allow list of values. + // . 2 = Allow formula. + // . 4 = Evaluate but does not fill image values. + CImg& _fill(const char *const expression, const bool repeat_values, const unsigned int mode, + CImgList *const list_images, const char *const calling_function, + const CImg *provides_copy, CImg *const result_end) { if (is_empty() || !expression || !*expression) return *this; - const unsigned int omode = cimg::exception_mode(); + const unsigned int excmode = cimg::exception_mode(); cimg::exception_mode(0); CImg is_error_expr; - bool is_error_seq = false, is_value_sequence = false; + bool is_done = false, is_value_sequence = false; cimg_abort_init; + if (result_end) result_end->assign(); - if (formula_mode) { - - // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser. + // Detect value sequence. + if (mode&1) { double value; char sep; const int err = cimg_sscanf(expression,"%lf %c",&value,&sep); if (err==1 || (err==2 && sep==',')) { - if (err==1) { if (formula_mode==2) return *this; return fill((T)value); } + if (err==1) { if (mode&4) return *this; return fill((T)value); } else is_value_sequence = true; } + } - // Try to fill values according to a formula. + // Try to fill values according to a formula. + if (mode&2 && !is_value_sequence) { _cimg_abort_init_openmp; - if (!is_value_sequence) try { - CImg base = provides_copy?provides_copy->get_shared():get_shared(); - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || - *expression=='*' || *expression==':'), - calling_function,base,this,list_images,true); - if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && - mp.need_input_copy) - base.assign().assign(*this,false); // Needs input copy + try { + CImg base = provides_copy?provides_copy->get_shared():get_shared(); + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'), + calling_function,base,this,list_images,true); + if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && + mp.need_input_copy) + base.assign().assign(*this,false); // Needs input copy - // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation). - unsigned int M; - if (mp.result_dim) { - M = cimg::max(_width,_height,_depth); - M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height); - } else { - M = cimg::max(_width,_height,_depth,_spectrum); - M = M==_width?cimg::max(_height,_depth,_spectrum): - M==_height?cimg::max(_width,_depth,_spectrum): - M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth); - } + // Determine M2, smallest image dimension (used as axis for the most inner loop in parallelized iterations). + // M1 is the total number of parallelized iterations. + unsigned int M1 = 0, M2 = 0; + cimg::unused(M1,M2); + if (mp.result_dim) { + M2 = cimg::min(_width,_height,_depth); + M1 = M2==_width?_height*_depth:M2==_height?_width*_depth:_width*_height; + } else { + M2 = cimg::min(_width,_height,_depth,_spectrum); + M1 = M2==_width?_height*_depth*_spectrum:M2==_height?_width*_depth*_spectrum: + M2==_depth?_width*_height*_spectrum:_width*_height*_depth; + } - bool do_in_parallel = false; + bool do_in_parallel = false; #if cimg_use_openmp!=0 - if (mp.is_noncritical_run && (*expression=='*' || *expression==':')) - throw CImgArgumentException(_cimg_instance - "%s(): Cannot evaluate expression '%s' in parallel, " - "as 'run()' is used outside a 'critical()' section.", - cimg_instance,calling_function,expression); - cimg_openmp_if(!mp.is_noncritical_run && - (*expression=='*' || *expression==':' || - (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2))) - do_in_parallel = true; + if (mp.is_noncritical_run && (*expression=='*' || *expression==':')) + throw CImgArgumentException(_cimg_instance + "%s(): Cannot evaluate expression '%s' in parallel, " + "as 'run()' is used outside a 'critical()' section.", + cimg_instance,calling_function,expression); + cimg_openmp_if(!mp.is_noncritical_run && + (*expression=='*' || *expression==':' || (mp.is_parallelizable && M1>=2 && M1*M2>=16))) + do_in_parallel = true; #endif - if (mp.result_dim) { // Vector-valued expression - const unsigned int N = std::min(mp.result_dim,_spectrum); - const ulongT whd = (ulongT)_width*_height*_depth; - T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; + if (mp.result_dim) { // Vector-valued expression + const unsigned int N = std::min(mp.result_dim,_spectrum); + const ulongT whd = (ulongT)_width*_height*_depth; + T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; - if (*expression=='<') { - CImg res(1,mp.result_dim); - mp.begin_t(); - cimg_rofYZ(*this,y,z) { - cimg_abort_test; - if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0); - else cimg_rofX(*this,x) { - mp(x,y,z,0,res._data); - const double *ptrs = res._data; - T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } - } - } - mp.end_t(); + if (*expression=='<') { + CImg res(1,mp.result_dim); + mp.begin_t(); + cimg_rofYZ(*this,y,z) { + cimg_abort_test; + if (mode&4) cimg_rofX(*this,x) mp(x,y,z,0); + else cimg_rofX(*this,x) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } + mp.end_t(); - } else if (*expression=='>' || !do_in_parallel) { - CImg res(1,mp.result_dim); - mp.begin_t(); - cimg_forYZ(*this,y,z) { - cimg_abort_test; - if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0); - else cimg_forX(*this,x) { - mp(x,y,z,0,res._data); - const double *ptrs = res._data; - T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } - } - } - mp.end_t(); + } else if (*expression=='>' || !do_in_parallel) { + CImg res(1,mp.result_dim); + mp.begin_t(); + cimg_forYZ(*this,y,z) { + cimg_abort_test; + if (mode&4) cimg_forX(*this,x) mp(x,y,z,0); + else cimg_forX(*this,x) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } + mp.end_t(); - } else { + } else { #if cimg_use_openmp!=0 - cimg_pragma_openmp(parallel) + cimg_pragma_openmp(parallel) { _cimg_math_parser *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp, @@ -32296,7 +32794,7 @@ namespace cimg_library { cimg_pragma_openmp(for cimg_openmp_collapse(2)) \ cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \ cimg_abort_test; \ - if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,0); \ + if (mode&4) cimg_for##_X(*this,_x) lmp(x,y,z,0); \ else { \ CImg res(1,lmp.result_dim); \ T *__ptrd = data(_sx,_sy,_sz,0); \ @@ -32311,8 +32809,8 @@ namespace cimg_library { } \ } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp - if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) } - else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) } + if (M2==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) } + else if (M2==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) } else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) } lmp.end_t(); @@ -32320,26 +32818,26 @@ namespace cimg_library { if (&lmp!=&mp) delete &lmp; } #endif - } + } - } else { // Scalar-valued expression - T *ptrd = *expression=='<'?end() - 1:_data; - if (*expression=='<') { - mp.begin_t(); - if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); } + } else { // Scalar-valued expression + T *ptrd = *expression=='<'?end() - 1:_data; + if (*expression=='<') { + mp.begin_t(); + if (mode&4) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); } else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); } - mp.end_t(); + mp.end_t(); - } else if (*expression=='>' || !do_in_parallel) { - mp.begin_t(); - if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); } - else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } - mp.end_t(); + } else if (*expression=='>' || !do_in_parallel) { + mp.begin_t(); + if (mode&4) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); } + else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } + mp.end_t(); - } else { + } else { #if cimg_use_openmp!=0 - cimg_pragma_openmp(parallel) + cimg_pragma_openmp(parallel) { _cimg_math_parser *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp, @@ -32352,7 +32850,7 @@ namespace cimg_library { cimg_pragma_openmp(for cimg_openmp_collapse(3)) \ cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \ cimg_abort_test; \ - if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,c); \ + if (mode&4) cimg_for##_X(*this,_x) lmp(x,y,z,c); \ else { \ T *_ptrd = data(_sx,_sy,_sz,_sc); \ const ulongT off = (ulongT)_off; \ @@ -32360,9 +32858,9 @@ namespace cimg_library { } \ } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp - if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) } - else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) } - else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) } + if (M2==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) } + else if (M2==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) } + else if (M2==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) } else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) } lmp.end_t(); @@ -32370,24 +32868,30 @@ namespace cimg_library { if (&lmp!=&mp) delete &lmp; } #endif - } } - mp.end(); - } catch (CImgException& e) { CImg::string(e._message).move_to(is_error_expr); } + } + mp.end(); + + if (result_end && mp.result_end) // Transfer result of the end() block if requested. + result_end->assign(mp.result_end + (mp.result_end_dim?1:0),std::max(1U,mp.result_end_dim)); + + is_done = true; + } catch (CImgException& e) { CImg::string(e._message).move_to(is_error_expr); } } // Try to fill values according to a value sequence. - if (!formula_mode || is_value_sequence || is_error_expr) { - is_error_seq = _fill_from_values(expression,repeat_values); - cimg::exception_mode(omode); - if (is_error_seq) { - if (is_error_expr) throw CImgArgumentException("%s",is_error_expr._data); - else throw CImgArgumentException(_cimg_instance - "%s(): Invalid sequence of filling values '%s'.", - cimg_instance,calling_function,expression); - } + if (!is_done && mode&1) is_done = !_fill_from_values(expression,repeat_values); + + if (!is_done) { + cimg::exception_mode(excmode); + if (is_error_expr) throw CImgArgumentException(_cimg_instance + "%s", + cimg_instance,is_error_expr._data); + else throw CImgArgumentException(_cimg_instance + "%s(): Invalid sequence of filling values '%s'.", + cimg_instance,calling_function,expression); } - cimg::exception_mode(omode); + cimg::exception_mode(excmode); cimg_abort_test; return *this; } @@ -32417,7 +32921,7 @@ namespace cimg_library { } // Fill image according to a value sequence, given as a string. - // Return 'true' if an error occured. + // Return 'true' if an error occured, 'false' otherwise. bool _fill_from_values(const char *const values, const bool repeat_values) { CImg item(256); const char *nvalues = values; @@ -46691,7 +47195,7 @@ namespace cimg_library { *(ptrd++) = (float)color._spectrum; cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++); } else { - *(ptrd++) = (float)shared_ind; + *(ptrd++) = (float)cimg::uint2float((unsigned int)shared_ind); *(ptrd++) = 0; *(ptrd++) = 0; } @@ -46723,7 +47227,7 @@ namespace cimg_library { *(ptrd++) = (float)opacity._spectrum; cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++); } else { - *(ptrd++) = (float)shared_ind; + *(ptrd++) = (float)cimg::uint2float((unsigned int)shared_ind); *(ptrd++) = 0; *(ptrd++) = 0; } @@ -46835,24 +47339,32 @@ namespace cimg_library { const unsigned int nb_inds = (unsigned int)*(ptrs++); primitives[p].assign(1,nb_inds); tp *ptrp = primitives[p]._data; - for (unsigned int i = 0; i=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; - int w1 = width() - 1, h1 = height() - 1, dx01 = x1 - x0, dy01 = y1 - y0; const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); - if (pattern==~0U && y0>y1) { - cimg::swap(x0,x1,y0,y1); - dx01*=-1; dy01*=-1; - } + if (pattern==~0U && y0>y1) { cimg::swap(x0,x1,y0,y1); dx01*=-1; dy01*=-1; } static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); cimg_init_scanline(opacity); const int - step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2, - cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + step = y0<=y1?1:-1, + hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), + cy1 = cimg::cut(y1,0,h1) + step; dy01+=dy01?0:1; for (int y = cy0; y!=cy1; y+=step) { @@ -47432,29 +47942,32 @@ namespace cimg_library { - This function uses several call to the single CImg::draw_line() procedure, depending on the vectors size in \p points. **/ - template - CImg& draw_line(const CImg& points, + template + CImg& draw_line(const CImg& points, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty() || !points || points._width<2) return *this; - bool ninit_hatch = init_hatch; - switch (points._height) { - case 0 : case 1 : + if (is_empty() || !points) return *this; + if (!color) throw CImgArgumentException(_cimg_instance - "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).", + "draw_line(): Specified color is (null).", + cimg_instance); + if (points.height()!=2) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified point set (%u,%u,%u,%u).", cimg_instance, - points._width,points._height,points._depth,points._spectrum,points._data); + points._width,points._height,points._depth,points._spectrum); + CImg ipoints; + if (cimg::type::is_float()) ipoints = points.get_round(); + else ipoints.assign(points,cimg::type::string()==cimg::type::string()); - default : { - const int x0 = (int)points(0,0), y0 = (int)points(0,1); - int ox = x0, oy = y0; - for (unsigned int i = 1; i ipoints; + if (cimg::type::is_float()) ipoints = points.get_round(); + else ipoints.assign(points,cimg::type::string()==cimg::type::string()); + + if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity); + if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),color,opacity); + if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1), + ipoints(2,0),ipoints(2,1),color,opacity); cimg_init_scanline(opacity); int xmin = 0, ymin = 0, - xmax = points.get_shared_row(0).max_min(xmin), - ymax = points.get_shared_row(1).max_min(ymin); + xmax = ipoints.get_shared_row(0).max_min(xmin), + ymax = ipoints.get_shared_row(1).max_min(ymin); if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this; if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity); ymin = std::max(0,ymin); ymax = std::min(height() - 1,ymax); - CImg Xs(points._width,ymax - ymin + 1); + CImg Xs(ipoints._width,ymax - ymin + 1); CImg count(Xs._height,1,1,1,0); unsigned int n = 0, nn = 1; bool go_on = true; while (go_on) { - unsigned int an = (nn + 1)%points._width; - const int - x0 = cimg::uiround(points(n,0)), - y0 = cimg::uiround(points(n,1)); - if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; } - const int - x1 = cimg::uiround(points(nn,0)), - y1 = cimg::uiround(points(nn,1)); + unsigned int an = (nn + 1)%ipoints._width; + const int x0 = ipoints(n,0), y0 = ipoints(n,1); + if (ipoints(nn,1)==y0) while (ipoints(an,1)==y0) { nn = an; (an+=1)%=ipoints._width; } + const int x1 = ipoints(nn,0), y1 = ipoints(nn,1); unsigned int tn = an; - while (points(tn,1)==y1) (tn+=1)%=points._width; - + while (ipoints(tn,1)==y1) (tn+=1)%=ipoints._width; if (y0!=y1) { const int - y2 = cimg::uiround(points(tn,1)), + y2 = ipoints(tn,1), x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1, step = cimg::sign(y01), - tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2, + tmax = std::max(1,cimg::abs(y01)), + htmax = tmax*cimg::sign(x01)/2, tend = tmax - (step==cimg::sign(y12)); unsigned int y = (unsigned int)y0 - ymin; for (int t = 0; t<=tend; ++t, y+=step) @@ -49475,43 +49986,38 @@ namespace cimg_library { } //! Draw a outlined 2D or 3D polygon \overloading. - template - CImg& draw_polygon(const CImg& points, + template + CImg& draw_polygon(const CImg& points, const tc *const color, const float opacity, const unsigned int pattern) { if (is_empty() || !points) return *this; if (!color) throw CImgArgumentException(_cimg_instance "draw_polygon(): Specified color is (null).", cimg_instance); - if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity); - if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1), - (int)points(1,0),(int)points(1,1),color,opacity,pattern); - bool ninit_hatch = true; - switch (points._height) { - case 0 : case 1 : + if (points.height()!=2) throw CImgArgumentException(_cimg_instance "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).", cimg_instance, points._width,points._height,points._depth,points._spectrum); - default : { - CImg npoints(points._width,2); - int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1); - unsigned int nb_points = 1; - for (unsigned int p = 1; p ipoints; + if (cimg::type::is_float()) ipoints = points.get_round(); + else ipoints.assign(points,cimg::type::string()==cimg::type::string()); + + if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity); + if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1), + color,opacity,pattern); + if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1), + ipoints(2,0),ipoints(2,1),color,opacity,pattern); + bool ninit_hatch = true; + const int x0 = ipoints(0,0), y0 = ipoints(0,1); + int ox = x0, oy = y0; + for (unsigned int i = 1; i _get_select(const CImgDisplay& disp, const int normalization, const int x, const int y, const int z) const { if (is_empty()) return CImg(1,1,1,1,0); @@ -53420,7 +53932,7 @@ namespace cimg_library { unsigned int len = (unsigned int)std::strlen(message); cimg_forC(*this,c) cimg_snprintf(message._data + len,message._width - len,"%g ",(double)(*this)(x,0,0,c)); - len = std::strlen(message); + len = (unsigned int)std::strlen(message); cimg_snprintf(message._data + len,message._width - len,")"); } if (x0>=0 && x1>=0) { @@ -53483,7 +53995,13 @@ namespace cimg_library { CImg &screen = visu?visu:visu0; std::FILE *file; do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u." +#ifdef cimg_use_png + "png", +#else + "bmp", +#endif + snap_number++); if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp); @@ -53500,9 +54018,9 @@ namespace cimg_library { do { #ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++); #else - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++); #endif if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); @@ -54891,7 +55409,7 @@ namespace cimg_library { \param[out] description Description, as stored in the filename. \note - libtiff support is enabled by defining the precompilation - directive \c cimg_use_tif. + directive \c cimg_use_tiff. - When libtiff is enabled, 2D and 3D (multipage) several channel per pixel are supported for char,uchar,short,ushort,float and \c double pixel types. @@ -57074,7 +57592,7 @@ namespace cimg_library { else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared"); if (!is_empty()) cimg_foroff(*this,off) { - std::fprintf(cimg::output(),"%g",(double)_data[off]); + std::fprintf(cimg::output(),cimg::type::format_s(),cimg::type::format(_data[off])); if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" "); if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); } } @@ -57867,7 +58385,13 @@ namespace cimg_library { static unsigned int snap_number = 0; std::FILE *file; do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u." +#ifdef cimg_use_png + "png", +#else + "bmp", +#endif + snap_number++); if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp); @@ -57879,7 +58403,7 @@ namespace cimg_library { static unsigned int snap_number = 0; std::FILE *file; do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.off",snap_number++); if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu).__draw_text(" Saving object... ",font_size,0).display(disp); @@ -57893,9 +58417,9 @@ namespace cimg_library { do { #ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++); #else - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++); #endif if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); @@ -57911,7 +58435,7 @@ namespace cimg_library { static unsigned int snap_number = 0; std::FILE *file; do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.eps",snap_number++); if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp); @@ -57932,7 +58456,7 @@ namespace cimg_library { static unsigned int snap_number = 0; std::FILE *file; do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.svg",snap_number++); if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp); @@ -59306,7 +59830,7 @@ namespace cimg_library { \param use_bigtiff Allow to save big tiff files (>4Gb). \note - libtiff support is enabled by defining the precompilation - directive \c cimg_use_tif. + directive \c cimg_use_tiff. - When libtiff is enabled, 2D and 3D (multipage) several channel per pixel are supported for char,uchar,short,ushort,float and \c double pixel types. @@ -63080,7 +63604,13 @@ namespace cimg_library { static unsigned int snap_number = 0; std::FILE *file; do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u." +#ifdef cimg_use_png + "png", +#else + "bmp", +#endif + snap_number++); if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); if (visu0) { @@ -63096,9 +63626,9 @@ namespace cimg_library { std::FILE *file; do { #ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++); #else - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++); #endif if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); @@ -65112,7 +65642,12 @@ namespace cimg_library { const char *codec=0, const bool keep_open=false) const { #ifndef cimg_use_opencv cimg::unused(codec,keep_open); - return save_ffmpeg_external(filename,fps); + if (keep_open) cimg::warn(_cimglist_instance + "save_video(): Cannot output streamed video, as this requires features from the " + "OpenCV library ('-Dcimg_use_opencv') must be defined).", + cimglist_instance); + if (!is_empty()) return save_ffmpeg_external(filename,fps); + return *this; #else try { static cv::VideoWriter *writers[32] = {}; diff --git a/examples/CImg_demo.cpp b/examples/CImg_demo.cpp index 4a03db80e..ad2993276 100644 --- a/examples/CImg_demo.cpp +++ b/examples/CImg_demo.cpp @@ -1288,7 +1288,7 @@ void* item_breakout() { board.fill(0); visu0 = background; cimg_forXY(board,x,y) if (0.2f + cimg::rand(-1,1)>=0) { CImg cbrick = CImg::vector(100 + cimg::rand()*155,100 + cimg::rand()*155,100 + cimg::rand()*155). - unroll('v').resize(brick.width(),brick.height()); + unroll('c').resize(brick.width(),brick.height()); cimg_forC(cbrick,k) (cbrick.get_shared_channel(k).mul(brick))/=255; visu0.draw_image(x*32,y*16,cbrick); board(x,y) = 1; diff --git a/examples/image_surface3d.cpp b/examples/image_surface3d.cpp index 292125975..cba7cef87 100644 --- a/examples/image_surface3d.cpp +++ b/examples/image_surface3d.cpp @@ -119,9 +119,9 @@ int main(int argc,char **argv) { (rtype==4?"Gouraud-shaded faces":(rtype==5?"Phong-shaded faces":"Isophotes")))))); static bool first_time = true; if (rtype==6) visu.display_object3d(disp,isopoints,isoprimitives,isocolors,first_time,1,-1,true, - 500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data()); + 500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data(),true); else visu.display_object3d(disp,points,primitives,colors,first_time,rtype,-1,true, - 500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data()); + 500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data(),true); first_time = false; switch (disp.key()) { case 0: break; diff --git a/html/header.html b/html/header.html index b04d1d934..6e382d21a 100644 --- a/html/header.html +++ b/html/header.html @@ -23,7 +23,7 @@
Logo

- Latest stable version: 3.1.6        Current pre-release: 3.2.0 + Latest stable version: 3.2.5


@@ -55,9 +55,9 @@
  • Links   Links
  • -
  • - Book_fr   - Book (Fr) +
  • + Book   + Book

  • diff --git a/html/header_doxygen.html b/html/header_doxygen.html index 42a695d03..c15a8fe95 100644 --- a/html/header_doxygen.html +++ b/html/header_doxygen.html @@ -26,7 +26,7 @@
    Logo

    - Latest stable version: 3.1.6        Current pre-release: 3.2.0 + Latest stable version: 3.2.5


    @@ -58,9 +58,9 @@
  • Links   Links
  • -
  • - Book_fr   - Book (Fr) +
  • + Book   + Book

  • diff --git a/html/img/book_cimg_en.jpg b/html/img/book_cimg_en.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ac3b55ecfa4f47db4a96d7d8d8d28f8aaa29a64b GIT binary patch literal 36326 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%0Ai&)qRgNs=s1{q= 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 literal 0 HcmV?d00001 diff --git a/html/index.html b/html/index.html index 39518763b..422a8a881 100644 --- a/html/index.html +++ b/html/index.html @@ -106,15 +106,20 @@

    - + -

    Book (Fr)

    +

    Book (En/Fr)

      -
    • If you understand French, you may be interested by the nice book we wrote - on how to use the Library to develop image processing algorithms, from scratch. - In these 318 pages, we review the important concepts of the library and address a wide variety of applications in image processing +
    • We have a comprehensive book, written in English, on how to use the Library + to develop various image processing algorithms, from scratch + (published by Taylor & Francis Group). + In this 308 pages book, we review the important concepts of the library and address a wide variety of applications in image processing (Filtering, Mathematical Morphology, Feature Extraction, Segmentation, Multispectral Approaches, 3D Visualization, etc.).

      +
      +
    • +
    • + If you understand French, you may be interested by the French version of this book

    diff --git a/resources/compile_win_visualcpp.bat b/resources/compile_win_visualcpp.bat index f553071b4..619901731 100644 --- a/resources/compile_win_visualcpp.bat +++ b/resources/compile_win_visualcpp.bat @@ -10,6 +10,6 @@ REM ---------------------------------------------------------------- CD ..\examples\ SET CPPFILE=CImg_demo captcha curve_editor2d dtmri_view3d edge_explorer2d fade_images gaussian_fit1d generate_loop_macros hough_transform2d image2ascii image_registration2d image_surface3d jawbreaker mcf_levelsets2d mcf_levelsets3d odykill pde_heatflow2d pde_TschumperleDeriche2d plotter1d radon_transform2d scene3d spherical_function3d tetris tron tutorial wavelet_atrous use_chlpca use_draw_gradient use_nlmeans use_RGBclass use_skeleton FOR %%F IN (%CPPFILE%) DO ( - cl /W4 /wd"4127" /wd"4311" /wd"4312" /wd"4512" /wd"4571" /wd"4640" /wd"4706" /wd"4710" /wd"4800" /wd"4804" /wd"4820" /wd"4996" /Ox /Ob2 /Oi /Ot /c /EHsc /D "_CRT_SECURE_NO_WARNINGS" /I"%SDKPATH%\Include" /I"..\\" %%F.cpp + cl /W4 /wd"4127" /wd"4311" /wd"4312" /wd"4512" /wd"4571" /wd"4640" /wd"4706" /wd"4710" /wd"4800" /wd"4804" /wd"4820" /wd"4996" /c /EHsc /D "_CRT_SECURE_NO_WARNINGS" /I"%SDKPATH%\Include" /I"..\\" %%F.cpp link /LIBPATH:"%SDKPATH%\Lib" %%F.obj user32.lib gdi32.lib shell32.lib )