13

輪郭から画像内のオブジェクトを検出して細かく特定しようとしています。私が取得する輪郭には、ノイズが含まれていることがよくあります (背景からの可能性がありますが、わかりません)。オブジェクトは、次のような長方形または正方形に似ている必要があります。

ここに画像の説明を入力

cv::matchShapesシェイプ マッチング ( ) を使用して、ノイズの有無にかかわらず、それらのオブジェクトを含む輪郭を検出すると非常に良い結果が得られますが、ノイズの場合は細かい位置に問題があります。

ノイズは次のようになります。

ここに画像の説明を入力またはここに画像の説明を入力たとえば。

私の考えは、凸欠陥を見つけて、それらが強くなりすぎたら、どうにかして凹みにつながる部分を切り落とすことでした. 欠陥の検出は問題ありません。通常、「不要な構造」ごとに2つの欠陥がありますが、輪郭から何をどこで削除する必要があるかを決定する方法に行き詰まっています.

以下は、いくつかの輪郭、それらのマスク (輪郭を簡単に抽出できるようにするため)、およびしきい値処理された凸性欠陥を含む凸包です。

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

ここに画像の説明を入力ここに画像の説明を入力ここに画像の説明を入力

輪郭を歩いて、輪郭によって「左折」が実行されるかどうかをローカルに判断し (時計回りに歩く場合)、そうであれば、次の左折が行われるまで輪郭ポイントを削除できますか? 多分凸欠陥から始めますか?

私はアルゴリズムまたはコードを探しています。プログラミング言語は重要ではなく、アルゴリズムの方が重要です。

4

4 に答える 4

14

このアプローチはポイントでのみ機能します。このためのマスクを作成する必要はありません。

主なアイデアは次のとおりです。

  1. 輪郭上の欠陥を見つける
  2. 少なくとも 2 つの欠陥を見つけた場合は、最も近い 2 つの欠陥を見つけます
  3. 最も近い 2 つの欠陥の間の点を輪郭から削除します
  4. 新しい輪郭で 1 からやり直す

次の結果が得られます。ご覧のとおり、滑らかな欠陥 (例: 7 番目の画像) にはいくつかの欠点がありますが、はっきりと見える欠陥にはかなりうまく機能します。これで問題が解決するかどうかはわかりませんが、出発点になる可能性があります。実際には、かなり高速である必要があります (以下のコード、特にremoveFromContour関数を確実に最適化できます)。また、このアプローチの唯一のパラメーターは凸状欠陥の量であるため、小さな欠陥ブロブと大きな欠陥ブロブの両方でうまく機能します。

ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力

#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_SIMPLEinを使用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);
    }
}
于 2016-02-07T09:59:39.043 に答える
2

長方形/正方形の境界を検出するための次のアプローチを思い付きました。いくつかの仮定に基づいて機能します。形状は長方形または正方形であり、画像の中心にあり、傾いていません。

  • マスクされた (塗りつぶされた)画像を x 軸に沿って半分に分割し、 2 つの領域 (上半分と下半分) を取得します。
  • 各領域を x 軸に射影する
  • これらの投影のすべてのゼロ以外のエントリを取得し、それらの中央値を取得します。これらの中央値は、y 境界を示します
  • 同様に、画像をy軸に沿って半分に分割し、投影をy軸に取り、中央値を計算してx境界を取得します
  • 境界を使用して領域を切り取る

サンプル画像の上半分の中央線と投影を以下に示します。 proj-n-med-line

2 つのサンプルの境界と切り取られた領域の結果: s1 s2

コードは Octave/Matlab にあり、これを Octave でテストしました (これを実行するにはイメージ パッケージが必要です)。

clear all
close all

im = double(imread('kTouF.png'));
[r, c] = size(im);
% top half
p = sum(im(1:int32(end/2), :), 1);
y1 = -median(p(find(p > 0))) + int32(r/2);
% bottom half
p = sum(im(int32(end/2):end, :), 1);
y2 = median(p(find(p > 0))) + int32(r/2);
% left half
p = sum(im(:, 1:int32(end/2)), 2);
x1 = -median(p(find(p > 0))) + int32(c/2);
% right half
p = sum(im(:, int32(end/2):end), 2);
x2 = median(p(find(p > 0))) + int32(c/2);

