Gimp 程序中的 OpenCV Python 脚本 - 草地/硬表面边缘检测

2023-12-06

我想开发一个 Python OpenCV 脚本来复制/改进我开发的 Gimp 程序。该过程的目标是提供一个遵循草地和硬表面之间分界线的 x,y 点阵列。这个阵列将使我能够完成我的 500 磅 54 英寸宽的压力清洗机器人,它有一个 Raspberry Pi Zero(和摄像头),这样它就可以以每秒几英寸的速度沿着该边缘移动。我将监视并/或者当我在沙发上看电视时通过 wifi 视频流和 iPhone 应用程序控制机器人。

这是原始图像示例(60x80 像素):

enter image description here

Gimp程序是:

  1. 将图像转换为索引 2 种颜色。基本上一侧是草地,另一侧是砖块或人行道。该死的影子哦,那是我:)

enter image description here

  1. 在两种颜色中,采用较低的色相值,并使用以下魔杖设置在该值的像素上使用魔杖。色调设置 23 是我去除阴影的方式,羽化设置 15 是我去除孤岛/锯齿(裂缝中的草:)的方式。

enter image description here

  1. 使用以下高级设置值对路径进行高级选择(默认值的更改为黄色)。基本上我只想要线段,我的 (x,y) 点数组将是黄色路径点。

enter image description here

  1. 接下来,我将路径导出到 .xml 文件,从中可以解析并隔离上图中的黄点。这是 .xml 文件:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
              "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">

<svg xmlns="http://www.w3.org/2000/svg"
     width="0.833333in" height="1.11111in"
     viewBox="0 0 60 80">
  <path id="Selection"
        fill="none" stroke="black" stroke-width="1"
        d="M 60.00,0.00
           C 60.00,0.00 60.00,80.00 60.00,80.00
             60.00,80.00 29.04,80.00 29.04,80.00
             29.04,80.00 29.04,73.00 29.04,73.00
             29.04,73.00 30.00,61.00 30.00,61.00
             30.00,61.00 30.00,41.00 30.00,41.00
             30.00,41.00 29.00,30.85 29.00,30.85
             29.00,30.85 24.00,30.85 24.00,30.85
             24.00,30.85 0.00,39.00 0.00,39.00
             0.00,39.00 0.00,0.00 0.00,0.00
             0.00,0.00 60.00,0.00 60.00,0.00 Z" />
</svg>

我的目标是在 Pi Zero 上执行此 OpenCV 程序的时间约为 1-2 秒或更短(目前大约需要 0.18 秒)。

我拼凑了一些东西,结果与 Gimp xml 文件中的点相同。我完全不确定它是否在做 Gimp 在遮罩色调范围方面所做的事情。我还没有弄清楚如何在蒙版上应用最小半径,我很确定当蒙版在硬表面边缘出现“草”丛作为蒙版的一部分时,我将需要它。以下是迄今为止的所有轮廓点(ptscanvas.bmp):

enter image description here

截至美国东部时间 2018 年 7 月 6 日下午 5:08,这是“仍然混乱”的脚本,可以运行并找到这些点;

import numpy as np
import time, sys, cv2

img = cv2.imread('2-60.JPG')
cv2.imshow('Original',img)
# get a blank pntscanvas for drawing points on 
pntscanvas = np.zeros(img.shape, np.uint8)

print (sys.version)  
if sys.version_info[0] < 3:
    raise Exception("Python 3 or a more recent version is required.")

def doredo():
    start_time = time.time()

    # Use kmeans to convert to 2 color image
    hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    Z = hsv_img.reshape((-1,3))
    Z = np.float32(Z)
    # define criteria, number of clusters(K) 
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    K = 2
    ret,label,center=cv2.kmeans(Z,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)

    # Create a mask by selecting a hue range around the lowest hue of the 2 colors
    if center[0,0] < center[1,0]:
        hueofinterest = center[0,0]
    else:
        hueofinterest = center[1,0]
    hsvdelta = 8
    lowv = np.array([hueofinterest - hsvdelta, 0, 0])
    higv = np.array([hueofinterest + hsvdelta, 255, 255])
    mask = cv2.inRange(hsv_img, lowv, higv)

    # Extract contours from the mask
    ret,thresh = cv2.threshold(mask,250,255,cv2.THRESH_BINARY_INV)
    im2,contours,hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    
    # Find the biggest area contour
    cnt = contours[0]
    max_area = cv2.contourArea(cnt)

    for cont in contours:
        if cv2.contourArea(cont) > max_area:
            cnt = cont
            max_area = cv2.contourArea(cont)

    # Make array of all edge points of the largets contour, named allpnts  
    perimeter = cv2.arcLength(cnt,True)
    epsilon = 0.01*cv2.arcLength(cnt,True) # 0.0125*cv2.arcLength(cnt,True) seems to work better
    allpnts = cv2.approxPolyDP(cnt,epsilon,True)
    
    end_time = time.time()
    print("Elapsed cv2 time was %g seconds" % (end_time - start_time))

    # Convert back into uint8, and make 2 color image for saving and showing
    center = np.uint8(center)
    res = center[label.flatten()]
    res2 = res.reshape((hsv_img.shape))

    # Save, show and print stuff
    cv2.drawContours(pntscanvas, allpnts, -1, (0, 0, 255), 2)
    cv2.imwrite("pntscanvas.bmp", pntscanvas)
    cv2.imshow("pntscanvas.bmp", pntscanvas)
    print('allpnts')
    print(allpnts)
    print("center")
    print(center)
    print('lowv',lowv)
    print('higv',higv)
    cv2.imwrite('mask.bmp',mask)
    cv2.imshow('mask.bmp',mask)
    cv2.imwrite('CvKmeans2Color.bmp',res2)
    cv2.imshow('CvKmeans2Color.bmp',res2)

