Commit aa19fd50 authored by Marina Kolpakova's avatar Marina Kolpakova

Fixed bug #1654

parent 2ca6a505
...@@ -36,11 +36,11 @@ ...@@ -36,11 +36,11 @@
// The original code was written by // The original code was written by
// Marius Muja // Marius Muja
// and later modified and prepared // and later modified and prepared
// for integration into OpenCV by // for integration into OpenCV by
// Antonella Cascitelli, // Antonella Cascitelli,
// Marco Di Stefano and // Marco Di Stefano and
// Stefano Fabri // Stefano Fabri
// from Univ. of Rome // from Univ. of Rome
// //
#include "precomp.hpp" #include "precomp.hpp"
...@@ -52,7 +52,7 @@ namespace cv ...@@ -52,7 +52,7 @@ namespace cv
{ {
using std::queue; using std::queue;
typedef std::pair<int,int> coordinate_t; typedef std::pair<int,int> coordinate_t;
typedef float orientation_t; typedef float orientation_t;
typedef std::vector<coordinate_t> template_coords_t; typedef std::vector<coordinate_t> template_coords_t;
...@@ -61,14 +61,14 @@ typedef std::pair<Point, float> location_scale_t; ...@@ -61,14 +61,14 @@ typedef std::pair<Point, float> location_scale_t;
class ChamferMatcher class ChamferMatcher
{ {
private: private:
class Matching; class Matching;
int max_matches_; int max_matches_;
float min_match_distance_; float min_match_distance_;
///////////////////////// Image iterators //////////////////////////// ///////////////////////// Image iterators ////////////////////////////
class ImageIterator class ImageIterator
{ {
public: public:
...@@ -76,15 +76,15 @@ private: ...@@ -76,15 +76,15 @@ private:
virtual bool hasNext() const = 0; virtual bool hasNext() const = 0;
virtual location_scale_t next() = 0; virtual location_scale_t next() = 0;
}; };
class ImageRange class ImageRange
{ {
public: public:
virtual ImageIterator* iterator() const = 0; virtual ImageIterator* iterator() const = 0;
}; };
// Sliding window // Sliding window
class SlidingWindowImageRange : public ImageRange class SlidingWindowImageRange : public ImageRange
{ {
int width_; int width_;
...@@ -94,64 +94,64 @@ private: ...@@ -94,64 +94,64 @@ private:
int scales_; int scales_;
float min_scale_; float min_scale_;
float max_scale_; float max_scale_;
public: public:
SlidingWindowImageRange(int width, int height, int x_step = 3, int y_step = 3, int scales = 5, float min_scale = 0.6, float max_scale = 1.6) : SlidingWindowImageRange(int width, int height, int x_step = 3, int y_step = 3, int scales = 5, float min_scale = 0.6, float max_scale = 1.6) :
width_(width), height_(height), x_step_(x_step),y_step_(y_step), scales_(scales), min_scale_(min_scale), max_scale_(max_scale) width_(width), height_(height), x_step_(x_step),y_step_(y_step), scales_(scales), min_scale_(min_scale), max_scale_(max_scale)
{ {
} }
ImageIterator* iterator() const; ImageIterator* iterator() const;
}; };
class LocationImageRange : public ImageRange class LocationImageRange : public ImageRange
{ {
const std::vector<Point>& locations_; const std::vector<Point>& locations_;
int scales_; int scales_;
float min_scale_; float min_scale_;
float max_scale_; float max_scale_;
LocationImageRange(const LocationImageRange&); LocationImageRange(const LocationImageRange&);
LocationImageRange& operator=(const LocationImageRange&); LocationImageRange& operator=(const LocationImageRange&);
public: public:
LocationImageRange(const std::vector<Point>& locations, int scales = 5, float min_scale = 0.6, float max_scale = 1.6) : LocationImageRange(const std::vector<Point>& locations, int scales = 5, float min_scale = 0.6, float max_scale = 1.6) :
locations_(locations), scales_(scales), min_scale_(min_scale), max_scale_(max_scale) locations_(locations), scales_(scales), min_scale_(min_scale), max_scale_(max_scale)
{ {
} }
ImageIterator* iterator() const ImageIterator* iterator() const
{ {
return new LocationImageIterator(locations_, scales_, min_scale_, max_scale_); return new LocationImageIterator(locations_, scales_, min_scale_, max_scale_);
} }
}; };
class LocationScaleImageRange : public ImageRange class LocationScaleImageRange : public ImageRange
{ {
const std::vector<Point>& locations_; const std::vector<Point>& locations_;
const std::vector<float>& scales_; const std::vector<float>& scales_;
LocationScaleImageRange(const LocationScaleImageRange&); LocationScaleImageRange(const LocationScaleImageRange&);
LocationScaleImageRange& operator=(const LocationScaleImageRange&); LocationScaleImageRange& operator=(const LocationScaleImageRange&);
public: public:
LocationScaleImageRange(const std::vector<Point>& locations, const std::vector<float>& scales) : LocationScaleImageRange(const std::vector<Point>& locations, const std::vector<float>& scales) :
locations_(locations), scales_(scales) locations_(locations), scales_(scales)
{ {
assert(locations.size()==scales.size()); assert(locations.size()==scales.size());
} }
ImageIterator* iterator() const ImageIterator* iterator() const
{ {
return new LocationScaleImageIterator(locations_, scales_); return new LocationScaleImageIterator(locations_, scales_);
} }
}; };
public: public:
/** /**
* Class that represents a template for chamfer matching. * Class that represents a template for chamfer matching.
...@@ -160,26 +160,26 @@ public: ...@@ -160,26 +160,26 @@ public:
{ {
friend class ChamferMatcher::Matching; friend class ChamferMatcher::Matching;
friend class ChamferMatcher; friend class ChamferMatcher;
public: public:
std::vector<Template*> scaled_templates; std::vector<Template*> scaled_templates;
std::vector<int> addr; std::vector<int> addr;
int addr_width; int addr_width;
float scale; float scale;
template_coords_t coords; template_coords_t coords;
template_orientations_t orientations; template_orientations_t orientations;
Size size; Size size;
Point center; Point center;
public: public:
Template() : addr_width(-1) Template() : addr_width(-1)
{ {
} }
Template(Mat& edge_image, float scale_ = 1); Template(Mat& edge_image, float scale_ = 1);
~Template() ~Template()
{ {
for (size_t i=0;i<scaled_templates.size();++i) { for (size_t i=0;i<scaled_templates.size();++i) {
...@@ -190,9 +190,9 @@ public: ...@@ -190,9 +190,9 @@ public:
orientations.clear(); orientations.clear();
} }
void show() const; void show() const;
private: private:
/** /**
* Resizes a template * Resizes a template
...@@ -200,16 +200,16 @@ public: ...@@ -200,16 +200,16 @@ public:
* @param scale Scale to be resized to * @param scale Scale to be resized to
*/ */
Template* rescale(float scale); Template* rescale(float scale);
std::vector<int>& getTemplateAddresses(int width); std::vector<int>& getTemplateAddresses(int width);
}; };
/** /**
* Used to represent a matching result. * Used to represent a matching result.
*/ */
class Match class Match
{ {
public: public:
...@@ -217,9 +217,9 @@ public: ...@@ -217,9 +217,9 @@ public:
Point offset; Point offset;
const Template* tpl; const Template* tpl;
}; };
typedef std::vector<Match> Matches; typedef std::vector<Match> Matches;
private: private:
/** /**
* Implements the chamfer matching algorithm on images taking into account both distance from * Implements the chamfer matching algorithm on images taking into account both distance from
...@@ -230,42 +230,42 @@ private: ...@@ -230,42 +230,42 @@ private:
{ {
float truncate_; float truncate_;
bool use_orientation_; bool use_orientation_;
std::vector<Template*> templates; std::vector<Template*> templates;
public: public:
Matching(bool use_orientation = true, float truncate = 10) : truncate_(truncate), use_orientation_(use_orientation) Matching(bool use_orientation = true, float truncate = 10) : truncate_(truncate), use_orientation_(use_orientation)
{ {
} }
~Matching() ~Matching()
{ {
for (size_t i = 0; i<templates.size(); i++) { for (size_t i = 0; i<templates.size(); i++) {
delete templates[i]; delete templates[i];
} }
} }
/** /**
* Add a template to the detector from an edge image. * Add a template to the detector from an edge image.
* @param templ An edge image * @param templ An edge image
*/ */
void addTemplateFromImage(Mat& templ, float scale = 1.0); void addTemplateFromImage(Mat& templ, float scale = 1.0);
/** /**
* Run matching using an edge image. * Run matching using an edge image.
* @param edge_img Edge image * @param edge_img Edge image
* @return a match object * @return a match object
*/ */
ChamferMatcher::Matches* matchEdgeImage(Mat& edge_img, const ImageRange& range, float orientation_weight = 0.5, int max_matches = 20, float min_match_distance = 10.0); ChamferMatcher::Matches* matchEdgeImage(Mat& edge_img, const ImageRange& range, float orientation_weight = 0.5, int max_matches = 20, float min_match_distance = 10.0);
void addTemplate(Template& template_); void addTemplate(Template& template_);
private: private:
float orientation_diff(float o1, float o2) float orientation_diff(float o1, float o2)
{ {
return fabs(o1-o2); return fabs(o1-o2);
} }
/** /**
* Computes the chamfer matching cost for one position in the target image. * Computes the chamfer matching cost for one position in the target image.
* @param offset Offset where to compute cost * @param offset Offset where to compute cost
...@@ -276,7 +276,7 @@ private: ...@@ -276,7 +276,7 @@ private:
* @return matching result * @return matching result
*/ */
ChamferMatcher::Match* localChamferDistance(Point offset, Mat& dist_img, Mat& orientation_img, Template* tpl, float orientation_weight); ChamferMatcher::Match* localChamferDistance(Point offset, Mat& dist_img, Mat& orientation_img, Template* tpl, float orientation_weight);
private: private:
/** /**
* Matches all templates. * Matches all templates.
...@@ -284,12 +284,12 @@ private: ...@@ -284,12 +284,12 @@ private:
* @param orientation_img Orientation image. * @param orientation_img Orientation image.
*/ */
ChamferMatcher::Matches* matchTemplates(Mat& dist_img, Mat& orientation_img, const ImageRange& range, float orientation_weight); ChamferMatcher::Matches* matchTemplates(Mat& dist_img, Mat& orientation_img, const ImageRange& range, float orientation_weight);
void computeDistanceTransform(Mat& edges_img, Mat& dist_img, Mat& annotate_img, float truncate_dt, float a, float b); void computeDistanceTransform(Mat& edges_img, Mat& dist_img, Mat& annotate_img, float truncate_dt, float a, float b);
void computeEdgeOrientations(Mat& edge_img, Mat& orientation_img); void computeEdgeOrientations(Mat& edge_img, Mat& orientation_img);
void fillNonContourOrientations(Mat& annotated_img, Mat& orientation_img); void fillNonContourOrientations(Mat& annotated_img, Mat& orientation_img);
public: public:
/** /**
* Finds a contour in an edge image. The original image is altered by removing the found contour. * Finds a contour in an edge image. The original image is altered by removing the found contour.
...@@ -298,7 +298,7 @@ private: ...@@ -298,7 +298,7 @@ private:
* @return True while a contour is still found in the image. * @return True while a contour is still found in the image.
*/ */
static bool findContour(Mat& templ_img, template_coords_t& coords); static bool findContour(Mat& templ_img, template_coords_t& coords);
/** /**
* Computes contour points orientations using the approach from: * Computes contour points orientations using the approach from:
* *
...@@ -309,8 +309,8 @@ private: ...@@ -309,8 +309,8 @@ private:
* @param orientations Contour points orientations * @param orientations Contour points orientations
*/ */
static void findContourOrientations(const template_coords_t& coords, template_orientations_t& orientations); static void findContourOrientations(const template_coords_t& coords, template_orientations_t& orientations);
/** /**
* Computes the angle of a line segment. * Computes the angle of a line segment.
* *
...@@ -321,14 +321,14 @@ private: ...@@ -321,14 +321,14 @@ private:
* @return Angle in radians. * @return Angle in radians.
*/ */
static float getAngle(coordinate_t a, coordinate_t b, int& dx, int& dy); static float getAngle(coordinate_t a, coordinate_t b, int& dx, int& dy);
/** /**
* Finds a point in the image from which to start contour following. * Finds a point in the image from which to start contour following.
* @param templ_img * @param templ_img
* @param p * @param p
* @return * @return
*/ */
static bool findFirstContourPoint(Mat& templ_img, coordinate_t& p); static bool findFirstContourPoint(Mat& templ_img, coordinate_t& p);
/** /**
* Method that extracts a single continuous contour from an image given a starting point. * Method that extracts a single continuous contour from an image given a starting point.
...@@ -339,54 +339,54 @@ private: ...@@ -339,54 +339,54 @@ private:
* @param direction * @param direction
*/ */
static void followContour(Mat& templ_img, template_coords_t& coords, int direction); static void followContour(Mat& templ_img, template_coords_t& coords, int direction);
}; };
class LocationImageIterator : public ImageIterator class LocationImageIterator : public ImageIterator
{ {
const std::vector<Point>& locations_; const std::vector<Point>& locations_;
size_t iter_; size_t iter_;
int scales_; int scales_;
float min_scale_; float min_scale_;
float max_scale_; float max_scale_;
float scale_; float scale_;
float scale_step_; float scale_step_;
int scale_cnt_; int scale_cnt_;
bool has_next_; bool has_next_;
LocationImageIterator(const LocationImageIterator&); LocationImageIterator(const LocationImageIterator&);
LocationImageIterator& operator=(const LocationImageIterator&); LocationImageIterator& operator=(const LocationImageIterator&);
public: public:
LocationImageIterator(const std::vector<Point>& locations, int scales, float min_scale, float max_scale); LocationImageIterator(const std::vector<Point>& locations, int scales, float min_scale, float max_scale);
bool hasNext() const { bool hasNext() const {
return has_next_; return has_next_;
} }
location_scale_t next(); location_scale_t next();
}; };
class LocationScaleImageIterator : public ImageIterator class LocationScaleImageIterator : public ImageIterator
{ {
const std::vector<Point>& locations_; const std::vector<Point>& locations_;
const std::vector<float>& scales_; const std::vector<float>& scales_;
size_t iter_; size_t iter_;
bool has_next_; bool has_next_;
LocationScaleImageIterator(const LocationScaleImageIterator&); LocationScaleImageIterator(const LocationScaleImageIterator&);
LocationScaleImageIterator& operator=(const LocationScaleImageIterator&); LocationScaleImageIterator& operator=(const LocationScaleImageIterator&);
public: public:
LocationScaleImageIterator(const std::vector<Point>& locations, const std::vector<float>& scales) : LocationScaleImageIterator(const std::vector<Point>& locations, const std::vector<float>& scales) :
locations_(locations), scales_(scales) locations_(locations), scales_(scales)
...@@ -394,20 +394,20 @@ private: ...@@ -394,20 +394,20 @@ private:
assert(locations.size()==scales.size()); assert(locations.size()==scales.size());
reset(); reset();
} }
void reset() void reset()
{ {
iter_ = 0; iter_ = 0;
has_next_ = (locations_.size()==0 ? false : true); has_next_ = (locations_.size()==0 ? false : true);
} }
bool hasNext() const { bool hasNext() const {
return has_next_; return has_next_;
} }
location_scale_t next(); location_scale_t next();
}; };
class SlidingWindowImageIterator : public ImageIterator class SlidingWindowImageIterator : public ImageIterator
{ {
int x_; int x_;
...@@ -415,9 +415,9 @@ private: ...@@ -415,9 +415,9 @@ private:
float scale_; float scale_;
float scale_step_; float scale_step_;
int scale_cnt_; int scale_cnt_;
bool has_next_; bool has_next_;
int width_; int width_;
int height_; int height_;
int x_step_; int x_step_;
...@@ -425,22 +425,22 @@ private: ...@@ -425,22 +425,22 @@ private:
int scales_; int scales_;
float min_scale_; float min_scale_;
float max_scale_; float max_scale_;
public: public:
SlidingWindowImageIterator(int width, int height, int x_step, int y_step, int scales, float min_scale, float max_scale); SlidingWindowImageIterator(int width, int height, int x_step, int y_step, int scales, float min_scale, float max_scale);
bool hasNext() const { bool hasNext() const {
return has_next_; return has_next_;
} }
location_scale_t next(); location_scale_t next();
}; };
int count; int count;
Matches matches; Matches matches;
int pad_x; int pad_x;
...@@ -451,7 +451,7 @@ private: ...@@ -451,7 +451,7 @@ private:
float orientation_weight; float orientation_weight;
float truncate; float truncate;
Matching * chamfer_; Matching * chamfer_;
public: public:
ChamferMatcher(int _max_matches = 20, float _min_match_distance = 1.0, int _pad_x = 3, ChamferMatcher(int _max_matches = 20, float _min_match_distance = 1.0, int _pad_x = 3,
int _pad_y = 3, int _scales = 5, float _minScale = 0.6, float _maxScale = 1.6, int _pad_y = 3, int _scales = 5, float _minScale = 0.6, float _maxScale = 1.6,
...@@ -467,371 +467,371 @@ public: ...@@ -467,371 +467,371 @@ public:
orientation_weight = _orientation_weight; orientation_weight = _orientation_weight;
truncate = _truncate; truncate = _truncate;
count = 0; count = 0;
matches.resize(max_matches_); matches.resize(max_matches_);
chamfer_ = new Matching(true); chamfer_ = new Matching(true);
} }
void showMatch(Mat& img, int index = 0); void showMatch(Mat& img, int index = 0);
void showMatch(Mat& img, Match match_); void showMatch(Mat& img, Match match_);
const Matches& matching(Template&, Mat&); const Matches& matching(Template&, Mat&);
private: private:
void addMatch(float cost, Point offset, const Template* tpl); void addMatch(float cost, Point offset, const Template* tpl);
}; };
///////////////////// implementation /////////////////////////// ///////////////////// implementation ///////////////////////////
ChamferMatcher::SlidingWindowImageIterator::SlidingWindowImageIterator( int width, ChamferMatcher::SlidingWindowImageIterator::SlidingWindowImageIterator( int width,
int height, int height,
int x_step = 3, int x_step = 3,
int y_step = 3, int y_step = 3,
int scales = 5, int scales = 5,
float min_scale = 0.6, float min_scale = 0.6,
float max_scale = 1.6) : float max_scale = 1.6) :
width_(width), width_(width),
height_(height), height_(height),
x_step_(x_step), x_step_(x_step),
y_step_(y_step), y_step_(y_step),
scales_(scales), scales_(scales),
min_scale_(min_scale), min_scale_(min_scale),
max_scale_(max_scale) max_scale_(max_scale)
{ {
x_ = 0; x_ = 0;
y_ = 0; y_ = 0;
scale_cnt_ = 0; scale_cnt_ = 0;
scale_ = min_scale_; scale_ = min_scale_;
has_next_ = true; has_next_ = true;
scale_step_ = (max_scale_-min_scale_)/scales_; scale_step_ = (max_scale_-min_scale_)/scales_;
} }
location_scale_t ChamferMatcher::SlidingWindowImageIterator::next() location_scale_t ChamferMatcher::SlidingWindowImageIterator::next()
{ {
location_scale_t next_val = std::make_pair(Point(x_,y_),scale_); location_scale_t next_val = std::make_pair(Point(x_,y_),scale_);
x_ += x_step_; x_ += x_step_;
if (x_ >= width_) { if (x_ >= width_) {
x_ = 0; x_ = 0;
y_ += y_step_; y_ += y_step_;
if (y_ >= height_) { if (y_ >= height_) {
y_ = 0; y_ = 0;
scale_ += scale_step_; scale_ += scale_step_;
scale_cnt_++; scale_cnt_++;
if (scale_cnt_ == scales_) { if (scale_cnt_ == scales_) {
has_next_ = false; has_next_ = false;
scale_cnt_ = 0; scale_cnt_ = 0;
scale_ = min_scale_; scale_ = min_scale_;
} }
} }
} }
return next_val; return next_val;
} }
ChamferMatcher::ImageIterator* ChamferMatcher::SlidingWindowImageRange::iterator() const ChamferMatcher::ImageIterator* ChamferMatcher::SlidingWindowImageRange::iterator() const
{ {
return new SlidingWindowImageIterator(width_, height_, x_step_, y_step_, scales_, min_scale_, max_scale_); return new SlidingWindowImageIterator(width_, height_, x_step_, y_step_, scales_, min_scale_, max_scale_);
} }
ChamferMatcher::LocationImageIterator::LocationImageIterator(const std::vector<Point>& locations, ChamferMatcher::LocationImageIterator::LocationImageIterator(const std::vector<Point>& locations,
int scales = 5, int scales = 5,
float min_scale = 0.6, float min_scale = 0.6,
float max_scale = 1.6) : float max_scale = 1.6) :
locations_(locations), locations_(locations),
scales_(scales), scales_(scales),
min_scale_(min_scale), min_scale_(min_scale),
max_scale_(max_scale) max_scale_(max_scale)
{ {
iter_ = 0; iter_ = 0;
scale_cnt_ = 0; scale_cnt_ = 0;
scale_ = min_scale_; scale_ = min_scale_;
has_next_ = (locations_.size()==0 ? false : true); has_next_ = (locations_.size()==0 ? false : true);
scale_step_ = (max_scale_-min_scale_)/scales_; scale_step_ = (max_scale_-min_scale_)/scales_;
} }
location_scale_t ChamferMatcher::LocationImageIterator:: next() location_scale_t ChamferMatcher::LocationImageIterator:: next()
{ {
location_scale_t next_val = std::make_pair(locations_[iter_],scale_); location_scale_t next_val = std::make_pair(locations_[iter_],scale_);
iter_ ++; iter_ ++;
if (iter_==locations_.size()) { if (iter_==locations_.size()) {
iter_ = 0; iter_ = 0;
scale_ += scale_step_; scale_ += scale_step_;
scale_cnt_++; scale_cnt_++;
if (scale_cnt_ == scales_) { if (scale_cnt_ == scales_) {
has_next_ = false; has_next_ = false;
scale_cnt_ = 0; scale_cnt_ = 0;
scale_ = min_scale_; scale_ = min_scale_;
} }
} }
return next_val; return next_val;
} }
location_scale_t ChamferMatcher::LocationScaleImageIterator::next() location_scale_t ChamferMatcher::LocationScaleImageIterator::next()
{ {
location_scale_t next_val = std::make_pair(locations_[iter_],scales_[iter_]); location_scale_t next_val = std::make_pair(locations_[iter_],scales_[iter_]);
iter_ ++; iter_ ++;
if (iter_==locations_.size()) { if (iter_==locations_.size()) {
iter_ = 0; iter_ = 0;
has_next_ = false; has_next_ = false;
} }
return next_val; return next_val;
} }
bool ChamferMatcher::Matching::findFirstContourPoint(Mat& templ_img, coordinate_t& p) bool ChamferMatcher::Matching::findFirstContourPoint(Mat& templ_img, coordinate_t& p)
{ {
for (int y=0;y<templ_img.rows;++y) { for (int y=0;y<templ_img.rows;++y) {
for (int x=0;x<templ_img.cols;++x) { for (int x=0;x<templ_img.cols;++x) {
if (templ_img.at<uchar>(y,x)!=0) { if (templ_img.at<uchar>(y,x)!=0) {
p.first = x; p.first = x;
p.second = y; p.second = y;
return true; return true;
} }
} }
} }
return false; return false;
} }
void ChamferMatcher::Matching::followContour(Mat& templ_img, template_coords_t& coords, int direction = -1) void ChamferMatcher::Matching::followContour(Mat& templ_img, template_coords_t& coords, int direction = -1)
{ {
const int dir[][2] = { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} }; const int dir[][2] = { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} };
coordinate_t next; coordinate_t next;
coordinate_t next_temp; coordinate_t next_temp;
unsigned char ptr; unsigned char ptr;
assert (direction==-1 || !coords.empty()); assert (direction==-1 || !coords.empty());
coordinate_t crt = coords.back(); coordinate_t crt = coords.back();
// mark the current pixel as visited // mark the current pixel as visited
templ_img.at<uchar>(crt.second,crt.first) = 0; templ_img.at<uchar>(crt.second,crt.first) = 0;
if (direction==-1) { if (direction==-1) {
for (int j = 0; j<7; ++j) { for (int j = 0; j<7; ++j) {
next.first = crt.first + dir[j][1]; next.first = crt.first + dir[j][1];
next.second = crt.second + dir[j][0]; next.second = crt.second + dir[j][0];
if (next.first >= 0 && next.first < templ_img.cols && if (next.first >= 0 && next.first < templ_img.cols &&
next.second >= 0 && next.second < templ_img.rows){ next.second >= 0 && next.second < templ_img.rows){
ptr = templ_img.at<uchar>(next.second, next.first); ptr = templ_img.at<uchar>(next.second, next.first);
if (ptr!=0) { if (ptr!=0) {
coords.push_back(next); coords.push_back(next);
followContour(templ_img, coords,j); followContour(templ_img, coords,j);
// try to continue contour in the other direction // try to continue contour in the other direction
reverse(coords.begin(), coords.end()); reverse(coords.begin(), coords.end());
followContour(templ_img, coords, (j+4)%8); followContour(templ_img, coords, (j+4)%8);
break; break;
} }
} }
} }
} }
else { else {
int k = direction; int k = direction;
int k_cost = 3; int k_cost = 3;
next.first = crt.first + dir[k][1]; next.first = crt.first + dir[k][1];
next.second = crt.second + dir[k][0]; next.second = crt.second + dir[k][0];
if (next.first >= 0 && next.first < templ_img.cols && if (next.first >= 0 && next.first < templ_img.cols &&
next.second >= 0 && next.second < templ_img.rows){ next.second >= 0 && next.second < templ_img.rows){
ptr = templ_img.at<uchar>(next.second, next.first); ptr = templ_img.at<uchar>(next.second, next.first);
if (ptr!=0) { if (ptr!=0) {
k_cost = std::abs(dir[k][1]) + std::abs(dir[k][0]); k_cost = std::abs(dir[k][1]) + std::abs(dir[k][0]);
} }
int p = k; int p = k;
int n = k; int n = k;
for (int j = 0 ;j<3; ++j) { for (int j = 0 ;j<3; ++j) {
p = (p + 7) % 8; p = (p + 7) % 8;
n = (n + 1) % 8; n = (n + 1) % 8;
next.first = crt.first + dir[p][1]; next.first = crt.first + dir[p][1];
next.second = crt.second + dir[p][0]; next.second = crt.second + dir[p][0];
if (next.first >= 0 && next.first < templ_img.cols && if (next.first >= 0 && next.first < templ_img.cols &&
next.second >= 0 && next.second < templ_img.rows){ next.second >= 0 && next.second < templ_img.rows){
ptr = templ_img.at<uchar>(next.second, next.first); ptr = templ_img.at<uchar>(next.second, next.first);
if (ptr!=0) { if (ptr!=0) {
int p_cost = std::abs(dir[p][1]) + std::abs(dir[p][0]); int p_cost = std::abs(dir[p][1]) + std::abs(dir[p][0]);
if (p_cost<k_cost) { if (p_cost<k_cost) {
k_cost = p_cost; k_cost = p_cost;
k = p; k = p;
} }
} }
next.first = crt.first + dir[n][1]; next.first = crt.first + dir[n][1];
next.second = crt.second + dir[n][0]; next.second = crt.second + dir[n][0];
if (next.first >= 0 && next.first < templ_img.cols && if (next.first >= 0 && next.first < templ_img.cols &&
next.second >= 0 && next.second < templ_img.rows){ next.second >= 0 && next.second < templ_img.rows){
ptr = templ_img.at<uchar>(next.second, next.first); ptr = templ_img.at<uchar>(next.second, next.first);
if (ptr!=0) { if (ptr!=0) {
int n_cost = std::abs(dir[n][1]) + std::abs(dir[n][0]); int n_cost = std::abs(dir[n][1]) + std::abs(dir[n][0]);
if (n_cost<k_cost) { if (n_cost<k_cost) {
k_cost = n_cost; k_cost = n_cost;
k = n; k = n;
} }
} }
} }
} }
} }
if (k_cost!=3) { if (k_cost!=3) {
next.first = crt.first + dir[k][1]; next.first = crt.first + dir[k][1];
next.second = crt.second + dir[k][0]; next.second = crt.second + dir[k][0];
if (next.first >= 0 && next.first < templ_img.cols && if (next.first >= 0 && next.first < templ_img.cols &&
next.second >= 0 && next.second < templ_img.rows) { next.second >= 0 && next.second < templ_img.rows) {
coords.push_back(next); coords.push_back(next);
followContour(templ_img, coords, k); followContour(templ_img, coords, k);
} }
} }
} }
} }
} }
bool ChamferMatcher::Matching::findContour(Mat& templ_img, template_coords_t& coords) bool ChamferMatcher::Matching::findContour(Mat& templ_img, template_coords_t& coords)
{ {
coordinate_t start_point; coordinate_t start_point;
bool found = findFirstContourPoint(templ_img,start_point); bool found = findFirstContourPoint(templ_img,start_point);
if (found) { if (found) {
coords.push_back(start_point); coords.push_back(start_point);
followContour(templ_img, coords); followContour(templ_img, coords);
return true; return true;
} }
return false; return false;
} }
float ChamferMatcher::Matching::getAngle(coordinate_t a, coordinate_t b, int& dx, int& dy) float ChamferMatcher::Matching::getAngle(coordinate_t a, coordinate_t b, int& dx, int& dy)
{ {
dx = b.first-a.first; dx = b.first-a.first;
dy = -(b.second-a.second); // in image coordinated Y axis points downward dy = -(b.second-a.second); // in image coordinated Y axis points downward
float angle = atan2((float)dy,(float)dx); float angle = atan2((float)dy,(float)dx);
if (angle<0) { if (angle<0) {
angle+=(float)CV_PI; angle+=(float)CV_PI;
} }
return angle; return angle;
} }
void ChamferMatcher::Matching::findContourOrientations(const template_coords_t& coords, template_orientations_t& orientations) void ChamferMatcher::Matching::findContourOrientations(const template_coords_t& coords, template_orientations_t& orientations)
{ {
const int M = 5; const int M = 5;
int coords_size = (int)coords.size(); int coords_size = (int)coords.size();
std::vector<float> angles(2*M); std::vector<float> angles(2*M);
orientations.insert(orientations.begin(), coords_size, float(-3*CV_PI)); // mark as invalid in the beginning orientations.insert(orientations.begin(), coords_size, float(-3*CV_PI)); // mark as invalid in the beginning
if (coords_size<2*M+1) { // if contour not long enough to estimate orientations, abort if (coords_size<2*M+1) { // if contour not long enough to estimate orientations, abort
return; return;
} }
for (int i=M;i<coords_size-M;++i) { for (int i=M;i<coords_size-M;++i) {
coordinate_t crt = coords[i]; coordinate_t crt = coords[i];
coordinate_t other; coordinate_t other;
int k = 0; int k = 0;
int dx, dy; int dx, dy;
// compute previous M angles // compute previous M angles
for (int j=M;j>0;--j) { for (int j=M;j>0;--j) {
other = coords[i-j]; other = coords[i-j];
angles[k++] = getAngle(other,crt, dx, dy); angles[k++] = getAngle(other,crt, dx, dy);
} }
// compute next M angles // compute next M angles
for (int j=1;j<=M;++j) { for (int j=1;j<=M;++j) {
other = coords[i+j]; other = coords[i+j];
angles[k++] = getAngle(crt, other, dx, dy); angles[k++] = getAngle(crt, other, dx, dy);
} }
// get the middle two angles // get the middle two angles
nth_element(angles.begin(), angles.begin()+M-1, angles.end()); nth_element(angles.begin(), angles.begin()+M-1, angles.end());
nth_element(angles.begin()+M-1, angles.begin()+M, angles.end()); nth_element(angles.begin()+M-1, angles.begin()+M, angles.end());
// sort(angles.begin(), angles.end()); // sort(angles.begin(), angles.end());
// average them to compute tangent // average them to compute tangent
orientations[i] = (angles[M-1]+angles[M])/2; orientations[i] = (angles[M-1]+angles[M])/2;
} }
} }
//////////////////////// Template ///////////////////////////////////// //////////////////////// Template /////////////////////////////////////
ChamferMatcher::Template::Template(Mat& edge_image, float scale_) : addr_width(-1), scale(scale_) ChamferMatcher::Template::Template(Mat& edge_image, float scale_) : addr_width(-1), scale(scale_)
{ {
template_coords_t local_coords; template_coords_t local_coords;
template_orientations_t local_orientations; template_orientations_t local_orientations;
while (ChamferMatcher::Matching::findContour(edge_image, local_coords)) { while (ChamferMatcher::Matching::findContour(edge_image, local_coords)) {
ChamferMatcher::Matching::findContourOrientations(local_coords, local_orientations); ChamferMatcher::Matching::findContourOrientations(local_coords, local_orientations);
coords.insert(coords.end(), local_coords.begin(), local_coords.end()); coords.insert(coords.end(), local_coords.begin(), local_coords.end());
orientations.insert(orientations.end(), local_orientations.begin(), local_orientations.end()); orientations.insert(orientations.end(), local_orientations.begin(), local_orientations.end());
local_coords.clear(); local_coords.clear();
local_orientations.clear(); local_orientations.clear();
} }
size = edge_image.size(); size = edge_image.size();
Point min, max; Point min, max;
min.x = size.width; min.x = size.width;
min.y = size.height; min.y = size.height;
max.x = 0; max.x = 0;
max.y = 0; max.y = 0;
center = Point(0,0); center = Point(0,0);
for (size_t i=0;i<coords.size();++i) { for (size_t i=0;i<coords.size();++i) {
center.x += coords[i].first; center.x += coords[i].first;
center.y += coords[i].second; center.y += coords[i].second;
if (min.x>coords[i].first) min.x = coords[i].first; if (min.x>coords[i].first) min.x = coords[i].first;
if (min.y>coords[i].second) min.y = coords[i].second; if (min.y>coords[i].second) min.y = coords[i].second;
if (max.x<coords[i].first) max.x = coords[i].first; if (max.x<coords[i].first) max.x = coords[i].first;
if (max.y<coords[i].second) max.y = coords[i].second; if (max.y<coords[i].second) max.y = coords[i].second;
} }
size.width = max.x - min.x; size.width = max.x - min.x;
size.height = max.y - min.y; size.height = max.y - min.y;
int coords_size = (int)coords.size(); int coords_size = (int)coords.size();
center.x /= MAX(coords_size, 1); center.x /= MAX(coords_size, 1);
center.y /= MAX(coords_size, 1); center.y /= MAX(coords_size, 1);
for (int i=0;i<coords_size;++i) { for (int i=0;i<coords_size;++i) {
coords[i].first -= center.x; coords[i].first -= center.x;
coords[i].second -= center.y; coords[i].second -= center.y;
} }
} }
vector<int>& ChamferMatcher::Template::getTemplateAddresses(int width) vector<int>& ChamferMatcher::Template::getTemplateAddresses(int width)
{ {
if (addr_width!=width) { if (addr_width!=width) {
addr.resize(coords.size()); addr.resize(coords.size());
addr_width = width; addr_width = width;
for (size_t i=0; i<coords.size();++i) { for (size_t i=0; i<coords.size();++i) {
addr[i] = coords[i].second*width+coords[i].first; addr[i] = coords[i].second*width+coords[i].first;
} }
} }
return addr; return addr;
} }
...@@ -843,35 +843,35 @@ vector<int>& ChamferMatcher::Template::getTemplateAddresses(int width) ...@@ -843,35 +843,35 @@ vector<int>& ChamferMatcher::Template::getTemplateAddresses(int width)
ChamferMatcher::Template* ChamferMatcher::Template::rescale(float new_scale) ChamferMatcher::Template* ChamferMatcher::Template::rescale(float new_scale)
{ {
if (fabs(scale-new_scale)<1e-6) return this; if (fabs(scale-new_scale)<1e-6) return this;
for (size_t i=0;i<scaled_templates.size();++i) { for (size_t i=0;i<scaled_templates.size();++i) {
if (fabs(scaled_templates[i]->scale-new_scale)<1e-6) { if (fabs(scaled_templates[i]->scale-new_scale)<1e-6) {
return scaled_templates[i]; return scaled_templates[i];
} }
} }
float scale_factor = new_scale/scale; float scale_factor = new_scale/scale;
Template* tpl = new Template(); Template* tpl = new Template();
tpl->scale = new_scale; tpl->scale = new_scale;
tpl->center.x = int(center.x*scale_factor+0.5); tpl->center.x = int(center.x*scale_factor+0.5);
tpl->center.y = int(center.y*scale_factor+0.5); tpl->center.y = int(center.y*scale_factor+0.5);
tpl->size.width = int(size.width*scale_factor+0.5); tpl->size.width = int(size.width*scale_factor+0.5);
tpl->size.height = int(size.height*scale_factor+0.5); tpl->size.height = int(size.height*scale_factor+0.5);
tpl->coords.resize(coords.size()); tpl->coords.resize(coords.size());
tpl->orientations.resize(orientations.size()); tpl->orientations.resize(orientations.size());
for (size_t i=0;i<coords.size();++i) { for (size_t i=0;i<coords.size();++i) {
tpl->coords[i].first = int(coords[i].first*scale_factor+0.5); tpl->coords[i].first = int(coords[i].first*scale_factor+0.5);
tpl->coords[i].second = int(coords[i].second*scale_factor+0.5); tpl->coords[i].second = int(coords[i].second*scale_factor+0.5);
tpl->orientations[i] = orientations[i]; tpl->orientations[i] = orientations[i];
} }
scaled_templates.push_back(tpl); scaled_templates.push_back(tpl);
return tpl; return tpl;
} }
...@@ -879,42 +879,42 @@ ChamferMatcher::Template* ChamferMatcher::Template::rescale(float new_scale) ...@@ -879,42 +879,42 @@ ChamferMatcher::Template* ChamferMatcher::Template::rescale(float new_scale)
void ChamferMatcher::Template::show() const void ChamferMatcher::Template::show() const
{ {
int pad = 50; int pad = 50;
//Attention size is not correct //Attention size is not correct
Mat templ_color (Size(size.width+(pad*2), size.height+(pad*2)), CV_8UC3); Mat templ_color (Size(size.width+(pad*2), size.height+(pad*2)), CV_8UC3);
templ_color.setTo(0); templ_color.setTo(0);
for (size_t i=0;i<coords.size();++i) { for (size_t i=0;i<coords.size();++i) {
int x = center.x+coords[i].first+pad; int x = center.x+coords[i].first+pad;
int y = center.y+coords[i].second+pad; int y = center.y+coords[i].second+pad;
templ_color.at<Vec3b>(y,x)[1]=255; templ_color.at<Vec3b>(y,x)[1]=255;
//CV_PIXEL(unsigned char, templ_color,x,y)[1] = 255; //CV_PIXEL(unsigned char, templ_color,x,y)[1] = 255;
if (i%3==0) { if (i%3==0) {
if (orientations[i] < -CV_PI) { if (orientations[i] < -CV_PI) {
continue; continue;
} }
Point p1; Point p1;
p1.x = x; p1.x = x;
p1.y = y; p1.y = y;
Point p2; Point p2;
p2.x = x + pad*(int)(sin(orientations[i])*100)/100; p2.x = x + pad*(int)(sin(orientations[i])*100)/100;
p2.y = y + pad*(int)(cos(orientations[i])*100)/100; p2.y = y + pad*(int)(cos(orientations[i])*100)/100;
line(templ_color, p1,p2, CV_RGB(255,0,0)); line(templ_color, p1,p2, CV_RGB(255,0,0));
} }
} }
circle(templ_color,Point(center.x + pad, center.y + pad),1,CV_RGB(0,255,0)); circle(templ_color,Point(center.x + pad, center.y + pad),1,CV_RGB(0,255,0));
namedWindow("templ",1); namedWindow("templ",1);
imshow("templ",templ_color); imshow("templ",templ_color);
cvWaitKey(0); cvWaitKey(0);
templ_color.release(); templ_color.release();
} }
...@@ -923,17 +923,17 @@ void ChamferMatcher::Template::show() const ...@@ -923,17 +923,17 @@ void ChamferMatcher::Template::show() const
void ChamferMatcher::Matching::addTemplateFromImage(Mat& templ, float scale) void ChamferMatcher::Matching::addTemplateFromImage(Mat& templ, float scale)
{ {
Template* cmt = new Template(templ, scale); Template* cmt = new Template(templ, scale);
if(templates.size() > 0) if(templates.size() > 0)
templates.clear(); templates.clear();
templates.push_back(cmt); templates.push_back(cmt);
cmt->show(); cmt->show();
} }
void ChamferMatcher::Matching::addTemplate(Template& template_){ void ChamferMatcher::Matching::addTemplate(Template& template_){
if(templates.size() > 0) if(templates.size() > 0)
templates.clear(); templates.clear();
templates.push_back(&template_); templates.push_back(&template_);
} }
/** /**
* Alternative version of computeDistanceTransform, will probably be used to compute distance * Alternative version of computeDistanceTransform, will probably be used to compute distance
...@@ -941,224 +941,227 @@ void ChamferMatcher::Matching::addTemplate(Template& template_){ ...@@ -941,224 +941,227 @@ void ChamferMatcher::Matching::addTemplate(Template& template_){
*/ */
void ChamferMatcher::Matching::computeDistanceTransform(Mat& edges_img, Mat& dist_img, Mat& annotate_img, float truncate_dt, float a = 1.0, float b = 1.5) void ChamferMatcher::Matching::computeDistanceTransform(Mat& edges_img, Mat& dist_img, Mat& annotate_img, float truncate_dt, float a = 1.0, float b = 1.5)
{ {
int d[][2] = { {-1,-1}, { 0,-1}, { 1,-1}, int d[][2] = { {-1,-1}, { 0,-1}, { 1,-1},
{-1,0}, { 1,0}, {-1,0}, { 1,0},
{-1,1}, { 0,1}, { 1,1} }; {-1,1}, { 0,1}, { 1,1} };
Size s = edges_img.size(); Size s = edges_img.size();
int w = s.width; int w = s.width;
int h = s.height; int h = s.height;
// set distance to the edge pixels to 0 and put them in the queue // set distance to the edge pixels to 0 and put them in the queue
std::queue<std::pair<int,int> > q; std::queue<std::pair<int,int> > q;
for (int y=0;y<h;++y) { for (int y=0;y<h;++y) {
for (int x=0;x<w;++x) { for (int x=0;x<w;++x) {
unsigned char edge_val = edges_img.at<uchar>(y,x); unsigned char edge_val = edges_img.at<uchar>(y,x);
if ( (edge_val!=0) ) { if ( (edge_val!=0) ) {
q.push(std::make_pair(x,y)); q.push(std::make_pair(x,y));
dist_img.at<float>(y,x)= 0; dist_img.at<float>(y,x)= 0;
if (&annotate_img!=NULL) { if (&annotate_img!=NULL) {
annotate_img.at<Vec2i>(y,x)[0]=x; annotate_img.at<Vec2i>(y,x)[0]=x;
annotate_img.at<Vec2i>(y,x)[1]=y; annotate_img.at<Vec2i>(y,x)[1]=y;
} }
} }
else { else {
dist_img.at<float>(y,x)=-1; dist_img.at<float>(y,x)=-1;
} }
} }
} }
// breadth first computation of distance transform // breadth first computation of distance transform
std::pair<int,int> crt; std::pair<int,int> crt;
while (!q.empty()) { while (!q.empty()) {
crt = q.front(); crt = q.front();
q.pop(); q.pop();
int x = crt.first; int x = crt.first;
int y = crt.second; int y = crt.second;
float dist_orig = dist_img.at<float>(y,x); float dist_orig = dist_img.at<float>(y,x);
float dist; float dist;
for (size_t i=0;i<sizeof(d)/sizeof(d[0]);++i) { for (size_t i=0;i<sizeof(d)/sizeof(d[0]);++i) {
int nx = x + d[i][0]; int nx = x + d[i][0];
int ny = y + d[i][1]; int ny = y + d[i][1];
if (nx<0 || ny<0 || nx>=w || ny>=h) continue; if (nx<0 || ny<0 || nx>=w || ny>=h) continue;
if (std::abs(d[i][0]+d[i][1])==1) { if (std::abs(d[i][0]+d[i][1])==1) {
dist = (dist_orig)+a; dist = (dist_orig)+a;
} }
else { else {
dist = (dist_orig)+b; dist = (dist_orig)+b;
} }
float dt = dist_img.at<float>(ny,nx); float dt = dist_img.at<float>(ny,nx);
if (dt==-1 || dt>dist) { if (dt==-1 || dt>dist) {
dist_img.at<float>(ny,nx) = dist; dist_img.at<float>(ny,nx) = dist;
q.push(std::make_pair(nx,ny)); q.push(std::make_pair(nx,ny));
if (&annotate_img!=NULL) { if (&annotate_img!=NULL) {
annotate_img.at<Vec2i>(ny,nx)[0]=annotate_img.at<Vec2i>(y,x)[0]; annotate_img.at<Vec2i>(ny,nx)[0]=annotate_img.at<Vec2i>(y,x)[0];
annotate_img.at<Vec2i>(ny,nx)[1]=annotate_img.at<Vec2i>(y,x)[1]; annotate_img.at<Vec2i>(ny,nx)[1]=annotate_img.at<Vec2i>(y,x)[1];
} }
} }
} }
} }
// truncate dt // truncate dt
if (truncate_dt>0) { if (truncate_dt>0) {
Mat dist_img_thr = dist_img.clone(); Mat dist_img_thr = dist_img.clone();
threshold(dist_img, dist_img_thr, truncate_dt,0.0 ,THRESH_TRUNC); threshold(dist_img, dist_img_thr, truncate_dt,0.0 ,THRESH_TRUNC);
dist_img_thr.copyTo(dist_img); dist_img_thr.copyTo(dist_img);
} }
} }
void ChamferMatcher::Matching::computeEdgeOrientations(Mat& edge_img, Mat& orientation_img) void ChamferMatcher::Matching::computeEdgeOrientations(Mat& edge_img, Mat& orientation_img)
{ {
Mat contour_img(edge_img.size(), CV_8UC1); Mat contour_img(edge_img.size(), CV_8UC1);
orientation_img.setTo(3*(-CV_PI)); orientation_img.setTo(3*(-CV_PI));
template_coords_t coords; template_coords_t coords;
template_orientations_t orientations; template_orientations_t orientations;
while (ChamferMatcher::Matching::findContour(edge_img, coords)) { while (ChamferMatcher::Matching::findContour(edge_img, coords)) {
ChamferMatcher::Matching::findContourOrientations(coords, orientations); ChamferMatcher::Matching::findContourOrientations(coords, orientations);
// set orientation pixel in orientation image // set orientation pixel in orientation image
for (size_t i = 0; i<coords.size();++i) { for (size_t i = 0; i<coords.size();++i) {
int x = coords[i].first; int x = coords[i].first;
int y = coords[i].second; int y = coords[i].second;
// if (orientations[i]>-CV_PI) // if (orientations[i]>-CV_PI)
// { // {
//CV_PIXEL(unsigned char, contour_img, x, y)[0] = 255; //CV_PIXEL(unsigned char, contour_img, x, y)[0] = 255;
contour_img.at<uchar>(y,x)=255; contour_img.at<uchar>(y,x)=255;
// } // }
//CV_PIXEL(float, orientation_img, x, y)[0] = orientations[i]; //CV_PIXEL(float, orientation_img, x, y)[0] = orientations[i];
orientation_img.at<float>(y,x)=orientations[i]; orientation_img.at<float>(y,x)=orientations[i];
} }
coords.clear(); coords.clear();
orientations.clear(); orientations.clear();
} }
//imwrite("contours.pgm", contour_img); //imwrite("contours.pgm", contour_img);
} }
void ChamferMatcher::Matching::fillNonContourOrientations(Mat& annotated_img, Mat& orientation_img) void ChamferMatcher::Matching::fillNonContourOrientations(Mat& annotated_img, Mat& orientation_img)
{ {
int cols = annotated_img.cols; int cols = annotated_img.cols;
int rows = annotated_img.rows; int rows = annotated_img.rows;
assert(orientation_img.cols==cols && orientation_img.rows==rows); assert(orientation_img.cols==cols && orientation_img.rows==rows);
for (int y=0;y<rows;++y) { for (int y=0;y<rows;++y) {
for (int x=0;x<cols;++x) { for (int x=0;x<cols;++x) {
int xorig = annotated_img.at<Vec2i>(y,x)[0]; int xorig = annotated_img.at<Vec2i>(y,x)[0];
int yorig = annotated_img.at<Vec2i>(y,x)[1]; int yorig = annotated_img.at<Vec2i>(y,x)[1];
if (x!=xorig || y!=yorig) { if (x!=xorig || y!=yorig) {
//orientation_img.at<float>(yorig,xorig)=orientation_img.at<float>(y,x); //orientation_img.at<float>(yorig,xorig)=orientation_img.at<float>(y,x);
orientation_img.at<float>(y,x)=orientation_img.at<float>(yorig,xorig); orientation_img.at<float>(y,x)=orientation_img.at<float>(yorig,xorig);
} }
} }
} }
} }
ChamferMatcher::Match* ChamferMatcher::Matching::localChamferDistance(Point offset, Mat& dist_img, Mat& orientation_img, ChamferMatcher::Match* ChamferMatcher::Matching::localChamferDistance(Point offset, Mat& dist_img, Mat& orientation_img,
ChamferMatcher::Template* tpl, float alpha) ChamferMatcher::Template* tpl, float alpha)
{ {
int x = offset.x; int x = offset.x;
int y = offset.y; int y = offset.y;
float beta = 1-alpha; float beta = 1-alpha;
std::vector<int>& addr = tpl->getTemplateAddresses(dist_img.cols); std::vector<int>& addr = tpl->getTemplateAddresses(dist_img.cols);
float* ptr = dist_img.ptr<float>(y)+x; float* ptr = dist_img.ptr<float>(y)+x;
float sum_distance = 0; float sum_distance = 0;
for (size_t i=0; i<addr.size();++i) { for (size_t i=0; i<addr.size();++i) {
if(addr[i] < (dist_img.cols*dist_img.rows) - (offset.y*dist_img.cols + offset.x)){ if(addr[i] < (dist_img.cols*dist_img.rows) - (offset.y*dist_img.cols + offset.x)){
sum_distance += *(ptr+addr[i]); sum_distance += *(ptr+addr[i]);
} }
} }
float cost = (sum_distance/truncate_)/addr.size(); float cost = (sum_distance/truncate_)/addr.size();
if (&orientation_img!=NULL) { if (&orientation_img!=NULL) {
float* optr = orientation_img.ptr<float>(y)+x; float* optr = orientation_img.ptr<float>(y)+x;
float sum_orientation = 0; float sum_orientation = 0;
int cnt_orientation = 0; int cnt_orientation = 0;
for (size_t i=0;i<addr.size();++i) { for (size_t i=0;i<addr.size();++i) {
if(addr[i] < (orientation_img.cols*orientation_img.rows) - (offset.y*orientation_img.cols + offset.x)){ if(addr[i] < (orientation_img.cols*orientation_img.rows) - (offset.y*orientation_img.cols + offset.x)){
if (tpl->orientations[i]>=-CV_PI && (*(optr+addr[i]))>=-CV_PI) { if (tpl->orientations[i]>=-CV_PI && (*(optr+addr[i]))>=-CV_PI) {
sum_orientation += orientation_diff(tpl->orientations[i], (*(optr+addr[i]))); sum_orientation += orientation_diff(tpl->orientations[i], (*(optr+addr[i])));
cnt_orientation++; cnt_orientation++;
} }
} }
} }
if (cnt_orientation>0) { if (cnt_orientation>0) {
cost = (float)(beta*cost+alpha*(sum_orientation/(2*CV_PI))/cnt_orientation); cost = (float)(beta*cost+alpha*(sum_orientation/(2*CV_PI))/cnt_orientation);
} }
} }
if(cost > 0){ if(cost > 0){
ChamferMatcher::Match* istance(new ChamferMatcher::Match()); ChamferMatcher::Match* istance = new ChamferMatcher::Match();
istance->cost = cost; istance->cost = cost;
istance->offset = offset; istance->offset = offset;
istance->tpl = tpl; istance->tpl = tpl;
return istance; return istance;
} }
return NULL; return NULL;
} }
ChamferMatcher::Matches* ChamferMatcher::Matching::matchTemplates(Mat& dist_img, Mat& orientation_img, const ImageRange& range, float orientation_weight) ChamferMatcher::Matches* ChamferMatcher::Matching::matchTemplates(Mat& dist_img, Mat& orientation_img, const ImageRange& range, float orientation_weight)
{ {
ChamferMatcher::Matches* matches(new Matches()); ChamferMatcher::Matches* matches(new Matches());
// try each template // try each template
for(size_t i = 0; i < templates.size(); i++) { for(size_t i = 0; i < templates.size(); i++) {
ImageIterator* it = range.iterator(); ImageIterator* it = range.iterator();
while (it->hasNext()) { while (it->hasNext()) {
location_scale_t crt = it->next(); location_scale_t crt = it->next();
Point loc = crt.first; Point loc = crt.first;
float scale = crt.second; float scale = crt.second;
Template* tpl = templates[i]->rescale(scale); Template* tpl = templates[i]->rescale(scale);
if (loc.x-tpl->center.x<0 || loc.x+tpl->size.width/2>=dist_img.cols) continue; if (loc.x-tpl->center.x<0 || loc.x+tpl->size.width/2>=dist_img.cols) continue;
if (loc.y-tpl->center.y<0 || loc.y+tpl->size.height/2>=dist_img.rows) continue; if (loc.y-tpl->center.y<0 || loc.y+tpl->size.height/2>=dist_img.rows) continue;
ChamferMatcher::Match* is = localChamferDistance(loc, dist_img, orientation_img, tpl, orientation_weight); ChamferMatcher::Match* is = localChamferDistance(loc, dist_img, orientation_img, tpl, orientation_weight);
if(is) if(is)
matches->push_back(*is); {
} matches->push_back(*is);
delete is;
delete it; }
} }
return matches;
delete it;
}
return matches;
} }
...@@ -1170,184 +1173,184 @@ ChamferMatcher::Matches* ChamferMatcher::Matching::matchTemplates(Mat& dist_img, ...@@ -1170,184 +1173,184 @@ ChamferMatcher::Matches* ChamferMatcher::Matching::matchTemplates(Mat& dist_img,
*/ */
ChamferMatcher::Matches* ChamferMatcher::Matching::matchEdgeImage(Mat& edge_img, const ImageRange& range, float orientation_weight, int /*max_matches*/, float /*min_match_distance*/) ChamferMatcher::Matches* ChamferMatcher::Matching::matchEdgeImage(Mat& edge_img, const ImageRange& range, float orientation_weight, int /*max_matches*/, float /*min_match_distance*/)
{ {
CV_Assert(edge_img.channels()==1); CV_Assert(edge_img.channels()==1);
Mat dist_img; Mat dist_img;
Mat annotated_img; Mat annotated_img;
Mat orientation_img; Mat orientation_img;
annotated_img.create(edge_img.size(), CV_32SC2); annotated_img.create(edge_img.size(), CV_32SC2);
dist_img.create(edge_img.size(),CV_32FC1); dist_img.create(edge_img.size(),CV_32FC1);
dist_img.setTo(0); dist_img.setTo(0);
// Computing distance transform // Computing distance transform
computeDistanceTransform(edge_img,dist_img, annotated_img, truncate_); computeDistanceTransform(edge_img,dist_img, annotated_img, truncate_);
//orientation_img = NULL; //orientation_img = NULL;
if (use_orientation_) { if (use_orientation_) {
orientation_img.create(edge_img.size(), CV_32FC1); orientation_img.create(edge_img.size(), CV_32FC1);
orientation_img.setTo(0); orientation_img.setTo(0);
Mat edge_clone = edge_img.clone(); Mat edge_clone = edge_img.clone();
computeEdgeOrientations(edge_clone, orientation_img ); computeEdgeOrientations(edge_clone, orientation_img );
edge_clone.release(); edge_clone.release();
fillNonContourOrientations(annotated_img, orientation_img); fillNonContourOrientations(annotated_img, orientation_img);
} }
// Template matching // Template matching
ChamferMatcher::Matches* matches = matchTemplates( dist_img, ChamferMatcher::Matches* matches = matchTemplates( dist_img,
orientation_img, orientation_img,
range, range,
orientation_weight); orientation_weight);
if (use_orientation_) { if (use_orientation_) {
orientation_img.release(); orientation_img.release();
} }
dist_img.release(); dist_img.release();
annotated_img.release(); annotated_img.release();
return matches; return matches;
} }
void ChamferMatcher::addMatch(float cost, Point offset, const Template* tpl) void ChamferMatcher::addMatch(float cost, Point offset, const Template* tpl)
{ {
bool new_match = true; bool new_match = true;
for (int i=0; i<count; ++i) { for (int i=0; i<count; ++i) {
if (std::abs(matches[i].offset.x-offset.x)+std::abs(matches[i].offset.y-offset.y)<min_match_distance_) { if (std::abs(matches[i].offset.x-offset.x)+std::abs(matches[i].offset.y-offset.y)<min_match_distance_) {
// too close, not a new match // too close, not a new match
new_match = false; new_match = false;
// if better cost, replace existing match // if better cost, replace existing match
if (cost<matches[i].cost) { if (cost<matches[i].cost) {
matches[i].cost = cost; matches[i].cost = cost;
matches[i].offset = offset; matches[i].offset = offset;
matches[i].tpl = tpl; matches[i].tpl = tpl;
} }
// re-bubble to keep ordered // re-bubble to keep ordered
int k = i; int k = i;
while (k>0) { while (k>0) {
if (matches[k-1].cost>matches[k].cost) { if (matches[k-1].cost>matches[k].cost) {
std::swap(matches[k-1],matches[k]); std::swap(matches[k-1],matches[k]);
} }
k--; k--;
} }
break; break;
} }
} }
if (new_match) { if (new_match) {
// if we don't have enough matches yet, add it to the array // if we don't have enough matches yet, add it to the array
if (count<max_matches_) { if (count<max_matches_) {
matches[count].cost = cost; matches[count].cost = cost;
matches[count].offset = offset; matches[count].offset = offset;
matches[count].tpl = tpl; matches[count].tpl = tpl;
count++; count++;
} }
// otherwise find the right position to insert it // otherwise find the right position to insert it
else { else {
// if higher cost than the worst current match, just ignore it // if higher cost than the worst current match, just ignore it
if (matches[count-1].cost<cost) { if (matches[count-1].cost<cost) {
return; return;
} }
int j = 0; int j = 0;
// skip all matches better than current one // skip all matches better than current one
while (matches[j].cost<cost) j++; while (matches[j].cost<cost) j++;
// shift matches one position // shift matches one position
int k = count-2; int k = count-2;
while (k>=j) { while (k>=j) {
matches[k+1] = matches[k]; matches[k+1] = matches[k];
k--; k--;
} }
matches[j].cost = cost; matches[j].cost = cost;
matches[j].offset = offset; matches[j].offset = offset;
matches[j].tpl = tpl; matches[j].tpl = tpl;
} }
} }
} }
void ChamferMatcher::showMatch(Mat& img, int index) void ChamferMatcher::showMatch(Mat& img, int index)
{ {
if (index>=count) { if (index>=count) {
std::cout << "Index too big.\n" << std::endl; std::cout << "Index too big.\n" << std::endl;
} }
assert(img.channels()==3); assert(img.channels()==3);
Match match = matches[index]; Match match = matches[index];
const template_coords_t& templ_coords = match.tpl->coords; const template_coords_t& templ_coords = match.tpl->coords;
int x, y; int x, y;
for (size_t i=0;i<templ_coords.size();++i) { for (size_t i=0;i<templ_coords.size();++i) {
x = match.offset.x + templ_coords[i].first; x = match.offset.x + templ_coords[i].first;
y = match.offset.y + templ_coords[i].second; y = match.offset.y + templ_coords[i].second;
if ( x > img.cols-1 || x < 0 || y > img.rows-1 || y < 0) continue; if ( x > img.cols-1 || x < 0 || y > img.rows-1 || y < 0) continue;
img.at<Vec3b>(y,x)[0]=0; img.at<Vec3b>(y,x)[0]=0;
img.at<Vec3b>(y,x)[2]=0; img.at<Vec3b>(y,x)[2]=0;
img.at<Vec3b>(y,x)[1]=255; img.at<Vec3b>(y,x)[1]=255;
} }
} }
void ChamferMatcher::showMatch(Mat& img, Match match) void ChamferMatcher::showMatch(Mat& img, Match match)
{ {
assert(img.channels()==3); assert(img.channels()==3);
const template_coords_t& templ_coords = match.tpl->coords; const template_coords_t& templ_coords = match.tpl->coords;
for (size_t i=0;i<templ_coords.size();++i) { for (size_t i=0;i<templ_coords.size();++i) {
int x = match.offset.x + templ_coords[i].first; int x = match.offset.x + templ_coords[i].first;
int y = match.offset.y + templ_coords[i].second; int y = match.offset.y + templ_coords[i].second;
if ( x > img.cols-1 || x < 0 || y > img.rows-1 || y < 0) continue; if ( x > img.cols-1 || x < 0 || y > img.rows-1 || y < 0) continue;
img.at<Vec3b>(y,x)[0]=0; img.at<Vec3b>(y,x)[0]=0;
img.at<Vec3b>(y,x)[2]=0; img.at<Vec3b>(y,x)[2]=0;
img.at<Vec3b>(y,x)[1]=255; img.at<Vec3b>(y,x)[1]=255;
} }
match.tpl->show(); match.tpl->show();
} }
const ChamferMatcher::Matches& ChamferMatcher::matching(Template& tpl, Mat& image_){ const ChamferMatcher::Matches& ChamferMatcher::matching(Template& tpl, Mat& image_){
chamfer_->addTemplate(tpl); chamfer_->addTemplate(tpl);
matches.clear(); matches.clear();
matches.resize(max_matches_); matches.resize(max_matches_);
count = 0; count = 0;
Matches* matches_ = chamfer_->matchEdgeImage( image_, Matches* matches_ = chamfer_->matchEdgeImage( image_,
ChamferMatcher:: ChamferMatcher::
SlidingWindowImageRange(image_.cols, SlidingWindowImageRange(image_.cols,
image_.rows, image_.rows,
pad_x, pad_x,
pad_y, pad_y,
scales, scales,
minScale, minScale,
maxScale), maxScale),
orientation_weight, orientation_weight,
max_matches_, max_matches_,
min_match_distance_); min_match_distance_);
for(int i = 0; i < (int)matches_->size(); i++){
addMatch(matches_->at(i).cost, matches_->at(i).offset, matches_->at(i).tpl);
}
matches_->clear(); for(int i = 0; i < (int)matches_->size(); i++){
delete matches_; addMatch(matches_->at(i).cost, matches_->at(i).offset, matches_->at(i).tpl);
matches_ = NULL; }
matches.resize(count); matches_->clear();
delete matches_;
matches_ = NULL;
matches.resize(count);
return matches;
return matches;
} }
int chamerMatching( Mat& img, Mat& templ, int chamerMatching( Mat& img, Mat& templ,
std::vector<std::vector<Point> >& results, std::vector<float>& costs, std::vector<std::vector<Point> >& results, std::vector<float>& costs,
double templScale, int maxMatches, double minMatchDistance, int padX, double templScale, int maxMatches, double minMatchDistance, int padX,
...@@ -1355,22 +1358,22 @@ int chamerMatching( Mat& img, Mat& templ, ...@@ -1355,22 +1358,22 @@ int chamerMatching( Mat& img, Mat& templ,
double orientationWeight, double truncate ) double orientationWeight, double truncate )
{ {
CV_Assert(img.type() == CV_8UC1 && templ.type() == CV_8UC1); CV_Assert(img.type() == CV_8UC1 && templ.type() == CV_8UC1);
ChamferMatcher matcher_(maxMatches, (float)minMatchDistance, padX, padY, scales, ChamferMatcher matcher_(maxMatches, (float)minMatchDistance, padX, padY, scales,
(float)minScale, (float)maxScale, (float)minScale, (float)maxScale,
(float)orientationWeight, (float)truncate); (float)orientationWeight, (float)truncate);
ChamferMatcher::Template template_(templ, (float)templScale); ChamferMatcher::Template template_(templ, (float)templScale);
ChamferMatcher::Matches match_instances = matcher_.matching(template_, img); ChamferMatcher::Matches match_instances = matcher_.matching(template_, img);
size_t i, nmatches = match_instances.size(); size_t i, nmatches = match_instances.size();
results.resize(nmatches); results.resize(nmatches);
costs.resize(nmatches); costs.resize(nmatches);
int bestIdx = -1; int bestIdx = -1;
double minCost = DBL_MAX; double minCost = DBL_MAX;
for( i = 0; i < nmatches; i++ ) for( i = 0; i < nmatches; i++ )
{ {
const ChamferMatcher::Match& match = match_instances[i]; const ChamferMatcher::Match& match = match_instances[i];
...@@ -1381,12 +1384,12 @@ int chamerMatching( Mat& img, Mat& templ, ...@@ -1381,12 +1384,12 @@ int chamerMatching( Mat& img, Mat& templ,
bestIdx = (int)i; bestIdx = (int)i;
} }
costs[i] = (float)cval; costs[i] = (float)cval;
const template_coords_t& templ_coords = match.tpl->coords; const template_coords_t& templ_coords = match.tpl->coords;
std::vector<Point>& templPoints = results[i]; std::vector<Point>& templPoints = results[i];
size_t j, npoints = templ_coords.size(); size_t j, npoints = templ_coords.size();
templPoints.resize(npoints); templPoints.resize(npoints);
for (j = 0; j < npoints; j++ ) for (j = 0; j < npoints; j++ )
{ {
int x = match.offset.x + templ_coords[j].first; int x = match.offset.x + templ_coords[j].first;
...@@ -1394,7 +1397,7 @@ int chamerMatching( Mat& img, Mat& templ, ...@@ -1394,7 +1397,7 @@ int chamerMatching( Mat& img, Mat& templ,
templPoints[j] = Point(x,y); templPoints[j] = Point(x,y);
} }
} }
return bestIdx; return bestIdx;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment