如何裁剪凸面缺陷? [英] How to crop away convexity defects?

查看:250
本文介绍了如何裁剪凸面缺陷?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试检测和精确定位来自轮廓的图像中的某些对象。我经常包含一些噪音(也许形成背景,我不知道)的轮廓。对象应类似于以下矩形或正方形:





我得到非常好的结果与形状匹配( cv :: matchShapes )检测轮廓与其中的对象,有和没有噪音,但我有问题,



或例如。



我的想法是找到凸性缺陷,如果他们变得太强,裁剪导致凹陷的部分。检测缺陷是确定的,通常我得到两个缺陷每个不需要的结构,但我被困在如何决定什么和在哪里应该从轮廓中删除点。



这里是一些轮廓,他们的面具(所以你可以很容易地提取轮廓)和凸包括包括阈值凸面缺陷:















< a href =https://i.stack.imgur.com/d8wz8.png =nofollow noreferrer>





我可以只通过轮廓,并在本地决定是否通过轮廓执行左转(如果顺时针走),如果是,删除轮廓点,直到下一个左转为止?也许从凸性缺陷开始?



我在寻找算法或代码,编程语言不应该重要,算法更重要。

解决方案

此方法仅适用于点。您不需要为此创建掩码。



主要思想是:


  1. 找到轮廓缺陷

  2. 如果我发现至少两个缺陷,找到两个最接近的缺陷

  3. 从轮廓中删除两个最接近的缺陷之间的点

  4. 在新轮廓上从1重新开始

我得到以下结果。您可以看到,它对于平滑缺陷(例如第7张图片)有一些缺点,但对于清晰可见的缺陷非常有用。我不知道这是否会解决你的问题,但可以作为一个出发点。实际上应该相当快(你可以肯定优化下面的代码,特别是 removeFromContour 函数)。此外,这种方法的唯一参数是凸性缺陷的数量,因此它适用于小的和大的缺陷块。










< img src =https://i.stack.imgur.com/ldROV.pngalt =enter image description here>

  #include< opencv2 / opencv.hpp> 
using namespace cv;
using namespace std;

int ed2(const Point& lhs,const Point& rhs)
{
return(lhs.x - rhs.x)*(lhs.x - rhs.x) +(lhs.y-rhs.y)*(lhs.y-rhs.y);
}