print ("Waiting for 'Spacebar' to Do/Redo OR 'Esc' to Exit")
while(1):
    ch = cv2.waitKey(50)
    if ch == 27:
        break
    if ch == ord(' '):
        doredo()
        
cv2.destroyAllWindows()

剩下要做的事:

  1. 在非边缘像素上添加遮罩半径,以处理原始遮罩,例如 Gimp 在遮罩上运行最小半径之前创建的遮罩:

enter image description here

1a.编辑:截至 2018 年 7 月 9 日,我一直专注于这个问题,因为这似乎是我最大的问题。我无法让 cv2.findcontours 平滑“边缘草”,就像 Gimp 的魔杖半径功能那样。左边是一个 2 色“问题”蒙版和直接使用 cv2.findcontours 找到的叠加结果“红色”点,右边是在 cv2 之前应用于左侧图像“问题”蒙版的 Gimp 半径蒙版。 findcontours 应用于它,产生正确的图像和点:

enter image description here enter image description here

我尝试查看 Gimps 源代码,但它超出了我的理解范围,而且我找不到任何可以执行此操作的 OpenCV 例程。有没有办法对 OpenCV 中边缘掩模的“非边缘”像素应用最小半径平滑???我所说的“非边缘”是指,正如您所看到的,Gimp 不会对这些“角”(在黄色高亮区域内)进行半径处理,而似乎只是将半径平滑应用于图像“内部”的边缘(注意:Gimps 半径算法消除了所有掩模中的小岛,这意味着应用 cv2.findcontours 来获取兴趣点后,您不必找到最大区域轮廓):

enter image description here

  1. 从图像边缘上的所有点中删除不相关的阵列点。
  2. 弄清楚为什么它找到的阵列点似乎围绕着绿草而不是硬表面,我以为我正在处理硬表面色调。
  3. 找出为什么 CvKmeans2Color.bmp 中的硬表面颜色显示为橙色而不是米色,如 Gimps 转换中那样,以及为什么这与 Gimps 转换中的像素不匹配?这是 CvKmeans2Color.bmp 和 Gimp:

enter image description here enter image description here

编辑:截至美国东部时间 2018 年 7 月 12 日下午 5 点:我已经使用了最容易创建代码的语言,VB6,呃,我知道。不管怎样,我已经能够制作一个在像素级别上工作的线/边缘平滑例程来完成我想要的最小半径掩模。它的工作原理就像吃豆人一样,沿着边缘的右侧尽可能靠近地漫游,并在吃豆人的左侧留下一条面包屑痕迹。不确定我可以从该代码创建一个 python 脚本,但至少我有一个开始的地方,因为没有人确认有 OpenCV 替代方法来做到这一点。如果有人有兴趣here是一个已编译的 .exe 文件,无需安装即可在大多数 Windows 系统上运行(我认为)。这是它的屏幕截图(蓝色/绿蓝像素是未平滑的边缘,绿色/绿蓝像素是圆角边缘):

enter image description here

您可以通过这个 VB6 例程了解我的流程逻辑的要点:

Sub BeginFollowingEdgePixel()
   Dim lastwasend As Integer
   wasinside = False
   While (1)
      If HitFrontBumper Then
         GoTo Hit
      Else
         Call MoveForward
      End If
      If circr = orgpos(0) And circc = orgpos(1) Then
         orgpixr = -1 'resets Start/Next button to begin at first first found blue edge pixel
         GoTo outnow 'this condition indicates that you have followed all blue edge pixels
      End If
      Call PaintUnderFrontBumperWhite
      Call PaintGreenOutsideLeftBumper
