[QT+opencv]十字标定中心坐标计算【霍夫变换版】

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第1张图片[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第2张图片[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第3张图片

这样两张图片,第二张的识别就很简单了,图像像素少。本篇写第一张图片的识别,基本思路是 识别轮廓->轮廓直线拟合->计算交点坐标->计算中心坐标。有人跟我说用骨架细化的算法比较简单,下次我在写骨架细化,先做了第一种比较笨的方法实现的,可能有点麻烦,但这是我拿到图像的第一思路。最后用QT做了一个窗口来输出。

完整版代码在最后。

 

 

1.轮廓识别

拿到图像发现周围有一圈黑色的,这样是无法识别的,先转灰度图,然后转二值图,设置一下阈值就行了。然后用canny函数识别轮廓。

    cvtColor( src, image_gray, CV_BGR2GRAY );//灰度化
    threshold(image_gray, src, 110, 255, THRESH_TOZERO);//二值化

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第4张图片

这是二值化后的图像,关于opencv中阈值的设置,以后在记录,也是看了一些博客才学会用法,255是白色,0是黑色,我这里设置的阈值是110,THRESH_TOZERO将像素值低于110变成0,其余的保留。为什么设置成110,其实是试了很久,有好几组测试用例,测试了半天,阈值的大小会影响最后我识别的轮廓精度...所以我这里设置成110最合适,换一组图片不阈值都需要改的。

    GaussianBlur( src, src, Size(3,3), 0.1, 0, BORDER_DEFAULT );
    blur( src, src, Size(3,3) ); //滤波
    Canny( src, src, thresh, thresh*3, 3 );

canny识别轮廓,滤波去噪。thresh我设置的值是45,写在头文件里了。大概识别出来图像就是这样的,很乱。。。主要是图片本身就这样的,我也很无奈啊,原图真的挺大的,是bmp格式,我这里都放的缩略图,图像低一点像素都不会这样。

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第5张图片

2.轮廓直线拟合

    Mat contours1;
    Canny(src,contours1,125,350); 
    std::vector lines;//存放绘制线条,lines[0]代表了极坐标中r ,lines[1]代表了极坐标中角度theta

    //调用hough函数
    HoughLines(contours1,lines,1,PI/180,305);
    threshold(image_gray, image_gray, 255, 255, THRESH_BINARY_INV);
    imwrite("E:\\white3.jpg",image_gray);//将lines放到空白图像中,图像继承原图像的长宽。
    Mat hough_output = imread("E:\\white3.jpg",CV_LOAD_IMAGE_COLOR);
    std::vector::const_iterator it = lines.begin();
    while(it != lines.end())
    {
        float rho = (*it)[0];
        float theta=(*it)[1];
        if(theta < PI/4. || theta > 3. *PI / 4.)
        {
            //接近于垂直线条
            Point pt1(rho/cos(theta),0);
            Point pt2((rho-hough_output.rows*sin(theta))/cos(theta),hough_output.rows);
            line(hough_output,pt1,pt2,Scalar(255),1);
        }
        else
        {
            //接近于水平线 r=xcos(theta)+ysin(theta)
            Point pt1(0,rho/sin(theta));
            Point pt2(hough_output.cols,(rho-hough_output.cols*cos(theta))/sin(theta));
            line(hough_output,pt1,pt2,Scalar(255),1);
        }
        ++it;
    }

将轮廓的线拟合成一条,霍夫变换调用这里设置的阈值是305,反正就是越大识别越精确。这样是不是看着舒服多了~但是这里有一个问题,因为huogh调用完后是在原图上输出,还会有弯弯曲曲的边,我就设置了一张空白的图像,图像大小是原来的图像大小,用二值化生成的,将所有像素都变成255,挺笨的。。但是我不知道用什么方法可以去除源图像中的canny。关于函数具体的计算,可以查看别的大佬的博客,我也是看了很多博客找到的用法,大概就是识别一个像素点的周围8个像素中的点,连线在走下一个是否在一个直线上,一个一个点去判断,阈值设置最短直线长度,其他的短线都去掉了。直线方程是用极坐标的,在下面的while中绘制线条到图像时,起点分为x轴和y轴两个,用极坐标方程求出与x轴y轴交点,再判断下一个点。

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第6张图片

 

HoughLines(contours1,lines,1,PI/180,305);//阈值305

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第7张图片

 

 

 

将305阈值换成100的结果如下图。

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第8张图片

3.计算交点坐标

首先先处理之前做好的一个轮廓图像,这里用到了霍夫的第二个函数,HoughLinesp:统计概率霍夫线变换

简单来说就是HoughLines是用极坐标识别了整条线上的点到原点的r和theta,但是HoughLinesP是识别了这条线上的两个端点。(HoughLinesP是在HoughLines的基础上加了一个概率P,表明可以采用累积概率霍夫变换来找出二值图中的直线。HoughLinesP的第二个参数可以得到直线的两个端点坐标,这点是优势。累积概率霍夫变换相比于标准和多尺度计算量小,执行效率高)

这里计算交点因此需要算出直线斜率然后找点。(我知道效率很低,虽然可以直接用HoughLines来求交点,目前第一版我是这样做的,后期再修改吧);这里有一个sidewide的计算,是需求里要求计算十字架边宽度的。

    Mat houghGray;
    Mat houghthre;
    
    cvtColor(hough_output, houghGray, CV_BGR2GRAY);//转灰度
    threshold(houghGray, houghthre, 100, 255, THRESH_BINARY);//二值化
    Canny( houghthre, houghthre, thresh, thresh*3, 3 );//识别轮廓
    
    vector Lines2;//端点四个,0->xmin,1->ymin,2->xmax,3->ymax
    HoughLinesP(houghthre, Lines2, 1, CV_PI /180, 200, 100, 100);
    vector vecPoint;
    for(int i=0;isidewide)
                        sidewide = temp;

                }

            }
            else if(ka == '-inf')//竖直横线
            {
                if(kj!='inf')
                {
                    LineB = Lines2[j];
                    Point2f crossPoint;
                    crossPoint.x = LineA[0];
                    crossPoint.y = LineB[1];
                    vecPoint.push_back(crossPoint);
                    circle(houghthre, crossPoint, 3, Scalar(255, 255, 255));
                }
                else
                {
                    LineB = Lines2[j];
                    double temp = qAbs(LineB[0]-LineA[0]);
                    if(temp>sidewide)
                        sidewide = temp;

                }

            }

        }
    }

