首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从图像中提取不同形状

如何从图像中提取不同形状
EN

Stack Overflow用户
提问于 2017-11-04 07:25:16
回答 3查看 1.7K关注 0票数 1

我是一个图像处理世界的新手,我有一个问题陈述,我需要一个先开始解决它。

问题陈述:

我有一个由图案组成的图像。此模式是使用不同的单个形状创建的。下面是用于形成模式的模式和个人形状。

详细问题陈述

我有15个独特的形状(下图),我可以用它画出不同的图案(已经给出了一个例子)。我有400多个图案。我想使用图像处理来找出用于生成特定图案的不同形状(及其在模式中的位置)。

所有独特的形状:

更多的模式图像

我想要实现的

我想输入图案图像,找出用于形成图案的个别形状和形状放在图案中的位置?:

注意:,我没有包括所有的个别形状,因为问题变得太大了。

模式图像:

个人形状:

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-11-04 20:30:05

要知道哪些参考形状组成您的图像,您可以

  1. 定位中心点,它存在于您的所有形状。
  2. 知道点在哪里,找出正确的形状。

对于这个答案的范围,我使用这些已经预处理的图像。第一个图像是简单的阈值化,第二个我使用了片段。

在预处理后的图像上很容易找到中心点。您可以使用cv::connectedComponentsWithStats检索所有黑色组件,然后删除太大的组件。您可以在下面的函数getCenterPoints中找到代码。

然后,只要简单地将此图像和原始图像组合起来,就可以很容易地获得轮廓(稍后需要):

现在我们可以找到点,但我们也需要一种方式来判断哪一个形状构成了最终的图像。我们可以使用形状的几何结构为每个形状构建一个简单的描述符:我们保存一个Mat 4值,表示中心在垂直和水平方向上与轮廓之间的距离:

这唯一地标识了所有参考形状。然后,我们对这个4元向量进行规范化,使它成为尺度不变的。使用这个描述符可以避免繁琐的“多尺度模板匹配”,而且速度更快,扩展性更强。您可以在下面的函数computeShapeDescriptor中找到这方面的代码。

为了计算形状描述符,我们还需要形状中心的正确位置,这就是我们前面发现的blob的质心。我们基本上再次使用cv::connectedComponentWithStats。见下面的getCentroids

现在我们知道了如何找到点来定位所有的形状,并知道如何描述它们。要在图像中找到相应的参考形状,只需比较描述符。最相似的一个应该是正确的!

供参考的完整代码:

代码语言:javascript
复制
#include <opencv2\opencv.hpp>
#include <vector>

void computeShapeDescriptor(const cv::Mat1b shape_outline, cv::Point center, cv::Mat1d& desc)
{
    desc = cv::Mat1d(1, 4, 0.0);

    // Go up until I find a outline pixel
    for (int i = center.y; i >= 0; --i) {
        if (shape_outline(i, center.x) > 0) {
            desc(0) = std::abs(i - center.y);
            break;
        }
    }
    // Go right until I find a outline pixel
    for (int i = center.x; i < shape_outline.cols; ++i) {
        if (shape_outline(center.y, i) > 0) {
            desc(1) = std::abs(i - center.x);
            break;
        }
    }
    // Go down until I find a outline pixel
    for (int i = center.y; i < shape_outline.rows; ++i) {
        if (shape_outline(i, center.x) > 0) {
            desc(2) = std::abs(i - center.y);
            break;
        }
    }
    // Go left until I find a outline pixel
    for (int i = center.x; i >= 0; --i) {
        if (shape_outline(center.y, i) > 0) {
            desc(3) = std::abs(i - center.x);
            break;
        }
    }

    desc /= cv::norm(desc, cv::NORM_L1);
}

void getCenterPoints(const cv::Mat1b& src, cv::Mat1b& dst)
{
    dst = cv::Mat1b(src.rows, src.cols, uchar(0));

    cv::Mat1i labels;
    cv::Mat1i stats;
    cv::Mat1d centroids;
    int n_labels = cv::connectedComponentsWithStats(~src, labels, stats, centroids);
    for (int i = 1; i < n_labels; ++i) {
        if (stats(i, cv::CC_STAT_AREA) < 100)
        {
            dst.setTo(255, labels == i);
        }
    }
}

void getCentroids(const cv::Mat1b& src, cv::Mat1d& centroids)
{
    // Find the central pixel
    cv::Mat1i labels;
    cv::Mat1i stats;

    cv::connectedComponentsWithStats(src, labels, stats, centroids);
    // 'centroids' contains in each row x,y coordinates of the centroid
}


