我试图从轮廓中检测和精确定位图像中的一些物体。我得到的轮廓通常包括一些噪声(可能来自背景,我不知道)。对象看起来应该类似于矩形或方形,如下所示:

用形状匹配(cv::matchShapes)来检测物体中的轮廓,无论有没有噪声,我都取得了很好的效果,但在噪声的情况下,我在精确定位方面有问题。
噪音看起来是:

或

例如。
我的想法是找出凸性缺陷,如果它们变得太强,以某种方式将导致凹凸的部分剪掉。检测缺陷是好的,通常我得到两个缺陷“不想要的结构”,但我被困在如何决定什么和我应该删除的轮廓点。
下面是一些轮廓,它们的掩码(这样就可以很容易地提取轮廓)和凸包,包括阈值凸性缺陷:



























我是否可以通过等高线并局部决定“左转”是否由等高线执行(如果顺时针行走),如果是的话,删除轮廓点直到下一个左转?也许从凸起的缺陷开始?
我正在寻找算法或代码,编程语言应该不重要,算法更重要。
发布于 2016-02-07 09:59:39
这种方法只在点上起作用。你不需要为这个做面具。
主要的想法是:
我得到以下结果。正如您所看到的,它对于平滑的缺陷有一些缺点(例如,第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_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);
}
}发布于 2016-02-06 07:26:56
我想出了下面的方法来检测矩形/平方的边界。它基于很少的假设:形状是矩形或方形的,它是在图像的中心,它是不倾斜的。
中线和投影的上半部的样本图像如下所示。

两个样本的边界和裁剪区域:


代码在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)发布于 2021-12-11 20:08:37
下面是遵循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)https://stackoverflow.com/questions/35226993
复制相似问题