使用python和opencv进行简单有效的硬币分割

随着社区的努力,新一代的OpenCv Python绑定越来越好。新的绑定,称为“cv2”的是替换旧的“cv”绑定;在新一代绑定中,几乎所有的操作都会返回本机python对象或numpy对象,这是相当不错的,因为它简化了很多,而且在某些方面也提高了性能,因为您现在还可以使用numpy的优化操作,并且可以与其他框架(如SCIKIT图像它还使用numpy数组来表示图像。

在这个例子中,我将演示如何通过使用阈值的简单方法分割图像中的硬币,甚至实时视频捕获,形态算符,和轮廓近似。这种方法比使用OTSU阈值和分水岭分割的方法简单得多。在Opencv python教程中,我强烈建议您阅读,因为它的健壮性。不幸的是,使用OTSU阈值的方法高度依赖于照明标准化。我们可以提取图像的小块来实现类似于自适应OTSU的二值化(比如在letptonica中实现的二值化——Tesseract OCR使用的框架)来解决这个问题,但让我们看看另一种方法。供参考,请参阅使用我的网络摄像头拍摄的非标准化照明图像输出的OTSU阈值:

原始图像与OTSU二值化
原始图像与OTSU二值化

1。设置视频捕获配置

使用python绑定创建实时视频捕获的第一步是实例化video capture类,设置属性,然后从相机开始读取帧:

import numpy as nimport cv2 cap=cv2.videocapture(0)cap.set(cv2.cv.cv_cap_prop_frame_width,1280)盖设置(cv2.cv.cv_cap_prop_frame_height,720)

在较新版本(尚未发布)中,的常量cv_cap_prop_frame_宽度现在在CV2模块,现在,我们就用CV2.CV模块。

2。读取图像帧

下一步是使用视频捕获对象读取帧,然后将其转换为灰色(我们不会使用颜色信息分割硬币):

如果是真的:ret,frame=cap.read()ROI=frame[0:500,0:500]灰色=CV2.CVT颜色(ROI,cv2.颜色bgr2灰色)

注意,这里我提取的是完整图像的一小部分(硬币所在的位置)。但如果你的图像上只有硬币,你就不必这么做。此刻,我们有以下灰色图像:

捕捉到的原始灰色图像。
捕捉到的原始灰色图像。

三。应用自适应阈值

在这一步中,我们将在应用高斯模糊核之后应用自适应阈值,以消除图像中的噪声:

灰色模糊=cv2.gaussianblur(灰色,(15)15)0)thresh=cv2.自适应阈值(灰色模糊,255,cv2.自适应阈值高斯_c,cv2.阈值_二进制_inv,11,1)

请参见图像中高斯核的效果:

原始灰度图像和应用高斯核后的图像。
原始灰度图像和应用高斯核后的图像。

现在,模糊图像自适应阈值的效果是:

自适应阈值对模糊图像的影响

请注意,在那一刻,我们已经将硬币分割开来了,除了硬币中心和周围的一些地方有小的噪音。

4。形态学

这个形态学算子是用来扩张的,对图像像素进行侵蚀和其他操作。在这里,由于相机有时会出现一些伪影,我们将使用闭合的形态操作来确保硬币的边界始终闭合,否则,我们可能会发现一个半圆形或类似的硬币。要了解关闭操作(即已放大像素的侵蚀操作)的效果,请参见下图:

形态闭合

您可以看到在操作的一些迭代之后,圆圈开始被填满。要使用关闭操作,我们将使用形态学opencv python绑定中的函数:

内核=NP.个((3,3)np.uint8)关闭=cv2.morphologyex(thresh,变形关闭,内核,迭代=4)

现在请看关闭操作对我们硬币的影响:

硬币的关闭操作

形态算符的操作非常简单,bepaly亚洲主要原理是将一个元素(在我们的例子中,我们有一个3×3的块元素)应用到图像的像素中。如果你想理解它,请看这个动画解释了侵蚀的操作.

5。轮廓检测与滤波

在应用了形态学运算符之后,我们所要做的就是找到每枚硬币的轮廓,然后过滤面积小于或大于硬币面积的轮廓。您可以将在OpenCV中查找轮廓的过程想象为查找连接组件及其边界的操作。要做到这一点,我们将使用opencv发现轮廓功能。

cont_img=closing.copy()轮廓,层次结构=cv2.findcontours(续,cv2.retr_外部,cv2.chain_近似简单)

请注意,我们复制了关闭图像,因为函数findcontours将更改作为第一个参数传递的图像,我们也在使用后外侧的旗帜,这意味着返回的轮廓只是最外面的轮廓。参数链约简单还将返回轮廓的紧凑表示,更多信息看到这里.

找到轮廓后,我们需要迭代每一个区域并检查它们的面积,以过滤包含大于或小于硬币面积的区域的轮廓。我们还需要将椭圆拟合到找到的轮廓上。我们可以用最小包围圈来完成这项工作,但因为我的相机不在硬币上,硬币出现时有一个小的倾斜,描述了一个椭圆。