int main()
{
    // Load the reference shapes
    cv::Mat1b reference = cv::imread("path_to_reference_shapes", cv::IMREAD_GRAYSCALE);

    // -------------------------
    // Compute descriptor for each reference shape
    // -------------------------

    // Get the centers
    cv::Mat1b reference_centers;
    getCenterPoints(reference, reference_centers);

    // Get the centroids
    cv::Mat1d shape_centroids;
    getCentroids(reference_centers, shape_centroids);

    // Find the outline
    cv::Mat1b reference_outline = ~(reference | reference_centers);

    // Prepare output image
    cv::Mat3b reference_output;
    cv::cvtColor(reference, reference_output, cv::COLOR_GRAY2BGR);

    // Compute the descriptor for each shape
    std::vector<cv::Mat1f> shape_descriptors;
    for (int i = 1; i < shape_centroids.rows; ++i)
    {
        cv::Point center;
        center.x = std::round(shape_centroids(i, 0));
        center.y = std::round(shape_centroids(i, 1));

        cv::Mat1d desc;
        computeShapeDescriptor(reference_outline, center, desc);

        shape_descriptors.push_back(desc.clone());

        // Draw the ID of the shape
        cv::putText(reference_output, cv::String(std::to_string(i)), center, cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 0, 255));
    }

    // -------------------------
    // Find shapes in image
    // -------------------------

    cv::Mat1b img = cv::imread("path_to_image", cv::IMREAD_GRAYSCALE);

    // Get the centers
    cv::Mat1b img_centers;
    getCenterPoints(img, img_centers);

    // Get the centroids
    cv::Mat1d img_centroids;
    getCentroids(img_centers, img_centroids);

    // Find the outline
    cv::Mat1b img_outline = ~(img | img_centers);

    // Prepare output image
    cv::Mat3b img_output;
    cv::cvtColor(img, img_output, cv::COLOR_GRAY2BGR);

    // Compute the descriptor for each found shape, and assign to nearest descriptor among reference shapes
    for (int i = 1; i < img_centroids.rows; ++i)
    {
        cv::Point center;
        center.x = std::round(img_centroids(i, 0));
        center.y = std::round(img_centroids(i, 1));

        cv::Mat1d desc;
        computeShapeDescriptor(img_outline, center, desc);

        // Compute the distance with all reference descriptors
        double minDist = 1e10;
        int minIdx = 0;
        for (size_t j = 0; j < shape_descriptors.size(); ++j)
        {
            // Actual distance computation
            double dist = 0.0;
            for (int c = 0; c < desc.cols; ++c) {
                dist += std::abs(desc(c) - shape_descriptors[j](c));
            }

            if (minDist > dist) {
                minDist = dist;
                minIdx = j;
            }
        }

        // Draw the ID of the shape
        cv::putText(img_output, cv::String(std::to_string(minIdx + 1)), center, cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 0, 255, 255));
    }


    return 0;
}
票数 4
EN

Stack Overflow用户

发布于 2017-11-04 15:34:30

您可以使用点定位单个形状(二值化+连接组件标记)。

然后,很容易检测到方角的缺失/存在,例如通过相邻形状对之间的小窗口。这将给您一个二进制代码,您可以使用它来区分您的模式。

票数 2
EN

Stack Overflow用户

发布于 2017-11-04 15:11:37

考虑到您有一个形状字典,您可以模板匹配,以找到哪个形状是在您的图像。模板匹配非常简单:计算形状和图像的相关性、均方误差等“拟合优度”度量,形状位于图像的每个点。例如,参见这些讲稿。如果使用相关性(在这种情况下这是有意义的),则可以使用FFT来显著加快计算速度。例如,看看这个OpenCV教程

以上假设模板的大小和方向与图像中显示的相同。如果大小可能不同,您需要使用多尺度模板匹配方法,这一点我稍微多了一点,但并不困难。只要试着在不同的尺度和方向上多次匹配每个形状。或者旋转和缩放你的图像。我猜想,给出你的例子,你只需要测试4个方向和1个尺度,所以这是一个合理的方法。

更灵活的方法是检测点(例如,模板匹配),填充周围的点来填充形状(假设它们都是简单的多边形),并提取检测区域的边界。然后,可以使用例如傅里叶描述符将该边界与字典中的边界相匹配。这将允许您检测任意尺度和方向的形状。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47108493

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档