nomove:
      If NoLeftBumperContact Then
         Call MoveLeft
         Call PaintUnderLeftBumperWhite
         Call PaintGreenOutsideLeftBumper
         If NoLeftBumperContact Then
            If BackBumperContact Then
               Call MakeLeftTheNewForward
            End If
         End If
      ElseIf HitFrontBumper Then
Hit:
         Call PaintAheadOfForwardBumperGreen
         Call PaintGreenOutsideLeftSide
         Call MakeRightTheNewForward
         GoTo nomove
      Else
         Call PaintAheadOfForwardBumperGreen
         Call PaintGreenOutsideLeftSide
         Call PaintUnderFrontBumperWhite
      End If
      If (circr = 19 + circrad Or circr = -circrad Or circc = 19 + circrad Or circc = -circrad) Then
         If lastwasend = 0 And wasinside = True Then
            'finished following one edge pixel
            lastwasend = 1
            GoTo outnow
            Call redrawit
         End If
      Else
         If IsCircleInsideImage Then
            wasinside = True
         End If
         lastwasend = 0
      End If
      Pause (pausev) 'seconds between moves - Pressing Esc advances early
   Wend
outnow:
End Sub

好吧,我终于有时间看这个了。我将解决您的每一点,然后展示代码中的更改。如果您有任何问题或建议,请告诉我。

  1. 看起来你自己能够很好地完成这件事。

    1.a.这可以通过在对图像进行任何处理之前模糊图像来解决。为了实现此目的,对代码进行了以下更改;

    ...
    start_time = time.time()                                              
    
    blur_img = cv2.GaussianBlur(img,(5,5),0) #here                        
    
    # Use kmeans to convert to 2 color image                              
    hsv_img = cv2.cvtColor(blur_img, cv2.COLOR_BGR2HSV)
    ...
    
  2. 我更改了代码以删除完全遵循图像侧面的线上的点。草边也与此重合应该基本上是不可能的。

    ...
    allpnts = cv2.approxPolyDP(cnt,epsilon,True)                          
    
    new_allpnts = []                                                      
    
    
    for i in range(len(allpnts)):                                         
        a = (i-1) % len(allpnts)                                          
        b = (i+1) % len(allpnts)                                          
    
        if ((allpnts[i,0,0] == 0 or allpnts[i,0,0] == (img.shape[1]-1)) and (allpnts[i,0,1] == 0 or allpnts[i,0,1] == (img.shape[0]-1))):          
            tmp1 = allpnts[a,0] - allpnts[i,0]                            
            tmp2 = allpnts[b,0] - allpnts[i,0]                                                                                                                     
            if not (0 in tmp1 and 0 in tmp2):                             
                new_allpnts.append(allpnts[i])
        else:
            new_allpnts.append(allpnts[i])
    ...
    cv2.drawContours(pntscanvas, new_allpnts, -1, (0, 0, 255), 2)
    ...
    
  3. 由于如何在图像中找到轮廓,我们可以简单地翻转阈值函数并找到图像其他部分周围的轮廓。变化如下:

    ...
    #Extract contours from the mask                                      
    ret,thresh = cv2.threshold(mask,250,255,cv2.THRESH_BINARY) #here      
    im2,contours,hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    ...
    
  4. 至于颜色差异,您已将图像转换为 HSV 格式,并且在保存之前不会将其切换回 BGR。 HSV 的这一更改确实会给你带来更好的结果,所以我会保留它,但它是一个不同的调色板。变化如下:

    ...
    cv2.imshow('mask.bmp',mask)                                           
    res2 = cv2.cvtColor(res2, cv2.COLOR_HSV2BGR)                          
    cv2.imwrite('CvKmeans2Color.bmp',res2)                                
    cv2.imshow('CvKmeans2Color.bmp',res2)
    ...
    

