天天看点

使用OpenCV测量图像中物体的大小

本文翻译自pyimagesearch技术博客上的一篇文章,《measuring size of objects in an image with opencv》,原文作者:adrian rosebrock 。

网址:https://www.pyimagesearch.com/2016/03/28/measuring-size-of-objects-in-an-image-with-opencv/

使用OpenCV测量图像中物体的大小

图像目标尺寸检测类似于计算从我们的相机到一个物体的距离——在这两种情况下,我们都需要事先定义一个比率来测量每个给定度量单位的像素数(pixels_per_metric)。在这里所说的这个被称为“pixels_per_metric”的比率指标,我在接下来的部分中对其更正式的定义。

为了确定图像中物体的大小,我们首先需要使用一个参照物作为“校准”点。我们的参照物应该有两个重要的属性:

我们应该知道这个物体的真实尺寸(在宽度或高度上的毫米或英寸等值的大小)。

我们应该能够轻松地在图片中找到这个参照物,要么基于参照物的位置(如,参照物可以是一副图像中左上角的物体)或基于参照物的外表(例如参照物可以是图片中具有最独特的颜色或独一无二的形状,不同于所有其他的物体)。

在任何一种情况下,我们的参考应该以某种方式唯一可识别。

在这个例子中,我们将使用美分硬币作为我们的参照物,并且在所有示例中,确保它始终是我们图像中最左边的对象。

使用OpenCV测量图像中物体的大小

图1:我们将使用美分硬币作为参照物,并确保它始终处于图像最左侧位置,这使得我们可以通过对它们位置的轮廓大小进行排序,进一步来提取信息。

通过保证美分硬币是最左边的物体,我们可以从左到右对我们的物体等高线区域进行排列,抓住这个硬币(它将始终对应于排序列表中的第一个等高线区域)。并使用它来定义我们的pixels_per_metric比率,我们将其定义为:

pixels_per_metric =物体像素宽 / 物体真实宽

美分硬币的真实宽度是0.955英寸。现在,假设我们图像中硬币的像素宽为150像素(基于它的相关边界框)。那么这种情况下pixels_per_metric这样计算:

pixels_per_metric = 150px / 0.955in = 157px

因此,在我们这幅图像中,每英寸大约有157个像素。有了这个比率,我们可以计算图像中其他物体的大小了。

既然我们已经理解了pixels_per_metric,我们就可以实现用于测量图像中对象大小的python程序脚本了。

打开一个新的py文件,插入以下代码:

第2-8行用来导入我们所需的python包。在本例中,我们将大量使用imutils包,因此,如果您没有安装它,请确保在使用之前安装它:

另外如果你已经安装过这个imutils包,也请确保它为最新版本,在我这里所使用最新的版本是0.3.6。

第10行和第11行定义了一个midpoint函数,顾名思义,它用于计算两个(x,y)坐标之间的中点。

然后我们在第14-19行中解析我们的命令行参数。我们需要两个参数,–image,它是我们输入图像的路径,其中包含我们想要测量的对象,–width,也就是我们的参照物的宽度(英寸),–image路径图像中所认定的那个最左边的物体。

我们现在可以加载我们的图像并对其进行预处理:

第2-4行从磁盘加载我们的图像,将其转换为灰度,然后使用高斯过滤器平滑它。然后我们执行边缘检测和扩张+磨平,以消除边缘图中边缘之间的任何间隙(第8-10行)。

第13-15行找到等高线,也就是我们边缘图中物体相对应的轮廓线。

然后,这些等高线区域从左到右(使得我们可以提取到参照物)在第19行中进行排列。然后我们在第20行时,对pixelspermetric值进行初始化。

下一步是对每一个等高线区域值大小进行检查校验。

在第2行,我们开始对每个单独的轮廓值进行循环。如果等高线区域大小不够大,我们就会丢弃该区域,认为它是边缘检测过程遗留下来的噪音(第4和5行)。

