Skip to content

Commit 0538f0f

Browse files
author
Pradeep
committed
af_bilateral API and cpu backend implementation
1 parent 4d59bb9 commit 0538f0f

9 files changed

Lines changed: 357 additions & 0 deletions

File tree

include/af/image.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extern "C" {
1111

1212
// Resize an image/matrix/array
1313
AFAPI af_err af_resize(af_array *out, const af_array in, const dim_type odim0, const dim_type odim1, const af_interp_type method);
14+
1415
// image dilation operation
1516
AFAPI af_err af_dilate(af_array *out, const af_array in, const af_array mask);
1617

@@ -21,6 +22,9 @@ extern "C" {
2122

2223
AFAPI af_err af_erode3d(af_array *out, const af_array in, const af_array mask);
2324

25+
// image bilateral filter
26+
AFAPI af_err af_bilateral(af_array *out, const af_array in, const float spatial_sigma, const float chromatic_sigma, const bool isColor);
27+
2428
#ifdef __cplusplus
2529
}
2630
#endif

src/backend/bilateral.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include <af/dim4.hpp>
2+
#include <af/defines.h>
3+
#include <af/image.h>
4+
#include <bilateral.hpp>
5+
#include <helper.hpp>
6+
#include <backend.hpp>
7+
8+
using af::dim4;
9+
using namespace detail;
10+
11+
template<typename T, bool isColor>
12+
static inline af_array bilateral(const af_array &in, const float &sp_sig, const float &chr_sig)
13+
{
14+
return getHandle(*bilateral<T, isColor>(getArray<T>(in), sp_sig, chr_sig));
15+
}
16+
17+
template<bool isColor>
18+
static af_err bilateral(af_array *out, const af_array &in, const float &s_sigma, const float &c_sigma)
19+
{
20+
af_err ret = AF_ERR_RUNTIME;
21+
22+
try {
23+
ArrayInfo info = getInfo(in);
24+
af_dtype type = info.getType();
25+
af::dim4 dims = info.dims();
26+
27+
if (isColor) {
28+
if (dims.ndims()<3) return AF_ERR_ARG;
29+
} else {
30+
if (dims.ndims()<2 || dims.ndims()>3) return AF_ERR_ARG;
31+
}
32+
33+
af_array output;
34+
switch(type) {
35+
case f32: output = bilateral<float , isColor> (in, s_sigma, c_sigma); break;
36+
case f64: output = bilateral<double , isColor> (in, s_sigma, c_sigma); break;
37+
case b8 : output = bilateral<char , isColor> (in, s_sigma, c_sigma); break;
38+
case s32: output = bilateral<int , isColor> (in, s_sigma, c_sigma); break;
39+
case u32: output = bilateral<uint , isColor> (in, s_sigma, c_sigma); break;
40+
case u8 : output = bilateral<uchar , isColor> (in, s_sigma, c_sigma); break;
41+
default : ret = AF_ERR_NOT_SUPPORTED; break;
42+
}
43+
if (ret!=AF_ERR_NOT_SUPPORTED) {
44+
std::swap(*out,output);
45+
ret = AF_SUCCESS;
46+
}
47+
}
48+
CATCHALL;
49+
50+
return ret;
51+
}
52+
53+
af_err af_bilateral(af_array *out, const af_array in, const float spatial_sigma, const float chromatic_sigma, const bool isColor)
54+
{
55+
if (isColor)
56+
return bilateral<true>(out,in,spatial_sigma,chromatic_sigma);
57+
else
58+
return bilateral<false>(out,in,spatial_sigma,chromatic_sigma);
59+
}

src/backend/cpu/bilateral.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include <af/dim4.hpp>
2+
#include <af/defines.h>
3+
#include <ArrayInfo.hpp>
4+
#include <Array.hpp>
5+
#include <bilateral.hpp>
6+
#include <cmath>
7+
#include <algorithm>
8+
9+
using af::dim4;
10+
11+
namespace cpu
12+
{
13+
14+
static inline dim_type clamp(dim_type a, dim_type mn, dim_type mx)
15+
{
16+
return (a<mn ? mn : (a>mx ? mx : a));
17+
}
18+
19+
static inline unsigned getIdx(const dim4 &strides,
20+
int i, int j = 0, int k = 0, int l = 0)
21+
{
22+
return (l * strides[3] +
23+
k * strides[2] +
24+
j * strides[1] +
25+
i * strides[0]);
26+
}
27+
28+
template<typename T, bool isColor>
29+
Array<T> * bilateral(const Array<T> &in, const float &s_sigma, const float &c_sigma)
30+
{
31+
const dim4 dims = in.dims();
32+
const dim4 istrides = in.strides();
33+
34+
Array<T>* out = createEmptyArray<T>(dims);
35+
const dim4 ostrides = out->strides();
36+
37+
dim_type bCount = dims[2];
38+
if (isColor) bCount*= dims[3];
39+
40+
T *outData = out->get();
41+
const T * inData = in.get();
42+
43+
// clamp spatical and chromatic sigma's
44+
float space_ = std::min(11.5f, std::max(s_sigma, 0.f));
45+
float color_ = std::max(c_sigma, 0.f);
46+
const dim_type radius = std::max((dim_type)(space_ * 1.5f), (dim_type)1);
47+
const float svar = space_*space_;
48+
const float cvar = color_*color_;
49+
50+
for(dim_type batchId=0; batchId<bCount; ++batchId) {
51+
// channels or batch for gray and channel are handled by outer loop
52+
for(dim_type j=0; j<dims[1]; ++j) {
53+
// j steps along 2nd dimension
54+
for(dim_type i=0; i<dims[0]; ++i) {
55+
// i steps along 1st dimension
56+
float norm = 0.0f;
57+
float res = 0.0f;
58+
const T center = inData[ getIdx(istrides, i, j) ];
59+
for(dim_type wj=-radius; wj<=radius; ++wj) {
60+
// clamps offsets
61+
dim_type tj = clamp(j+wj, 0, dims[1]-1);
62+
63+
for(dim_type wi=-radius; wi<=radius; ++wi) {
64+
// clamps offsets
65+
dim_type ti = clamp(i+wi, 0, dims[0]-1);
66+
67+
// proceed
68+
const T val= inData[ getIdx(istrides, ti, tj) ];
69+
const float gauss_space = std::exp((wi*wi+wj*wj)/(-2.f*svar));
70+
const float gauss_range = std::exp(((center-val)*(center-val))/(-2.f*cvar));
71+
const float weight = gauss_space*gauss_range;
72+
norm += weight;
73+
res += val*weight;
74+
}
75+
} // filter loop ends here
76+
77+
outData[ getIdx(ostrides, i, j) ] = (T)(res/norm);
78+
} //1st dimension loop ends here
79+
} //2nd dimension loop ends here
80+
outData += ostrides[2];
81+
inData += istrides[2];
82+
}
83+
84+
return out;
85+
}
86+
87+
#define INSTANTIATE(T)\
88+
template Array<T> * bilateral<T,true >(const Array<T> &in, const float &s_sigma, const float &c_sigma);\
89+
template Array<T> * bilateral<T,false>(const Array<T> &in, const float &s_sigma, const float &c_sigma);
90+
91+
INSTANTIATE(float )
92+
INSTANTIATE(double)
93+
INSTANTIATE(char )
94+
INSTANTIATE(int )
95+
INSTANTIATE(uint )
96+
INSTANTIATE(uchar )
97+
98+
}

src/backend/cpu/bilateral.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <Array.hpp>
2+
3+
namespace cpu
4+
{
5+
6+
template<typename T, bool isColor>
7+
Array<T> * bilateral(const Array<T> &in, const float &s_sigma, const float &c_sigma);
8+
9+
}

src/backend/cuda/bilateral.cu

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <af/dim4.hpp>
2+
#include <af/defines.h>
3+
#include <ArrayInfo.hpp>
4+
#include <Array.hpp>
5+
#include <bilateral.hpp>
6+
#include <stdexcept>
7+
8+
using af::dim4;
9+
10+
namespace cuda
11+
{
12+
13+
template<typename T, bool isColor>
14+
Array<T> * bilateral(const Array<T> &in, const float &s_sigma, const float &c_sigma)
15+
{
16+
Array<T> *out = 0;
17+
throw std::runtime_error("bilateral not supported in cuda");
18+
return out;
19+
}
20+
21+
#define INSTANTIATE(T)\
22+
template Array<T> * bilateral<T,true >(const Array<T> &in, const float &s_sigma, const float &c_sigma);\
23+
template Array<T> * bilateral<T,false>(const Array<T> &in, const float &s_sigma, const float &c_sigma);
24+
25+
INSTANTIATE(float )
26+
INSTANTIATE(double)
27+
INSTANTIATE(char )
28+
INSTANTIATE(int )
29+
INSTANTIATE(uint )
30+
INSTANTIATE(uchar )
31+
32+
}

src/backend/cuda/bilateral.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <Array.hpp>
2+
3+
namespace cuda
4+
{
5+
6+
template<typename T, bool isColor>
7+
Array<T> * bilateral(const Array<T> &in, const float &s_sigma, const float &c_sigma);
8+
9+
}

src/backend/opencl/bilateral.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <af/dim4.hpp>
2+
#include <af/defines.h>
3+
#include <ArrayInfo.hpp>
4+
#include <Array.hpp>
5+
#include <bilateral.hpp>
6+
#include <stdexcept>
7+
8+
using af::dim4;
9+
10+
namespace opencl
11+
{
12+
13+
template<typename T, bool isColor>
14+
Array<T> * bilateral(const Array<T> &in, const float &s_sigma, const float &c_sigma)
15+
{
16+
Array<T> *out = nullptr;
17+
throw std::runtime_error("bilateral not supported in opencl");
18+
return out;
19+
}
20+
21+
#define INSTANTIATE(T)\
22+
template Array<T> * bilateral<T,true >(const Array<T> &in, const float &s_sigma, const float &c_sigma);\
23+
template Array<T> * bilateral<T,false>(const Array<T> &in, const float &s_sigma, const float &c_sigma);
24+
25+
INSTANTIATE(float )
26+
INSTANTIATE(double)
27+
INSTANTIATE(char )
28+
INSTANTIATE(int )
29+
INSTANTIATE(uint )
30+
INSTANTIATE(uchar )
31+
32+
}

src/backend/opencl/bilateral.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <Array.hpp>
2+
3+
namespace opencl
4+
{
5+
6+
template<typename T, bool isColor>
7+
Array<T> * bilateral(const Array<T> &in, const float &s_sigma, const float &c_sigma);
8+
9+
}

test/bilateral.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#include <gtest/gtest.h>
2+
#include <arrayfire.h>
3+
#include <af/dim4.hpp>
4+
#include <af/traits.hpp>
5+
#include <string>
6+
#include <vector>
7+
#include <testHelpers.hpp>
8+
#include <cmath>
9+
10+
using std::string;
11+
using std::vector;
12+
using af::dim4;
13+
14+
template<typename T>
15+
class Bilateral : public ::testing::Test
16+
{
17+
public:
18+
virtual void SetUp() {}
19+
};
20+
21+
// create a list of types to be tested
22+
// FIXME: since af_load_image returns only f32 type arrays
23+
// only float, double, int data types test are enabledpassing
24+
// Note: compareArraysRMSD is handling upcasting while working
25+
// with two different type of types
26+
//
27+
//typedef ::testing::Types<float, double, int, uint, char, uchar> TestTypes;
28+
typedef ::testing::Types<float, double, int> TestTypes;
29+
30+
// register the type list
31+
TYPED_TEST_CASE(Bilateral, TestTypes);
32+
33+
TYPED_TEST(Bilateral, InvalidArgs)
34+
{
35+
vector<TypeParam> in(100,1);
36+
37+
af_array inArray = 0;
38+
af_array outArray = 0;
39+
40+
// check for gray scale bilateral
41+
af::dim4 dims(5,5,2,2);
42+
ASSERT_EQ(AF_SUCCESS, af_create_array(&inArray, &in.front(),
43+
dims.ndims(), dims.get(), (af_dtype) af::dtype_traits<TypeParam>::af_type));
44+
ASSERT_EQ(AF_ERR_ARG, af_bilateral(&outArray, inArray, 0.12f, 0.34f, false));
45+
ASSERT_EQ(AF_SUCCESS, af_destroy_array(inArray));
46+
47+
// check for color image bilateral
48+
dims = af::dim4(100,1,1,1);
49+
ASSERT_EQ(AF_SUCCESS, af_create_array(&inArray, &in.front(),
50+
dims.ndims(), dims.get(), (af_dtype) af::dtype_traits<TypeParam>::af_type));
51+
ASSERT_EQ(AF_ERR_ARG, af_bilateral(&outArray, inArray, 0.12f, 0.34f, true));
52+
ASSERT_EQ(AF_SUCCESS, af_destroy_array(inArray));
53+
}
54+
55+
template<typename T, bool isColor>
56+
void bilateralTest(string pTestFile)
57+
{
58+
vector<dim4> inDims;
59+
vector<string> inFiles;
60+
vector<dim_type> outSizes;
61+
vector<string> outFiles;
62+
63+
readImageTests(pTestFile, inDims, inFiles, outSizes, outFiles);
64+
65+
size_t testCount = inDims.size();
66+
67+
for (size_t testId=0; testId<testCount; ++testId) {
68+
69+
af_array inArray = 0;
70+
af_array outArray = 0;
71+
af_array goldArray= 0;
72+
dim_type nElems = 0;
73+
74+
inFiles[testId].insert(0,string(TEST_DIR"/bilateral/"));
75+
outFiles[testId].insert(0,string(TEST_DIR"/bilateral/"));
76+
77+
ASSERT_EQ(AF_SUCCESS, af_load_image(&inArray, inFiles[testId].c_str(), isColor));
78+
ASSERT_EQ(AF_SUCCESS, af_load_image(&goldArray, outFiles[testId].c_str(), isColor));
79+
ASSERT_EQ(AF_SUCCESS, af_get_elements(&nElems, goldArray));
80+
81+
ASSERT_EQ(AF_SUCCESS, af_bilateral(&outArray, inArray, 2.25f, 25.56f, isColor));
82+
83+
T * outData = new T[nElems];
84+
ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray));
85+
86+
T * goldData= new T[nElems];
87+
ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)goldData, goldArray));
88+
89+
ASSERT_EQ(true, compareArraysRMSD(nElems, goldData, outData, 0.02f));
90+
91+
ASSERT_EQ(AF_SUCCESS, af_destroy_array(inArray));
92+
ASSERT_EQ(AF_SUCCESS, af_destroy_array(outArray));
93+
ASSERT_EQ(AF_SUCCESS, af_destroy_array(goldArray));
94+
}
95+
}
96+
97+
TYPED_TEST(Bilateral, Grayscale)
98+
{
99+
bilateralTest<TypeParam, false>(string(TEST_DIR"/bilateral/gray.test"));
100+
}
101+
102+
TYPED_TEST(Bilateral, Color)
103+
{
104+
bilateralTest<TypeParam, true>(string(TEST_DIR"/bilateral/color.test"));
105+
}

0 commit comments

Comments
 (0)