Перейти к содержимому
Compvision.ru
Smorodov

Простой пример преобразования Хафа

Recommended Posts

Написал протенький код для экспериментов с вариациями на тему Хафа:

post-1-0-85420700-1342093193_thumb.png


#include <iostream>
#include <vector>
#include <stdio.h>
#include "opencv2/core/core.hpp"
#include "opencv2/core/gpumat.hpp"
#include "opencv2/core/opengl_interop.hpp"
#include "opencv2/gpu/gpu.hpp"
#include "opencv2/ml/ml.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "fstream"
#include "iostream"
using namespace std;
using namespace cv;

//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
Mat HoughTransform(Mat &src,vector<Point> pts)
{
// максимальное расстояние от начала координат до точки изобажения
int Rmax=sqrtl(powl(src.rows,2)+powl(src.cols,2));
// ищем с разрешением в 1 градус
int thetaMax=360;
Mat acc=Mat::zeros(Rmax,thetaMax,CV_32FC1);

for(int i=0;i<pts.size();i++)
{
// x cosθ + y sinθ = R
for(int theta=0;theta<thetaMax;theta++)
{
double thetaRadians=static_cast<float>(theta)*CV_PI/180.0;
double Rd=pts[i].x*cos(thetaRadians)+pts[i].y*sin(thetaRadians);
if(Rd>0 && Rd<Rmax)
{
acc.at<float>((int)Rd,theta)+=1;
}
}
}
cv::GaussianBlur(acc,acc,Size(3,3),1);
return acc;
}
//----------------------------------------------------------------------
// поиск локальных максимумов
//----------------------------------------------------------------------
void FindLines(Mat& img,vector<Point>& pts)
{
vector<Rect> Rects;
Mat img8U( img.size(), CV_8UC3 );
cv::normalize(img,img,0,255,CV_MINMAX);
img.convertTo(img8U,CV_8UC1);

Mat rect_12 = getStructuringElement(CV_SHAPE_RECT, Size(3,3));
erode(img8U, img8U, rect_12,Point(),1);
Mat rect_6 = getStructuringElement(CV_SHAPE_RECT, Size(3,3));
dilate(img8U,img8U,rect_6,Point(),2);
vector<vector<Point> > contours;

vector<Vec4i> hierarchy;

findContours(img8U,contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, Point());

if(contours.size()>0)
{
for( int i = 0; i < contours.size(); i++ )
{
//if(contourArea(contours[i])>5)
//{
Rect r=cv::boundingRect(contours[i]);
Rects.push_back(r);
//}
}
}
if(Rects.size()>1)
{
for(int i=0;i<Rects.size();i++)
{
Mat subImg=img(Rects[i]);
double M,m;
Point p,P;
minMaxLoc(subImg,&m,&M,&p,&P);
P.x+=Rects[i].x;
P.y+=Rects[i].y;
pts.push_back(P);
}
}
Rects.clear();
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
void drawLines(Mat& img,vector<Point>& par)
{
for(int i=0;i<par.size();i++)
{
double R=par[i].y;
double th=par[i].x/180.0*CV_PI;
if(fabs(sin(th))>fabs(cos(th)))
{
for(int m=0;m<img.cols;m++)
{
int x=m;
int y=(R-(double)x*cosl(th))/sinl(th);
if(x>0 && x<img.cols && y>0 && y<img.rows)
{
img.at<unsigned char>(y,x)=128;
}
}
}
else
{
for(int m=0;m<img.rows;m++)
{
int y=m;
double R=par[i].y;
double th=par[i].x/180.0*CV_PI;
int x=(R-(double)y*sin(th))/cos(th);
if(x>0 && x<img.cols && y>0 && y<img.rows)
{
img.at<unsigned char>(y,x)=128;
}
}
}

}
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
int width;
int height;

Mat Img=imread("C:\\ImagesForTest\\lines.jpg",0);
width=Img.cols;
height=Img.rows;

// Сначала выделим границы
Mat Edges;
cv::Canny(Img,Edges,200,200,3,true);
namedWindow("Edges");
imshow("Edges",Edges);
// Чтобы не перелопачивать все изображение запомним точки границ
vector<Point> pts;
for(int i=0;i<Edges.rows;i++)
{
for(int j=0;j<Edges.cols;j++)
{
if(Edges.at<unsigned char>(i,j)>0){pts.push_back(Point(j,i));}
}
}
// Преобразование Хафа переведем точки в параметрическое пространство прямых
Mat HoughAccumulator;
HoughAccumulator=HoughTransform(Edges,pts);
cv::normalize(HoughAccumulator,HoughAccumulator,0,1,CV_MINMAX);
namedWindow("HoughAccumulator");
imshow("HoughAccumulator",HoughAccumulator);
Mat mean,stdDev;
cv::meanStdDev(HoughAccumulator,mean,stdDev);
double Mean=mean.at<double>(0,0);

HoughAccumulator-=Mean; // Выровняем относительно нуля

// сигма применяется к целочисленному изображению, поэтоу домножим
double StdDev=stdDev.at<double>(0,0)*255;

// Порог по аккумулятору (разбиваем картинку на участки, в которых будем искать локальные максимумы)
Mat thrAcc;
HoughAccumulator.convertTo(thrAcc,CV_8UC1,255);

// зададим порог отсечения (меньше порог - больше прямых)
// Погрог 3 сигмы (отсекаем почти все)
cv::threshold(thrAcc,thrAcc,3*StdDev,255,3);

// Найдем параметры прямых, за которые саккумулировалось наибольшее кол-во голосов
vector<Point> LinesParams;


/*
GridAdaptedFeatureDetector detect(new FastFeatureDetector(10), 100, 10, 10);
vector<KeyPoint> keypoints; // Ключевые точки (CPU)
detect.detect(thrAcc,keypoints);
for (int i=0;i<keypoints.size();i++)
{
LinesParams.push_back(keypoints[i].pt);
}
*/

FindLines(thrAcc,LinesParams);

cout << LinesParams.size() << endl;

// Нарисуем точки в пространстве параметров (каждая точка - это прямая)
for(int i=0;i<LinesParams.size();i++)
{
circle(thrAcc,Point(LinesParams[i]),3,CV_RGB(255,255,255),-1);
}
namedWindow("thrAcc");
imshow("thrAcc",thrAcc);

// Нарисуем прямые по заданным параметрам
drawLines(Img,LinesParams);

namedWindow("Image");
imshow("Image",Img);

waitKey(0);
pts.clear();
return 0;
}
[/code]

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вариант детектора отрезков:

post-1-0-75701600-1342096585_thumb.png


#include <iostream>
#include <vector>
#include <stdio.h>
#include "opencv2/core/core.hpp"
#include "opencv2/core/gpumat.hpp"
#include "opencv2/core/opengl_interop.hpp"
#include "opencv2/gpu/gpu.hpp"
#include "opencv2/ml/ml.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "fstream"
#include "iostream"
using namespace std;
using namespace cv;

//-----------------------------------------------------------------------------------------------------
// Переводим точки границ в пространство параметров прямой линии (в полярных координатах)
//-----------------------------------------------------------------------------------------------------
Mat HoughTransform(Mat &src,vector<Point> pts)
{
// максимальное расстояние от начала координат до точки изобажения
int Rmax=sqrtl(powl(src.rows,2)+powl(src.cols,2));
// ищем с разрешением в 1 градус
int thetaMax=360;
Mat acc=Mat::zeros(Rmax,thetaMax,CV_32FC1);

for(int i=0;i<pts.size();i++)
{
// x cosθ + y sinθ = R
for(int theta=0;theta<thetaMax;theta++)
{
double thetaRadians=static_cast<float>(theta)*CV_PI/180.0;
double Rd=pts[i].x*cos(thetaRadians)+pts[i].y*sin(thetaRadians);
if(Rd>0 && Rd<Rmax)
{
acc.at<float>((int)Rd,theta)+=1;
}
}
}
cv::GaussianBlur(acc,acc,Size(3,3),1);
return acc;
}
//----------------------------------------------------------------------
// поиск локальных максимумов
//----------------------------------------------------------------------
void FindLines(Mat& img,vector<Point>& pts)
{
vector<Rect> Rects;
Mat img8U( img.size(), CV_8UC3 );
cv::normalize(img,img,0,255,CV_MINMAX);
img.convertTo(img8U,CV_8UC1);

Mat rect_12 = getStructuringElement(CV_SHAPE_RECT, Size(3,3));
erode(img8U, img8U, rect_12,Point(),1);
Mat rect_6 = getStructuringElement(CV_SHAPE_RECT, Size(3,3));
dilate(img8U,img8U,rect_6,Point(),2);
vector<vector<Point> > contours;

vector<Vec4i> hierarchy;

findContours(img8U,contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, Point());

if(contours.size()>0)
{
for( int i = 0; i < contours.size(); i++ )
{
//if(contourArea(contours[i])>5)
//{
Rect r=cv::boundingRect(contours[i]);
Rects.push_back(r);
//}
}
}
if(Rects.size()>1)
{
for(int i=0;i<Rects.size();i++)
{
Mat subImg=img(Rects[i]);
double M,m;
Point p,P;
minMaxLoc(subImg,&m,&M,&p,&P);
P.x+=Rects[i].x;
P.y+=Rects[i].y;
pts.push_back(P);
}
}
Rects.clear();
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
void drawLines(Mat& img,vector<Point>& par,vector<Point>& pts,int gap)
{
for(int i=0;i<par.size();i++)
{

double R=par[i].y;
double th=par[i].x/180.0*CV_PI;

if(fabs(sin(th))>fabs(cos(th))) // Чтобы не делить на ноль и малые числа (для вертикальных и горизонтальных линий)
{
for(int m=0;m<pts.size();m++)
{
int x=pts[m].x;
int y=(R-(double)x*cosl(th))/sinl(th);
if(fabsl(y-pts[m].y)<gap && x>0 && x<img.cols && y>0 && y<img.rows) // Если точка не слишком далеко от прямой, нарисуем её
{
img.at<unsigned char>(y,x)=128; // Отмечаем точку
}
}
}
else
{
for(int m=0;m<pts.size();m++)
{
int y=pts[m].y;
int x=(R-(double)y*sin(th))/cos(th);
if(fabsl(x-pts[m].x)<gap && x>0 && x<img.cols && y>0 && y<img.rows) // Если точка не слишком далеко от прямой, нарисуем её
{
img.at<unsigned char>(y,x)=128; // Отмечаем точку
}
}
}

}
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
int width;
int height;

Mat Img=imread("C:\\ImagesForTest\\lines.jpg",0);
width=Img.cols;
height=Img.rows;

// Сначала выделим границы
Mat Edges;
cv::Canny(Img,Edges,200,200,3,true);
namedWindow("Edges");
imshow("Edges",Edges);
// Чтобы не перелопачивать все изображение запомним точки границ
vector<Point> pts;
for(int i=0;i<Edges.rows;i++)
{
for(int j=0;j<Edges.cols;j++)
{
if(Edges.at<unsigned char>(i,j)>0){pts.push_back(Point(j,i));}
}
}
// Преобразование Хафа переведем точки в параметрическое пространство прямых
Mat HoughAccumulator;
HoughAccumulator=HoughTransform(Edges,pts);
cv::normalize(HoughAccumulator,HoughAccumulator,0,1,CV_MINMAX);
namedWindow("HoughAccumulator");
imshow("HoughAccumulator",HoughAccumulator);
Mat mean,stdDev;
cv::meanStdDev(HoughAccumulator,mean,stdDev);
double Mean=mean.at<double>(0,0);

HoughAccumulator-=Mean; // Выровняем относительно нуля

// сигма применяется к целочисленному изображению, поэтоу домножим
double StdDev=stdDev.at<double>(0,0)*255;

// Порог по аккумулятору (разбиваем картинку на участки, в которых будем искать локальные максимумы)
Mat thrAcc;
HoughAccumulator.convertTo(thrAcc,CV_8UC1,255);

// зададим порог отсечения (меньше порог - больше прямых)
// Погрог 3 сигмы (отсекаем почти все)
cv::threshold(thrAcc,thrAcc,3*StdDev,255,3);

// Найдем параметры прямых, за которые саккумулировалось наибольшее кол-во голосов
vector<Point> LinesParams;

FindLines(thrAcc,LinesParams);

cout << LinesParams.size() << endl;

// Нарисуем точки в пространстве параметров (каждая точка - это прямая)
for(int i=0;i<LinesParams.size();i++)
{
circle(thrAcc,Point(LinesParams[i]),3,CV_RGB(255,255,255),-1);
}
namedWindow("thrAcc");
imshow("thrAcc",thrAcc);

// Нарисуем прямые по заданным параметрам
// Здесь точки границ, подставляются в уравнения прямых (чтобы получались отрезки, а не бесконечные прямые)
drawLines(Img,LinesParams,pts,20);

namedWindow("Image");
imshow("Image",Img);

waitKey(0);
pts.clear();
return 0;
}[/code]

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Наглядная демонстрация преобразования Хафа:

Здесь скрипт:

http://www.activovision.com/octavi/doku.php?id=hough_transform

Видео работы скрипта:

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×