[arrayfire] 161/408: SUSAN Corner Detector

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Mon Sep 21 19:11:46 UTC 2015


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to branch debian/sid
in repository arrayfire.

commit de9ba747ba17ef4ade0e306e8561259ce5336c2f
Author: pradeep <pradeep at arrayfire.com>
Date:   Tue Jul 21 16:52:36 2015 -0400

    SUSAN Corner Detector
    
    * API documentation
    * CPU backend implementation
    * Example for SUSAN detector
    * Basic unit tests for SUSAN detector
    * Test data commit # update to use SUSAN test data
---
 docs/details/vision.dox            |  31 +++++++
 examples/computer_vision/susan.cpp |  89 +++++++++++++++++++
 include/af/vision.h                |  44 ++++++++++
 src/api/c/susan.cpp                |  70 +++++++++++++++
 src/api/cpp/susan.cpp              |  25 ++++++
 src/backend/cpu/susan.cpp          | 171 +++++++++++++++++++++++++++++++++++++
 src/backend/cpu/susan.hpp          |  24 ++++++
 src/backend/cuda/susan.cu          |  40 +++++++++
 src/backend/cuda/susan.hpp         |  24 ++++++
 src/backend/opencl/susan.cpp       |  40 +++++++++
 src/backend/opencl/susan.hpp       |  24 ++++++
 test/data                          |   2 +-
 test/susan.cpp                     | 125 +++++++++++++++++++++++++++
 13 files changed, 708 insertions(+), 1 deletion(-)

diff --git a/docs/details/vision.dox b/docs/details/vision.dox
index 68f13cf..d381068 100644
--- a/docs/details/vision.dox
+++ b/docs/details/vision.dox
@@ -30,6 +30,37 @@ local maximas and have a high positive response.
 
 =======================================================================
 
+\defgroup cv_func_susan susan
+\ingroup featdetect_mat
+
+\brief SUSAN corner detector
+
+SUSAN is an acronym standing for *Smallest Univalue Segment Assimilating Nucleus*. This method
+places a circular disc over the pixel to be tested (a.k.a nucleus) to compute the corner measure
+of that corresponding pixel. The region covered by the circular disc is **M**, and a pixel in this
+region is represented by \f$\vec{m} \in M\f$ where \f$\vec{m}_0\f$ is the nucleus. Every pixel in the region
+is compared to the nucleus using the following comparison function:
+
+\f$ c(\vec{m}) = e^{-{(({I(\vec{m}) - I(\vec{m}_0))} / t})^6}\f$
+
+where *t* is radius of the region, *I* is the brightness of the pixel.
+
+Response of SUSAN operator is given by the following equation:
+
+\f$ R(M) = \begin{cases} g - n(M) \quad \text{if } n(M) < g\\ 0 \quad \text{otherwise},\\ \end{cases}\f$
+
+where \f$ n(M) =  \sum\nolimits_{\vec{m} \in M} c(\vec{m})\f$, g is named the *geometric threshold* and n is the number
+of pixels in the mask which are within **t** of the nucleus.
+
+Importance of the parameters, **t** and **g** is explained below:
+
+- *t* determines how similar points have to be to the nucleusbefore they are considered to
+  be a part of the univalue segment
+- g determines the minimum size of the univalue segment. For a large enough *g*, SUSAN operator becomes
+  an edge dectector.
+
+=======================================================================
+
 \defgroup cv_func_orb orb
 \ingroup featdescriptor_mat
 