对于轮廓中的cnt:area=cv2。如果area<2000或area>4000,则Contourarea(cnt):如果len(cnt)<5,则Continue Ellipse=cv2。FitEllipse(cnt)cv2。Ellipse(roi,椭圆,(0255,0)2)

注意,在上面的代码中,我们正在迭代每个轮廓,过滤面积小于2000或大于4000的硬币(这些是我在距离相机这个距离找到的巴西硬币的硬编码值)。稍后,我们检查轮廓的点数,因为函数拟合椭圆需要大于或等于5的点数,最后我们使用椭圆函数在原始图像上绘制绿色椭圆。

要用轮廓显示最终图像,我们只需使用imshow函数显示带有图像的新窗口:

cv2.imshow('最终结果',投资回报率)

最后,这是上述所有步骤结束后的结果:

检测到轮廓的最终图像
检测到轮廓的最终图像

完整的源代码:

import numpy as nimport cv2def run_main():cap=cv2.videocapture(0)cap.set(cv2.cv.cv_cap_prop_frame_width,1280)盖设置(cv2.cv.cv_cap_prop_frame_height,720)while(真):ret,frame=cap.read()ROI=frame[0:500,0:500]灰色=CV2.CVT颜色(ROI,cv2.color_bgr2gray)灰色_blur=cv2.gaussianblur(灰色,(15)15)0)thresh=cv2.自适应阈值(灰色模糊,255,cv2.自适应阈值高斯cv2.thresh_二进制_inv,11,1)内核=NP.个((3,3)np.uint8)关闭=cv2.morphologyex(thresh,cv2.变形关闭,内核,迭代次数=4)连续img=关闭.copy()轮廓,层次结构=cv2.findcontours(续,cv2.retr_外部,cv2.chain_about_simple)for cnt in outlines:area=cv2.Contourarea(cnt)if area<2000 or area>4000:Continue if len(cnt)<5:Continue Ellipse=cv2.FitEllipse(cnt)cv2.Ellipse(roi,椭圆,(0255,0)2)cv2.imshow(“形态闭合”,关闭)cv2.imshow(“自适应阈值”,thresh)cv2.imshow('轮廓',ROI)如果cv2.waitkey(1)&0xff==ord('q'):break cap.release()cv2.destroyallWindows()如果uuu name_uuu==“uuu main_uuuuuu”:运行main()
克里斯蒂安S佩隆

26评论

    1. 你好,它非常适合演示,因为只有白色背景。
      你认为我们能在一个强大的背景下得到非常精确的硬币边缘吗?bepaly亚洲
      我在说一个专业的申请…

  1. 我刚刚意识到几周前我读这篇文章是为了更好地理解OpenCV中的图像分割,现在我要测试pyevolve。小世界?伟大的工作。

  2. 你好

    我试过这个程序,但发生了一个错误,你能帮我吗?

    回溯(最近一次呼叫的最后一次):
    文件“c:/python27/scripts/countoursf.py”,第15行,在里面
    雷特帧=cap.read()
    名称错误:未定义名称“cap”

    再次出错

    如果cv2.waitkey(1)&0xff==ord(“q”):
    打破

  3. 你好,
    很好的教程!!!!
    你能告诉我吗?如何在找到轮廓后提取矩形而不是椭圆?如果图像中有许多矩形,如何提取大的矩形?
    谢谢。
    当做

  4. 你好,
    bepaly亚洲很好的教程。你能告诉我如何显示一个内有所有硬币的边框吗?我的意思是所有这些硬币都在一个大矩形里,谢谢。

    当做

  5. 你好。
    优秀的辅导,我开始用Opencvpython编程,有一个问题。
    怎么能数一数照片里的硬币数?
    非常感谢bepaly亚洲

  6. 你好。
    优秀的辅导,我开始用Opencvpython编程,有一个问题。
    怎么能数一数照片里的硬币数?
    非常感谢bepaly亚洲

  7. 你好,克里斯蒂安。
    谢谢你分享这个,这很有帮助。bepaly亚洲
    快速问:我对计算硬币的数量感兴趣。在代码中也可以这样做吗?(我的歉意,我已经9年没有编程了)。

    拉斐-

  8. 你好
    我在安卓上试这个

    列表轮廓=new arraylist();
    imgproc.findcontours(关闭,等高线,新矩阵()imgproc.retr_列表,imgproc.chain_about_simple);
    对于(int idx=0;idx<outlines.size();IDX++)

    matofpoint2f temp=新的matofpoint2f(outlines.get(idx.toarray());
    rotatedrect elipse1=imgproc.fitelepis(温度);
    imgproc.椭圆(originalmat,ELIPSE1新标量(0255,0)2);
    }
    没有显示。我错过了什么?

  9. 你的代码令人难以置信。但我有个问题。你怎么能认出除了圆以外的其他几何图形呢?谢谢,我为谷歌的英语感到抱歉。

  10. 它说当我试图显示ROI时,它没有被定义。一bepaly亚洲切都在运行,包括for循环,但出于某种原因,当我试图显示ROI时,它会抛出一个错误。有什么建议吗?

留下评论

您的电子邮件地址将不会发布。

此网站使用Akismet来减少垃圾邮件。了解如何处理评论数据.