Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Sharybin <sergey.vfx@gmail.com>2013-10-08 17:53:59 +0400
committerSergey Sharybin <sergey.vfx@gmail.com>2014-01-28 13:37:19 +0400
commit9178dc9d384f18ab2f23656fb6b01a3b444526ef (patch)
tree7ca0ded6874ce96c9ac7747dca790b0a12ae5e90 /extern/libmv
parentf194ab765996cb9830da2396f28f1ddad1778d0b (diff)
Rework detector API and implement Harris detector
Switch the detector API to a single function which accepts a float image and detector options. This makes usage of feature detection more unified across different algorithms. Options structure is pretty much straightforward and contains detector to be used and all the detector-specific settings. Also implemented Harris feature detection algorithm which is not as fast as FAST one but is expected to detect more robust feature points. It is also likely that less features are detected, but better quality than quantity. Blender will now use Harris detector by default, later we'll remove FAST detector.
Diffstat (limited to 'extern/libmv')
-rw-r--r--extern/libmv/CMakeLists.txt1
-rw-r--r--extern/libmv/ChangeLog53
-rw-r--r--extern/libmv/files.txt1
-rw-r--r--extern/libmv/libmv-capi.cc136
-rw-r--r--extern/libmv/libmv-capi.h27
-rw-r--r--extern/libmv/libmv-capi_stub.cc11
-rw-r--r--extern/libmv/libmv/image/array_nd.h17
-rw-r--r--extern/libmv/libmv/image/convolve.cc17
-rw-r--r--extern/libmv/libmv/image/image_converter.h81
-rw-r--r--extern/libmv/libmv/simple_pipeline/detect.cc272
-rw-r--r--extern/libmv/libmv/simple_pipeline/detect.h87
11 files changed, 514 insertions, 189 deletions
diff --git a/extern/libmv/CMakeLists.txt b/extern/libmv/CMakeLists.txt
index 80a212b64f8..ef7e49b4d81 100644
--- a/extern/libmv/CMakeLists.txt
+++ b/extern/libmv/CMakeLists.txt
@@ -103,6 +103,7 @@ if(WITH_LIBMV)
libmv/image/array_nd.h
libmv/image/convolve.h
libmv/image/correlation.h
+ libmv/image/image_converter.h
libmv/image/image.h
libmv/image/sample.h
libmv/image/tuple.h
diff --git a/extern/libmv/ChangeLog b/extern/libmv/ChangeLog
index c34ea066172..225138b0166 100644
--- a/extern/libmv/ChangeLog
+++ b/extern/libmv/ChangeLog
@@ -1,3 +1,30 @@
+commit b63b8d6989f460fda7d963a2c8b21e8ffa6f8066
+Author: Sergey Sharybin <sergey.vfx@gmail.com>
+Date: Fri Jan 24 19:32:34 2014 +0600
+
+ Rework detector API and implement Harris detector
+
+ Switch the detector API to a single function which accepts
+ a float image and detector options. This makes usage of
+ feature detection more unified across different algorithms.
+
+ Options structure is pretty much straightforward and contains
+ detector to be used and all the detector-specific settings.
+
+ Also implemented Harris feature detection algorithm which
+ is not as fast as FAST one but is expected to detect more
+ robust feature points.
+
+ Reviewers: keir
+
+ Differential Revision: https://developer.blender.org/D258
+
+commit 6458915f64fceba108c5279b7320ca8c76e8a742
+Author: Sergey Sharybin <sergey.vfx@gmail.com>
+Date: Fri Jan 24 19:14:18 2014 +0600
+
+ Add arcanist configuration file
+
commit 0a69fadadc5aacbd339f839ac5bd12c3571278b1
Author: Sergey Sharybin <sergey.vfx@gmail.com>
Date: Thu Jan 9 15:50:11 2014 +0600
@@ -659,29 +686,3 @@ Date: Sat Apr 6 00:38:40 2013 +0600
Also fixed variable shadowing which lead to wrong
markers positions (were either zero or undefined).
-
-commit a597ca9291ff7c6e209e0500945568afa84d8b4e
-Author: Sergey Sharybin <sergey.vfx@gmail.com>
-Date: Sat Apr 6 00:12:12 2013 +0600
-
- Camera intrinsics unit tests fixes
-
- - Existing test ApplyIsInvertibleSimple was not
- doing right thing - distortion model used there
- was ininvertible.
-
- Tweaked parameters in a way model is invertible now.
-
- - Added some own tests which tests;
-
- * Principal point always maps from pixel space to
- zero normalized position.
-
- * Some basic tests to check whether individual
- apply/invert works correct.
-
-commit a9a3016c7beb02e62655221d46783b206010d04c
-Author: Sergey Sharybin <sergey.vfx@gmail.com>
-Date: Thu Apr 4 02:59:58 2013 +0600
-
- Suppress strict compiler warnings in glags/glog libraries
diff --git a/extern/libmv/files.txt b/extern/libmv/files.txt
index e5297c7cced..a639ea3afa5 100644
--- a/extern/libmv/files.txt
+++ b/extern/libmv/files.txt
@@ -7,6 +7,7 @@ libmv/image/array_nd.h
libmv/image/convolve.cc
libmv/image/convolve.h
libmv/image/correlation.h
+libmv/image/image_converter.h
libmv/image/image.h
libmv/image/sample.h
libmv/image/tuple.h
diff --git a/extern/libmv/libmv-capi.cc b/extern/libmv/libmv-capi.cc
index 474501afbcc..9a536f718aa 100644
--- a/extern/libmv/libmv-capi.cc
+++ b/extern/libmv/libmv-capi.cc
@@ -58,6 +58,14 @@
#include "libmv/simple_pipeline/reconstruction_scale.h"
#include "libmv/simple_pipeline/keyframe_selection.h"
+#include "libmv/multiview/homography.h"
+
+#include "libmv/image/convolve.h"
+
+#ifdef _MSC_VER
+# define snprintf _snprintf
+#endif
+
struct libmv_Reconstruction {
libmv::EuclideanReconstruction reconstruction;
@@ -69,7 +77,7 @@ struct libmv_Reconstruction {
};
struct libmv_Features {
- int count, margin;
+ int count;
libmv::Feature *features;
};
@@ -107,6 +115,21 @@ void libmv_setLoggingVerbosity(int verbosity)
/* ************ Utility ************ */
+static void byteBufToImage(const unsigned char *buf, int width, int height, int channels, libmv::FloatImage *image)
+{
+ int x, y, k, a = 0;
+
+ image->Resize(height, width, channels);
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ for (k = 0; k < channels; k++) {
+ (*image)(y, x, k) = (float)buf[a++] / 255.0f;
+ }
+ }
+ }
+}
+
static void floatBufToImage(const float *buf, int width, int height, int channels, libmv::FloatImage *image)
{
int x, y, k, a = 0;
@@ -833,63 +856,88 @@ struct libmv_CameraIntrinsics *libmv_reconstructionExtractIntrinsics(struct libm
/* ************ Feature detector ************ */
-struct libmv_Features *libmv_detectFeaturesFAST(const unsigned char *data,
- int width, int height, int stride,
- int margin, int min_trackness, int min_distance)
+static libmv_Features *libmv_featuresFromVector(
+ const libmv::vector<libmv::Feature> &features)
{
- libmv::Feature *features = NULL;
- std::vector<libmv::Feature> v;
struct libmv_Features *libmv_features = LIBMV_STRUCT_NEW(libmv_Features, 1);
- int i = 0, count;
-
- if (margin) {
- data += margin * stride+margin;
- width -= 2 * margin;
- height -= 2 * margin;
- }
-
- v = libmv::DetectFAST(data, width, height, stride, min_trackness, min_distance);
-
- count = v.size();
-
+ int count = features.size();
if (count) {
- features = LIBMV_STRUCT_NEW(libmv::Feature, count);
+ libmv_features->features = LIBMV_STRUCT_NEW(libmv::Feature, count);
- for(std::vector<libmv::Feature>::iterator it = v.begin(); it != v.end(); it++) {
- features[i++] = *it;
+ for (int i = 0; i < count; i++) {
+ libmv_features->features[i] = features.at(i);
}
}
+ else {
+ libmv_features->features = NULL;
+ }
- libmv_features->features = features;
libmv_features->count = count;
- libmv_features->margin = margin;
- return (struct libmv_Features *)libmv_features;
+ return libmv_features;
}
-struct libmv_Features *libmv_detectFeaturesMORAVEC(const unsigned char *data,
- int width, int height, int stride,
- int margin, int count, int min_distance)
+static void libmv_convertDetectorOptions(libmv_DetectOptions *options,
+ libmv::DetectOptions *detector_options)
{
- libmv::Feature *features = NULL;
- struct libmv_Features *libmv_features = LIBMV_STRUCT_NEW(libmv_Features, 1);
+ switch (options->detector) {
+#define LIBMV_CONVERT(the_detector) \
+ case LIBMV_DETECTOR_ ## the_detector: \
+ detector_options->type = libmv::DetectOptions::the_detector; \
+ break;
+ LIBMV_CONVERT(FAST)
+ LIBMV_CONVERT(MORAVEC)
+ LIBMV_CONVERT(HARRIS)
+#undef LIBMV_CONVERT
+ }
+ detector_options->margin = options->margin;
+ detector_options->min_distance = options->min_distance;
+ detector_options->fast_min_trackness = options->fast_min_trackness;
+ detector_options->moravec_max_count = options->moravec_max_count;
+ detector_options->moravec_pattern = options->moravec_pattern;
+ detector_options->harris_threshold = options->harris_threshold;
+}
- if (count) {
- if (margin) {
- data += margin * stride+margin;
- width -= 2 * margin;
- height -= 2 * margin;
- }
+struct libmv_Features *libmv_detectFeaturesByte(const unsigned char *image_buffer,
+ int width, int height, int channels,
+ libmv_DetectOptions *options)
+{
+ // Prepare the image.
+ libmv::FloatImage image;
+ byteBufToImage(image_buffer, width, height, channels, &image);
- features = LIBMV_STRUCT_NEW(libmv::Feature, count);
- libmv::DetectMORAVEC(data, stride, width, height, features, &count, min_distance, NULL);
- }
+ // Configure detector.
+ libmv::DetectOptions detector_options;
+ libmv_convertDetectorOptions(options, &detector_options);
- libmv_features->count = count;
- libmv_features->margin = margin;
- libmv_features->features = features;
+ // Run the detector.
+ libmv::vector<libmv::Feature> detected_features;
+ libmv::Detect(image, detector_options, &detected_features);
- return libmv_features;
+ // Convert result to C-API.
+ libmv_Features *result = libmv_featuresFromVector(detected_features);
+ return result;
+}
+
+struct libmv_Features *libmv_detectFeaturesFloat(const float *image_buffer,
+ int width, int height, int channels,
+ libmv_DetectOptions *options)
+{
+ // Prepare the image.
+ libmv::FloatImage image;
+ floatBufToImage(image_buffer, width, height, channels, &image);
+
+ // Configure detector.
+ libmv::DetectOptions detector_options;
+ libmv_convertDetectorOptions(options, &detector_options);
+
+ // Run the detector.
+ libmv::vector<libmv::Feature> detected_features;
+ libmv::Detect(image, detector_options, &detected_features);
+
+ // Convert result to C-API.
+ libmv_Features *result = libmv_featuresFromVector(detected_features);
+ return result;
}
void libmv_featuresDestroy(struct libmv_Features *libmv_features)
@@ -910,8 +958,8 @@ void libmv_getFeature(const struct libmv_Features *libmv_features, int number, d
{
libmv::Feature feature = libmv_features->features[number];
- *x = feature.x + libmv_features->margin;
- *y = feature.y + libmv_features->margin;
+ *x = feature.x;
+ *y = feature.y;
*score = feature.score;
*size = feature.size;
}
diff --git a/extern/libmv/libmv-capi.h b/extern/libmv/libmv-capi.h
index 13cc3ae8499..d183bc4cd41 100644
--- a/extern/libmv/libmv-capi.h
+++ b/extern/libmv/libmv-capi.h
@@ -118,10 +118,29 @@ double libmv_reprojectionError(const struct libmv_Reconstruction *libmv_reconstr
struct libmv_CameraIntrinsics *libmv_reconstructionExtractIntrinsics(struct libmv_Reconstruction *libmv_Reconstruction);
/* Feature detector */
-struct libmv_Features *libmv_detectFeaturesFAST(const unsigned char *data, int width, int height, int stride,
- int margin, int min_trackness, int min_distance);
-struct libmv_Features *libmv_detectFeaturesMORAVEC(const unsigned char *data, int width, int height, int stride,
- int margin, int count, int min_distance);
+enum {
+ LIBMV_DETECTOR_FAST,
+ LIBMV_DETECTOR_MORAVEC,
+ LIBMV_DETECTOR_HARRIS,
+};
+
+typedef struct libmv_DetectOptions {
+ int detector;
+ int margin;
+ int min_distance;
+ int fast_min_trackness;
+ int moravec_max_count;
+ unsigned char *moravec_pattern;
+ double harris_threshold;
+} libmv_DetectOptions;
+
+struct libmv_Features *libmv_detectFeaturesByte(const unsigned char *image_buffer,
+ int width, int height, int channels,
+ libmv_DetectOptions *options);
+struct libmv_Features *libmv_detectFeaturesFloat(const float *image_buffer,
+ int width, int height, int channels,
+ libmv_DetectOptions *options);
+
void libmv_featuresDestroy(struct libmv_Features *libmv_features);
int libmv_countFeatures(const struct libmv_Features *libmv_features);
void libmv_getFeature(const struct libmv_Features *libmv_features, int number, double *x, double *y, double *score,
diff --git a/extern/libmv/libmv-capi_stub.cc b/extern/libmv/libmv-capi_stub.cc
index bda9605b422..bd5d16c9077 100644
--- a/extern/libmv/libmv-capi_stub.cc
+++ b/extern/libmv/libmv-capi_stub.cc
@@ -146,15 +146,16 @@ void libmv_reconstructionDestroy(struct libmv_Reconstruction * /*libmv_reconstru
/* ************ feature detector ************ */
-struct libmv_Features *libmv_detectFeaturesFAST(const unsigned char * /*data*/, int /*width*/, int /*height*/,
- int /*stride*/, int /*margin*/, int /*min_trackness*/,
- int /*min_distance*/)
+struct libmv_Features *libmv_detectFeaturesByte(const unsigned char */*image_buffer*/,
+ int /*width*/, int /*height*/, int /*channels*/,
+ libmv_DetectOptions */*options*/)
{
return NULL;
}
-struct libmv_Features *libmv_detectFeaturesMORAVEC(const unsigned char * /*data*/, int /*width*/, int /*height*/,
- int /*stride*/, int /*margin*/, int /*count*/, int /*min_distance*/)
+struct libmv_Features *libmv_detectFeaturesFloat(const float */*image_buffer*/,
+ int /*width*/, int /*height*/, int /*channels*/,
+ libmv_DetectOptions */*options*/)
{
return NULL;
}
diff --git a/extern/libmv/libmv/image/array_nd.h b/extern/libmv/libmv/image/array_nd.h
index f40fadcbde7..c5099f24d7b 100644
--- a/extern/libmv/libmv/image/array_nd.h
+++ b/extern/libmv/libmv/image/array_nd.h
@@ -471,6 +471,23 @@ void MultiplyElements(const ArrayND<TA, 3> &a,
}
}
+template <typename TA, typename TB, typename TC>
+void MultiplyElements(const Array3D<TA> &a,
+ const Array3D<TB> &b,
+ Array3D<TC> *c) {
+ // Specialization for N==3
+ c->ResizeLike(a);
+ assert(a.Shape(0) == b.Shape(0));
+ assert(a.Shape(1) == b.Shape(1));
+ assert(a.Shape(2) == b.Shape(2));
+ for (int i = 0; i < a.Shape(0); ++i) {
+ for (int j = 0; j < a.Shape(1); ++j) {
+ for (int k = 0; k < a.Shape(2); ++k) {
+ (*c)(i, j, k) = TC(a(i, j, k) * b(i, j, k));
+ }
+ }
+ }
+}
} // namespace libmv
diff --git a/extern/libmv/libmv/image/convolve.cc b/extern/libmv/libmv/image/convolve.cc
index 4682c619f04..464043581d2 100644
--- a/extern/libmv/libmv/image/convolve.cc
+++ b/extern/libmv/libmv/image/convolve.cc
@@ -182,6 +182,23 @@ void ConvolveGaussian(const Array3Df &in,
ConvolveHorizontal(tmp, kernel, out_pointer);
}
+void ImageDerivatives(const Array3Df &in,
+ double sigma,
+ Array3Df *gradient_x,
+ Array3Df *gradient_y) {
+ Vec kernel, derivative;
+ ComputeGaussianKernel(sigma, &kernel, &derivative);
+ Array3Df tmp;
+
+ // Compute first derivative in x.
+ ConvolveVertical(in, kernel, &tmp);
+ ConvolveHorizontal(tmp, derivative, gradient_x);
+
+ // Compute first derivative in y.
+ ConvolveHorizontal(in, kernel, &tmp);
+ ConvolveVertical(tmp, derivative, gradient_y);
+}
+
void BlurredImageAndDerivatives(const Array3Df &in,
double sigma,
Array3Df *blurred_image,
diff --git a/extern/libmv/libmv/image/image_converter.h b/extern/libmv/libmv/image/image_converter.h
new file mode 100644
index 00000000000..b3a3fa2bf8c
--- /dev/null
+++ b/extern/libmv/libmv/image/image_converter.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2009 libmv authors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBMV_IMAGE_IMAGE_CONVERTER_H
+#define LIBMV_IMAGE_IMAGE_CONVERTER_H
+
+#include "libmv/image/array_nd.h"
+
+namespace libmv {
+
+// The factor comes from http://www.easyrgb.com/
+// RGB to XYZ : Y is the luminance channel
+// var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
+template<typename T>
+inline T RGB2GRAY(const T r, const T g, const T b) {
+ return static_cast<T>(r * 0.2126 + g * 0.7152 + b * 0.0722);
+}
+
+/*
+// Specialization for the uchar type. (that do not want to work...)
+template<>
+inline unsigned char RGB2GRAY<unsigned char>(const unsigned char r,
+ const unsigned char g,
+ const unsigned char b) {
+ return (unsigned char)(r * 0.2126 + g * 0.7152 + b * 0.0722 +0.5);
+}*/
+
+template<class ImageIn, class ImageOut>
+void Rgb2Gray(const ImageIn &imaIn, ImageOut *imaOut) {
+ // It is all fine to cnvert RGBA image here as well,
+ // all the additional channels will be nicely ignored.
+ assert(imaIn.Depth() >= 3);
+
+ imaOut->Resize(imaIn.Height(), imaIn.Width(), 1);
+ // Convert each RGB pixel into Gray value (luminance)
+
+ for (int j = 0; j < imaIn.Height(); ++j) {
+ for (int i = 0; i < imaIn.Width(); ++i) {
+ (*imaOut)(j, i) = RGB2GRAY(imaIn(j, i, 0) , imaIn(j, i, 1), imaIn(j, i, 2));
+ }
+ }
+}
+
+// Convert given float image to an unsigned char array of pixels.
+template<class Image>
+unsigned char *FloatImageToUCharArray(const Image &image) {
+ unsigned char *buffer =
+ new unsigned char[image.Width() * image.Height() * image.Depth()];
+
+ for (int y = 0; y < image.Height(); y++) {
+ for (int x = 0; x < image.Width(); x++) {
+ for (int d = 0; d < image.Depth(); d++) {
+ int index = (y * image.Width() + x) * image.Depth() + d;
+ buffer[index] = 255.0 * image(y, x, d);
+ }
+ }
+ }
+
+ return buffer;
+}
+
+} // namespace libmv
+
+#endif // LIBMV_IMAGE_IMAGE_CONVERTER_H
diff --git a/extern/libmv/libmv/simple_pipeline/detect.cc b/extern/libmv/libmv/simple_pipeline/detect.cc
index 27639e958a0..1ddb23dcd28 100644
--- a/extern/libmv/libmv/simple_pipeline/detect.cc
+++ b/extern/libmv/libmv/simple_pipeline/detect.cc
@@ -22,96 +22,133 @@
**
****************************************************************************/
-#include "libmv/simple_pipeline/detect.h"
-#include <third_party/fast/fast.h>
#include <stdlib.h>
#include <memory.h>
+#include <queue>
+#include <third_party/fast/fast.h>
+
+#include "libmv/base/scoped_ptr.h"
+#include "libmv/image/array_nd.h"
+#include "libmv/image/image_converter.h"
+#include "libmv/image/convolve.h"
+#include "libmv/logging/logging.h"
+#include "libmv/simple_pipeline/detect.h"
#ifdef __SSE2__
-#include <emmintrin.h>
+# include <emmintrin.h>
#endif
namespace libmv {
-typedef unsigned int uint;
+namespace {
+
+class FeatureComparison {
+ public:
+ bool operator() (const Feature &left, const Feature &right) const {
+ return right.score > left.score;
+ }
+};
+
+// Filter the features so there are no features closer than
+// minimal distance to each other.
+// This is a naive implementation with O(n^2) asymptotic.
+void FilterFeaturesByDistance(const vector<Feature> &all_features,
+ int min_distance,
+ vector<Feature> *detected_features) {
+ const int min_distance_squared = min_distance * min_distance;
+
+ // Use priority queue to sort the features by their score.
+ //
+ // Do this on copy of the input features to prevent possible
+ // distortion in callee function behavior.
+ std::priority_queue<Feature,
+ std::vector<Feature>,
+ FeatureComparison> priority_features;
+
+ for (int i = 0; i < all_features.size(); i++) {
+ priority_features.push(all_features.at(i));
+ }
+
+ while (!priority_features.empty()) {
+ bool ok = true;
+ Feature a = priority_features.top();
+
+ for (int i = 0; i < detected_features->size(); i++) {
+ Feature &b = detected_features->at(i);
+ if (Square(a.x - b.x) + Square(a.y - b.y) < min_distance_squared) {
+ ok = false;
+ break;
+ }
+ }
-static int featurecmp(const void *a_v, const void *b_v) {
- Feature *a = (Feature*)a_v;
- Feature *b = (Feature*)b_v;
+ if (ok) {
+ detected_features->push_back(a);
+ }
- return b->score - a->score;
+ priority_features.pop();
+ }
}
-std::vector<Feature> DetectFAST(const unsigned char* data,
- int width, int height,
- int stride,
- int min_trackness,
- int min_distance) {
- std::vector<Feature> features;
+void DetectFAST(const FloatImage &grayscale_image,
+ const DetectOptions &options,
+ vector<Feature> *detected_features) {
+ const int min_distance = options.min_distance;
+ const int min_trackness = options.fast_min_trackness;
+ const int margin = options.margin;
+ const int width = grayscale_image.Width() - 2 * margin;
+ const int height = grayscale_image.Width() - 2 * margin;
+ const int stride = grayscale_image.Width();
+
+ // TODO(sergey): Use scoped_array to guard de-allocation of the array.
+ // Same goes to `scores` and `all` arrays here.
+ unsigned char *byte_image = FloatImageToUCharArray(grayscale_image);
+ const int byte_image_offset = margin * stride + margin;
+
// TODO(MatthiasF): Support targetting a feature count (binary search trackness)
int num_features;
- xy* all = fast9_detect(data, width, height,
- stride, min_trackness, &num_features);
+ xy *all = fast9_detect(byte_image + byte_image_offset,
+ width,
+ height,
+ stride,
+ min_trackness,
+ &num_features);
if (num_features == 0) {
free(all);
- return features;
+ delete [] byte_image;
+ return;
}
- int* scores = fast9_score(data, stride, all, num_features, min_trackness);
+ int *scores = fast9_score(byte_image + byte_image_offset,
+ stride,
+ all,
+ num_features,
+ min_trackness);
// TODO(MatthiasF): merge with close feature suppression
- xy* nonmax = nonmax_suppression(all, scores, num_features, &num_features);
+ xy *nonmax = nonmax_suppression(all, scores, num_features, &num_features);
free(all);
+ delete [] byte_image;
// Remove too close features
// TODO(MatthiasF): A resolution independent parameter would be better than
// distance e.g. a coefficient going from 0 (no minimal distance) to 1
// (optimal circle packing)
// FIXME(MatthiasF): this method will not necessarily give all maximum markers
if (num_features) {
- Feature *all_features = new Feature[num_features];
-
+ vector<Feature> all_features;
for (int i = 0; i < num_features; ++i) {
- Feature a = { (float)nonmax[i].x, (float)nonmax[i].y, (float)scores[i], 0 };
- all_features[i] = a;
- }
-
- qsort((void *)all_features, num_features, sizeof(Feature), featurecmp);
-
- features.reserve(num_features);
-
- int prev_score = all_features[0].score;
- const int min_distance_squared = min_distance * min_distance;
- for (int i = 0; i < num_features; ++i) {
- bool ok = true;
- Feature a = all_features[i];
- if (a.score>prev_score)
- abort();
- prev_score = a.score;
-
- // compare each feature against filtered set
- for (int j = 0; j < features.size(); j++) {
- Feature& b = features[j];
- if ((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) < min_distance_squared) {
- // already a nearby feature
- ok = false;
- break;
- }
- }
-
- if (ok) {
- // add the new feature
- features.push_back(a);
- }
+ Feature new_feature = {(float)nonmax[i].x + margin,
+ (float)nonmax[i].y + margin,
+ (float)scores[i],
+ 0};
+ all_features.push_back(new_feature);
}
-
- delete [] all_features;
+ FilterFeaturesByDistance(all_features, min_distance, detected_features);
}
free(scores);
free(nonmax);
- return features;
}
#ifdef __SSE2__
-static uint SAD(const ubyte* imageA, const ubyte* imageB,
- int strideA, int strideB) {
+static unsigned int SAD(const ubyte* imageA, const ubyte* imageB,
+ int strideA, int strideB) {
__m128i a = _mm_setzero_si128();
for (int i = 0; i < 16; i++) {
a = _mm_adds_epu16(a,
@@ -121,9 +158,9 @@ static uint SAD(const ubyte* imageA, const ubyte* imageB,
return _mm_extract_epi16(a, 0) + _mm_extract_epi16(a, 4);
}
#else
-static uint SAD(const ubyte* imageA, const ubyte* imageB,
- int strideA, int strideB) {
- uint sad = 0;
+static unsigned int SAD(const ubyte* imageA, const ubyte* imageB,
+ int strideA, int strideB) {
+ unsigned int sad = 0;
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {
sad += abs((int)imageA[i*strideA+j] - imageB[i*strideB+j]);
@@ -133,11 +170,20 @@ static uint SAD(const ubyte* imageA, const ubyte* imageB,
}
#endif
-void DetectMORAVEC(const ubyte* image,
- int stride, int width, int height,
- Feature* detected, int* count,
- int distance,
- ubyte* pattern) {
+void DetectMORAVEC(const FloatImage &grayscale_image,
+ const DetectOptions &options,
+ vector<Feature> *detected_features) {
+ const int distance = options.min_distance;
+ const int margin = options.margin;
+ const unsigned char *pattern = options.moravec_pattern;
+ const int count = options.moravec_max_count;
+ const int width = grayscale_image.Width() - 2 * margin;
+ const int height = grayscale_image.Width() - 2 * margin;
+ const int stride = grayscale_image.Width();
+
+ // TODO(sergey): Use scoped_array to guard de-allocation of the array.
+ unsigned char *byte_image = FloatImageToUCharArray(grayscale_image);
+
unsigned short histogram[256];
memset(histogram, 0, sizeof(histogram));
ubyte* scores = new ubyte[width*height];
@@ -145,7 +191,7 @@ void DetectMORAVEC(const ubyte* image,
const int r = 1; // radius for self similarity comparison
for (int y = distance; y < height-distance; y++) {
for (int x = distance; x < width-distance; x++) {
- const ubyte* s = &image[y*stride+x];
+ const ubyte* s = &byte_image[y*stride+x];
int score = // low self-similarity with overlapping patterns
// OPTI: load pattern once
SAD(s, s-r*stride-r, stride, stride)+SAD(s, s-r*stride, stride, stride)+SAD(s, s-r*stride+r, stride, stride)+
@@ -182,19 +228,105 @@ void DetectMORAVEC(const ubyte* image,
int min = 255, total = 0;
for (; min > 0; min--) {
int h = histogram[min];
- if (total+h > *count) break;
+ if (total+h > count) break;
total += h;
}
- int i = 0;
for (int y = 16; y < height-16; y++) {
for (int x = 16; x < width-16; x++) {
int s = scores[y*width+x];
Feature f = { (float)x+8.0f, (float)y+8.0f, (float)s, 16 };
- if (s > min) detected[i++] = f;
+ if (s > min) {
+ detected_features->push_back(f);
+ }
}
}
- *count = i;
delete[] scores;
+ delete[] byte_image;
+}
+
+void DetectHarris(const FloatImage &grayscale_image,
+ const DetectOptions &options,
+ vector<Feature> *detected_features) {
+ const double alpha = 0.06;
+ const double sigma = 0.9;
+
+ const int min_distance = options.min_distance;
+ const int margin = options.margin;
+ const double threshold = options.harris_threshold;
+
+ FloatImage gradient_x, gradient_y;
+ ImageDerivatives(grayscale_image, sigma, &gradient_x, &gradient_y);
+
+ FloatImage gradient_xx, gradient_yy, gradient_xy;
+ MultiplyElements(gradient_x, gradient_x, &gradient_xx);
+ MultiplyElements(gradient_y, gradient_y, &gradient_yy);
+ MultiplyElements(gradient_x, gradient_y, &gradient_xy);
+
+ FloatImage gradient_xx_blurred,
+ gradient_yy_blurred,
+ gradient_xy_blurred;
+ ConvolveGaussian(gradient_xx, sigma, &gradient_xx_blurred);
+ ConvolveGaussian(gradient_yy, sigma, &gradient_yy_blurred);
+ ConvolveGaussian(gradient_xy, sigma, &gradient_xy_blurred);
+
+ vector<Feature> all_features;
+ for (int y = margin; y < gradient_xx_blurred.Height() - margin; ++y) {
+ for (int x = margin; x < gradient_xx_blurred.Width() - margin; ++x) {
+ // Construct matrix
+ //
+ // A = [ Ix^2 Ix*Iy ]
+ // [ Ix*Iy Iy^2 ]
+ Mat2 A;
+ A(0, 0) = gradient_xx_blurred(y, x);
+ A(1, 1) = gradient_yy_blurred(y, x);
+ A(0, 1) = A(1, 0) = gradient_xy_blurred(y, x);
+
+ double detA = A.determinant();
+ double traceA = A.trace();
+ double harris_function = detA - alpha * traceA * traceA;
+ if (harris_function > threshold) {
+ Feature new_feature = {(float)x, (float)y, (float)harris_function, 0.0f};
+ all_features.push_back(new_feature);
+ }
+ }
+ }
+
+ FilterFeaturesByDistance(all_features, min_distance, detected_features);
+}
+
+} // namespace
+
+DetectOptions::DetectOptions()
+ : type(DetectOptions::HARRIS),
+ margin(0),
+ min_distance(120),
+ fast_min_trackness(128),
+ moravec_max_count(0),
+ moravec_pattern(NULL),
+ harris_threshold(0.0) {}
+
+void Detect(const FloatImage &image,
+ const DetectOptions &options,
+ vector<Feature> *detected_features) {
+ // Currently all the detectors requires image to be grayscale.
+ // Do it here to avoid code duplication.
+ FloatImage grayscale_image;
+ if (image.Depth() != 1) {
+ Rgb2Gray(image, &grayscale_image);
+ } else {
+ // TODO(sergey): Find a way to avoid such image duplication/
+ grayscale_image = image;
+ }
+
+ if (options.type == DetectOptions::FAST) {
+ DetectFAST(grayscale_image, options, detected_features);
+ } else if (options.type == DetectOptions::MORAVEC) {
+ DetectMORAVEC(grayscale_image, options, detected_features);
+ } else if (options.type == DetectOptions::HARRIS) {
+ DetectHarris(grayscale_image, options, detected_features);
+ } else {
+ LOG(FATAL) << "Unknown detector has been passed to featur detection";
+ }
}
} // namespace libmv
diff --git a/extern/libmv/libmv/simple_pipeline/detect.h b/extern/libmv/libmv/simple_pipeline/detect.h
index f5bcd07afd7..2c782294d1b 100644
--- a/extern/libmv/libmv/simple_pipeline/detect.h
+++ b/extern/libmv/libmv/simple_pipeline/detect.h
@@ -27,6 +27,9 @@
#include <vector>
+#include "libmv/base/vector.h"
+#include "libmv/image/image.h"
+
namespace libmv {
typedef unsigned char ubyte;
@@ -50,47 +53,51 @@ struct Feature {
float size;
};
-/*!
- Detect features in an image.
-
- You need to input a single channel 8-bit image using pointer to image \a data,
- \a width, \a height and \a stride (i.e bytes per line).
-
- You can tweak the count of detected features using \a min_trackness, which is
- the minimum score to add a feature, and \a min_distance which is the minimal
- distance accepted between two featuress.
-
- \note You can binary search over \a min_trackness to get a given feature count.
-
- \note a way to get an uniform distribution of a given feature count is:
- \a min_distance = \a width * \a height / desired_feature_count ^ 2
-
- \return All detected feartures matching given parameters
-*/
-std::vector<Feature> DetectFAST(const unsigned char* data, int width, int height,
- int stride, int min_trackness = 128,
- int min_distance = 120);
-
-/*!
- Detect features in an image.
-
- \a image is a single channel 8-bit image of size \a width x \a height
-
- \a detected is an array with space to hold \a *count features.
- \a *count is the maximum count to detect on input and the actual
- detected count on output.
-
- \a distance is the minimal distance between detected features.
-
- if \a pattern is null all good features will be found.
- if \a pattern is not null only features similar to \a pattern will be found.
+struct DetectOptions {
+ DetectOptions();
+
+ // TODO(sergey): Write descriptions to each of algorithms.
+ enum DetectorType {
+ FAST,
+ MORAVEC,
+ HARRIS,
+ };
+ DetectorType type;
+
+ // Margin in pixels from the image boundary.
+ // No features will be detected within the margin.
+ int margin;
+
+ // Minimal distance between detected features.
+ int min_distance;
+
+ // Minimum score to add a feature. Only used by FAST detector.
+ //
+ // TODO(sergey): Write more detailed documentation about which
+ // units this value is measured in and so on.
+ int fast_min_trackness;
+
+ // Maximum count to detect. Only used by MORAVEC detector.
+ int moravec_max_count;
+
+ // Find only features similar to this pattern. Only used by MORAVEC detector.
+ //
+ // This is an image patch denoted in byte array with dimensions of 16px by 16px
+ // used to filter features by similarity to this patch.
+ unsigned char *moravec_pattern;
+
+ // Threshold value of the Harris function to add new featrue
+ // to the result.
+ double harris_threshold;
+};
- \note \a You can crop the image (to avoid detecting markers near the borders) without copying:
- image += marginY*stride+marginX, width -= 2*marginX, height -= 2*marginY;
-*/
-void DetectMORAVEC(const ubyte* image, int stride, int width, int height,
- Feature* detected, int* count, int distance /*=32*/,
- ubyte* pattern /*=0*/);
+// Detect features on a given image using given detector options.
+//
+// Image could have 1-4 channels, it'll be converted to a grayscale
+// by the detector function if needed.
+void Detect(const FloatImage &image,
+ const DetectOptions &options,
+ vector<Feature> *detected_features);
} // namespace libmv