diff --git a/examples/computer_vision/susan.cpp b/examples/computer_vision/susan.cpp
new file mode 100644
index 0000000..3cafe71
--- /dev/null
+++ b/examples/computer_vision/susan.cpp
@@ -0,0 +1,89 @@
+/*******************************************************
+ * Copyright (c) 2015, ArrayFire
+ * All rights reserved.
+ *
+ * This file is distributed under 3-clause BSD license.
+ * The complete license agreement can be obtained at:
+ * http://arrayfire.com/licenses/BSD-3-Clause
+ ********************************************************/
+
+#include <cstdio>
+#include <arrayfire.h>
+#include <cstdlib>
+
+using namespace af;
+
+static void susan_demo(bool console)
+{
+    // Load image
+    array img_color;
+    if (console)
+        img_color = loadImage(ASSETS_DIR "/examples/images/square.png", true);
+    else
+        img_color = loadImage(ASSETS_DIR "/examples/images/man.jpg", true);
+    // Convert the image from RGB to gray-scale
+    array img = colorSpace(img_color, AF_GRAY, AF_RGB);
+    // For visualization in ArrayFire, color images must be in the [0.0f-1.0f] interval
+    img_color /= 255.f;
+
+    features feat = susan(img, 3, 32.0f, 10, 0.05f, 3);
+
+    if (feat.getNumFeatures() > 0) {
+        printf("Found features, proceeding ahead\n");
+    } else {
+        printf("No features found, exiting\n");
+        return;
+    }
+
+    float* h_x = feat.getX().host<float>();
+    float* h_y = feat.getY().host<float>();
+
+    // Draw draw_len x draw_len crosshairs where the corners are
+    const int draw_len = 3;
+    for (size_t f = 0; f < feat.getNumFeatures(); f++) {
+        int x = h_x[f];
+        int y = h_y[f];
+        img_color(y, seq(x-draw_len, x+draw_len), 0) = 0.f;
+        img_color(y, seq(x-draw_len, x+draw_len), 1) = 1.f;
+        img_color(y, seq(x-draw_len, x+draw_len), 2) = 0.f;
+
+        // Draw vertical line of (draw_len * 2 + 1) pixels centered on  the corner
+        // Set only the first channel to 1 (green lines)
+        img_color(seq(y-draw_len, y+draw_len), x, 0) = 0.f;
+        img_color(seq(y-draw_len, y+draw_len), x, 1) = 1.f;
+        img_color(seq(y-draw_len, y+draw_len), x, 2) = 0.f;
+    }
+
+    printf("Features found: %lu\n", feat.getNumFeatures());
+
+    if (!console) {
+        af::Window wnd("FAST Feature Detector");
+
+        // Previews color image with green crosshairs
+        while(!wnd.close())
+            wnd.image(img_color);
+    } else {
+        af_print(feat.getX());
+        af_print(feat.getY());
+        af_print(feat.getScore());
+    }
+}
+
+int main(int argc, char** argv)
+{
+    int device = argc > 1 ? atoi(argv[1]) : 0;
+    bool console = argc > 2 ? argv[2][0] == '-' : false;
+
+    try {
+        af::setDevice(device);
+        af::info();
+        std::cout << "** ArrayFire FAST Feature Detector Demo **" << std::endl << std::endl;
+        susan_demo(console);
+
+    } catch (af::exception& ae) {
+        std::cerr << ae.what() << std::endl;
+        throw;
+    }
+
+    return 0;
+}
diff --git a/include/af/vision.h b/include/af/vision.h
index 5c2e0aa..de2b045 100644
--- a/include/af/vision.h
+++ b/include/af/vision.h
@@ -166,6 +166,29 @@ AFAPI void nearestNeighbour(array& idx, array& dist,
  */
 AFAPI array matchTemplate(const array &searchImg, const array &templateImg, const matchType mType=AF_SAD);
 
+
+/**
+   C++ Interface for SUSAN corner detector
+
+   \param[in]  in is input grayscale/intensity image
+   \param[in]  radius Nuclei radius for each pixel neighborhood
+   \param[in]  diff_thr intensity difference threshold
+   \param[in]  geom_thr geometric threshold a.k.a **t** from equations in description
+   \param[in]  feature_ratio is maximum number of features that will be returned by the function
+   \param[in]  edge indicates how many pixels width area should be skipped for corner detection
+   \return If SUSAN corner detection is successfull returns an object of Features class, composed of arrays for x and y
+               coordinates, score, orientation and size of selected features, otherwise exception is thrown.
+
+   \note If \p in is a 3d array, a batch operation will be performed.
+
+   \ingroup cv_func_susan
+*/
+AFAPI features susan(const array& in,
+                     const unsigned radius=3,
+                     const float diff_thr=32.0f,
+                     const float geom_thr=10.0f,
+                     const float feature_ratio=0.05f,
+                     const unsigned edge=3);
 }
 #endif
 
