[英]Detect rectangles from pixel data retrieved from binary png image
我已經從看起來像這樣的png圖像中檢索了所有像素...
我開始使所有填充線變為水平,以獲取每條線的開始結束像素....當我水平掃描時,我也獲得了水平層中的垂直線.....我想可以手動進行全部編碼,但這需要時間...。我的問題是,有沒有與opencv,imageJ或其他程序有關的經驗,可以告訴我這些庫中的任何一個是否可以解決問題...我也願意接受任何算法建議...(使用Java)... 。
->最大的問題是1到4像素之間的線的粗細,否則我可以很容易地找到連接點
這是使用圖像形態的可能解決方案。 下面的代碼是C ++,因為我對Java的經驗很少。
要解決您的問題,您需要:
壞消息是,從2.4.3版開始,OpenCV尚不支持這兩種操作。 好消息是我已經實現了這兩種操作,該代碼可在我的博客上找到:
我將使用我的thinning()
和hitmiss()
函數以及您的測試圖像。
加載圖像后,將其轉換為單通道二進制圖像。
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);
由於線的寬度從1到4像素不等,因此請進行細化處理以獲得單寬度的線。
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);
為了完整起見,這里是完整的代碼。 通過一些努力,您可以將其移植到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;
}
看一下OpenCV squares.c演示或以下線程之一:
您可以對所獲得的二進制圖像使用形態學運算。 在Matlab中,您可以玩bwmorph
bw = I == 0; % look at dark lines in image I
[y x] = find( bwmorph( bw, 'branchpoints' ) );
將為您提供交點的x
y
坐標。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.