vector< Point> removeFromContour(const vector< Point>& contour,const vector< int>& defectsIdx)
{
int minDist = INT_MAX;
int startIdx;
int endIdx;

//找到最近的缺陷
for(int i = 0; i< defectsIdx.size(); ++ i)
{
for = j + 1; j< defectIdx.size(); ++ j)
{
float dist = ed2(contour [defectsIdx [i]],contour [defectsIdx [j]
if(minDist> dist)
{
minDist = dist;
startIdx = defectsIdx [i];
endIdx = defectsIdx [j];
}
}
}

//检查是否交换间隔
if(startIdx <= endIdx)
{
int len1 = endIdx - startIdx;
int len2 = contour.size() - endIdx + startIdx;
if(len2< len1)
{
swap(startIdx,endIdx);
}
}
else
{
int len1 = startIdx - endIdx;
int len2 = contour.size() - startIdx + endIdx;
if(len1< len2)
{
swap(startIdx,endIdx);
}
}

//删除不需要的点
vector< Point>出口;
if(startIdx< = endIdx)
{
out.insert(out.end(),contour.begin(),contour.begin()+ startIdx);
out.insert(out.end(),contour.begin()+ endIdx,contour.end());
}
else
{
out.insert(out.end(),contour.begin()+ endIdx,contour.begin()+ startIdx);
}

return out;
}

int main()
{
Mat1b img = imread(path_to_mask,IMREAD_GRAYSCALE);

Mat3b out;
cvtColor(img,out,COLOR_GRAY2BGR);

vector< vector< Point>>轮廓;
findContours(img.clone(),contoururs,RETR_EXTERNAL,CHAIN_APPROX_NONE);

vector< Point> pts = contoururs [0];

vector< int> hullIdx;
convexHull(pts,hullIdx,false);

vector< Vec4i>缺陷;
convexityDefects(pts,hullIdx,defects);

while(true)
{
//用于调试
Mat3b dbg;
cvtColor(img,dbg,COLOR_GRAY2BGR);

vector< vector< Point>> tmp = {pts};
drawContours(dbg,tmp,0,Scalar(255,127,0));

vector< int> defectsIdx;
for(const Vec4i& v:defects)
{
float depth = float(v [3])/ 256.f;
if(depth> 2)//通过深度过滤缺陷
{
//找到缺陷
defectsIdx.push_back(v [2]);

int startidx = v [0]; Point ptStart(pts [startidx]);
int endidx = v [1]; Point ptEnd(pts [endidx]);
int faridx = v [2]; Point ptFar(pts [faridx]);

line(dbg,ptStart,ptEnd,Scalar(255,0,0),1);
line(dbg,ptStart,ptFar,Scalar(0,255,0),1);
line(dbg,ptEnd,ptFar,Scalar(0,0,255),1);
circle(dbg,ptFar,4,Scalar(127,127,255),2);
}
}

if(defectsIdx.size()< 2)
{
break;
}

//如果我有两个以上的缺陷,删除两个最近的缺陷之间的点
pts = removeFromContour(pts,defectsIdx);
convexHull(pts,hullIdx,false);
convexityDefects(pts,hullIdx,defects);
}


//绘制结果contour
vector< vector< Point>> tmp = {pts};
drawContours(out,tmp,0,Scalar(0,0,255),1);

imshow(Result,out);
waitKey();

return 0;
}






UPDATE



使用近似轮廓(例如使用中的 CHAIN_APPROX_SIMPLE > findContours )可能更快,但轮廓的长度必须使用 arcLength()计算。



这是要在 removeFromContour 交换部分中替换的代码段:

  //检查是否交换间隔
if(startIdx <= endIdx)
{
// int len11 = endIdx - startIdx;
vector< Point> inside(contour.begin()+ startIdx,contour.begin()+ endIdx);
int len1 =(inside.empty())? 0:arcLength(inside,false);

// int len22 = contour.size() - endIdx + startIdx;
vector< Point> outside1(contour.begin(),contour.begin()+ startIdx);
vector< Point> outside2(contour.begin()+ endIdx,contour.end());
int len2 =(outside1.empty()?0:arcLength(outside1,false))+(outside2.empty()?0:arcLength(outside2,false)

if(len2< len1)
{
swap(startIdx,endIdx);
}
}
else
{
// int len1 = startIdx - endIdx;
vector< Point> inside(contour.begin()+ endIdx,contour.begin()+ startIdx);
int len1 =(inside.empty())? 0:arcLength(inside,false);


// int len2 = contour.size() - startIdx + endIdx;
vector< Point> outside1(contour.begin(),contour.begin()+ endIdx);
vector< Point> outside2(contour.begin()+ startIdx,contour.end());
int len2 =(outside1.empty()?0:arcLength(outside1,false))+(outside2.empty()?0:arcLength(outside2,false)

if(len1< len2)
{
swap(startIdx,endIdx);
}
}


I'm trying to detect and fine-locate some objects in images from contours. The contours that I get often include some noise (maybe form the background, I don't know). The objects should look similar to rectangles or squares like:

I get very good results with shape matching (cv::matchShapes) to detect contours with those objects in them, with and without noise, but I have problems with the fine-location in case of noise.

Noise looks like:

or for example.

My idea was to find convexity defects and if they become too strong, somehow crop away the part that leads to concavity. Detecting the defects is ok, typically I get two defects per "unwanted structure", but I'm stuck on how to decide what and where I should remove points from the contours.

Here are some contours, their masks (so you can extract the contours easily) and the convex hull including thresholded convexity defects:

Could I just walk through the contour and locally decide whether a "left turn" is performed by the contour (if walking clockwise) and if so, remove contour points until the next left turn is taken? Maybe starting at a convexity defect?

I'm looking for algorithms or code, programming language should not be important, algorithm is more important.

解决方案

This approach works only on points. You don't need to create masks for this.

The main idea is:

  1. Find defects on contour
  2. If I find at least two defects, find the two closest defects
  3. Remove from the contour the points between the two closest defects
  4. Restart from 1 on the new contour

I get the following results. As you can see, it has some drawbacks for smooth defects (e.g. 7th image), but works pretty good for clearly visible defects. I don't know if this will solve your problem, but can be a starting point. In practice should be quite fast (you can surely optimize the code below, specially the removeFromContour function). Also, the only parameter of this approach is the amount of the convexity defect, so it works well with both small and big defecting blobs.

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int ed2(const Point& lhs, const Point& rhs)
{
    return (lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y);
}

vector<Point> removeFromContour(const vector<Point>& contour, const vector<int>& defectsIdx)
{
    int minDist = INT_MAX;
    int startIdx;
    int endIdx;

    // Find nearest defects
    for (int i = 0; i < defectsIdx.size(); ++i)
    {
        for (int j = i + 1; j < defectsIdx.size(); ++j)
        {
            float dist = ed2(contour[defectsIdx[i]], contour[defectsIdx[j]]);
            if (minDist > dist)
            {
                minDist = dist;
                startIdx = defectsIdx[i];
                endIdx = defectsIdx[j];
            }
        }
    }

    // Check if intervals are swapped
    if (startIdx <= endIdx)
    {
        int len1 = endIdx - startIdx;
        int len2 = contour.size() - endIdx + startIdx;
        if (len2 < len1)
        {
            swap(startIdx, endIdx);
        }
    }
    else
    {
        int len1 = startIdx - endIdx;
        int len2 = contour.size() - startIdx + endIdx;
        if (len1 < len2)
        {
            swap(startIdx, endIdx);
        }
    }

    // Remove unwanted points
    vector<Point> out;
    if (startIdx <= endIdx)
    {
        out.insert(out.end(), contour.begin(), contour.begin() + startIdx);
        out.insert(out.end(), contour.begin() + endIdx, contour.end());
    } 
    else
    {
        out.insert(out.end(), contour.begin() + endIdx, contour.begin() + startIdx);
    }

    return out;
}

int main()
{
    Mat1b img = imread("path_to_mask", IMREAD_GRAYSCALE);

    Mat3b out;
    cvtColor(img, out, COLOR_GRAY2BGR);

    vector<vector<Point>> contours;
    findContours(img.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

    vector<Point> pts = contours[0];

    vector<int> hullIdx;
    convexHull(pts, hullIdx, false);

    vector<Vec4i> defects;
    convexityDefects(pts, hullIdx, defects);

    while (true)
    {
        // For debug
        Mat3b dbg;
        cvtColor(img, dbg, COLOR_GRAY2BGR);

        vector<vector<Point>> tmp = {pts};
        drawContours(dbg, tmp, 0, Scalar(255, 127, 0));

        vector<int> defectsIdx;
        for (const Vec4i& v : defects)
        {
            float depth = float(v[3]) / 256.f;
            if (depth > 2) //  filter defects by depth
            {
                // Defect found
                defectsIdx.push_back(v[2]);

                int startidx = v[0]; Point ptStart(pts[startidx]);
                int endidx = v[1]; Point ptEnd(pts[endidx]);
                int faridx = v[2]; Point ptFar(pts[faridx]);

                line(dbg, ptStart, ptEnd, Scalar(255, 0, 0), 1);
                line(dbg, ptStart, ptFar, Scalar(0, 255, 0), 1);
                line(dbg, ptEnd, ptFar, Scalar(0, 0, 255), 1);
                circle(dbg, ptFar, 4, Scalar(127, 127, 255), 2);
            }
        }

        if (defectsIdx.size() < 2)
        {
            break;
        }

        // If I have more than two defects, remove the points between the two nearest defects
        pts = removeFromContour(pts, defectsIdx);
        convexHull(pts, hullIdx, false);
        convexityDefects(pts, hullIdx, defects);
    }


    // Draw result contour
    vector<vector<Point>> tmp = { pts };
    drawContours(out, tmp, 0, Scalar(0, 0, 255), 1);

    imshow("Result", out);
    waitKey();

    return 0;
}


UPDATE

Working on an approximated contour (e.g. using CHAIN_APPROX_SIMPLE in findContours) may be faster, but the length of contours must be computed using arcLength().

This is the snippet to replace in the swapping part of removeFromContour:

// Check if intervals are swapped
if (startIdx <= endIdx)
{
    //int len11 = endIdx - startIdx;
    vector<Point> inside(contour.begin() + startIdx, contour.begin() + endIdx);
    int len1 = (inside.empty()) ? 0 : arcLength(inside, false);

    //int len22 = contour.size() - endIdx + startIdx;
    vector<Point> outside1(contour.begin(), contour.begin() + startIdx);
    vector<Point> outside2(contour.begin() + endIdx, contour.end());
    int len2 = (outside1.empty() ? 0 : arcLength(outside1, false)) + (outside2.empty() ? 0 : arcLength(outside2, false));

    if (len2 < len1)
    {
        swap(startIdx, endIdx);
    }
}
else
{
    //int len1 = startIdx - endIdx;
    vector<Point> inside(contour.begin() + endIdx, contour.begin() + startIdx);
    int len1 = (inside.empty()) ? 0 : arcLength(inside, false);


    //int len2 = contour.size() - startIdx + endIdx;
    vector<Point> outside1(contour.begin(), contour.begin() + endIdx);
    vector<Point> outside2(contour.begin() + startIdx, contour.end());
    int len2 = (outside1.empty() ? 0 : arcLength(outside1, false)) + (outside2.empty() ? 0 : arcLength(outside2, false));

    if (len1 < len2)
    {
        swap(startIdx, endIdx);
    }
}

这篇关于如何裁剪凸面缺陷?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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