How to track line segments in a binary skeletonized image?
I have a binarized and skeletonized image. I used Zhang-Suen's algorithm for skeletonization. Now I need to get line segments from an image in 2-point format (start and end points of line segment). So far I have used the OpenCV findContours function with the CV_CHAIN_APPROX_SIMPLE and CV_RETR_LIST parameters. However, three problems arise:
- This method returns repeating line segments (in the opposite direction)
- Linked structures are sometimes disabled due to the "hierarchy function"
- Unhelpful results in the vicinity of line intersections.
Is there another way to keep track of image line segments?
source to share
This should work. It does 4 scans of the image (you can probably reduce the number of scans, but the logic will be more complicated).
In each scan, it tracks horizontal lines, vertical lines, downward diagonal lines, and upward diagonal lines.
Lines are stored in vector<Vec4i>
, where each Vec4i
is a string with Xstart, Ystart, Xend, Yend
;
Let me know if this works for you.
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
Mat1b w;
// Add bg border
copyMakeBorder(img, w, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(0));
vector<Vec4i> lines;
Vec4i line;
// Scan horizontal lines
for (int y = 1; y < w.rows - 1; ++y)
{
for (int x = 1; x < w.cols - 1; ++x)
{
if (w(y, x) == 255)
{
int yy = y;
int xx = x;
//Save first point
line[0] = xx - 1;
line[1] = yy - 1;
while (true)
{
if (w(yy, xx + 1))
{
// Mark as detected
w(yy, xx) = 1;
++xx;
}
else
{
// End of the line
line[2] = xx - 1;
line[3] = yy - 1;
if (line[2] - line[0] > 0)
{
lines.push_back(line);
}
break;
}
}
}
}
}
// Scan vertical lines
for (int y = 1; y < w.rows - 1; ++y)
{
for (int x = 1; x < w.cols - 1; ++x)
{
if (w(y, x) == 255)
{
int yy = y;
int xx = x;
//Save first point
line[0] = xx - 1;
line[1] = yy - 1;
while (true)
{
if (w(yy + 1, xx))
{
// Mark as detected
w(yy, xx) = 1;
++yy;
}
else
{
// End of the line
line[2] = xx - 1;
line[3] = yy - 1;
if (line[3] - line[1] > 0)
{
lines.push_back(line);
}
break;
}
}
}
}
}
// Scan for diagonal low lines
for (int y = 1; y < w.rows - 1; ++y)
{
for (int x = 1; x < w.cols - 1; ++x)
{
if (w(y, x) == 255)
{
int yy = y;
int xx = x;
//Save first point
line[0] = xx - 1;
line[1] = yy - 1;
while (true)
{
if (w(yy + 1, xx + 1))
{
// Mark as detected
w(yy, xx) = 1;
++xx;
++yy;
}
else
{
// End of the line
line[2] = xx - 1;
line[3] = yy - 1;
if (line[2] - line[0] > 0)
{
lines.push_back(line);
}
break;
}
}
}
}
}
// Scan for diagonal high lines
for (int y = 1; y < w.rows - 1; ++y)
{
for (int x = 1; x < w.cols - 1; ++x)
{
if (w(y, x) == 255)
{
int yy = y;
int xx = x;
//Save first point
line[0] = xx - 1;
line[1] = yy - 1;
while (true)
{
if (w(yy - 1, xx + 1))
{
// Mark as detected
w(yy, xx) = 1;
++xx;
--yy;
}
else
{
// End of the line
line[2] = xx - 1;
line[3] = yy - 1;
if (line[2] - line[0] > 0)
{
lines.push_back(line);
}
break;
}
}
}
}
}
RNG rng(12345);
Mat3b res;
cvtColor(img, res, COLOR_GRAY2BGR);
for (int i = 0; i < lines.size(); ++i)
{
const Vec4i& lin = lines[i];
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
cv::line(res, Point(lin[0], lin[1]), Point(lin[2], lin[3]), color);
}
imshow("res", res);
waitKey();
return 0;
}
Starting with this image:
Drawing each line found (from start to finish) with random colors gives:
Increased:
source to share