% crop the image using the bounds
rect = [x1 y1 x2-x1 y2-y1];
cr = imcrop(im, rect);
im2 = zeros(size(im));
im2(y1:y2, x1:x2) = cr;

figure,
axis equal
subplot(1, 2, 1)
imagesc(im)
hold on
plot([x1 x2 x2 x1 x1], [y1 y1 y2 y2 y1], 'g-')
hold off
subplot(1, 2, 2)
imagesc(im2)
于 2016-02-06T07:26:56.310 に答える
1

これは、Miki のコードに従った Python の実装です。

import numpy as np
import cv2

def ed2(lhs, rhs):
    return(lhs[0] - rhs[0])*(lhs[0] - rhs[0]) + (lhs[1] - rhs[1])*(lhs[1] - rhs[1])


def remove_from_contour(contour, defectsIdx, tmp):
    minDist = sys.maxsize
    startIdx, endIdx = 0, 0

    for i in range(0,len(defectsIdx)):
        for j in range(i+1, len(defectsIdx)):
            dist = ed2(contour[defectsIdx[i]][0], contour[defectsIdx[j]][0])
            if minDist > dist:
                minDist = dist
                startIdx = defectsIdx[i]
                endIdx = defectsIdx[j]

    if startIdx <= endIdx:
        inside = contour[startIdx:endIdx]
        len1 = 0 if inside.size == 0 else cv2.arcLength(inside, False)
        outside1 = contour[0:startIdx]
        outside2 = contour[endIdx:len(contour)]
        len2 = (0 if outside1.size == 0 else cv2.arcLength(outside1, False)) + (0 if outside2.size == 0 else cv2.arcLength(outside2, False))
        if len2 < len1:
            startIdx,endIdx = endIdx,startIdx     
    else:
        inside = contour[endIdx:startIdx]
        len1 = 0 if inside.size == 0 else cv2.arcLength(inside, False)
        outside1 = contour[0:endIdx]
        outside2 = contour[startIdx:len(contour)]
        len2 = (0 if outside1.size == 0 else cv2.arcLength(outside1, False)) + (0 if outside2.size == 0 else cv2.arcLength(outside2, False))
        if len1 < len2:
            startIdx,endIdx = endIdx,startIdx

    if startIdx <= endIdx:
        out = np.concatenate((contour[0:startIdx], contour[endIdx:len(contour)]), axis=0)
    else:
        out = contour[endIdx:startIdx]
    return out


def remove_defects(mask, debug=False):
    tmp = mask.copy()
    mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)

    # get contour
    contours, _ = cv2.findContours(
        mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    assert len(contours) > 0, "No contours found"
    contour = sorted(contours, key=cv2.contourArea)[-1] #largest contour
    if debug:
        init = cv2.drawContours(tmp.copy(), [contour], 0, (255, 0, 255), 1, cv2.LINE_AA)
        figure, ax = plt.subplots(1)
        ax.imshow(init)
        ax.set_title("Initital Contour")

    hull = cv2.convexHull(contour, returnPoints=False)
    defects = cv2.convexityDefects(contour, hull)

    while True:
        defectsIdx = []
        
        for i in range(defects.shape[0]):
            s, e, f, d = defects[i, 0]
            start = tuple(contour[s][0])
            end = tuple(contour[e][0])
            far = tuple(contour[f][0])
            
            depth = d / 256
            if depth > 2:
                defectsIdx.append(f)

        if len(defectsIdx) < 2:
            break

        contour = remove_from_contour(contour, defectsIdx, tmp)
        hull = cv2.convexHull(contour, returnPoints=False)
        defects = cv2.convexityDefects(contour, hull)

    if debug:
      rslt = cv2.drawContours(tmp.copy(), [contour], 0, (0, 255, 255), 1)
      figure, ax = plt.subplots(1)
      ax.imshow(rslt)
      ax.set_title("Corrected Contour")

mask = cv2.imread("a.png")
remove_defects(mask, True)
于 2021-12-11T20:08:37.107 に答える