如何在二值骨架化图像中追踪线段?
How to trace line segments in a binary skeletonized image?
我有一个二值化和骨架化的图像。我使用 Zhang-Suen 算法进行骨架化。现在我需要以 2 点格式(线段的起点和终点)从图像中获取线段。
到目前为止,我一直在使用 OpenCV 函数 findContours,带有 CV_CHAIN_APPROX_SIMPLE 和 CV_RETR_LIST 选项。但是,出现了三个问题:
- 此方法returns复制线段(相反方向)
- 由于 "hierarchy feature"
,连接的结构有时会断开连接
- 线条交叉点附近的结果很乱。
还有其他方法可以从图像中追踪线段吗?
我需要追踪的图像的放大部分:
应该可以。它对图像进行 4 次扫描(您可以减少扫描次数,但逻辑会更复杂)。
在每次扫描中,它会跟踪水平线、垂直线、向下的对角线和向上的对角线。
行存储在 vector<Vec4i>
中,其中每个 Vec4i
是一行 Xstart, Ystart, Xend, Yend
;
让我知道这是否适合你。
#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;
}
从这张图片开始:
用随机颜色绘制每条检测到的线(从头到尾)给出:
缩放:
我有一个二值化和骨架化的图像。我使用 Zhang-Suen 算法进行骨架化。现在我需要以 2 点格式(线段的起点和终点)从图像中获取线段。 到目前为止,我一直在使用 OpenCV 函数 findContours,带有 CV_CHAIN_APPROX_SIMPLE 和 CV_RETR_LIST 选项。但是,出现了三个问题:
- 此方法returns复制线段(相反方向)
- 由于 "hierarchy feature" ,连接的结构有时会断开连接
- 线条交叉点附近的结果很乱。
还有其他方法可以从图像中追踪线段吗?
我需要追踪的图像的放大部分:
应该可以。它对图像进行 4 次扫描(您可以减少扫描次数,但逻辑会更复杂)。
在每次扫描中,它会跟踪水平线、垂直线、向下的对角线和向上的对角线。
行存储在 vector<Vec4i>
中,其中每个 Vec4i
是一行 Xstart, Ystart, Xend, Yend
;
让我知道这是否适合你。
#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;
}
从这张图片开始:
用随机颜色绘制每条检测到的线(从头到尾)给出:
缩放: