Opencv 提取一张纸上的对象

Opencv 提取一张纸上的对象,opencv,image-processing,computer-vision,Opencv,Image Processing,Computer Vision,从一张纸上工具的图片中,我被要求找到它们的轮廓线,以便对它们进行矢量化 我是计算机视觉相关问题的初学者,我唯一想到的就是OpenCV和边缘检测 结果比我想象的要好,这仍然是非常不可靠的,特别是如果源图片不是“完美的” 我拍了两张他们给我的扳手的照片 玩过之后,我发现: 然后,我尝试使用不太好的图片: 那是完全不可开发的。 我可以通过改变Canny thresold得到一些更好的东西,但这必须是自动化的(因为图片相对正确) 所以我有几个问题: 我是否采取了正确的方法?格拉布切更适合这个吗?G

从一张纸上工具的图片中,我被要求找到它们的轮廓线,以便对它们进行矢量化

我是计算机视觉相关问题的初学者,我唯一想到的就是OpenCV和边缘检测

结果比我想象的要好,这仍然是非常不可靠的,特别是如果源图片不是“完美的”

我拍了两张他们给我的扳手的照片

玩过之后,我发现:

然后,我尝试使用不太好的图片:

那是完全不可开发的。 我可以通过改变Canny thresold得到一些更好的东西,但这必须是自动化的(因为图片相对正确)

所以我有几个问题:

  • 我是否采取了正确的方法?格拉布切更适合这个吗?Grabcut和Canny边缘检测的组合?我仍然需要在最后的顶点,但我觉得GrabCut也做了我想要的
  • 边界很粗糙,有一定的误差。我可以增加
    approxPolyDP
    的乘法器,但不会损失好零件的精度
  • 与上述观点相关,我正在考虑集成Savitzky-Golay算法来平滑轮廓,而不是使用
    approxPolyDP
    进行多边形简化。这是个好主意吗
  • 通常,外边界线必须形成一个简单的可切割块。在OpenCL中有没有一种方法可以避免这一行去做不可能的事情,比如传递自己或者,简单地说,检测问题?当然,这些配置是不可能的,但在检测失败时会发生(如第二张图片中所示)
  • 我正在寻找一种方法来进行自动Canny thresold计算,因为我必须为每个图像手动调整它。你有一个很好的例子吗
  • 我注意到在边缘检测之前将图像转换为灰度有时会恶化结果,有时会使其更好。我应该选择哪一个?(顺便说一句,工具可以是任何颜色!)
以下是我的测试来源:

const cv = require('opencv');

const lowThresh = 90;
const highThresh = 90;
const nIters = 1;

const GRAY   = [120, 120, 120];
const WHITE = [255, 255, 255];

cv.readImage('./files/viv1.jpg', function(err, im) {
  if (err) throw err;

  width = im.width()
  height = im.height()
  if (width < 1 || height < 1) throw new Error('Image has no size');

  const out = new cv.Matrix(height, width);
  im.convertGrayscale();
  im_canny = im.copy();
  im_canny.canny(lowThresh, highThresh);
  im_canny.dilate(nIters);

  contours = im_canny.findContours();

  let maxArea = 0;
  let biggestContour;

  for (i = 0; i < contours.size(); i++) {
    const area = contours.area(i);
    if (area > maxArea) {
      maxArea = area;
      biggestContour = i;
    }

    out.drawContour(contours, i, GRAY);
  }

  const arcLength = contours.arcLength(biggestContour, true);
  contours.approxPolyDP(biggestContour, 0.001 * arcLength, true);

  out.drawContour(contours, biggestContour, WHITE, 5);

  out.save('./tmp/out.png');
  console.log('Image saved to ./tmp/out.png');
});
const cv=require('opencv');
常数低阈值=90;
常数highThresh=90;
常数=1;
常数灰=[120120120];
常量白色=[255,255,255];
cv.readImage('./files/viv1.jpg',函数(err,im){
如果(错误)抛出错误;
宽度=im.width()
高度=仪表高度()
如果(宽度<1 | |高度<1)抛出新错误(“图像没有大小”);
const out=新的cv.矩阵(高度、宽度);
im.grayscale();
im_canny=im.copy();
我精明,精明(低thresh,高thresh);
扩张(硝石);
等高线=im_canny.findContours();
设maxArea=0;
让我们来看看你的轮廓;
对于(i=0;i最大面积){
最大面积=面积;
最大轮廓=i;
}
out.drawContour(轮廓,i,灰色);
}
const arcLength=等高线.arcLength(最大等高线,真);
等高线。近似多边形(最大等高线,0.001*弧长,真);
out.drawContour(轮廓,最大轮廓,白色,5);
out.save('./tmp/out.png');
log('Image saved to./tmp/out.png');
});

您需要添加一些预处理来清理图像。由于阴影、照明不良、工具上的高亮度等原因,图像中的强度变化很大,因此应均衡图像。这将有助于您在当前光线不足或光线较高的区域获得更好的响应

下面是一个关于C++中直方图均衡化的opencv教程:

希望这有帮助

编辑:
你可以有一个基于损失函数(?)的自动阈值。例如:如果您知道该工具将完全捕获到框架中,那么您知道应该在x=10到x=800(比如)的每一列中获得一个高值。然后,您可以继续降低阈值,直到在每个列上获得一个从x=10到x=800的高值。这是一种非常幼稚的方法,但我认为这是一个有趣的实验,因为您自己生成图像,并且可以控制对象的放置。

您也可以尝试通过第一次运行图像。在这种情况下,这种类型的二值化非常擅长分割前景和背景,即使在不一致的照明/阴影(这似乎是上面第二个示例中的问题)下也是如此。Adathresh需要一些参数微调,但一旦整个工具从背景中分割出来,Canny边缘检测应该会产生更一致的结果


至于轮廓的粗糙度,您可以尝试将
findContours
模式设置为所述的
CV\u CHAIN\u近似方法之一

非均匀背景的检测算法removal@AnderBiguri你说的是格拉布切吗?我越来越认为这可能是答案。我想我将使用基于Canny的解决方案来确定GrabCut的对象边界。然后应用GrabCut,这也将允许用户在需要时进行纠正(使用遮罩)。然后从GrabCut的输出中再次检测边缘(因为背景将被移除,所以它将非常有效,就像我在谷歌上拍摄绕道图像一样)。看起来可行。为了确定物体的大小,我想我会使用阿鲁科标记。这个系统是如何工作的(流程方面)?为什么我要问机器视觉系统,在你的图像中。。。嗯,看起来不太理想。第一张照片相当不错,虽然你仍然在中间看到圆形渐变明亮。第二个很可怕。如果你能解决这个问题(假设它是可行的),你很可能会为自己省下很多工作或可能的失败。是的,第二张图片是自愿的,用来测试最坏的情况。但最终,用户将收到一张纸作为背景和说明,所以这可能不会像这样糟糕。我只是想要一个系统,将尽可能坚实,因为用户将采取这些图片在家里等,那里的光线条件