Sid
Sid

Reputation: 129

Find all the lines pointing one way in an Image

I'm trying to come up with a filter that would show ONLY the lines pointing in one direction.

Say we have our Original Image like so: Original Image

What filter do I need to apply to select only the vertical lines. Like the ones highlighted in red here. Red Highlight on Vertical lines

Can this filter be generic to allow me to select other directions? Diagonal from bottom left to top right, for example.

Upvotes: 2

Views: 1089

Answers (1)

Max Walczak
Max Walczak

Reputation: 434

The most straightforward (although not the fastest) solution would be to use Hough transform to detect lines and then measure the slope of the lines and leave only the vertical ones.

Please take a look at this tutorial first https://docs.opencv.org/2.4/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.html.

Here's a code sample that solves your problem more or less. You might want to adjust some parameters to get better results:

std::vector<cv::Vec4i> extractVerticalLines(const std::vector<cv::Vec4i>& lines, int tollerance)
{
    std::vector<cv::Vec4i> output;
    for(const auto& line : lines)
    {
        // subtract x0 and x1 component (horizontal beginning and horizontal end)
        if(std::abs(line[0] - line[2]) <= tollerance)
        {
            output.push_back(line);
        }
    }

    return output;
}

void drawLines(cv::Mat& inputImage, const std::vector<cv::Vec4i>& lines, const cv::Scalar& color, int thickness)
{
    for(const auto& line : lines)
    {
        cv::line(inputImage, cv::Point2i(line[0], line[1]), cv::Point2i(line[2], line[3]), color, thickness);
    }
}

std::vector<cv::Vec4i> extractAllLines(const cv::Mat& image, int threshold, double minLength = 100)
{
    std::vector<cv::Vec4i> lines;
    cv::HoughLinesP(image, lines, 1, CV_PI / 180, threshold, minLength);
    return lines;
}

int main()
{
    auto image = cv::imread("lines.png", cv::IMREAD_GRAYSCALE);
    auto output = cv::Mat(image.size(), image.type(), cv::Scalar::all(0));
    image = ~image; // invert colors because background should be black and lines white

    auto lines = extractAllLines(image, 50);
    auto verticalLines = extractVerticalLines(lines, 5);
    drawLines(output, verticalLines, cv::Scalar::all(255), 1);
    cv::imshow("Result", output);
    cv::waitKey(0);
}

This yields the following result: Result Keep in mind that there are multiple lines that merge together in the output. If you want there to be exactly one line vertically, you'd have to skeletonize the image first so that everything is 1px thick or try some smart filtering of the results.

Upvotes: 1

Related Questions