这里输出了所有的交点,存储在vecPoint中。

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第9张图片

4.求中心点

四个交点除以4!

 

float xSum = 0;
    float ySum = 0;
    for(int i=0;i
    {
    
        xSum = xSum+vecPoint[i].x;
        ySum = ySum+vecPoint[i].y;
    }
    ui->xPoint->setText(QString::number(xSum/vecPoint.size()));
    ui->yPoint->setText(QString::number(ySum/vecPoint.size()));
    ui->side->setText(QString::number((sidewide)));
 

最后结果窗口就是这样的。[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第10张图片

5.存在的问题

有的时候不能刚好识别出四个轮廓的,跟图片亮度和我设置的阈值有关,有的时候还识别不出来图片太暗的时候,因此我改善了一下,把阈值的手动设定,如图:

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第11张图片

二值图做的精准,那识别就没问题了。下面这张测试用例阈值要设置成120才能识别两个边~

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第12张图片

 

[QT+opencv]十字标定中心坐标计算【霍夫变换版】_第13张图片

整体代码

mainwindow.h

 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "qlabel.h"
using namespace cv;
using namespace std;
 
namespace Ui {
    
class MainWindow;
}
 
class MainWindow : public QMainWindow
{
    
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    cv::Mat image;
    QImage img;
    cv::Mat image_gray;
    double sidewide;
    int thresh = 45;
    int max_thresh = 255;
 
private slots:
    void on_select_clicked();
    void on_detection_clicked();
 
private:
    Ui::MainWindow *ui;
};
 
#endif // MAINWINDOW_H
 

mainwindow.cpp

 

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define PI 3.1415926
using namespace cv;
using namespace std;
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    
    ui->setupUi(this);
}
 
MainWindow::~MainWindow()
{
    
    delete ui;
}
 
void MainWindow::on_select_clicked()
{
    
    QString filename = QFileDialog::getOpenFileName(this,tr("Open Image"),"",tr("Image File(*.bmp *.jpg *.jpeg *.png)"));
    QTextCodec *code = QTextCodec::codecForName("gb18030");
    std::string name = code->fromUnicode(filename).data();
    image = cv::imread(name);
    if(!image.data)
    {
    
        QMessageBox msgBox;
        msgBox.setText(tr("image data is null"));
        msgBox.exec();
    }
}
 
void MainWindow::on_detection_clicked()
{
    
    sidewide = 0;
    Mat src;
    src = image;
    int k;
    QString kStr = ui->k->text();
    bool ok;
    k = kStr.toInt(&ok,10);
    cvtColor( src, image_gray, CV_BGR2GRAY );//灰度化
    threshold(image_gray, src, k, 255, THRESH_TOZERO);
    //imwrite("E:\\src.jpg",src);
    GaussianBlur( src, src, Size(3,3), 0.1, 0, BORDER_DEFAULT );
    blur( src, src, Size(3,3) ); //滤波
    Canny( src, src, thresh, thresh*3, 3 );
 
    Mat contours1;
    Canny(src,contours1,125,350);
    //Mat contoursInv;
    //threshold(contours1,contoursInv,128,255,THRESH_BINARY_INV);
    std::vector lines;
 
    //调用hough函数
    HoughLines(contours1,lines,1,PI/180,305);
    threshold(image_gray, image_gray, 255, 255, THRESH_BINARY_INV);
    imwrite("C:\\white3.jpg",image_gray);
    //imshow("image_gray",image_gray);
    Mat hough_output = imread("E:\\white3.jpg",CV_LOAD_IMAGE_COLOR);
 
    //src.copyTo(hough_output);
    std::vector::const_iterator it = lines.begin();
    while(it != lines.end())
    {
    
        float rho = (*it)[0];
        float theta=(*it)[1];
        if(theta < PI/4. || theta > 3. *PI / 4.)
        {
    
            //接近于垂直线条
            Point pt1(rho/cos(theta),0);
            Point pt2((rho-hough_output.rows*sin(theta))/cos(theta),hough_output.rows);
            line(hough_output,pt1,pt2,Scalar(255),1);
        }
        else
        {
    
            //接近于水平线 r=xcos(theta)+ysin(theta)
            Point pt1(0,rho/sin(theta));
            Point pt2(hough_output.cols,(rho-hough_output.cols*cos(theta))/sin(theta));
            line(hough_output,pt1,pt2,Scalar(255),1);
        }
        ++it;
    }
 
    //imwrite("E:\\src.jpg",hough_output);
    Mat houghGray;
    Mat houghthre;
    //Mat New = imread("E:\\result2.jpg");
    cvtColor(hough_output, houghGray, CV_BGR2GRAY);
    threshold(houghGray, houghthre, 100, 255, THRESH_BINARY);
    Canny( houghthre, houghthre, thresh, thresh*3, 3 );
    //imshow("erodeImg",houghthre);
    vector Lines2;
    HoughLinesP(houghthre, Lines2, 1, CV_PI /180, 200, 100, 100);
    vector vecPoint;
    for(int i=0;i
    {
    
 
        Vec4i LineA = Lines2[i];
        Vec4i LineB;
        for(int j=i+1;j
        {
    
            double ka = (double)(LineA[3] - LineA[1])/(double)(LineA[2] - LineA[0]);
            double kb;
            double kj = (double)(Lines2[j][3] - Lines2[j][1])/(double)(Lines2[j][2] - Lines2[j][0]);
 
            if (kj*ka < 0)
            {
    
                LineB = Lines2[j];
                kb = kj;
                Point2f crossPoint;
                crossPoint.x = (ka*LineA[0] - LineA[1] - kb*LineB[0] + LineB[1]) / (ka - kb);
                crossPoint.y = (ka*kb*(LineA[0] - LineB[0]) + ka*LineB[1] - kb*LineA[1]) / (ka - kb);
                vecPoint.push_back(crossPoint);
                circle(houghthre, crossPoint, 5, Scalar(255, 255, 255), -1, 8, 0);
            }
            else if(ka==0)
            {
    
                if(kj!=0)
                {
    
                    LineB = Lines2[j];
                    Point2f crossPoint;
                    crossPoint.x = LineB[0];
                    //cout<< crossPoint.x<
                    crossPoint.y = LineA[1];
                    vecPoint.push_back(crossPoint);
                    circle(houghthre, crossPoint, 5, Scalar(255, 255, 255));
                }
                else
                {
    
                    LineB = Lines2[j];
                    double temp = qAbs(LineB[1]-LineA[1]);
                    if(temp>sidewide)
                        sidewide = temp;
 
                }
 
            }
            else if(ka == '-inf')
            {
    
                if(kj!='inf')
                {
    
                    LineB = Lines2[j];
                    Point2f crossPoint;
                    crossPoint.x = LineA[0];
                    crossPoint.y = LineB[1];
                    vecPoint.push_back(crossPoint);
                    circle(houghthre, crossPoint, 5, Scalar(255, 255, 255));
                }
                else
                {
    
                    LineB = Lines2[j];
                    double temp = qAbs(LineB[0]-LineA[0]);
                    if(temp>sidewide)
                        sidewide = temp;
 
                }
 
            }
 
        }
    }
    //imwrite("E:\\src.jpg",houghthre);
    float xSum = 0;
    float ySum = 0;
    for(int i=0;i
    {
    
        xSum = xSum+vecPoint[i].x;
        ySum = ySum+vecPoint[i].y;
    }
    ui->xPoint->setText(QString::number(xSum/vecPoint.size()));
    ui->yPoint->setText(QString::number(ySum/vecPoint.size()));
    ui->side->setText(QString::number((sidewide)));
}
 

 

 

 

你可能感兴趣的:(c++,opencv,QT)