如果等高线区域足够大,我们就会在第9-11行计算图像的旋转边界框,特别注意:cv2.cv.boxpoints函数是针对于opencv2.4版本,而cv2.boxpoints函数是针对于opencv 3版本。

然后我们将旋转的边界框坐标按顺序排列在左上角,右上角,右下角,左下角,正如上周的博客文章(第17行)所讨论的。

最后,第18-22行用绿色画出物体的轮廓,然后将边界框矩形的顶点画在小的红色圆圈中。现在我们已经有了边界框,接下来就可以计算出一系列的中点:

第4-6行将我们前面所得的有序边界框各个值拆分出来,然后计算左上角和右上角之间的中点,然后是计算左下角和右下角之间的中点。

  此外,我们还分别计算左上角与左下角,右上角和右下角的中点(第10和11行)。

  第14-17行在我们的图像上画出蓝色的中点,然后将各中间点用紫色线连接起来。

  接下来,我们需要通过查看我们的参照物来初始化pixelspermetric变量:

首先,我们计算中间点集之间的欧几里得距离(第2行和第3行)。

da变量将包含高度距离(以像素为单位),而db将保持我们的宽度距离。

然后,我们在第8行进行检查,看看我们的pixelspermetric变量是否已经被初始化了,如果没有,我们将db除以我们提供的宽度,从而得到每英寸的(近似)像素。

现在我们已经定义了pixelspermetric变量,我们可以测量图像中各物体的大小:

第2行和第3行计算物体的尺寸(英寸),方法是通过pixelsper度量值划分各自的欧几里得距离(参见上面的“pixels_per_metric ”一节,以获得关于这个比率如何工作的更多信息)。 第6-11行在我们的图像上画出物体的尺寸,而第14和15行显示输出结果。

为了测试我们的size.py脚本,只需执行以下命令:

您的输出应该如下图所示:

使用OpenCV测量图像中物体的大小

图2:使用opencv、python和计算机视觉+图像处理技术来测量图像中物体的大小

正如您所看到的,我们已经成功地计算出了我们图像中的每个物体的大小——我们的名片被正确地报告为3.5in x 2in。类似地,我们的镍币被准确地描述为0.8in x 0.8in。 然而,并不是所有的结果都是完美的。 两个游戏男孩的墨盒被报道有稍微不同的尺寸(实际上它们的尺寸是一样的)。两个镍币的尺寸也下降了0.1英寸。 这是为什么?为什么物体的测量不是百分之百准确的?

有两个原因:

  首先,我匆忙用我的iphone拍了这张照片。这个角度当然不是一个完美的90度角“向下看”(就像鸟的眼睛一样)在物体上。如果没有一个完美的90度视图(或者尽可能接近它),物体的尺寸就会被扭曲。

  

  其次,我没有使用相机的内在和外在参数来校准我的iphone。如果不确定这些参数,照片就会倾向于径向和切向镜头失真。执行一个额外的校准步骤来发现这些参数可以“不扭曲”我们的图像,并导致更好的对象大小近似(但是我将把关于失真校正的讨论作为未来博客文章的主题)。

  换句话说,在拍摄对象的照片时,尽量接近90度的视角——这将有助于提高对物体大小估计的准确性。

  让我们看第二个测量物体大小的例子,这次测量的是药片的尺寸:

使用OpenCV测量图像中物体的大小

图3:使用opencv测量图像中药丸的大小

在美国,几乎有一半的处方药物是圆的和/或白色的,因此,如果我们能根据他们的测量结果来过滤药片,我们就有更好的机会准确识别药物。

最后,我们有一个最后的例子,这次用的是3.5in x 2in 的名片来测量两个黑胶唱片和一个信封的大小:

使用OpenCV测量图像中物体的大小

同样,结果也不是很完美,但这是由于(1)视角和(2)镜头失真,正如上述分析的两个原因中所提到的。

本文还参考了另外一篇翻译原英文博客的博客,网址如下:

https://blog.csdn.net/u010636181/article/details/80659700

继续阅读