Add hyperbolic functions (#2508)

* add hyperbolic functions
* add hyperbolic function tests
* changelog
* add inverse hyperbolic functions for old compilers
This commit is contained in:
Heshy Roskes 2021-11-02 11:05:37 -04:00 committed by GitHub
parent fc92290bb9
commit d8c04011ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 322 additions and 2 deletions

View File

@ -17,6 +17,7 @@
- improve buffer and target save file format selection
- added VipsForeignPpmFormat, @format arg to ppm savers
- add fail-on to give better control over loader error sensitivity
- add hyperbolic functions sinh, cosh, tanh, asinh, acosh, atanh [hroskes]
16/8/21 started 8.11.4
- fix off-by-one error in new rank fast path

View File

@ -1375,6 +1375,60 @@ public:
return( math( VIPS_OPERATION_MATH_ATAN, options ) );
}
/**
* Find the hyperbolic sine of each pixel. Angles are in degrees.
*/
VImage
sinh( VOption *options = 0 ) const
{
return( math( VIPS_OPERATION_MATH_SINH, options ) );
}
/**
* Find the hyperbolic cosine of each pixel. Angles are in degrees.
*/
VImage
cosh( VOption *options = 0 ) const
{
return( math( VIPS_OPERATION_MATH_COSH, options ) );
}
/**
* Find the hyperbolic tangent of each pixel. Angles are in degrees.
*/
VImage
tanh( VOption *options = 0 ) const
{
return( math( VIPS_OPERATION_MATH_TANH, options ) );
}
/**
* Find the hyperbolic arc sine of each pixel. Angles are in radians.
*/
VImage
asinh( VOption *options = 0 ) const
{
return( math( VIPS_OPERATION_MATH_ASINH, options ) );
}
/**
* Find the hyperbolic arc cosine of each pixel. Angles are in radians.
*/
VImage
acosh( VOption *options = 0 ) const
{
return( math( VIPS_OPERATION_MATH_ACOSH, options ) );
}
/**
* Find the hyperbolic arc tangent of each pixel. Angles are in radians.
*/
VImage
atanh( VOption *options = 0 ) const
{
return( math( VIPS_OPERATION_MATH_ATANH, options ) );
}
/**
* Find the natural log of each pixel.
*/

View File

@ -82,7 +82,7 @@ def gen_function_list():
'draw_rect': ['draw_rect1', 'draw_point', 'draw_point1'],
'extract_area': ['crop'],
'linear': ['linear1'],
'math': ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'exp', 'exp10', 'log', 'log10'],
'math': ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh', 'exp', 'exp10', 'log', 'log10'],
'math2': ['pow', 'wop', 'atan2'],
'rank': ['median'],
'relational': ['equal', 'notequal', 'less', 'lesseq', 'more', 'moreeq'],

View File

