Path: blob/master/modules/imgproc/src/generalized_hough.cpp
16354 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// Intel License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000, Intel Corporation, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Intel Corporation may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041#include "precomp.hpp"42#include <functional>43#include <limits>4445using namespace cv;4647// common4849namespace50{51double toRad(double a)52{53return a * CV_PI / 180.0;54}5556bool notNull(float v)57{58return fabs(v) > std::numeric_limits<float>::epsilon();59}6061class GeneralizedHoughBase62{63protected:64GeneralizedHoughBase();65virtual ~GeneralizedHoughBase() {}6667void setTemplateImpl(InputArray templ, Point templCenter);68void setTemplateImpl(InputArray edges, InputArray dx, InputArray dy, Point templCenter);6970void detectImpl(InputArray image, OutputArray positions, OutputArray votes);71void detectImpl(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes);7273virtual void processTempl() = 0;74virtual void processImage() = 0;7576int cannyLowThresh_;77int cannyHighThresh_;78double minDist_;79double dp_;8081Size templSize_;82Point templCenter_;83Mat templEdges_;84Mat templDx_;85Mat templDy_;8687Size imageSize_;88Mat imageEdges_;89Mat imageDx_;90Mat imageDy_;9192std::vector<Vec4f> posOutBuf_;93std::vector<Vec3i> voteOutBuf_;9495private:96void calcEdges(InputArray src, Mat& edges, Mat& dx, Mat& dy);97void filterMinDist();98void convertTo(OutputArray positions, OutputArray votes);99};100101GeneralizedHoughBase::GeneralizedHoughBase()102{103cannyLowThresh_ = 50;104cannyHighThresh_ = 100;105minDist_ = 1.0;106dp_ = 1.0;107}108109void GeneralizedHoughBase::calcEdges(InputArray _src, Mat& edges, Mat& dx, Mat& dy)110{111Mat src = _src.getMat();112113CV_Assert( src.type() == CV_8UC1 );114CV_Assert( cannyLowThresh_ > 0 && cannyLowThresh_ < cannyHighThresh_ );115116Canny(src, edges, cannyLowThresh_, cannyHighThresh_);117Sobel(src, dx, CV_32F, 1, 0);118Sobel(src, dy, CV_32F, 0, 1);119}120121void GeneralizedHoughBase::setTemplateImpl(InputArray templ, Point templCenter)122{123calcEdges(templ, templEdges_, templDx_, templDy_);124125if (templCenter == Point(-1, -1))126templCenter = Point(templEdges_.cols / 2, templEdges_.rows / 2);127128templSize_ = templEdges_.size();129templCenter_ = templCenter;130131processTempl();132}133134void GeneralizedHoughBase::setTemplateImpl(InputArray edges, InputArray dx, InputArray dy, Point templCenter)135{136edges.getMat().copyTo(templEdges_);137dx.getMat().copyTo(templDx_);138dy.getMat().copyTo(templDy_);139140CV_Assert( templEdges_.type() == CV_8UC1 );141CV_Assert( templDx_.type() == CV_32FC1 && templDx_.size() == templEdges_.size() );142CV_Assert( templDy_.type() == templDx_.type() && templDy_.size() == templEdges_.size() );143144if (templCenter == Point(-1, -1))145templCenter = Point(templEdges_.cols / 2, templEdges_.rows / 2);146147templSize_ = templEdges_.size();148templCenter_ = templCenter;149150processTempl();151}152153void GeneralizedHoughBase::detectImpl(InputArray image, OutputArray positions, OutputArray votes)154{155calcEdges(image, imageEdges_, imageDx_, imageDy_);156157imageSize_ = imageEdges_.size();158159posOutBuf_.clear();160voteOutBuf_.clear();161162processImage();163164if (!posOutBuf_.empty())165{166if (minDist_ > 1)167filterMinDist();168convertTo(positions, votes);169}170else171{172positions.release();173if (votes.needed())174votes.release();175}176}177178void GeneralizedHoughBase::detectImpl(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes)179{180edges.getMat().copyTo(imageEdges_);181dx.getMat().copyTo(imageDx_);182dy.getMat().copyTo(imageDy_);183184CV_Assert( imageEdges_.type() == CV_8UC1 );185CV_Assert( imageDx_.type() == CV_32FC1 && imageDx_.size() == imageEdges_.size() );186CV_Assert( imageDy_.type() == imageDx_.type() && imageDy_.size() == imageEdges_.size() );187188imageSize_ = imageEdges_.size();189190posOutBuf_.clear();191voteOutBuf_.clear();192193processImage();194195if (!posOutBuf_.empty())196{197if (minDist_ > 1)198filterMinDist();199convertTo(positions, votes);200}201else202{203positions.release();204if (votes.needed())205votes.release();206}207}208209class Vec3iGreaterThanIdx210{211public:212Vec3iGreaterThanIdx( const Vec3i* _arr ) : arr(_arr) {}213bool operator()(size_t a, size_t b) const { return arr[a][0] > arr[b][0]; }214const Vec3i* arr;215};216217void GeneralizedHoughBase::filterMinDist()218{219size_t oldSize = posOutBuf_.size();220const bool hasVotes = !voteOutBuf_.empty();221222CV_Assert( !hasVotes || voteOutBuf_.size() == oldSize );223224std::vector<Vec4f> oldPosBuf(posOutBuf_);225std::vector<Vec3i> oldVoteBuf(voteOutBuf_);226227std::vector<size_t> indexies(oldSize);228for (size_t i = 0; i < oldSize; ++i)229indexies[i] = i;230std::sort(indexies.begin(), indexies.end(), Vec3iGreaterThanIdx(&oldVoteBuf[0]));231232posOutBuf_.clear();233voteOutBuf_.clear();234235const int cellSize = cvRound(minDist_);236const int gridWidth = (imageSize_.width + cellSize - 1) / cellSize;237const int gridHeight = (imageSize_.height + cellSize - 1) / cellSize;238239std::vector< std::vector<Point2f> > grid(gridWidth * gridHeight);240241const double minDist2 = minDist_ * minDist_;242243for (size_t i = 0; i < oldSize; ++i)244{245const size_t ind = indexies[i];246247Point2f p(oldPosBuf[ind][0], oldPosBuf[ind][1]);248249bool good = true;250251const int xCell = static_cast<int>(p.x / cellSize);252const int yCell = static_cast<int>(p.y / cellSize);253254int x1 = xCell - 1;255int y1 = yCell - 1;256int x2 = xCell + 1;257int y2 = yCell + 1;258259// boundary check260x1 = std::max(0, x1);261y1 = std::max(0, y1);262x2 = std::min(gridWidth - 1, x2);263y2 = std::min(gridHeight - 1, y2);264265for (int yy = y1; yy <= y2; ++yy)266{267for (int xx = x1; xx <= x2; ++xx)268{269const std::vector<Point2f>& m = grid[yy * gridWidth + xx];270271for(size_t j = 0; j < m.size(); ++j)272{273const Point2f d = p - m[j];274275if (d.ddot(d) < minDist2)276{277good = false;278goto break_out;279}280}281}282}283284break_out:285286if(good)287{288grid[yCell * gridWidth + xCell].push_back(p);289290posOutBuf_.push_back(oldPosBuf[ind]);291if (hasVotes)292voteOutBuf_.push_back(oldVoteBuf[ind]);293}294}295}296297void GeneralizedHoughBase::convertTo(OutputArray _positions, OutputArray _votes)298{299const int total = static_cast<int>(posOutBuf_.size());300const bool hasVotes = !voteOutBuf_.empty();301302CV_Assert( !hasVotes || voteOutBuf_.size() == posOutBuf_.size() );303304_positions.create(1, total, CV_32FC4);305Mat positions = _positions.getMat();306Mat(1, total, CV_32FC4, &posOutBuf_[0]).copyTo(positions);307308if (_votes.needed())309{310if (!hasVotes)311{312_votes.release();313}314else315{316_votes.create(1, total, CV_32SC3);317Mat votes = _votes.getMat();318Mat(1, total, CV_32SC3, &voteOutBuf_[0]).copyTo(votes);319}320}321}322}323324// GeneralizedHoughBallard325326namespace327{328class GeneralizedHoughBallardImpl CV_FINAL : public GeneralizedHoughBallard, private GeneralizedHoughBase329{330public:331GeneralizedHoughBallardImpl();332333void setTemplate(InputArray templ, Point templCenter) CV_OVERRIDE { setTemplateImpl(templ, templCenter); }334void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) CV_OVERRIDE { setTemplateImpl(edges, dx, dy, templCenter); }335336void detect(InputArray image, OutputArray positions, OutputArray votes) CV_OVERRIDE { detectImpl(image, positions, votes); }337void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) CV_OVERRIDE { detectImpl(edges, dx, dy, positions, votes); }338339void setCannyLowThresh(int cannyLowThresh) CV_OVERRIDE { cannyLowThresh_ = cannyLowThresh; }340int getCannyLowThresh() const CV_OVERRIDE { return cannyLowThresh_; }341342void setCannyHighThresh(int cannyHighThresh) CV_OVERRIDE { cannyHighThresh_ = cannyHighThresh; }343int getCannyHighThresh() const CV_OVERRIDE { return cannyHighThresh_; }344345void setMinDist(double minDist) CV_OVERRIDE { minDist_ = minDist; }346double getMinDist() const CV_OVERRIDE { return minDist_; }347348void setDp(double dp) CV_OVERRIDE { dp_ = dp; }349double getDp() const CV_OVERRIDE { return dp_; }350351void setMaxBufferSize(int) CV_OVERRIDE { }352int getMaxBufferSize() const CV_OVERRIDE { return 0; }353354void setLevels(int levels) CV_OVERRIDE { levels_ = levels; }355int getLevels() const CV_OVERRIDE { return levels_; }356357void setVotesThreshold(int votesThreshold) CV_OVERRIDE { votesThreshold_ = votesThreshold; }358int getVotesThreshold() const CV_OVERRIDE { return votesThreshold_; }359360private:361void processTempl() CV_OVERRIDE;362void processImage() CV_OVERRIDE;363364void calcHist();365void findPosInHist();366367int levels_;368int votesThreshold_;369370std::vector< std::vector<Point> > r_table_;371Mat hist_;372};373374GeneralizedHoughBallardImpl::GeneralizedHoughBallardImpl()375{376levels_ = 360;377votesThreshold_ = 100;378}379380void GeneralizedHoughBallardImpl::processTempl()381{382CV_Assert( levels_ > 0 );383384const double thetaScale = levels_ / 360.0;385386r_table_.resize(levels_ + 1);387std::for_each(r_table_.begin(), r_table_.end(), [](std::vector<Point>& e)->void { e.clear(); });388389for (int y = 0; y < templSize_.height; ++y)390{391const uchar* edgesRow = templEdges_.ptr(y);392const float* dxRow = templDx_.ptr<float>(y);393const float* dyRow = templDy_.ptr<float>(y);394395for (int x = 0; x < templSize_.width; ++x)396{397const Point p(x, y);398399if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x])))400{401const float theta = fastAtan2(dyRow[x], dxRow[x]);402const int n = cvRound(theta * thetaScale);403r_table_[n].push_back(p - templCenter_);404}405}406}407}408409void GeneralizedHoughBallardImpl::processImage()410{411calcHist();412findPosInHist();413}414415void GeneralizedHoughBallardImpl::calcHist()416{417CV_INSTRUMENT_REGION();418419CV_Assert( imageEdges_.type() == CV_8UC1 );420CV_Assert( imageDx_.type() == CV_32FC1 && imageDx_.size() == imageSize_);421CV_Assert( imageDy_.type() == imageDx_.type() && imageDy_.size() == imageSize_);422CV_Assert( levels_ > 0 && r_table_.size() == static_cast<size_t>(levels_ + 1) );423CV_Assert( dp_ > 0.0 );424425const double thetaScale = levels_ / 360.0;426const double idp = 1.0 / dp_;427428hist_.create(cvCeil(imageSize_.height * idp) + 2, cvCeil(imageSize_.width * idp) + 2, CV_32SC1);429hist_.setTo(0);430431const int rows = hist_.rows - 2;432const int cols = hist_.cols - 2;433434for (int y = 0; y < imageSize_.height; ++y)435{436const uchar* edgesRow = imageEdges_.ptr(y);437const float* dxRow = imageDx_.ptr<float>(y);438const float* dyRow = imageDy_.ptr<float>(y);439440for (int x = 0; x < imageSize_.width; ++x)441{442const Point p(x, y);443444if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x])))445{446const float theta = fastAtan2(dyRow[x], dxRow[x]);447const int n = cvRound(theta * thetaScale);448449const std::vector<Point>& r_row = r_table_[n];450451for (size_t j = 0; j < r_row.size(); ++j)452{453Point c = p - r_row[j];454455c.x = cvRound(c.x * idp);456c.y = cvRound(c.y * idp);457458if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows)459++hist_.at<int>(c.y + 1, c.x + 1);460}461}462}463}464}465466void GeneralizedHoughBallardImpl::findPosInHist()467{468CV_Assert( votesThreshold_ > 0 );469470const int histRows = hist_.rows - 2;471const int histCols = hist_.cols - 2;472473for(int y = 0; y < histRows; ++y)474{475const int* prevRow = hist_.ptr<int>(y);476const int* curRow = hist_.ptr<int>(y + 1);477const int* nextRow = hist_.ptr<int>(y + 2);478479for(int x = 0; x < histCols; ++x)480{481const int votes = curRow[x + 1];482483if (votes > votesThreshold_ && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1])484{485posOutBuf_.push_back(Vec4f(static_cast<float>(x * dp_), static_cast<float>(y * dp_), 1.0f, 0.0f));486voteOutBuf_.push_back(Vec3i(votes, 0, 0));487}488}489}490}491}492493Ptr<GeneralizedHoughBallard> cv::createGeneralizedHoughBallard()494{495return makePtr<GeneralizedHoughBallardImpl>();496}497498// GeneralizedHoughGuil499500namespace501{502class GeneralizedHoughGuilImpl CV_FINAL : public GeneralizedHoughGuil, private GeneralizedHoughBase503{504public:505GeneralizedHoughGuilImpl();506507void setTemplate(InputArray templ, Point templCenter) CV_OVERRIDE { setTemplateImpl(templ, templCenter); }508void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) CV_OVERRIDE { setTemplateImpl(edges, dx, dy, templCenter); }509510void detect(InputArray image, OutputArray positions, OutputArray votes) CV_OVERRIDE { detectImpl(image, positions, votes); }511void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) CV_OVERRIDE { detectImpl(edges, dx, dy, positions, votes); }512513void setCannyLowThresh(int cannyLowThresh) CV_OVERRIDE { cannyLowThresh_ = cannyLowThresh; }514int getCannyLowThresh() const CV_OVERRIDE { return cannyLowThresh_; }515516void setCannyHighThresh(int cannyHighThresh) CV_OVERRIDE { cannyHighThresh_ = cannyHighThresh; }517int getCannyHighThresh() const CV_OVERRIDE { return cannyHighThresh_; }518519void setMinDist(double minDist) CV_OVERRIDE { minDist_ = minDist; }520double getMinDist() const CV_OVERRIDE { return minDist_; }521522void setDp(double dp) CV_OVERRIDE { dp_ = dp; }523double getDp() const CV_OVERRIDE { return dp_; }524525void setMaxBufferSize(int maxBufferSize) CV_OVERRIDE { maxBufferSize_ = maxBufferSize; }526int getMaxBufferSize() const CV_OVERRIDE { return maxBufferSize_; }527528void setXi(double xi) CV_OVERRIDE { xi_ = xi; }529double getXi() const CV_OVERRIDE { return xi_; }530531void setLevels(int levels) CV_OVERRIDE { levels_ = levels; }532int getLevels() const CV_OVERRIDE { return levels_; }533534void setAngleEpsilon(double angleEpsilon) CV_OVERRIDE { angleEpsilon_ = angleEpsilon; }535double getAngleEpsilon() const CV_OVERRIDE { return angleEpsilon_; }536537void setMinAngle(double minAngle) CV_OVERRIDE { minAngle_ = minAngle; }538double getMinAngle() const CV_OVERRIDE { return minAngle_; }539540void setMaxAngle(double maxAngle) CV_OVERRIDE { maxAngle_ = maxAngle; }541double getMaxAngle() const CV_OVERRIDE { return maxAngle_; }542543void setAngleStep(double angleStep) CV_OVERRIDE { angleStep_ = angleStep; }544double getAngleStep() const CV_OVERRIDE { return angleStep_; }545546void setAngleThresh(int angleThresh) CV_OVERRIDE { angleThresh_ = angleThresh; }547int getAngleThresh() const CV_OVERRIDE { return angleThresh_; }548549void setMinScale(double minScale) CV_OVERRIDE { minScale_ = minScale; }550double getMinScale() const CV_OVERRIDE { return minScale_; }551552void setMaxScale(double maxScale) CV_OVERRIDE { maxScale_ = maxScale; }553double getMaxScale() const CV_OVERRIDE { return maxScale_; }554555void setScaleStep(double scaleStep) CV_OVERRIDE { scaleStep_ = scaleStep; }556double getScaleStep() const CV_OVERRIDE { return scaleStep_; }557558void setScaleThresh(int scaleThresh) CV_OVERRIDE { scaleThresh_ = scaleThresh; }559int getScaleThresh() const CV_OVERRIDE { return scaleThresh_; }560561void setPosThresh(int posThresh) CV_OVERRIDE { posThresh_ = posThresh; }562int getPosThresh() const CV_OVERRIDE { return posThresh_; }563564private:565void processTempl() CV_OVERRIDE;566void processImage() CV_OVERRIDE;567568int maxBufferSize_;569double xi_;570int levels_;571double angleEpsilon_;572573double minAngle_;574double maxAngle_;575double angleStep_;576int angleThresh_;577578double minScale_;579double maxScale_;580double scaleStep_;581int scaleThresh_;582583int posThresh_;584585struct ContourPoint586{587Point2d pos;588double theta;589};590591struct Feature592{593ContourPoint p1;594ContourPoint p2;595596double alpha12;597double d12;598599Point2d r1;600Point2d r2;601};602603void buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, std::vector< std::vector<Feature> >& features, Point2d center = Point2d());604void getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, std::vector<ContourPoint>& points);605606void calcOrientation();607void calcScale(double angle);608void calcPosition(double angle, int angleVotes, double scale, int scaleVotes);609610std::vector< std::vector<Feature> > templFeatures_;611std::vector< std::vector<Feature> > imageFeatures_;612613std::vector< std::pair<double, int> > angles_;614std::vector< std::pair<double, int> > scales_;615};616617double clampAngle(double a)618{619double res = a;620621while (res > 360.0)622res -= 360.0;623while (res < 0)624res += 360.0;625626return res;627}628629bool angleEq(double a, double b, double eps = 1.0)630{631return (fabs(clampAngle(a - b)) <= eps);632}633634GeneralizedHoughGuilImpl::GeneralizedHoughGuilImpl()635{636maxBufferSize_ = 1000;637xi_ = 90.0;638levels_ = 360;639angleEpsilon_ = 1.0;640641minAngle_ = 0.0;642maxAngle_ = 360.0;643angleStep_ = 1.0;644angleThresh_ = 15000;645646minScale_ = 0.5;647maxScale_ = 2.0;648scaleStep_ = 0.05;649scaleThresh_ = 1000;650651posThresh_ = 100;652}653654void GeneralizedHoughGuilImpl::processTempl()655{656buildFeatureList(templEdges_, templDx_, templDy_, templFeatures_, templCenter_);657}658659void GeneralizedHoughGuilImpl::processImage()660{661buildFeatureList(imageEdges_, imageDx_, imageDy_, imageFeatures_);662663calcOrientation();664665for (size_t i = 0; i < angles_.size(); ++i)666{667const double angle = angles_[i].first;668const int angleVotes = angles_[i].second;669670calcScale(angle);671672for (size_t j = 0; j < scales_.size(); ++j)673{674const double scale = scales_[j].first;675const int scaleVotes = scales_[j].second;676677calcPosition(angle, angleVotes, scale, scaleVotes);678}679}680}681682void GeneralizedHoughGuilImpl::buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, std::vector< std::vector<Feature> >& features, Point2d center)683{684CV_Assert( levels_ > 0 );685686const double maxDist = sqrt((double) templSize_.width * templSize_.width + templSize_.height * templSize_.height) * maxScale_;687688const double alphaScale = levels_ / 360.0;689690std::vector<ContourPoint> points;691getContourPoints(edges, dx, dy, points);692693features.resize(levels_ + 1);694std::for_each(features.begin(), features.end(), [=](std::vector<Feature>& e) { e.clear(); e.reserve(maxBufferSize_); });695696for (size_t i = 0; i < points.size(); ++i)697{698ContourPoint p1 = points[i];699700for (size_t j = 0; j < points.size(); ++j)701{702ContourPoint p2 = points[j];703704if (angleEq(p1.theta - p2.theta, xi_, angleEpsilon_))705{706const Point2d d = p1.pos - p2.pos;707708Feature f;709710f.p1 = p1;711f.p2 = p2;712713f.alpha12 = clampAngle(fastAtan2((float)d.y, (float)d.x) - p1.theta);714f.d12 = norm(d);715716if (f.d12 > maxDist)717continue;718719f.r1 = p1.pos - center;720f.r2 = p2.pos - center;721722const int n = cvRound(f.alpha12 * alphaScale);723724if (features[n].size() < static_cast<size_t>(maxBufferSize_))725features[n].push_back(f);726}727}728}729}730731void GeneralizedHoughGuilImpl::getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, std::vector<ContourPoint>& points)732{733CV_Assert( edges.type() == CV_8UC1 );734CV_Assert( dx.type() == CV_32FC1 && dx.size == edges.size );735CV_Assert( dy.type() == dx.type() && dy.size == edges.size );736737points.clear();738points.reserve(edges.size().area());739740for (int y = 0; y < edges.rows; ++y)741{742const uchar* edgesRow = edges.ptr(y);743const float* dxRow = dx.ptr<float>(y);744const float* dyRow = dy.ptr<float>(y);745746for (int x = 0; x < edges.cols; ++x)747{748if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x])))749{750ContourPoint p;751752p.pos = Point2d(x, y);753p.theta = fastAtan2(dyRow[x], dxRow[x]);754755points.push_back(p);756}757}758}759}760761void GeneralizedHoughGuilImpl::calcOrientation()762{763CV_Assert( levels_ > 0 );764CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) );765CV_Assert( imageFeatures_.size() == templFeatures_.size() );766CV_Assert( minAngle_ >= 0.0 && minAngle_ < maxAngle_ && maxAngle_ <= 360.0 );767CV_Assert( angleStep_ > 0.0 && angleStep_ < 360.0 );768CV_Assert( angleThresh_ > 0 );769770const double iAngleStep = 1.0 / angleStep_;771const int angleRange = cvCeil((maxAngle_ - minAngle_) * iAngleStep);772773std::vector<int> OHist(angleRange + 1, 0);774for (int i = 0; i <= levels_; ++i)775{776const std::vector<Feature>& templRow = templFeatures_[i];777const std::vector<Feature>& imageRow = imageFeatures_[i];778779for (size_t j = 0; j < templRow.size(); ++j)780{781Feature templF = templRow[j];782783for (size_t k = 0; k < imageRow.size(); ++k)784{785Feature imF = imageRow[k];786787const double angle = clampAngle(imF.p1.theta - templF.p1.theta);788if (angle >= minAngle_ && angle <= maxAngle_)789{790const int n = cvRound((angle - minAngle_) * iAngleStep);791++OHist[n];792}793}794}795}796797angles_.clear();798799for (int n = 0; n < angleRange; ++n)800{801if (OHist[n] >= angleThresh_)802{803const double angle = minAngle_ + n * angleStep_;804angles_.push_back(std::make_pair(angle, OHist[n]));805}806}807}808809void GeneralizedHoughGuilImpl::calcScale(double angle)810{811CV_Assert( levels_ > 0 );812CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) );813CV_Assert( imageFeatures_.size() == templFeatures_.size() );814CV_Assert( minScale_ > 0.0 && minScale_ < maxScale_ );815CV_Assert( scaleStep_ > 0.0 );816CV_Assert( scaleThresh_ > 0 );817818const double iScaleStep = 1.0 / scaleStep_;819const int scaleRange = cvCeil((maxScale_ - minScale_) * iScaleStep);820821std::vector<int> SHist(scaleRange + 1, 0);822823for (int i = 0; i <= levels_; ++i)824{825const std::vector<Feature>& templRow = templFeatures_[i];826const std::vector<Feature>& imageRow = imageFeatures_[i];827828for (size_t j = 0; j < templRow.size(); ++j)829{830Feature templF = templRow[j];831832templF.p1.theta += angle;833834for (size_t k = 0; k < imageRow.size(); ++k)835{836Feature imF = imageRow[k];837838if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon_))839{840const double scale = imF.d12 / templF.d12;841if (scale >= minScale_ && scale <= maxScale_)842{843const int s = cvRound((scale - minScale_) * iScaleStep);844++SHist[s];845}846}847}848}849}850851scales_.clear();852853for (int s = 0; s < scaleRange; ++s)854{855if (SHist[s] >= scaleThresh_)856{857const double scale = minScale_ + s * scaleStep_;858scales_.push_back(std::make_pair(scale, SHist[s]));859}860}861}862863void GeneralizedHoughGuilImpl::calcPosition(double angle, int angleVotes, double scale, int scaleVotes)864{865CV_Assert( levels_ > 0 );866CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) );867CV_Assert( imageFeatures_.size() == templFeatures_.size() );868CV_Assert( dp_ > 0.0 );869CV_Assert( posThresh_ > 0 );870871const double sinVal = sin(toRad(angle));872const double cosVal = cos(toRad(angle));873const double idp = 1.0 / dp_;874875const int histRows = cvCeil(imageSize_.height * idp);876const int histCols = cvCeil(imageSize_.width * idp);877878Mat DHist(histRows + 2, histCols + 2, CV_32SC1, Scalar::all(0));879880for (int i = 0; i <= levels_; ++i)881{882const std::vector<Feature>& templRow = templFeatures_[i];883const std::vector<Feature>& imageRow = imageFeatures_[i];884885for (size_t j = 0; j < templRow.size(); ++j)886{887Feature templF = templRow[j];888889templF.p1.theta += angle;890891templF.r1 *= scale;892templF.r2 *= scale;893894templF.r1 = Point2d(cosVal * templF.r1.x - sinVal * templF.r1.y, sinVal * templF.r1.x + cosVal * templF.r1.y);895templF.r2 = Point2d(cosVal * templF.r2.x - sinVal * templF.r2.y, sinVal * templF.r2.x + cosVal * templF.r2.y);896897for (size_t k = 0; k < imageRow.size(); ++k)898{899Feature imF = imageRow[k];900901if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon_))902{903Point2d c1, c2;904905c1 = imF.p1.pos - templF.r1;906c1 *= idp;907908c2 = imF.p2.pos - templF.r2;909c2 *= idp;910911if (fabs(c1.x - c2.x) > 1 || fabs(c1.y - c2.y) > 1)912continue;913914if (c1.y >= 0 && c1.y < histRows && c1.x >= 0 && c1.x < histCols)915++DHist.at<int>(cvRound(c1.y) + 1, cvRound(c1.x) + 1);916}917}918}919}920921for(int y = 0; y < histRows; ++y)922{923const int* prevRow = DHist.ptr<int>(y);924const int* curRow = DHist.ptr<int>(y + 1);925const int* nextRow = DHist.ptr<int>(y + 2);926927for(int x = 0; x < histCols; ++x)928{929const int votes = curRow[x + 1];930931if (votes > posThresh_ && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1])932{933posOutBuf_.push_back(Vec4f(static_cast<float>(x * dp_), static_cast<float>(y * dp_), static_cast<float>(scale), static_cast<float>(angle)));934voteOutBuf_.push_back(Vec3i(votes, scaleVotes, angleVotes));935}936}937}938}939}940941Ptr<GeneralizedHoughGuil> cv::createGeneralizedHoughGuil()942{943return makePtr<GeneralizedHoughGuilImpl>();944}945946947