免责声明:这些更改基于上面的 python 代码。对不在提供代码中的 python 代码进行的任何更改都会导致我的更改无效。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Gimp 程序中的 OpenCV Python 脚本 - 草地/硬表面边缘检测 的相关文章

  • 将 pandas 数据框中的列减去其第一个值

    我需要将 pandas 数据帧的一列中的所有元素减去其第一个值 在这段代码中 pandas 抱怨 self inferred type 我猜这是循环引用 df Time df Time df Time 0 在这段代码中 pandas 抱怨为
  • 如何使用pycaffe重构caffe网络

    我想要的是 加载网络后 我将分解一些特定的图层并保存新的网络 例如 原网 数据 gt conv1 gt conv2 gt fc1 gt fc2 gt softmax New net 数据 gt conv1 1 gt conv1 2 gt c
  • Dask DataFrame 的逐行处理

    我需要处理一个大文件并更改一些值 我想做这样的事情 for index row in dataFrame iterrows foo doSomeStuffWith row lol doOtherStuffWith row dataFrame
  • 获取单个方程的脚本

    在文本文件中输入 a 2 8 b 3 9 c 4 8 d 5 9 e a b f c d g 0 6 h 1 7 i e g j f h output i j 期望的输出 输出 2 8 3 9 0 6 4 8 5 9 1 7 如果输入文件名
  • 在 Python 中使用 sec 函数的反函数

    我正在创建一个程序 用于计算从一定高度范围和设定初始速度发射射弹的最佳角度 在我需要使用的最终方程中 存在一个反 sec 函数 它导致了一些麻烦 我已经导入了数学并尝试使用 asec 无论如何 但是数学似乎无法计算反秒函数 我也明白 sec
  • Python 中 genfromtxt() 的可变列数?

    我有一个 txt具有不同长度的行的文件 每一行都是代表一条轨迹的一系列点 由于每条轨迹都有自己的长度 因此各行的长度都不同 也就是说 列数从一行到另一行不同 据我所知 genfromtxt Python 中的模块要求列数相同 gt gt g
  • 在 python-docx 中搜索和替换

    我有一个包含以下字符串的文档 模板 你好 我的名字是鲍勃 鲍勃是一个很好的名字 我想使用 python docx 打开此文档并使用 查找和替换 方法 如果存在 来更改每个字符串 Bob gt Mark 最后 我想生成一个新文档 其中包含字符
  • 行为:如何从另一个文件导入步骤?

    我刚刚开始使用behave http pythonhosted org behave 一个Pythonic BDD框架 使用小黄瓜语法 http docs behat org guides 1 gherkin html 行为需要一个特征 例
  • 反加入熊猫

    我有两个表 我想附加它们 以便仅保留表 A 中的所有数据 并且仅在其键唯一时添加表 B 中的数据 键值在表 A 和 B 中是唯一的 但在某些情况下键将出现在表 A 和 B 中 我认为执行此操作的方法将涉及某种过滤联接 反联接 以获取表 B
  • 使用循环将对象添加到列表(python)

    我正在尝试使用 while 循环将对象添加到列表中 基本上这就是我想做的 class x pass choice raw input pick what you want to do while choice 0 if choice 1 E
  • 使用 python 将文本发送到带有逗号分隔符的列

    如何使用分隔符 在 Excel 中将一列分成两列 并使用 python 命名标题 这是我的代码 import openpyxl w openpyxl load workbook DDdata xlsx active w active a a
  • Python Flask 是否定义了路由顺序?

    在我看来 我的设置类似于以下内容 app route test def test app route
  • 在pycharm中调试python代码

    这个问题类似于this https stackoverflow com questions 10240018 how to use pycharm to debug python script一 我正在尝试调试pyethapp https
  • Python 矩阵每一行的总和

    lista 1 2 3 4 5 6 7 8 9 print lista def filas lista res for elemento in lista x sum lista elemento res append x print re
  • 是否可以写一个负的python类型注释

    这可能听起来不合理 但现在我需要否定类型注释 我的意思是这样的 an int Not Iterable a string Iterable 这是因为我为一个函数编写了一个重载 而 mypy 不理解我 我的功能看起来像这样 overload
  • 使用 Doc2vec 后如何解释 Clusters 结果?

    我正在使用 doc2vec 将关注者的前 100 条推文转换为矢量表示形式 例如 v1 v100 之后 我使用向量表示来进行 K 均值聚类 model Doc2Vec documents t size 100 alpha 035 windo
  • Python模块单元测试的最佳文件结构组织?

    遗憾的是 我发现有太多方法可以在 Python 中保存单元测试 而且它们通常没有很好的文档记录 我正在寻找一种 终极 结构 它可以满足以下大部分要求 be discoverable by test frameworks including
  • Pandas 在特定列将数据帧拆分为两个数据帧

    I have pandas我组成的 DataFrameconcat 一行由 96 个值组成 我想将 DataFrame 从值 72 中分离出来 这样 一行的前 72 个值存储在 Dataframe1 中 接下来的 24 个值存储在 Data
  • Google App Engine 中的自定义身份验证

    有谁知道或知道我可以在哪里学习如何使用 Python 和 Google App Engine 创建自定义身份验证流程 我不想使用 Google 帐户进行身份验证 并且希望能够创建自己的用户 如果不是专门针对 Google App Engin
  • 将此 MATLAB 代码转换为 Python 时我做错了什么?

    我正在努力将生成波形的 MATLAB 代码转换为 Python 就上下文而言 这是原子力显微镜带激发响应的模拟 与代码错误无关 在 MATLAB 中从 r vec 生成的图形与我在 Python 中生成的图形不同 我是否正确地将 MATLA

随机推荐