通过霍夫变换检测框

Detecting boxes via Hough Transform

我正在尝试使用 HoughTransform 检测框并提供不同的颜色。

到目前为止我的理解是一个方框是水平线和垂直线。

我的密码是

lines = cv2.HoughLines(edges,1,np.pi/180, 50) 

# The below for loop runs till r and theta values  
# are in the range of the 2d array 
for r,theta in lines[0]: 

# Stores the value of cos(theta) in a 
a = np.cos(theta) 

# Stores the value of sin(theta) in b 
b = np.sin(theta) 

# x0 stores the value rcos(theta) 
x0 = a*r 

# y0 stores the value rsin(theta) 
y0 = b*r 

# x1 stores the rounded off value of (rcos(theta)-1000sin(theta)) 
x1 = int(x0 + 1000*(-b)) 

# y1 stores the rounded off value of (rsin(theta)+1000cos(theta)) 
y1 = int(y0 + 1000*(a)) 

# x2 stores the rounded off value of (rcos(theta)+1000sin(theta)) 
x2 = int(x0 - 1000*(-b)) 

# y2 stores the rounded off value of (rsin(theta)-1000cos(theta)) 
y2 = int(y0 - 1000*(a)) 

# cv2.line draws a line in img from the point(x1,y1) to (x2,y2). 
# (255,255,255) denotes the colour of the line to be.  
cv2.line(img,(x1,y1), (x2,y2), (255,255,255),2) `

我该怎么做才能让盒子上色或识别?

垂直线和水平线的检测应该分开进行,这样才能更具体。

遍历所有线条并编制水平和垂直线条组合之间的交叉点列表

现在你有一个二维点列表,如果你绘制它们应该几乎在方框的角上。最后一步是将这些点收集到有意义的集合中。

为了得到这些集合,我会从最近的原点开始(只是为了从某个地方开始)。我会查看所有其他点,寻找最近的 other 点,该点具有更大的 x 但在起点的 +-5(或某个可配置范围)y 范围内。然后做同样的事情,但在 y 方向。你现在有了盒子的底角。您可以完成并开始您的 ocr,但为了更强大,还要找到最后一个角。

找到所有 4 个角后,从交集数组中删除所有这些点,然后添加要表示方框位置的新数组。冲洗并重复,因为现在不同的点将是最近的原点。在没有实际测试的情况下,我认为它会在 K 盒上阻塞(或需要对缺失的墙壁进行一些有条件的改进),但对于可变盒的形状和大小应该是非常通用的。

编辑 1:在测试中,我发现可能很难将两个相邻框的闭合角分开。我认为更通用和更强大的解决方案是在发生碰撞后,在大约 1/3 框最小边长处进行点聚类操作。这将平均角与最近的邻居一起。因此,这将稍微改变策略,因为您需要使用每个角两次(框向左和框向右),端点除外。

编写了一些测试代码并且可以正常运行,输出如下:

代码,对于 c++ 很抱歉,一点也没有优化,星期五快乐 :)

//CPP libaries
#include <stdio.h>
#include <mutex>
#include <thread>

//Included libraries
//Note: these headers have to be before any opencv due to a namespace collision (could probably be fixed)

#include <opencv2/opencv.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

using namespace cv;

// Finds the intersection of two lines, or returns false.
// The lines are defined by (o1, p1) and (o2, p2).
//
bool intersection(Point2f o1, Point2f p1, Point2f o2, Point2f p2,
    Point2f &r)
{
    Point2f x = o2 - o1;
    Point2f d1 = p1 - o1;
    Point2f d2 = p2 - o2;

    float cross = d1.x*d2.y - d1.y*d2.x;
    if (abs(cross) < /*EPS*/1e-8)
        return false;

    double t1 = (x.x * d2.y - x.y * d2.x) / cross;
    r = o1 + d1 * t1;
    return true;
}

std::vector<Point2f> clusterPts(std::vector<Point2f> inputPts, double clusterRadius_Squared)
{
    std::vector<Point2f> outputPts = std::vector<Point2f>();
    while(inputPts.size()>0)
    {
        Point2f clusterCenter = inputPts[0];
        while (true)
        {
            Point2f newClustCenter = Point2f(0, 0);
            int averagingCount = 0;
            std::vector<int> clusterIndicies = std::vector<int>();
            for (int i = 0; i < inputPts.size(); i++)
            {
                if (clusterRadius_Squared >= pow(inputPts[i].x - clusterCenter.x, 2) + pow(inputPts[i].y - clusterCenter.y, 2))
                {
                    newClustCenter.x += inputPts[i].x;
                    newClustCenter.y += inputPts[i].y;
                    averagingCount += 1;
                    clusterIndicies.push_back(i);
                }
            }
            newClustCenter = newClustCenter / (double)averagingCount;

            if (newClustCenter == clusterCenter)
            {
                //remove all points inside cluster from inputPts, stash cluster center, and break inner while loop
                std::vector<Point2f> remainingPts = std::vector<Point2f>();
                for (int i = 0; i < inputPts.size(); i++)
                {
                    if (std::find(clusterIndicies.begin(), clusterIndicies.end(), i) == clusterIndicies.end())
                    {
                        remainingPts.push_back(inputPts[i]);
                    }
                }
                inputPts = remainingPts;
                outputPts.push_back(clusterCenter);
                break;
            }
            else
            { 
                clusterCenter = newClustCenter;
            }
        }
    }
    return outputPts;
}

std::vector<Rect> findBoxes(std::vector<Point2f> corners, bool shrinkBoxes = false, int boxSideLength_Guess = 50)
{
    std::vector<Rect> outBoxes = std::vector<Rect>();
    int approxBoxSize = 1000 * boxSideLength_Guess;

    while (corners.size()>4)
    {
        //find point above or below (these points will be removed from array after used)
        int secondPtIndex = -1;
        for (int i = 1; i < corners.size(); i++)
        {
            if (abs(corners[i].x - corners[0].x) < boxSideLength_Guess / 2.0)
            {
                secondPtIndex = i;
                break;
            }
        }
        if (secondPtIndex == -1)
        {
            std::cout << "bad box point tossed" << std::endl;
            corners.erase(corners.begin() + 0);
            continue;
        }

        //now search for closest same level point on either side
        int thirdIndexRight = -1;
        int thirdIndexLeft = -1;
        double minDistRight = approxBoxSize;
        double minDistLeft = -approxBoxSize;
        for (int i = 2; i < corners.size(); i++)
        {
            if (abs(corners[i].y - corners[secondPtIndex].y) < boxSideLength_Guess / 2.0)
            {
                double dist = corners[i].x - corners[secondPtIndex].x;

                if (dist < 0 && dist > minDistLeft) //check left
                {
                    minDistLeft = dist;
                    thirdIndexLeft = i;
                }   
                else if(dist > 0 && dist < minDistRight) //check right
                {
                    minDistRight = dist;
                    thirdIndexRight = i;
                }
            }
        }

        if (thirdIndexLeft != -1) { approxBoxSize = 1.5 * abs(minDistLeft); }
        if (thirdIndexRight != -1) { approxBoxSize = 1.5 * minDistRight; }

        int fourthIndexRight = -1;
        int fourthIndexLeft = -1;

        for (int i = 1; i < corners.size(); i++)
        {
            if (i == thirdIndexLeft || i == thirdIndexRight) { continue; }

            if (thirdIndexLeft != -1 && abs(corners[i].x - corners[thirdIndexLeft].x) < boxSideLength_Guess / 2.0)
            { fourthIndexLeft = i; }
            if (thirdIndexRight != -1 && abs(corners[i].x - corners[thirdIndexRight].x) < boxSideLength_Guess / 2.0)
            { fourthIndexRight = i; }
        }

        if (!shrinkBoxes)
        {
            if (fourthIndexRight != -1)
            {
                outBoxes.push_back(Rect(corners[0], corners[thirdIndexRight]));
            }
            if (fourthIndexLeft != -1)
            {
                outBoxes.push_back(Rect(corners[0], corners[thirdIndexLeft]));
            }
        }
        else
        {
            if (fourthIndexRight != -1)
            {
                outBoxes.push_back(Rect(corners[0] * 0.90 + corners[thirdIndexRight] *0.10, corners[0] * 0.10 + corners[thirdIndexRight] * 0.90));
            }
            if (fourthIndexLeft != -1)
            {
                outBoxes.push_back(Rect(corners[0] * 0.90 + corners[thirdIndexLeft] * 0.10, corners[0] * 0.10 + corners[thirdIndexLeft] * 0.90));
            }
        }


        corners.erase(corners.begin() + secondPtIndex);
        corners.erase(corners.begin() + 0);
    }
    std::cout << approxBoxSize << std::endl;
    return outBoxes;
}


int main(int argc, char** argv)
{
    Mat image = imread("../../resources/images/boxPic.png", CV_LOAD_IMAGE_GRAYSCALE);

    imshow("source", image);

    //namedWindow("Display window", WINDOW_AUTOSIZE);// Create a window for display.
    //imshow("Display window", image);                   // Show our image inside it.

    Mat edges, lineOverlay, cornerOverlay, finalBoxes;
    Canny(image, edges, 50, 200, 3);
    //edges = image;
    //cvtColor(image, edges, COLOR_GRAY2BGR);
    cvtColor(image, lineOverlay, COLOR_GRAY2BGR);
    cvtColor(image, cornerOverlay, COLOR_GRAY2BGR);
    cvtColor(image, finalBoxes, COLOR_GRAY2BGR);

    std::cout << image.cols << " , "<<image.rows << std::endl;

    std::vector<Vec2f> linesHorizontal;
    std::vector<Point> ptsLH;
    HoughLines(edges, linesHorizontal, 5, CV_PI / 180, 2 * edges.cols * 0.6, 0.0,0.0, CV_PI / 4, 3 * CV_PI / 4);

    std::vector<Vec2f> linesVertical;
    std::vector<Point> ptsLV;
    HoughLines(edges, linesVertical, 5, CV_PI / 180, 2 * edges.rows * 0.6,0,0,-CV_PI/32,CV_PI/32);

    for (size_t i = 0; i < linesHorizontal.size(); i++)
    {
        float rho = linesHorizontal[i][0], theta = linesHorizontal[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a * rho, y0 = b * rho;
        pt1.x = cvRound(x0 + 1000 * (-b));
        pt1.y = cvRound(y0 + 1000 * (a));
        pt2.x = cvRound(x0 - 1000 * (-b));
        pt2.y = cvRound(y0 - 1000 * (a));
        ptsLH.push_back(pt1);
        ptsLH.push_back(pt2);
        line(lineOverlay, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
    }

    for (size_t i = 0; i < linesVertical.size(); i++)
    {
        float rho = linesVertical[i][0], theta = linesVertical[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a * rho, y0 = b * rho;
        pt1.x = cvRound(x0 + 1000 * (-b));
        pt1.y = cvRound(y0 + 1000 * (a));
        pt2.x = cvRound(x0 - 1000 * (-b));
        pt2.y = cvRound(y0 - 1000 * (a));
        ptsLV.push_back(pt1);
        ptsLV.push_back(pt2);
        line(lineOverlay, pt1, pt2, Scalar(0, 255, 0), 1, LINE_AA);
    }

    imshow("edged", edges);
    imshow("detected lines", lineOverlay);

    //look for collisions
    std::vector<Point2f> xPts;
    for (size_t i = 0; i < linesHorizontal.size(); i++)
    {
        for (size_t ii = 0; ii < linesVertical.size(); ii++)
        {
            Point2f xPt;
            bool intersectionExists = intersection(ptsLH[2 * i], ptsLH[2 * i + 1], ptsLV[2 * ii], ptsLV[2 * ii + 1], xPt);
            if (intersectionExists)
            {
                xPts.push_back(xPt);
            }
        }
    }
    waitKey(1000);
    std::vector<Point2f> boxCorners = clusterPts(xPts, 25*25);
    for (int i = 0; i < boxCorners.size(); i++)
    {
        circle(cornerOverlay, boxCorners[i], 5, Scalar(0, 255, 0), 2);
    }
    imshow("detected corners", cornerOverlay);

    //group make boxes for groups of points
    std::vector<Rect> ocrBoxes = findBoxes(boxCorners,true);
    for (int i = 0; i < ocrBoxes.size(); i++)
    {
        if (i % 3 == 0) { rectangle(finalBoxes, ocrBoxes[i], Scalar(255, 0, 0), 2); }
        else if(i % 3 == 1) { rectangle(finalBoxes, ocrBoxes[i], Scalar(0, 255, 0), 2); }
        else if (i % 3 == 2) { rectangle(finalBoxes, ocrBoxes[i], Scalar(0, 0, 255), 2); }
    }

    imshow("detected boxes", finalBoxes);

    waitKey(0);                                          // Wait for a keystroke in the window
    return 0;
}