我正在尝试从轮廓中检测并精确定位图像中的某些对象。我得到的轮廓经常包含一些噪音(也许是背景,我不知道)。对象看起来应该类似于矩形或正方形,例如:

opencv - 如何裁剪掉凸度缺陷?-LMLPHP

我通过形状匹配 ( cv::matchShapes ) 获得了非常好的结果,以检测其中包含或不包含噪声的对象的轮廓,但是在出现噪声的情况下,我在精确定位方面遇到了问题。

噪音看起来像:

例如 opencv - 如何裁剪掉凸度缺陷?-LMLPHPopencv - 如何裁剪掉凸度缺陷?-LMLPHP

我的想法是找到凸度缺陷,如果它们变得太强,以某种方式裁剪掉导致凹度的部分。检测缺陷是可以的,通常每个“不需要的结构”都会有两个缺陷,但我一直在思考如何决定应该从轮廓中删除点的内容和位置。

以下是一些轮廓、它们的掩码(因此您可以轻松提取轮廓)和凸包,包括阈值凸度缺陷:

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP opencv - 如何裁剪掉凸度缺陷?-LMLPHP

我是否可以穿过轮廓并在本地决定轮廓是否执行“左转”(如果顺时针走),如果是,则删除轮廓点直到进行下一次左转?也许从凸面缺陷开始?

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

最佳答案

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

主要思想是:

  • 查找轮廓上的缺陷
  • 如果我找到至少两个缺陷,则找到两个最接近的缺陷
  • 从轮廓中删除两个最近缺陷之间的点
  • 在新轮廓上从 1 重新开始

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

    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    opencv - 如何裁剪掉凸度缺陷?-LMLPHP
    #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;
    }
    

    更新

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

    这是在 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);
        }
    }
    

    关于opencv - 如何裁剪掉凸度缺陷?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35226993/

    10-13 00:06