[英]Detect rectangles from pixel data retrieved from binary png image
I have retrieved all pixels from a png image which looks like this... 我已经从看起来像这样的png图像中检索了所有像素...
I started to get all filled lines horizontal to get the start end pixel of each line .... as I scan horizontal I get also the vertical lines in horizontal layer.... I guess it is possible to code all manually but it takes time.... my question is has anyone experience with opencv,imageJ or other to tell if any of this libs could solve the problem... I am also open for any algorithm suggestion.... (using Java).... 我开始使所有填充线变为水平,以获取每条线的开始结束像素....当我水平扫描时,我也获得了水平层中的垂直线.....我想可以手动进行全部编码,但这需要时间...。我的问题是,有没有与opencv,imageJ或其他程序有关的经验,可以告诉我这些库中的任何一个是否可以解决问题...我也愿意接受任何算法建议...(使用Java)... 。
-> The biggest problem is the thickness of the lines between 1 and 4 pixels otherwise I could easily retrieve the joint points ->最大的问题是1到4像素之间的线的粗细,否则我可以很容易地找到连接点
Here is a possible solution using image morphology. 这是使用图像形态的可能解决方案。 The code below is in C++ since I only have little experience with Java. 下面的代码是C ++,因为我对Java的经验很少。
To solve your problem, you need: 要解决您的问题,您需要:
The bad news is both operations is not yet supported in OpenCV as of version 2.4.3. 坏消息是,从2.4.3版开始,OpenCV尚不支持这两种操作。 The good news is I have implemented both operations, the code is available on my blog: 好消息是我已经实现了这两种操作,该代码可在我的博客上找到:
I will be using my thinning()
and hitmiss()
functions and your test image. 我将使用我的thinning()
和hitmiss()
函数以及您的测试图像。
After loading the image, convert it to single-channel binary image. 加载图像后,将其转换为单通道二进制图像。
cv::Mat im = cv::imread("D1Xnm.png");
cv::Mat bw;
cv::cvtColor(im, bw, CV_BGR2GRAY);
cv::threshold(~bw, bw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Since the width of the lines vary from 1 to 4 pixels perform thinning to get one-width lines. 由于线的宽度从1到4像素不等,因此请进行细化处理以获得单宽度的线。
thinning(bw);
From the thinned image, notice that there are perfect and not perfect joint points as shown in the figure below. 从细化的图像中,请注意,存在完美的连接点和不完美的连接点,如下图所示。
To cover both the perfect and imperfect joint points, we need the following kernels for the hit-or-miss transform. 为了涵盖完美和不完美的连接点,我们需要以下内核来实现“命中或失败”变换。
std::vector<cv::Mat> k;
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 0,-1,-1,
-1,-1, 0,-1,-1,
0, 0, 0, 0, 1,
-1,-1, 0, 0,-1,
-1,-1, 1,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 0,-1,-1,
-1,-1, 0,-1,-1,
1, 0, 0, 0, 0,
-1, 0, 0,-1,-1,
-1,-1, 1,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 1,-1,-1,
-1,-1, 0,-1,-1,
1, 0, 0, 0, 0,
-1, 0, 0,-1,-1,
-1,-1, 0,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 1,-1,-1,
-1,-1, 0,-1,-1,
0, 0, 0, 0, 1,
-1,-1, 0, 0,-1,
-1,-1, 0,-1,-1 ));
cv::Mat dst = cv::Mat::zeros(bw.size(), CV_8U);
for (int i = 0; i < k.size(); i++)
{
cv::Mat tmp;
hitmiss(bw, tmp, k[i]);
dst |= tmp;
}
Open the original image to make the result clearer. 打开原始图像以使结果更清晰。
The joint points successfully located, draw it on the original image. 成功找到关节点,然后将其绘制在原始图像上。
std::vector<std::vector<cv::Point> > cnt;
cv::findContours(dst.clone(), cnt, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
cv::drawContours(im, cnt, -1, CV_RGB(255,0,0), 10);
For the sake of completeness, here is the full code. 为了完整起见,这里是完整的代码。 With some efforts you can port it to Java. 通过一些努力,您可以将其移植到Java。
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
void thinningIteration(cv::Mat& im, int iter)
{
cv::Mat marker = cv::Mat::zeros(im.size(), CV_8UC1);
for (int i = 1; i < im.rows; i++)
{
for (int j = 1; j < im.cols; j++)
{
uchar p2 = im.at<uchar>(i-1, j);
uchar p3 = im.at<uchar>(i-1, j+1);
uchar p4 = im.at<uchar>(i, j+1);
uchar p5 = im.at<uchar>(i+1, j+1);
uchar p6 = im.at<uchar>(i+1, j);
uchar p7 = im.at<uchar>(i+1, j-1);
uchar p8 = im.at<uchar>(i, j-1);
uchar p9 = im.at<uchar>(i-1, j-1);
int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
(p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
(p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
(p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);
if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0)
marker.at<uchar>(i,j) = 1;
}
}
im &= ~marker;
}
void thinning(cv::Mat& im)
{
im /= 255;
cv::Mat prev = cv::Mat::zeros(im.size(), CV_8UC1);
cv::Mat diff;
do {
thinningIteration(im, 0);
thinningIteration(im, 1);
cv::absdiff(im, prev, diff);
im.copyTo(prev);
}
while (cv::countNonZero(diff) > 0);
im *= 255;
}
void hitmiss(cv::Mat& src, cv::Mat& dst, cv::Mat& kernel)
{
CV_Assert(src.type() == CV_8U && src.channels() == 1);
cv::Mat k1 = (kernel == 1) / 255;
cv::Mat k2 = (kernel == -1) / 255;
cv::normalize(src, src, 0, 1, cv::NORM_MINMAX);
cv::Mat e1, e2;
cv::erode(src, e1, k1, cv::Point(-1,-1), 1, cv::BORDER_CONSTANT, cv::Scalar(0));
cv::erode(1 - src, e2, k2, cv::Point(-1,-1), 1, cv::BORDER_CONSTANT, cv::Scalar(0));
dst = e1 & e2;
}
int main()
{
cv::Mat im = cv::imread("D1Xnm.png");
if (im.empty())
return -1;
cv::Mat bw;
cv::cvtColor(im, bw, CV_BGR2GRAY);
cv::threshold(~bw, bw, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
thinning(bw);
std::vector<cv::Mat> k;
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 0,-1,-1,
-1,-1, 0,-1,-1,
0, 0, 0, 0, 1,
-1,-1, 0, 0,-1,
-1,-1, 1,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 0,-1,-1,
-1,-1, 0,-1,-1,
1, 0, 0, 0, 0,
-1, 0, 0,-1,-1,
-1,-1, 1,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 1,-1,-1,
-1,-1, 0,-1,-1,
1, 0, 0, 0, 0,
-1, 0, 0,-1,-1,
-1,-1, 0,-1,-1 ));
k.push_back((cv::Mat_<char>(5,5) << -1,-1, 1,-1,-1,
-1,-1, 0,-1,-1,
0, 0, 0, 0, 1,
-1,-1, 0, 0,-1,
-1,-1, 0,-1,-1 ));
cv::Mat dst = cv::Mat::zeros(bw.size(), CV_8U);
for (int i = 0; i < k.size(); i++)
{
cv::Mat tmp;
hitmiss(bw, tmp, k[i]);
dst |= tmp;
}
std::vector<std::vector<cv::Point> > cnt;
cv::findContours(dst.clone(), cnt, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
cv::drawContours(im, cnt, -1, CV_RGB(255,0,0), 10);
cv::imshow("src", im);
cv::imshow("bw", bw*255);
cv::imshow("dst", dst*255);
cv::waitKey();
return 0;
}
Take a look at OpenCV squares.c demo , or one of the threads below: 看一下OpenCV squares.c演示或以下线程之一:
You can use morphological operations on the binary image you got. 您可以对所获得的二进制图像使用形态学运算。 In matlab you may play with bwmorph
在Matlab中,您可以玩bwmorph
bw = I == 0; % look at dark lines in image I
[y x] = find( bwmorph( bw, 'branchpoints' ) );
will give you x
y
coordinates of junction points. 将为您提供交点的x
y
坐标。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.