Skip to content

Commit 5011a9e

Browse files
committed
FEAT: Adding clamp across all backends
- Added necessary tests - Updated examples to use builtin function
1 parent 440f383 commit 5011a9e

6 files changed

Lines changed: 269 additions & 5 deletions

File tree

examples/image_processing/filters.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@
1414

1515
using namespace af;
1616

17-
array clamp(const array &in, float min = 0.0f, float max = 255.0f)
18-
{
19-
return ((in<min)*0.0f + (in>max)*255.0f + (in >= min && in <= max)*in);
20-
}
21-
2217
/**
2318
* randomization - controls % of total number of pixels in the image
2419
* that will be effected by random noise

include/af/arith.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,35 @@ namespace af
4747
AFAPI array max (const double lhs, const array &rhs);
4848
/// @}
4949

50+
#if AF_API_VERSION >= 34
51+
/// \ingroup arith_func_clamp
52+
/// @{
53+
/// C++ Interface for clamping an array between two values
54+
///
55+
/// \param[in] in Input array
56+
/// \param[in] lo Value for lower limit
57+
/// \param[in] hi Value for upper limit
58+
/// \return array containing values from \p in clamped between \p lo and \p hi
59+
AFAPI array clamp(const array &in, const array &lo, const array &hi);
60+
#endif
61+
62+
#if AF_API_VERSION >= 34
63+
/// \copydoc clamp(const array&, const array&, const array&)
64+
AFAPI array clamp(const array &in, const array &lo, const double hi);
65+
#endif
66+
67+
#if AF_API_VERSION >= 34
68+
/// \copydoc clamp(const array&, const array&, const array&)
69+
AFAPI array clamp(const array &in, const double lo, const array &hi);
70+
/// @}
71+
#endif
72+
73+
#if AF_API_VERSION >= 34
74+
/// \copydoc clamp(const array&, const array&, const array&)
75+
AFAPI array clamp(const array &in, const double lo, const double hi);
76+
/// @}
77+
#endif
78+
5079
/// \ingroup arith_func_rem
5180
/// @{
5281
/// C++ Interface for remainder when array divides array,
@@ -806,6 +835,23 @@ extern "C" {
806835
*/
807836
AFAPI af_err af_maxof (af_array *out, const af_array lhs, const af_array rhs, const bool batch);
808837

