java - 从二进制 png 图像检索的像素数据中检测矩形

标签 java image opencv

我已经从看起来像这样的 png 图像中检索了所有像素... enter image description here

我开始让所有填充的线水平以获得每条线的开始和结束像素....当我水平扫描时我也得到水平层中的垂直线....我想可以手动编码但这需要时间....我的问题是有没有人有opencv,imageJ或其他方面的经验来判断这个库是否可以解决问题...我也愿意接受任何算法建议....(使用Java) ....

-> 最大的问题是 1 到 4 像素之间的线条粗细,否则我可以很容易地检索到关节点

最佳答案

这是一个使用图像形态学的可能解决方案。下面的代码是用 C++ 编写的,因为我对 Java 的经验很少。

要解决您的问题,您需要:

坏消息是 OpenCV 2.4.3 版本还不支持这两种操作。好消息是我已经实现了这两个操作,代码可以在我的博客上找到:

我将使用我的 thinning()hitmiss() 函数以及您的测试图像。

加载图像后,将其转换为单 channel 二值图像。

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);

enter image description here

由于线条的宽度从 1 到 4 个像素不等,因此执行细化以获得同宽线条。

thinning(bw);

enter image description here

从细化后的图像中,请注意存在完美和不完美的关节点,如下图所示。

enter image description here enter image description here

为了覆盖完美和不完美的关节点,我们需要以下内核来进行命中或未命中变换。

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;
}

enter image description here

打开原图,结果更清晰。

定位成功的关节点,绘制在原图上。

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);

enter image description here


为了完整起见,这里是完整的代码。通过一些努力,您可以将其移植到 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;
}

关于java - 从二进制 png 图像检索的像素数据中检测矩形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14755406/

相关文章:

javascript - 使用 VueJs 渲染 javascript 图像对象

java - 当从 Java 剪贴板对象中检索为图像时,来自 Outlook 的剪贴板副本始终设置为黑色背景

c++ - 在 openCV 中加入足够接近的轮廓

python - 通过边界框从图像中提取选定的文本

java - 非自由(SIFT,SURF)在android中与java的使用

java - Spring Bean 属性 'xxx' 不可写或具有无效的 setter 方法

java - 在 Java 中加载属性文件

java - 如何使用 itext7 Java 将多个图像添加到 PDF 中?

java - MigLayout 50% 宽度

java - 如何在 JPA 中查询 List<String> 类型的属性