@ -129,6 +129,28 @@ vips_math_build( VipsObject *object )
g_assert_not_reached(); \
}
/* inverse hyperbolic functions for old compilers
they were added to the standard in C99 and C++11
*/
#if ( \
(defined(__cplusplus) && __cplusplus >= 201103L) \
|| (defined(__STDC__) && __STDC_VERSION__ >= 199901L) \
)
#define HAS_INVERSE_HYPERBOLICS 1
#else
#define HAS_INVERSE_HYPERBOLICS 0
#endif
#if HAS_INVERSE_HYPERBOLICS
#define ASINH sinh
#define ACOSH cosh
#define ATANH tanh
#else
#define ASINH( X ) log((X) + sqrt( (X)*(X)+1 ))
#define ACOSH( X ) log((X) + sqrt( (X)*(X)-1 ))
#define ATANH( X ) (0.5 * log( (1+(X)) / (1-(X)) ))
#endif
/* sin/cos/tan in degrees.
*/
#define DSIN( X ) (sin( VIPS_RAD( X ) ))
@ -164,6 +186,12 @@ vips_math_buffer( VipsArithmetic *arithmetic,
case VIPS_OPERATION_MATH_ASIN: SWITCH( ADSIN ); break;
case VIPS_OPERATION_MATH_ACOS: SWITCH( ADCOS ); break;
case VIPS_OPERATION_MATH_ATAN: SWITCH( ADTAN ); break;
case VIPS_OPERATION_MATH_SINH: SWITCH( sinh ); break;
case VIPS_OPERATION_MATH_COSH: SWITCH( cosh ); break;
case VIPS_OPERATION_MATH_TANH: SWITCH( tanh ); break;
case VIPS_OPERATION_MATH_ASINH: SWITCH( ASINH ); break;
case VIPS_OPERATION_MATH_ACOSH: SWITCH( ACOSH ); break;
case VIPS_OPERATION_MATH_ATANH: SWITCH( ATANH ); break;
case VIPS_OPERATION_MATH_LOG: SWITCH( LOGZ ); break;
case VIPS_OPERATION_MATH_LOG10: SWITCH( LOGZ10 ); break;
case VIPS_OPERATION_MATH_EXP: SWITCH( exp ); break;
@ -398,6 +426,144 @@ vips_atan( VipsImage *in, VipsImage **out, ... )
return( result );
}
/**
* vips_sinh: (method)
* @in: input #VipsImage
* @out: (out): output #VipsImage
* @...: %NULL-terminated list of optional named arguments
*
* Perform #VIPS_OPERATION_MATH_SINH on an image. See vips_math().
*
* Returns: 0 on success, -1 on error
*/
int
vips_sinh( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_mathv( in, out, VIPS_OPERATION_MATH_SINH, ap );
va_end( ap );
return( result );
}
/**
* vips_cosh: (method)
* @in: input #VipsImage
* @out: (out): output #VipsImage
* @...: %NULL-terminated list of optional named arguments
*
* Perform #VIPS_OPERATION_MATH_COSH on an image. See vips_math().
*
* Returns: 0 on success, -1 on error
*/
int
vips_cosh( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_mathv( in, out, VIPS_OPERATION_MATH_COSH, ap );
va_end( ap );
return( result );
}
/**
* vips_tanh: (method)
* @in: input #VipsImage
* @out: (out): output #VipsImage
* @...: %NULL-terminated list of optional named arguments
*
* Perform #VIPS_OPERATION_MATH_TANH on an image. See vips_math().
*
* Returns: 0 on success, -1 on error
*/
int
vips_tanh( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_mathv( in, out, VIPS_OPERATION_MATH_TANH, ap );
va_end( ap );
return( result );
}
/**
* vips_asinh: (method)
* @in: input #VipsImage
* @out: (out): output #VipsImage
* @...: %NULL-terminated list of optional named arguments
*
* Perform #VIPS_OPERATION_MATH_ASINH on an image. See vips_math().
*
* Returns: 0 on success, -1 on error
*/
int
vips_asinh( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_mathv( in, out, VIPS_OPERATION_MATH_ASINH, ap );
va_end( ap );
return( result );
}
/**
* vips_acosh: (method)
* @in: input #VipsImage
* @out: (out): output #VipsImage
* @...: %NULL-terminated list of optional named arguments
*
* Perform #VIPS_OPERATION_MATH_ACOSH on an image. See vips_math().
*
* Returns: 0 on success, -1 on error
*/
int
vips_acosh( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_mathv( in, out, VIPS_OPERATION_MATH_ACOSH, ap );
va_end( ap );
return( result );
}
/**
* vips_atanh: (method)
* @in: input #VipsImage
* @out: (out): output #VipsImage
* @...: %NULL-terminated list of optional named arguments
*
* Perform #VIPS_OPERATION_MATH_ATANH on an image. See vips_math().
*
* Returns: 0 on success, -1 on error
*/
int
vips_atanh( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_mathv( in, out, VIPS_OPERATION_MATH_ATANH, ap );
va_end( ap );
return( result );
}
/**
* vips_log: (method)
* @in: input #VipsImage

View File

@ -46,6 +46,12 @@ extern "C" {
* @VIPS_OPERATION_MATH_ASIN: asin(), angles in degrees
* @VIPS_OPERATION_MATH_ACOS: acos(), angles in degrees
* @VIPS_OPERATION_MATH_ATAN: atan(), angles in degrees
* @VIPS_OPERATION_MATH_SINH: sinh(), angles in radians
* @VIPS_OPERATION_MATH_COSH: cosh(), angles in radians
* @VIPS_OPERATION_MATH_TANH: tanh(), angles in radians
* @VIPS_OPERATION_MATH_ASINH: asinh(), angles in radians
* @VIPS_OPERATION_MATH_ACOSH: acosh(), angles in radians
* @VIPS_OPERATION_MATH_ATANH: atanh(), angles in radians
* @VIPS_OPERATION_MATH_LOG: log base e
* @VIPS_OPERATION_MATH_LOG10: log base 10
* @VIPS_OPERATION_MATH_EXP: e to the something
@ -60,6 +66,12 @@ typedef enum {
VIPS_OPERATION_MATH_ASIN,
VIPS_OPERATION_MATH_ACOS,
VIPS_OPERATION_MATH_ATAN,
VIPS_OPERATION_MATH_SINH,
VIPS_OPERATION_MATH_COSH,
VIPS_OPERATION_MATH_TANH,
VIPS_OPERATION_MATH_ASINH,
VIPS_OPERATION_MATH_ACOSH,
VIPS_OPERATION_MATH_ATANH,
VIPS_OPERATION_MATH_LOG,
VIPS_OPERATION_MATH_LOG10,
VIPS_OPERATION_MATH_EXP,

View File

@ -18,6 +18,12 @@ vips_operation_math_get_type( void )
{VIPS_OPERATION_MATH_ASIN, "VIPS_OPERATION_MATH_ASIN", "asin"},
{VIPS_OPERATION_MATH_ACOS, "VIPS_OPERATION_MATH_ACOS", "acos"},
{VIPS_OPERATION_MATH_ATAN, "VIPS_OPERATION_MATH_ATAN", "atan"},
{VIPS_OPERATION_MATH_SINH, "VIPS_OPERATION_MATH_SINH", "sinh"},
{VIPS_OPERATION_MATH_COSH, "VIPS_OPERATION_MATH_COSH", "cosh"},
{VIPS_OPERATION_MATH_TANH, "VIPS_OPERATION_MATH_TANH", "tanh"},
{VIPS_OPERATION_MATH_ASINH, "VIPS_OPERATION_MATH_ASINH", "asinh"},
{VIPS_OPERATION_MATH_ACOSH, "VIPS_OPERATION_MATH_ACOSH", "acosh"},
{VIPS_OPERATION_MATH_ATANH, "VIPS_OPERATION_MATH_ATANH", "atanh"},
{VIPS_OPERATION_MATH_LOG, "VIPS_OPERATION_MATH_LOG", "log"},
{VIPS_OPERATION_MATH_LOG10, "VIPS_OPERATION_MATH_LOG10", "log10"},
{VIPS_OPERATION_MATH_EXP, "VIPS_OPERATION_MATH_EXP", "exp"},

View File

@ -472,7 +472,88 @@ class TestArithmetic:
im = (pyvips.Image.black(100, 100) + [1, 2, 3]) / 3.0
self.run_unary([im], my_atan, fmt=noncomplex_formats)
# this require pyvips 2.1.16 for atan2
# this requires pyvips 2.1.16 for sinh
@pytest.mark.skipif(versiontuple(pyvips.__version__) <
versiontuple('2.1.16'),
reason='your pyvips is too old')
def test_sinh(self):
def my_sinh(x):
if isinstance(x, pyvips.Image):
return x.sinh()
else:
return math.sinh(x)
self.run_unary(self.all_images, my_sinh, fmt=noncomplex_formats)
# this requires pyvips 2.1.16 for cosh
@pytest.mark.skipif(versiontuple(pyvips.__version__) <
versiontuple('2.1.16'),
reason='your pyvips is too old')
def test_cosh(self):
def my_cosh(x):
if isinstance(x, pyvips.Image):
return x.cosh()
else:
return math.cosh(x)
self.run_unary(self.all_images, my_cosh, fmt=noncomplex_formats)
# this requires pyvips 2.1.16 for tanh
@pytest.mark.skipif(versiontuple(pyvips.__version__) <
versiontuple('2.1.16'),
reason='your pyvips is too old')
def test_tanh(self):
def my_tanh(x):
if isinstance(x, pyvips.Image):
return x.tanh()
else:
return math.tanh(x)
self.run_unary(self.all_images, my_tanh, fmt=noncomplex_formats)
# this requires pyvips 2.1.16 for asinh
@pytest.mark.skipif(versiontuple(pyvips.__version__) <
versiontuple('2.1.16'),
reason='your pyvips is too old')
def test_asinh(self):
def my_asinh(x):
if isinstance(x, pyvips.Image):
return x.asinh()
else:
return math.asinh(x)
im = (pyvips.Image.black(100, 100) + [1, 2, 3]) / 3.0
self.run_unary([im], my_asinh, fmt=noncomplex_formats)
# this requires pyvips 2.1.16 for acosh
@pytest.mark.skipif(versiontuple(pyvips.__version__) <
versiontuple('2.1.16'),
reason='your pyvips is too old')
def test_acosh(self):
def my_acosh(x):
if isinstance(x, pyvips.Image):
return x.acosh()
else:
return math.acosh(x)
im = (pyvips.Image.black(100, 100) + [1, 2, 3]) / 3.0
self.run_unary([im], my_acosh, fmt=noncomplex_formats)
# this requires pyvips 2.1.16 for atanh
@pytest.mark.skipif(versiontuple(pyvips.__version__) <
versiontuple('2.1.16'),
reason='your pyvips is too old')
def test_atanh(self):
def my_atanh(x):
if isinstance(x, pyvips.Image):
return x.atanh()
else:
return math.atanh(x)
im = (pyvips.Image.black(100, 100) + [1, 2, 3]) / 3.0
self.run_unary([im], my_atanh, fmt=noncomplex_formats)
# this requires pyvips 2.1.16 for atan2
@pytest.mark.skipif(versiontuple(pyvips.__version__) <
versiontuple('2.1.16'),
reason='your pyvips is too old')