Skip to content

Commit 9af851f

Browse files
author
Pradeep
committed
af_histogram cpu backend implementation
Placeholders for cuda and opencl backend are also implemented.
1 parent b35f5bb commit 9af851f

10 files changed

Lines changed: 342 additions & 0 deletions

File tree

include/af/array.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ extern "C" {
4141
// Compute second order difference along a given dimension.
4242
AFAPI af_err af_diff2(af_array *out, const af_array in, const int dim);
4343

44+
// histogram: return af_array will have elements of type u32
45+
AFAPI af_err af_histogram(af_array *out, const af_array in, const unsigned nbins, const double minval, const double maxval);
46+
4447
// re-shape the the dimensions of the input array
4548
AFAPI af_err af_moddims(af_array *out, const af_array in, const unsigned ndims, const dim_type * const dims);
4649

src/backend/cpu/histogram.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <af/dim4.hpp>
2+
#include <af/defines.h>
3+
#include <ArrayInfo.hpp>
4+
#include <Array.hpp>
5+
#include <histogram.hpp>
6+
7+
using af::dim4;
8+
9+
namespace cpu
10+
{
11+
12+
template<typename inType, typename outType>
13+
Array<outType> * histogram(const Array<inType> &in, const unsigned &nbins, const double &minval, const double &maxval)
14+
{
15+
const dim4 inDims = in.dims();
16+
dim4 outDims = dim4(nbins,1,inDims[2],inDims[3]);
17+
18+
// create an array with first two dimensions swapped
19+
Array<outType>* out = createEmptyArray<outType>(outDims);
20+
21+
// get data pointers for input and output Arrays
22+
outType *outData = out->get();
23+
const inType* inData= in.get();
24+
25+
dim_type batchCount = inDims[2];
26+
dim_type batchStride= in.strides()[2];
27+
dim_type numElements= inDims[0]*inDims[1];
28+
29+
// set all bin elements to zero
30+
outType *temp = outData;
31+
for(dim_type batchId=0; batchId<batchCount; batchId++) {
32+
for(dim_type i=0;i<nbins; i++)
33+
temp[i] = 0;
34+
temp += nbins;
35+
}
36+
37+
float step = (maxval - minval)/(float)nbins;
38+
39+
for(dim_type batchId=0; batchId<batchCount; batchId++) {
40+
for(dim_type i=0; i<numElements; i++) {
41+
int bin = (int)((inData[i] - minval) / step);
42+
bin = (bin < 0) ? 0 : bin;
43+
bin = (bin >= nbins) ? (nbins-1) : bin;
44+
outData[bin]++;
45+
}
46+
inData += batchStride;
47+
outData += nbins;
48+
}
49+
50+
return out;
51+
}
52+
53+
#define INSTANTIATE(in_t,out_t)\
54+
template Array<out_t> * histogram(const Array<in_t> &in, const unsigned &nbins, const double &minval, const double &maxval);
55+
56+
INSTANTIATE(float,uint)
57+
INSTANTIATE(double,uint)
58+
INSTANTIATE(char,uint)
59+
INSTANTIATE(int,uint)
60+
INSTANTIATE(uint,uint)
61+
INSTANTIATE(uchar,uint)
62+
63+
}

src/backend/cpu/histogram.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 inType, typename outType>
7+
Array<outType> * histogram(const Array<inType> &in, const unsigned &nbins, const double &minval, const double &maxval);
8+
9+
}

