該技術的核心我在下面分享使用cv::HoughLinesP()
查找灰度圖像中的線段。
應用程序首先將輸入圖像加載爲灰度。然後,它執行一個基本預處理操作來增強圖像的某些特性,旨在改善由cv::HoughLinesP()
執行檢測:
#include <cv.h>
#include <highgui.h>
#include <algorithm>
// Custom sort method adapted from: http://stackoverflow.com/a/328959/176769
// This is used later by std::sort()
struct sort_by_y_coord
{
bool operator()(cv::Vec4i const& a, cv::Vec4i const& b) const
{
if (a[1] < b[1]) return true;
if (a[1] > b[1]) return false;
return false;
}
};
int main()
{
/* Load input image as grayscale */
cv::Mat src = cv::imread("13531682.jpg", 0);
/* Pre-process the image to enhance the characteristics we are interested at */
medianBlur(src, src, 5);
int erosion_size = 2;
cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS,
cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
cv::Point(erosion_size, erosion_size));
cv::erode(src, src, element);
cv::dilate(src, src, element);
/* Identify all the lines in the image */
cv::Size size = src.size();
std::vector<cv::Vec4i> total_lines;
cv::HoughLinesP(src, total_lines, 1, CV_PI/180, 100, size.width/2.f, 20);
int n_lines = total_lines.size();
std::cout << "* Total lines: "<< n_lines << std::endl;
cv::Mat disp_lines(size, CV_8UC1, cv::Scalar(0, 0, 0));
// For debugging purposes, the block below writes all the lines into disp_lines
// for (unsigned i = 0; i < n_lines; ++i)
// {
// cv::line(disp_lines,
// cv::Point(total_lines[i][0], total_lines[i][2]),
// cv::Point(total_lines[i][3], total_lines[i][4]),
// cv::Scalar(255, 0 ,0));
// }
// cv::imwrite("total_lines.png", disp_lines);
此時,所有檢測到可以寫入用於可視化文件中的線段用途:
在這一點上,我們需要重新梳理我們的行向量,因爲cv::HoughLinesP()
沒有做到這一點,我們需要的載體排序以能夠識別線路組,通過測量和比較線路之間的距離:
/* Sort lines according to their Y coordinate.
The line closest to Y == 0 is at the first position of the vector.
*/
sort(total_lines.begin(), total_lines.end(), sort_by_y_coord());
/* Separate them according to their (visible) groups */
// Figure out the number of groups by distance between lines
std::vector<int> idx_of_groups; // stores the index position where a new group starts
idx_of_groups.push_back(0); // the first line indicates the start of the first group
// The loop jumps over the first line, since it was already added as a group
int y_dist = 35; // the next groups are identified by a minimum of 35 pixels of distance
for (unsigned i = 1; i < n_lines; i++)
{
if ((total_lines[i][5] - total_lines[i-1][6]) >= y_dist)
{
// current index marks the position of a new group
idx_of_groups.push_back(i);
std::cout << "* New group located at line #"<< i << std::endl;
}
}
int n_groups = idx_of_groups.size();
std::cout << "* Total groups identified: "<< n_groups << std::endl;
上面簡單地存儲在新vector<int>
線的向量的索引位置的代碼的最後部分,所以我們知道哪些線開始一個新組。
例如,假設存儲在新矢量中的索引是:0 4 8 12
。記住:他們定義每個組的開始。這意味着這些組的結束行是:0, 4-1, 4, 8-1, 8, 12-1, 12
。
知道,我們寫出下面的代碼:
/* Mark the beginning and end of each group */
for (unsigned i = 0; i < n_groups; i++)
{
// To do this, we discard the X coordinates of the 2 points from the line,
// so we can draw a line from X=0 to X=size.width
// beginning
cv::line(disp_lines,
cv::Point(0, total_lines[ idx_of_groups[i] ][7]),
cv::Point(size.width, total_lines[ idx_of_groups[i] ][8]),
cv::Scalar(255, 0 ,0));
// end
if (i != n_groups-1)
{
cv::line(disp_lines,
cv::Point(0, total_lines[ idx_of_groups[i+1]-1 ][9]),
cv::Point(size.width, total_lines[ idx_of_groups[i+1]-1 ][10]),
cv::Scalar(255, 0 ,0));
}
}
// mark the end position of the last group (not done by the loop above)
cv::line(disp_lines,
cv::Point(0, total_lines[n_lines-1][11]),
cv::Point(size.width, total_lines[n_lines-1][12]),
cv::Scalar(255, 0 ,0));
/* Save the output image and display it on the screen */
cv::imwrite("groups.png", disp_lines);
cv::imshow("groove", disp_lines);
cv::waitKey(0);
cv::destroyWindow("groove");
return 0;
}
而產生的圖像是:
這不是一個完美的比賽,但已經很接近了。稍微調整一下,這種方法可以變得更好。我首先要編寫一個更智能的邏輯sort_by_y_coord
,它應該放棄X座標(即小線段)之間距離較短的線條,以及X軸線上沒有完全對齊的線條(如第二組中的線條在輸出圖像中)。在花時間評估應用程序生成的第一個圖像之後,這個建議更有意義。
祝你好運。
也許你可以包含一個示例圖像? –
歡迎來到Stackoverflow。請仔細查看我的答案,然後如果它對您有幫助,請對其進行投票。您可以點擊附近的複選框將其選爲您問題的正式答案。通過這樣做,你將通過保持這個線程的組織來幫助像你和我們這樣的未來訪問者。 – karlphillip