如何从图像中提取不同的形状 [英] How to extract different shapes from an Image

查看:189
本文介绍了如何从图像中提取不同的形状的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



问题陈述:



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



详细问题陈述



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



所有独特形状:
更多模式图片



我想要实现的目标



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



注意:我没有将所有单独的形状包括在内这个问题变得太大了。



模式图片:




个人形状:

解决方案

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


  1. 本地化所有形状中的中心点

  2. 知道点在哪里,找到正确的形状。






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






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





然后您可以轻松获取轮廓(需要稍后的图片与此图片和原始图片的简单组合:





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









完整代码供参考:

  #include< opencv2 \ openncv.hpp> ; 
#include< vector>

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

//直到我找到一个轮廓像素
for(int i = center.y; i> = 0; --i){
if(shape_outline( i,center.x)> 0){
desc(0)= std :: abs(i - center.y);
休息;
}
}
//向右走直到找到一个轮廓像素
for(int i = center.x; i< shape_outline.cols; ++ i){
if(shape_outline(center.y,i)> 0){
desc(1)= std :: abs(i - center.x);
休息;
}
}
//向下直到我找到一个轮廓像素
for(int i = center.y; i< shape_outline.rows; ++ i){
if(shape_outline(i,center.x)> 0){
desc(2)= std :: abs(i - center.y);
休息;
}
}
//向左走,直到我找到一个轮廓像素
for(int i = center.x; i> = 0; --i){
if(shape_outline(center.y,i)> 0){
desc(3)= std :: abs(i - center.x);
休息;
}
}

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标签;
cv :: Mat1i stats;
cv :: Mat1d质心;
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)
{
//找到中心像素
cv :: Mat1i标签;
cv :: Mat1i stats;

cv :: connectedComponentsWithStats(src,labels,stats,centroids);
//'centroids'在每一行中包含x,y坐标的质心
}


int main()
{
//加载引用形状
cv :: Mat1b reference = cv :: imread(path_to_reference_shapes,cv :: IMREAD_GRAYSCALE);

// -------------------------
//每个参考形状$ b $的计算描述符b // -------------------------

//获取中心
cv :: Mat1b reference_centers ;
getCenterPoints(reference,reference_centers);

//获取质心
cv :: Mat1d shape_centroids;
getCentroids(reference_centers,shape_centroids);

//找到大纲
cv :: Mat1b reference_outline =〜(reference | reference_centers);

//准备输出图像
cv :: Mat3b reference_output;
cv :: cvtColor(reference,reference_output,cv :: COLOR_GRAY2BGR);

//计算每个形状的描述符
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());

//绘制形状的ID
cv :: putText(reference_output,cv :: String(std :: to_string(i)),center,cv :: FONT_HERSHEY_PLAIN,1, cv :: Scalar(0,0,255));
}

// -------------------------
//在图像中查找形状
// -------------------------

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

//获取中心
cv :: Mat1b img_centers;
getCenterPoints(img,img_centers);

//获取质心
cv :: Mat1d img_centroids;
getCentroids(img_centers,img_centroids);

//找到大纲
cv :: Mat1b img_outline =〜(img | img_centers);

//准备输出图像
cv :: Mat3b img_output;
cv :: cvtColor(img,img_output,cv :: COLOR_GRAY2BGR);

//计算每个找到的形状的描述符,并在参考形状中分配给最近的描述符
for(int i = 1; i< img_centroids.rows; ++ i)
{
cv ::点中心;
center.x = std :: round(img_centroids(i,0));
center.y = std :: round(img_centroids(i,1));

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

//用所有参考描述符计算距离
double minDist = 1e10;
int minIdx = 0;
for(size_t j = 0; j< shape_descriptors.size(); ++ j)
{
//实际距离计算
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;
}
}

//绘制形状的ID
cv :: putText(img_output,cv :: String(std :: to_string(minIdx + 1) )),center,cv :: FONT_HERSHEY_PLAIN,1,cv :: Scalar(0,0,255,255));
}


返回0;
}


I am a newbie in Image processing world and I have a problem statement which I need a head start to solve it.

Problem Statement:

I have an image which consist of a pattern. This pattern is created using different individual shapes. Below is the pattern and the individual shape used to form the pattern.

Detailed problem statement:

I have 15 unique shapes(image below) using which I can draw different patterns(one example is given already). I have more than 400 patterns. I want to use image processing to find out different shapes (and its position in the pattern) used to generate a particular pattern.

All unique shapes: Some more pattern images:

What I want to achieve:

I want to input the pattern image and find out the individual shapes which are used to form the pattern and the position the shapes are placed in the pattern ?:

Note: I have not included all the individual shapes as the question was becoming too big.

Pattern Image:

Individual Shapes:

解决方案

To know which reference shapes compose your images, you can

  1. localize the central dot which is present in all your shapes
  2. knowing where the dot is, find the correct shape.


For the scope of this answer I use these images which are already preprocessed. The first image is simply thresholded, for the second I used this snippet.


Find the central dots is pretty easy on the preprocessed images. You can use cv::connectedComponentsWithStats to retrieve all black components, and then remove the ones that are too big. You can find the code in the function getCenterPoints below.

Then you can easily get the outlines (needed later) with a simple combination of this image and the original one:

Now we are able to find the dots, but we need also a way to say which shape compose the final image. We can use the geometry of the shape to build a simple descriptor for each shape: we save in a Mat 4 values representing the distance of the center from the outline in vertical and horizontal direction:

This uniquely identifies all your reference shapes. Then we normalize this 4 element vector so it becomes scale-invariant. Using this descriptor allow us to avoid tedious "multiscale template matching" like stuff, and is also much faster and extendible. You can find the code for this in the function computeShapeDescriptor below.

In order to compute the shape descriptor we need also the correct position of the shape center, which is simply the centroid of the blob we found earlier. We basically use again cv::connectedComponentWithStats. See getCentroids below.


Now we know how to find the dots to localize all shapes, and know how to describe them. To find the corresponding reference shape in the image simply compare the descriptors. The one most similar would be the correct one!

Full code for reference:

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

这篇关于如何从图像中提取不同的形状的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