838+
#if AF_API_VERSION >= 34
839+
/**
840+
C Interface for max of two arrays
841+
842+
\param[out] out will contain the values from \p clamped between \p lo and \p hi
843+
\param[in] in Input array
844+
\param[in] lo Value for lower limit
845+
\param[in] hi Value for upper limit
846+
\param[in] batch specifies if operations need to be performed in batch mode
847+
\return \ref AF_SUCCESS if the execution completes properly
848+
849+
\ingroup arith_func_max
850+
*/
851+
AFAPI af_err af_clamp(af_array *out, const af_array in,
852+
const af_array lo, const af_array hi, const bool batch);
853+
#endif
854+
809855
/**
810856
C Interface for remainder
811857

src/api/c/clamp.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*******************************************************
2+
* Copyright (c) 2014, ArrayFire
3+
* All rights reserved.
4+
*
5+
* This file is distributed under 3-clause BSD license.
6+
* The complete license agreement can be obtained at:
7+
* http://arrayfire.com/licenses/BSD-3-Clause
8+
********************************************************/
9+
10+
#include <af/array.h>
11+
#include <af/defines.h>
12+
#include <af/arith.h>
13+
#include <af/data.h>
14+
#include <ArrayInfo.hpp>
15+
#include <optypes.hpp>
16+
#include <implicit.hpp>
17+
#include <err_common.hpp>
18+
#include <handle.hpp>
19+
#include <backend.hpp>
20+
21+
#include <arith.hpp>
22+
#include <logic.hpp>
23+
24+
using namespace detail;
25+
using af::dim4;
26+
27+
template<typename T>
28+
static inline af_array clampOp(const af_array in,
29+
const af_array lo,
30+
const af_array hi,
31+
const dim4 &odims)
32+
{
33+
const Array<T> L = castArray<T>(lo);
34+
const Array<T> H = castArray<T>(hi);
35+
const Array<T> I = castArray<T>(in);
36+
return getHandle(arithOp<T, af_min_t>(arithOp<T, af_max_t>(I, L, odims), H, odims));
37+
}
38+
39+
af_err af_clamp(af_array *out, const af_array in,
40+
const af_array lo, const af_array hi, const bool batch)
41+
{
42+
try {
43+
ArrayInfo linfo = getInfo(lo);
44+
ArrayInfo hinfo = getInfo(hi);
45+
ArrayInfo iinfo = getInfo(in);
46+
47+
DIM_ASSERT(2, linfo.dims() == hinfo.dims());
48+
TYPE_ASSERT(linfo.getType() == hinfo.getType());
49+
50+
dim4 odims = getOutDims(iinfo.dims(), linfo.dims(), batch);
51+
const af_dtype otype = implicit(iinfo.getType(), linfo.getType());
52+
53+
af_array res;
54+
switch (otype) {
55+
case f32: res = clampOp<float >(in, lo, hi, odims); break;
56+
case f64: res = clampOp<double >(in, lo, hi, odims); break;
57+
case c32: res = clampOp<cfloat >(in, lo, hi, odims); break;
58+
case c64: res = clampOp<cdouble>(in, lo, hi, odims); break;
59+
case s32: res = clampOp<int >(in, lo, hi, odims); break;
60+
case u32: res = clampOp<uint >(in, lo, hi, odims); break;
61+
case u8 : res = clampOp<uchar >(in, lo, hi, odims); break;
62+
case b8 : res = clampOp<char >(in, lo, hi, odims); break;
63+
case s64: res = clampOp<intl >(in, lo, hi, odims); break;
64+
case u64: res = clampOp<uintl >(in, lo, hi, odims); break;
65+
case s16: res = clampOp<short >(in, lo, hi, odims); break;
66+
case u16: res = clampOp<ushort >(in, lo, hi, odims); break;
67+
default: TYPE_ERROR(0, otype);
68+
}
69+
70+
std::swap(*out, res);
71+
}
72+
CATCHALL;
73+
return AF_SUCCESS;
74+
}

src/api/cpp/clamp.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*******************************************************
2+
* Copyright (c) 2014, ArrayFire
3+
* All rights reserved.
4+
*
5+
* This file is distributed under 3-clause BSD license.
6+
* The complete license agreement can be obtained at:
7+
* http://arrayfire.com/licenses/BSD-3-Clause
8+
********************************************************/
9+
10+
#include <af/array.h>
11+
#include <af/arith.h>
12+
#include <af/data.h>
13+
#include <af/gfor.h>
14+
#include "error.hpp"
15+
16+
namespace af
17+
{
18+
array clamp(const array &in, const array &lo, const array &hi)
19+
{
20+
af_array out;
21+
AF_THROW(af_clamp(&out, in.get(), lo.get(), hi.get(), gforGet()));
22+
return array(out);
23+
}
24+
25+
array clamp(const array &in, const array &lo, const double hi)
26+
{
27+
return clamp(in, lo, constant(hi, lo.dims(), lo.type()));
28+
}
29+
30+
array clamp(const array &in, const double lo, const array &hi)
31+
{
32+
return clamp(in, constant(lo, hi.dims(), hi.type()), hi);
33+
}
34+
35+
array clamp(const array &in, const double lo, const double hi)
36+
{
37+
return clamp(in,
38+
constant(lo, in.dims(), in.type()),
39+
constant(hi, in.dims(), in.type()));
40+
}
41+
}

src/api/unified/arith.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,10 @@ UNARY_HAPI_DEF(af_iszero)
100100
UNARY_HAPI_DEF(af_isinf)
101101
UNARY_HAPI_DEF(af_isnan)
102102
UNARY_HAPI_DEF(af_not)
103+
104+
af_err af_clamp(af_array *out, const af_array in,
105+
const af_array lo, const af_array hi, const bool batch)
106+
{
107+
CHECK_ARRAYS(in, lo, hi);
108+
return CALL(out, in, lo, hi, batch);
109+
}