src/backend/cuda/histogram.cu

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <af/dim4.hpp>
2+
#include <af/defines.h>
3+
#include <ArrayInfo.hpp>
4+
#include <Array.hpp>
5+
#include <histogram.hpp>
6+
#include <cassert>
7+
8+
using af::dim4;
9+
10+
namespace cuda
11+
{
12+
13+
template<typename inType, typename outType>
14+
Array<outType> * histogram(const Array<inType> &in, const unsigned &nbins, const double &minval, const double &maxval)
15+
{
16+
Array<outType>* out = 0;
17+
assert("histogram not implemented yet in cuda backend" && out!=0);
18+
return out;
19+
}
20+
21+
#define INSTANTIATE(in_t,out_t)\
22+
template Array<out_t> * histogram(const Array<in_t> &in, const unsigned &nbins, const double &minval, const double &maxval);
23+
24+
INSTANTIATE(float,uint)
25+
INSTANTIATE(double,uint)
26+
INSTANTIATE(char,uint)
27+
INSTANTIATE(int,uint)
28+
INSTANTIATE(uint,uint)
29+
INSTANTIATE(uchar,uint)
30+
31+
}

src/backend/cuda/histogram.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 inType, typename outType>
7+
Array<outType> * histogram(const Array<inType> &in, const unsigned &nbins, const double &minval, const double &maxval);
8+
9+
}

src/backend/histogram.cpp

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

src/backend/opencl/histogram.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <af/dim4.hpp>
2+
#include <af/defines.h>
3+
#include <ArrayInfo.hpp>
4+
#include <Array.hpp>
5+
#include <histogram.hpp>
6+
#include <cassert>
7+
8+
using af::dim4;
9+
10+
namespace opencl
11+
{
12+
13+
template<typename inType, typename outType>
14+
Array<outType> * histogram(const Array<inType> &in, const unsigned &nbins, const double &minval, const double &maxval)
15+
{
16+
Array<outType>* out = nullptr;
17+
assert("histogram not implemented yet in opencl backend" && out!=nullptr);
18+
return out;
19+
}
20+
21+
#define INSTANTIATE(in_t,out_t)\
22+
template Array<out_t> * histogram(const Array<in_t> &in, const unsigned &nbins, const double &minval, const double &maxval);
23+
24+
INSTANTIATE(float,uint)
25+
INSTANTIATE(double,uint)
26+
INSTANTIATE(char,uint)
27+
INSTANTIATE(int,uint)
28+
INSTANTIATE(uint,uint)
29+
INSTANTIATE(uchar,uint)
30+
31+
}

src/backend/opencl/histogram.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 inType, typename outType>
7+
Array<outType> * histogram(const Array<inType> &in, const unsigned &nbins, const double &minval, const double &maxval);
8+
9+
}