@@ -325,6 +348,27 @@ extern "C" {
     */
     AFAPI af_err af_match_template(af_array *out, const af_array search_img, const af_array template_img, const af_match_type m_type);
 
+    /**
+       C Interface for SUSAN corner detector
+
+       \param[out] out is af_features struct composed of arrays for x and y
+                   coordinates, score, orientation and size of selected features
+       \param[in]  in is input grayscale/intensity image
+       \param[in]  radius Nuclei radius for each pixel neighborhood
+       \param[in]  diff_thr intensity difference threshold a.k.a **t** from equations in description
+       \param[in]  geom_thr geometric threshold
+       \param[in]  feature_ratio is maximum number of features that will be returned by the function
+       \param[in]  edge indicates how many pixels width area should be skipped for corner detection
+       \return \ref AF_SUCCESS if SUSAN corner detection is successfull, otherwise an appropriate
+       error code is returned.
+
+       \note If \p in is a 3d array, a batch operation will be performed.
+
+       \ingroup cv_func_susan
+    */
+    AFAPI af_err af_susan(af_features* out, const af_array in, const unsigned radius, const float diff_thr, const float geom_thr,
+                          const float feature_ratio, const unsigned edge);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/api/c/susan.cpp b/src/api/c/susan.cpp
new file mode 100644
index 0000000..2b042ea
--- /dev/null
+++ b/src/api/c/susan.cpp
@@ -0,0 +1,70 @@
+/*******************************************************
+ * Copyright (c) 2015, ArrayFire
+ * All rights reserved.
+ *
+ * This file is distributed under 3-clause BSD license.
+ * The complete license agreement can be obtained at:
+ * http://arrayfire.com/licenses/BSD-3-Clause
+ ********************************************************/
+
+#include <af/dim4.hpp>
+#include <af/defines.h>
+#include <af/features.h>
+#include <af/vision.h>
+#include <handle.hpp>
+#include <err_common.hpp>
+#include <backend.hpp>
+#include <features.hpp>
+#include <susan.hpp>
+
+using af::dim4;
+using namespace detail;
+
+template<typename T>
+static af_features susan(af_array const &in,
+                         const unsigned radius, const float diff_thr, const float geom_thr,
+                         const float feature_ratio, const unsigned edge)
+{
+    Array<float> x = createEmptyArray<float>(dim4());
+    Array<float> y = createEmptyArray<float>(dim4());
+    Array<float> score = createEmptyArray<float>(dim4());
+
+    af_features_t feat;
+    feat.n = susan<T>(x, y, score,
+                      getArray<T>(in), radius, diff_thr, geom_thr,
+                      feature_ratio, edge);
+
+    Array<float> orientation = createValueArray<float>(feat.n, 0.0);
+    Array<float> size = createValueArray<float>(feat.n, 1.0);
+
+    feat.x           = getHandle(x);
+    feat.y           = getHandle(y);
+    feat.score       = getHandle(score);
+    feat.orientation = getHandle(orientation);
+    feat.size        = getHandle(size);
+
+    return getFeaturesHandle(feat);
+}
+
+af_err af_susan(af_features* out, const af_array in,
+                const unsigned radius, const float diff_thr, const float geom_thr,
+                const float feature_ratio, const unsigned edge)
+{
+    try {
+        ArrayInfo info = getInfo(in);
+        af::dim4 dims  = info.dims();
+        af_dtype type  = info.getType();
+        switch(type) {
+            case f32: *out = susan<float >(in, radius, diff_thr, geom_thr, feature_ratio, edge); break;
+            case f64: *out = susan<double>(in, radius, diff_thr, geom_thr, feature_ratio, edge); break;
+            case b8 : *out = susan<char  >(in, radius, diff_thr, geom_thr, feature_ratio, edge); break;
+            case s32: *out = susan<int   >(in, radius, diff_thr, geom_thr, feature_ratio, edge); break;
+            case u32: *out = susan<uint  >(in, radius, diff_thr, geom_thr, feature_ratio, edge); break;
+            case u8 : *out = susan<uchar >(in, radius, diff_thr, geom_thr, feature_ratio, edge); break;
+            default : TYPE_ERROR(1, type);
+        }
+    }
+    CATCHALL;
+
+    return AF_SUCCESS;
+}
diff --git a/src/api/cpp/susan.cpp b/src/api/cpp/susan.cpp
new file mode 100644
index 0000000..1711b7b
--- /dev/null
+++ b/src/api/cpp/susan.cpp
@@ -0,0 +1,25 @@
+/*******************************************************
+ * Copyright (c) 2015, ArrayFire
+ * All rights reserved.
+ *
+ * This file is distributed under 3-clause BSD license.
+ * The complete license agreement can be obtained at:
+ * http://arrayfire.com/licenses/BSD-3-Clause
+ ********************************************************/
+
+#include <af/vision.h>
+#include <af/array.h>
+#include "error.hpp"
+
+namespace af
+{
+
+features susan(const array& in, const unsigned radius, const float diff_thr, const float geom_thr,
+               const float feature_ratio, const unsigned edge)
+{
+    af_features temp;
+    AF_THROW(af_susan(&temp, in.get(), radius, diff_thr, geom_thr, feature_ratio, edge));
+    return features(temp);
+}
+
+}
diff --git a/src/backend/cpu/susan.cpp b/src/backend/cpu/susan.cpp
new file mode 100644
index 0000000..c2d504d
--- /dev/null
+++ b/src/backend/cpu/susan.cpp
@@ -0,0 +1,171 @@
+/*******************************************************
+ * Copyright (c) 2015, Arrayfire
+ * all rights reserved.
+ *
+ * This file is distributed under 3-clause bsd license.
+ * the complete license agreement can be obtained at:
+ * http://Arrayfire.com/licenses/bsd-3-clause
+ ********************************************************/
+
+#include <af/features.h>
+#include <Array.hpp>
+#include <cmath>
+#include <sort_index.hpp>
+#include <math.hpp>
+
+using af::features;
+
+namespace cpu
+{
+
+template<typename T>
+void susan_responses(T* resp_out, const T* in,
+                     const unsigned idim0, const unsigned idim1,
+                     const int radius, const float t, const float g,
+                     const unsigned border_len)
+{
+    const unsigned r = border_len;
+    const int rSqrd = radius*radius;
+
+    for (unsigned x = r; x < idim1 - r; ++x) {
+        for (unsigned y = r; y < idim0 - r; ++y) {
+            const unsigned idx = x * idim0 + y;
+            T m_0 = in[idx];
+            float nM = 0.0f;
+
+            for (int i=-radius; i<=radius; ++i) {
+                for (int j=-radius; j<=radius; ++j) {
+                    if (i*i + j*j < rSqrd) {
+                        int p = x + i;
+                        int q = y + j;
+                        T m = in[p * idim0 + q];
+                        float exp_pow = std::pow((m - m_0)/t, 6.0);
+                        float cM = std::exp(-exp_pow);
+                        nM += cM;
+                    }
+                }
+            }
+
+            resp_out[idx] = nM < g ? g - nM : T(0);
+        }
+    }
+}
+
+template<typename T>
+void non_maximal(float* x_out, float* y_out, float* resp_out,
+                 unsigned* count, const unsigned idim0, const unsigned idim1,
+                 const T* resp_in, const unsigned border_len, const unsigned max_corners)
+{
+    // Responses on the border don't have 8-neighbors to compare, discard them
+    const unsigned r = border_len + 1;
+
+    for (unsigned x = r; x < idim1 - r; x++) {
+        for (unsigned y = r; y < idim0 - r; y++) {
+            const T v = resp_in[x * idim0 + y];
+
+            // Find maximum neighborhood response
+            T max_v;
+            max_v = max(resp_in[(x-1) * idim0 + y-1], resp_in[x * idim0 + y-1]);
+            max_v = max(max_v, resp_in[(x+1) * idim0 + y-1]);
+            max_v = max(max_v, resp_in[(x-1) * idim0 + y  ]);
+            max_v = max(max_v, resp_in[(x+1) * idim0 + y  ]);
+            max_v = max(max_v, resp_in[(x-1) * idim0 + y+1]);
+            max_v = max(max_v, resp_in[(x)   * idim0 + y+1]);
+            max_v = max(max_v, resp_in[(x+1) * idim0 + y+1]);
+
+            // Stores corner to {x,y,resp}_out if it's response is maximum compared
+            // to its 8-neighborhood and greater or equal minimum response
+            if (v > max_v) {
+                const unsigned idx = *count;
+                *count += 1;
+                if (idx < max_corners) {
+                    x_out[idx]    = (float)x;
+                    y_out[idx]    = (float)y;
+                    resp_out[idx] = (float)v;
+                }
+            }
+        }
+    }
+}
+
+static void keep_corners(float* x_out, float* y_out, float* resp_out,
+                  const float* x_in, const float* y_in,
+                  const float* resp_in, const unsigned* resp_idx,
+                  const unsigned n_corners)
+{
+    // Keep only the first n_feat features
+    for (unsigned f = 0; f < n_corners; f++) {
+        x_out[f] = x_in[resp_idx[f]];
+        y_out[f] = y_in[resp_idx[f]];
+        resp_out[f] = resp_in[f];
+    }
+}
+
+template<typename T>
+unsigned susan(Array<float> &x_out, Array<float> &y_out, Array<float> &resp_out,
+               const Array<T> &in,
+               const unsigned radius, const float diff_thr, const float geom_thr,
+               const float feature_ratio, const unsigned edge)
+{
+    dim4 idims = in.dims();
+
+    const unsigned corner_lim = in.elements() * feature_ratio;
+    float* x_corners          = memAlloc<float>(corner_lim);
+    float* y_corners          = memAlloc<float>(corner_lim);
+    float* resp_corners       = memAlloc<float>(corner_lim);
+
+    T* resp = memAlloc<T>(in.elements());
+    unsigned corners_found = 0;
+
+    susan_responses<T>(resp, in.get(), idims[0], idims[1], radius, diff_thr, geom_thr, edge);
+
+    non_maximal<T>(x_corners, y_corners, resp_corners, &corners_found,
+                   idims[0], idims[1], resp, edge, corner_lim);
+
+    memFree(resp);
+
+    const unsigned corners_out = min(corners_found, corner_lim);
+    if (corners_out == 0)
+        return 0;
+
+    if (corners_found > corners_out) {
+        Array<float> susan_responses = createDeviceDataArray<float>(dim4(corners_found), (void*)resp_corners);
+        Array<float> susan_sorted = createEmptyArray<float>(dim4(corners_found));
+        Array<unsigned> susan_idx = createEmptyArray<unsigned>(dim4(corners_found));
+
+        // Sort susan responses
+        sort_index<float, false>(susan_sorted, susan_idx, susan_responses, 0);
+
+        x_out = createEmptyArray<float>(dim4(corners_out));
+        y_out = createEmptyArray<float>(dim4(corners_out));
+        resp_out = createEmptyArray<float>(dim4(corners_out));
+
+        // Keep only the corners with higher SUSAN responses
+        keep_corners(x_out.get(), y_out.get(), resp_out.get(),
+                     x_corners, y_corners, susan_sorted.get(), susan_idx.get(),
+                     corners_out);
+
+        memFree(x_corners);
+        memFree(y_corners);
+    } else {
+        x_out = createDeviceDataArray<float>(dim4(corners_out), (void*)x_corners);
+        y_out = createDeviceDataArray<float>(dim4(corners_out), (void*)y_corners);
+        resp_out = createDeviceDataArray<float>(dim4(corners_out), (void*)resp_corners);
+    }
+
+    return corners_out;
+}
+
+#define INSTANTIATE(T) \
+template unsigned susan<T>(Array<float> &x_out, Array<float> &y_out, Array<float> &score_out,   \
+                           const Array<T> &in, const unsigned radius, const float diff_thr,     \
+                           const float geom_thr, const float feature_ratio, const unsigned edge);
+
+INSTANTIATE(float )
+INSTANTIATE(double)
+INSTANTIATE(char  )
+INSTANTIATE(int   )
+INSTANTIATE(uint  )
+INSTANTIATE(uchar )
+
+}
diff --git a/src/backend/cpu/susan.hpp b/src/backend/cpu/susan.hpp
new file mode 100644
index 0000000..2e57711
--- /dev/null
+++ b/src/backend/cpu/susan.hpp
@@ -0,0 +1,24 @@
+/*******************************************************
+ * Copyright (c) 2015, Arrayfire
+ * all rights reserved.
+ *
+ * This file is distributed under 3-clause bsd license.
+ * the complete license agreement can be obtained at:
+ * http://Arrayfire.com/licenses/bsd-3-clause
+ ********************************************************/
+
+#include <af/features.h>
+#include <Array.hpp>
+
+using af::features;
+
+namespace cpu
+{
+
+template<typename T>
+unsigned susan(Array<float> &x_out, Array<float> &y_out, Array<float> &score_out,
+               const Array<T> &in,
+               const unsigned radius, const float diff_thr, const float geom_thr,
+               const float feature_ratio, const unsigned edge);
+
+}
diff --git a/src/backend/cuda/susan.cu b/src/backend/cuda/susan.cu
new file mode 100644
index 0000000..81d126a
--- /dev/null
+++ b/src/backend/cuda/susan.cu
@@ -0,0 +1,40 @@
+/*******************************************************
+ * Copyright (c) 2015, Arrayfire
+ * all rights reserved.
+ *
+ * This file is distributed under 3-clause bsd license.
+ * the complete license agreement can be obtained at:
+ * http://Arrayfire.com/licenses/bsd-3-clause
+ ********************************************************/
+
+#include <af/features.h>
+#include <Array.hpp>
+#include <err_cuda.hpp>
+
+using af::features;
+
+namespace cuda
+{
+
+template<typename T>
+unsigned susan(Array<float> &x_out, Array<float> &y_out, Array<float> &score_out,
+               const Array<T> &in,
+               const unsigned radius, const float diff_thr, const float geom_thr,
+               const float feature_ratio, const unsigned edge)
+{
+    CUDA_NOT_SUPPORTED();
+}
+
+#define INSTANTIATE(T) \
+template unsigned susan<T>(Array<float> &x_out, Array<float> &y_out, Array<float> &score_out,   \
+                           const Array<T> &in, const unsigned radius, const float diff_thr,     \
+                           const float geom_thr, const float feature_ratio, const unsigned edge);
+
+INSTANTIATE(float )
+INSTANTIATE(double)
+INSTANTIATE(char  )
+INSTANTIATE(int   )
+INSTANTIATE(uint  )
+INSTANTIATE(uchar )
+
+}
diff --git a/src/backend/cuda/susan.hpp b/src/backend/cuda/susan.hpp
new file mode 100644
index 0000000..86977f8
--- /dev/null
+++ b/src/backend/cuda/susan.hpp
@@ -0,0 +1,24 @@
+/*******************************************************
+ * Copyright (c) 2015, Arrayfire
+ * all rights reserved.
+ *
+ * This file is distributed under 3-clause bsd license.
+ * the complete license agreement can be obtained at:
+ * http://Arrayfire.com/licenses/bsd-3-clause
+ ********************************************************/
+
+#include <af/features.h>
+#include <Array.hpp>
+
+using af::features;
+
+namespace cuda
+{
+
+template<typename T>
+unsigned susan(Array<float> &x_out, Array<float> &y_out, Array<float> &score_out,
+               const Array<T> &in,
+               const unsigned radius, const float diff_thr, const float geom_thr,
+               const float feature_ratio, const unsigned edge);
+
+}
diff --git a/src/backend/opencl/susan.cpp b/src/backend/opencl/susan.cpp
new file mode 100644
index 0000000..1c3db1c
--- /dev/null
+++ b/src/backend/opencl/susan.cpp
@@ -0,0 +1,40 @@
+/*******************************************************
+ * Copyright (c) 2015, Arrayfire
+ * all rights reserved.
+ *
+ * This file is distributed under 3-clause bsd license.
+ * the complete license agreement can be obtained at:
+ * http://Arrayfire.com/licenses/bsd-3-clause
+ ********************************************************/
+
+#include <af/features.h>
+#include <Array.hpp>
+#include <err_opencl.hpp>
+
+using af::features;
+
+namespace opencl
+{
+
+template<typename T>
+unsigned susan(Array<float> &x_out, Array<float> &y_out, Array<float> &score_out,
+               const Array<T> &in,
+               const unsigned radius, const float diff_thr, const float geom_thr,
+               const float feature_ratio, const unsigned edge)
+{
+    OPENCL_NOT_SUPPORTED();
+}
+
+#define INSTANTIATE(T) \
+template unsigned susan<T>(Array<float> &x_out, Array<float> &y_out, Array<float> &score_out,   \
+                           const Array<T> &in, const unsigned radius, const float diff_thr,     \
+                           const float geom_thr, const float feature_ratio, const unsigned edge);
+
+INSTANTIATE(float )
+INSTANTIATE(double)
+INSTANTIATE(char  )
+INSTANTIATE(int   )
+INSTANTIATE(uint  )
+INSTANTIATE(uchar )
+
+}
diff --git a/src/backend/opencl/susan.hpp b/src/backend/opencl/susan.hpp
new file mode 100644
index 0000000..3fac35c
--- /dev/null
+++ b/src/backend/opencl/susan.hpp
@@ -0,0 +1,24 @@
+/*******************************************************
+ * Copyright (c) 2015, Arrayfire
+ * all rights reserved.
+ *
+ * This file is distributed under 3-clause bsd license.
+ * the complete license agreement can be obtained at:
+ * http://Arrayfire.com/licenses/bsd-3-clause
+ ********************************************************/
+
+#include <af/features.h>
+#include <Array.hpp>
+
+using af::features;
+
+namespace opencl
+{
+
+template<typename T>
+unsigned susan(Array<float> &x_out, Array<float> &y_out, Array<float> &score_out,
+               const Array<T> &in,
+               const unsigned radius, const float diff_thr, const float geom_thr,
+               const float feature_ratio, const unsigned edge);
+
+}
diff --git a/test/data b/test/data
index eb003ae..35de6d7 160000
--- a/test/data
+++ b/test/data
@@ -1 +1 @@
-Subproject commit eb003aedbd4467883a596c482f9c4b5d366c7db7
+Subproject commit 35de6d78b7e46f6e3441dcceb81e96a968e940d3
diff --git a/test/susan.cpp b/test/susan.cpp
new file mode 100644
index 0000000..1c558f7
--- /dev/null
+++ b/test/susan.cpp
@@ -0,0 +1,125 @@
+/*******************************************************
+ * Copyright (c) 2015, ArrayFire
+ * All rights reserved.
+ *
+ * This file is distributed under 3-clause BSD license.
+ * The complete license agreement can be obtained at:
+ * http://arrayfire.com/licenses/BSD-3-Clause
+ ********************************************************/
+
+#include <gtest/gtest.h>
+#include <arrayfire.h>
+#include <af/dim4.hpp>
+#include <af/traits.hpp>
+#include <af/compatible.h>
+#include <string>
+#include <vector>
+#include <cmath>
+#include <testHelpers.hpp>
+#include <typeinfo>
+
+using std::string;
+using std::vector;
+using af::dim4;
+
+typedef struct
+{
+    float f[5];
+} feat_t;
+
+bool feat_cmp(feat_t i, feat_t j)
+{
+    for (int k = 0; k < 5; k++)
+        if (i.f[k] != j.f[k])
+            return (i.f[k] < j.f[k]);
+
+    return false;
+}
+
+void array_to_feat(vector<feat_t>& feat, float *x, float *y, float *score, float *orientation, float *size, unsigned nfeat)
+{
+    feat.resize(nfeat);
+    for (unsigned i = 0; i < feat.size(); i++) {
+        feat[i].f[0] = x[i];
+        feat[i].f[1] = y[i];
+        feat[i].f[2] = score[i];
+        feat[i].f[3] = orientation[i];
+        feat[i].f[4] = size[i];
+    }
+}
+
+template<typename T>
+class Susan : public ::testing::Test
+{
+    public:
+        virtual void SetUp() {}
+};
+
+typedef ::testing::Types<float, double, int, uint, char, uchar> TestTypes;
+
+TYPED_TEST_CASE(Susan, TestTypes);
+
+template<typename T>
+void susanTest(string pTestFile, float t, float g)
+{
+    if (noDoubleTests<T>()) return;
+
+    vector<dim4>        inDims;
+    vector<string>     inFiles;
+    vector<vector<float> > gold;
+
+    readImageTests(pTestFile, inDims, inFiles, gold);
+
+    size_t testCount = inDims.size();
+
+    for (size_t testId=0; testId<testCount; ++testId) {
+        inFiles[testId].insert(0, string(TEST_DIR "/susan/"));
+
+        af::array in = af::loadImage(inFiles[testId].c_str(), false);
+
+        af::features out = af::susan(in, 3, 32, 10, 0.05f, 3);
+
+        float * outX           = new float[gold[0].size()];
+        float * outY           = new float[gold[1].size()];
+        float * outScore       = new float[gold[2].size()];
+        float * outOrientation = new float[gold[3].size()];
+        float * outSize        = new float[gold[4].size()];
+        out.getX().host(outX);
+        out.getY().host(outY);
+        out.getScore().host(outScore);
+        out.getOrientation().host(outOrientation);
+        out.getSize().host(outSize);
+
+        vector<feat_t> out_feat;
+        array_to_feat(out_feat, outX, outY, outScore, outOrientation, outSize, out.getNumFeatures());
+
+        vector<feat_t> gold_feat;
+        array_to_feat(gold_feat, &gold[0].front(), &gold[1].front(), &gold[2].front(), &gold[3].front(), &gold[4].front(), gold[0].size());
+
+        std::sort(out_feat.begin(), out_feat.end(), feat_cmp);
+        std::sort(gold_feat.begin(), gold_feat.end(), feat_cmp);
+
+        for (int elIter = 0; elIter < (int)out.getNumFeatures(); elIter++) {
+            ASSERT_EQ(out_feat[elIter].f[0], gold_feat[elIter].f[0]) << "at: " << elIter << std::endl;
+            ASSERT_EQ(out_feat[elIter].f[1], gold_feat[elIter].f[1]) << "at: " << elIter << std::endl;
+            ASSERT_LE(fabs(out_feat[elIter].f[2] - gold_feat[elIter].f[2]), 1e2) << "at: " << elIter << std::endl;
+            ASSERT_EQ(out_feat[elIter].f[3], gold_feat[elIter].f[3]) << "at: " << elIter << std::endl;
+            ASSERT_EQ(out_feat[elIter].f[4], gold_feat[elIter].f[4]) << "at: " << elIter << std::endl;
+        }
+
+        delete [] outX;
+        delete [] outY;
+        delete [] outScore;
+        delete [] outOrientation;
+        delete [] outSize;
+    }
+}
+
+#define SUSAN_TEST(image, tval, gval) \
+    TYPED_TEST(Susan, image) \
+    {   \
+        susanTest<TypeParam>(string(TEST_DIR "/susan/"#image".test"), tval, gval);\
+    }
+
+SUSAN_TEST(square_t32_g10, 32, 10);
+SUSAN_TEST(square_t32_g20, 32, 20);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/arrayfire.git



More information about the debian-science-commits mailing list