test/clamp.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*******************************************************
2+
* Copyright (c) 2014, ArrayFire
3+
* All rights reserved.
4+
*
5+
* This file is distributed under 3-clause BSD license.
6+
* The complete license agreement can be obtained at:
7+
* http://arrayfire.com/licenses/BSD-3-Clause
8+
********************************************************/
9+
10+
#include <gtest/gtest.h>
11+
#include <af/array.h>
12+
#include <af/arith.h>
13+
#include <af/data.h>
14+
#include <testHelpers.hpp>
15+
16+
using namespace std;
17+
using std::abs;
18+
using namespace af;
19+
20+
const int num = 10000;
21+
22+
TEST(ClampTests, FloatArrayArray)
23+
{
24+
array in = af::randu(num, f32);
25+
array lo = af::randu(num, f32)/10; // Ensure lo <= 0.1
26+
array hi = 1.0 - af::randu(num, f32)/10; // Ensure hi >= 0.9
27+
af::eval(lo, hi);
28+
29+
30+
std::vector<float> hout(num), hin(num), hlo(num), hhi(num);
31+
array out = clamp(in, lo, hi);
32+
out.host(&hout[0]);
33+
in.host(&hin[0]);
34+
lo.host(&hlo[0]);
35+
hi.host(&hhi[0]);
36+
37+
for (int i = 0; i < num; i++) {
38+
ASSERT_LE(hout[i], hhi[i]);
39+
ASSERT_GE(hout[i], hlo[i]);
40+
ASSERT_EQ(true, hout[i] == hin[i] || hout[i] == hlo[i] || hout[i] == hhi[i]);
41+
}
42+
}
43+
44+
TEST(ClampTests, FloatArrayScalar)
45+
{
46+
array in = af::randu(num, f32);
47+
array lo = af::randu(num, f32)/10; // Ensure lo <= 0.1
48+
float hi = 0.9;
49+
50+
std::vector<float> hout(num), hin(num), hlo(num);
51+
array out = clamp(in, lo, hi);
52+
53+
out.host(&hout[0]);
54+
in.host(&hin[0]);
55+
lo.host(&hlo[0]);
56+
57+
for (int i = 0; i < num; i++) {
58+
ASSERT_LE(hout[i], hi);
59+
ASSERT_GE(hout[i], hlo[i]);
60+
ASSERT_EQ(true, hout[i] == hin[i] || hout[i] == hlo[i] || hout[i] == hi);
61+
}
62+
}
63+
64+
TEST(ClampTests, FloatScalarArray)
65+
{
66+
array in = af::randu(num, f32);
67+
float lo = 0.1;
68+
array hi = 1.0 - af::randu(num, f32)/10; // Ensure hi >= 0.9
69+
70+
std::vector<float> hout(num), hin(num), hhi(num);
71+
array out = clamp(in, lo, hi);
72+
73+
out.host(&hout[0]);
74+
in.host(&hin[0]);
75+
hi.host(&hhi[0]);
76+
77+
for (int i = 0; i < num; i++) {
78+
ASSERT_LE(hout[i], hhi[i]);
79+
ASSERT_GE(hout[i], lo);
80+
ASSERT_EQ(true, hout[i] == hin[i] || hout[i] == lo || hout[i] == hhi[i]);
81+
}
82+
}
83+
84+
TEST(ClampTests, FloatScalarScalar)
85+
{
86+
array in = af::randu(num, f32);
87+
float lo = 0.1;
88+
float hi = 0.9;
89+
90+
std::vector<float> hout(num), hin(num);
91+
array out = clamp(in, lo, hi);
92+
93+
out.host(&hout[0]);
94+
in.host(&hin[0]);
95+
96+
for (int i = 0; i < num; i++) {
97+
ASSERT_LE(hout[i], hi);
98+
ASSERT_GE(hout[i], lo);
99+
ASSERT_EQ(true, hout[i] == hin[i] || hout[i] == lo || hout[i] == hi);
100+
}
101+
}

0 commit comments

Comments
 (0)