test/histogram.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
9+
using std::string;
10+
using std::vector;
11+
12+
template<typename T>
13+
class Histogram : public ::testing::Test
14+
{
15+
public:
16+
virtual void SetUp() {}
17+
};
18+
19+
// create a list of types to be tested
20+
typedef ::testing::Types<float, double, int, uint, char, uchar> TestTypes;
21+
22+
// register the type list
23+
TYPED_TEST_CASE(Histogram, TestTypes);
24+
25+
TYPED_TEST(Histogram,InvalidArgs)
26+
{
27+
af::dim4 dims(1);
28+
vector<TypeParam> in(100,1);
29+
30+
af_array inArray = 0;
31+
af_array outArray = 0;
32+
33+
// square test file is 100x100 originally
34+
// usee new dimensions for this argument
35+
// unit test
36+
af::dim4 newDims(5,5,2,2);
37+
ASSERT_EQ(AF_SUCCESS, af_create_array(&inArray, &in.front(), newDims.ndims(), newDims.get(), (af_dtype) af::dtype_traits<TypeParam>::af_type));
38+
39+
ASSERT_EQ(AF_ERR_ARG, af_histogram(&outArray,inArray,256,0,255));
40+
}
41+
42+
template<typename inType, typename outType>
43+
void histTest(string pTestFile, unsigned nbins, double minval, double maxval)
44+
{
45+
af::dim4 dims(1);
46+
47+
vector<inType> in;
48+
vector<vector<outType>> tests;
49+
ReadTests2<inType,uint,int>(pTestFile,dims,in,tests);
50+
51+
af_array outArray = 0;
52+
af_array inArray = 0;
53+
outType *outData;
54+
ASSERT_EQ(AF_SUCCESS, af_create_array(&inArray, &in.front(), dims.ndims(), dims.get(), (af_dtype) af::dtype_traits<inType>::af_type));
55+
56+
ASSERT_EQ(AF_SUCCESS,af_histogram(&outArray,inArray,nbins,minval,maxval));
57+
58+
outData = new outType[dims.elements()];
59+
60+
ASSERT_EQ(AF_SUCCESS, af_get_data_ptr((void*)outData, outArray));
61+
62+
for (size_t testIter=0; testIter<tests.size(); ++testIter) {
63+
vector<outType> currGoldBar = tests[testIter];
64+
size_t nElems = currGoldBar.size();
65+
for (size_t elIter=0; elIter<nElems; ++elIter) {
66+
ASSERT_EQ(currGoldBar[elIter],outData[elIter])<< "at: " << elIter<< std::endl;
67+
}
68+
}
69+
70+
// cleanup
71+
delete[] outData;
72+
ASSERT_EQ(AF_SUCCESS, af_destroy_array(inArray));
73+
ASSERT_EQ(AF_SUCCESS, af_destroy_array(outArray));
74+
}
75+
76+
TYPED_TEST(Histogram,256Bins0min255max_ones)
77+
{
78+
histTest<TypeParam,uint>(string(TEST_DIR"/histogram/256bin1min1max.test"),256,0,255);
79+
}
80+
81+
TYPED_TEST(Histogram,100Bins0min99max)
82+
{
83+
histTest<TypeParam,uint>(string(TEST_DIR"/histogram/100bin0min99max.test"),100,0,99);
84+
}
85+
86+
TYPED_TEST(Histogram,40Bins0min100max)
87+
{
88+
histTest<TypeParam,uint>(string(TEST_DIR"/histogram/40bin0min100max.test"),40,0,100);
89+
}
90+
91+
TYPED_TEST(Histogram,40Bins0min100max_Batch)
92+
{
93+
histTest<TypeParam,uint>(string(TEST_DIR"/histogram/40bin0min100max_batch.test"),40,0,100);
94+
}
95+
96+
TYPED_TEST(Histogram,256Bins0min255max_zeros)
97+
{
98+
histTest<TypeParam,uint>(string(TEST_DIR"/histogram/256bin0min0max.test"),256,0,255);
99+
}

test/testHelpers.hpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,41 @@ void ReadTests(const string &FileName, af::dim4 &dims,
5050
FAIL() << "TEST FILE NOT FOUND";
5151
}
5252
}
53+
54+
template<typename inType, typename outType, typename FileElementType>
55+
void ReadTests2(const string &FileName, af::dim4 &dims,
56+
vector<inType> &testInput,
57+
vector<vector<outType>> &testOutputs)
58+
{
59+
std::ifstream testFile(FileName);
60+
if(testFile.good()) {
61+
testFile >> dims;
62+
vector<inType> data(dims.elements());
63+
64+
unsigned testCount;
65+
testFile >> testCount;
66+
testOutputs.resize(testCount);
67+
68+
vector<unsigned> testSizes(testCount);
69+
for(unsigned i = 0; i < testCount; i++) {
70+
testFile >> testSizes[i];
71+
}
72+
73+
copy_n( istream_iterator<FileElementType>(testFile),
74+
dims.elements(),
75+
begin(data));
76+
77+
copy( begin(data),
78+
end(data),
79+
back_inserter(testInput));
80+
81+
for(unsigned i = 0; i < testCount; i++) {
82+
copy_n( istream_iterator<outType>(testFile),
83+
testSizes[i],
84+
back_inserter(testOutputs[i]));
85+
}
86+
}
87+
else {
88+
FAIL() << "TEST FILE NOT FOUND";
89+
}
90+
}

0 commit comments

Comments
 (0)