Kil*_*One 5 python opencv computer-vision raspberry-pi
我想开发一个Python OpenCV脚本来复制/改进我开发的Gimp程序.该过程的目标是提供遵循草和硬表面之间的分界线的x,y点阵列.这个阵列将允许我完成我的500磅54"宽压力洗涤机器人,它有一个Raspberry Pi Zero(和相机),因此它可以以每秒几英寸的速度跟随该边缘.我将监控和/或者当我在沙发上看电视时,通过其wifi视频流和iPhone应用程序控制机器人.
这是一个原始图像样本(60x80像素):
Gimp程序是:
<?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>
Run Code Online (Sandbox Code Playgroud)
我的Pi Zero上的OpenCV程序执行时间的目标是大约1-2秒或更短(目前需要约0.18秒).
我拼凑了一些结果,这些结果与Gimp xml文件中的相同点有关.我完全不确定它是否正在做Gimp关于蒙版色调范围的事情.我还没有弄清楚如何在面具上应用最小半径,我很确定当面具在硬面的边缘上作为面具的一部分获得"草"块时我将需要它.这是迄今为止的所有轮廓点(ptscanvas.bmp):
截至美国东部时间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()
Run Code Online (Sandbox Code Playgroud)
留下来做:
1A.编辑:截至2018年7月9日,我一直专注于这个问题,因为这似乎是我最大的问题.我无法让cv2.findcontours平滑掉'边缘草'以及Gimp的魔棒半径特征.在左边,是一个2色'问题'掩码和叠加的结果'红色'点,直接使用cv2.findcontours找到,在右边,Gimp圆角掩模应用于cv2之前的左图像'问题'掩码. findcontours应用于它,产生正确的图像和点:
我曾尝试查看Gimps源代码,但这超出了我的理解范围,我找不到任何可以做到这一点的OpenCV例程.有没有办法在OpenCV中对边缘掩码的"非边缘"像素应用最小半径平滑?通过'非边缘'我的意思是,你可以看到Gimp没有半径这些'角'(在黄色高光内),但似乎只是将半径平滑应用于图像内部的边缘(注意:Gimps半径算法消除了所有掩码中的小岛,这意味着在应用cv2.findcontours获取兴趣点后,您不必找到最大的区域轮廓):
编辑:截至美国东部时间2018年7月12日下午5点:我已经使用了我最容易创建代码的语言,VB6,呃,我知道.无论如何,我已经能够在像素级别上进行线/边平滑程序,以完成我想要的最小半径蒙版.它就像一个PacMan沿着边缘的右侧漫游,尽可能靠近它,并在Pac的左侧留下一条面包屑痕迹.不确定我是否可以从该代码创建一个python脚本,但至少我有一个地方可以开始,因为没有人确认有一个OpenCV替代方法来做到这一点.如果有人感兴趣,这里是一个编译的.exe文件,应该在没有安装的大多数Windows系统上运行(我认为).下面是它的截图(蓝色/绿色蓝色像素是未平滑边缘,绿色/绿色蓝色像素是圆角边缘):
您可以通过此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
Run Code Online (Sandbox Code Playgroud)
好吧,我终于有时间看这个了。我将解决您的每一点,然后展示代码中的更改。如果您有任何问题或建议,请告诉我。
看来你自己能够很好地做到这一点。
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)
...
Run Code Online (Sandbox Code Playgroud)我更改了代码以删除完全遵循图像侧面的线上的点。草边也与此重合应该基本上是不可能的。
...
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)
...
Run Code Online (Sandbox Code Playgroud)由于如何在图像中找到轮廓,我们可以简单地翻转阈值函数并找到图像其他部分周围的轮廓。变化如下:
...
#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)
...
Run Code Online (Sandbox Code Playgroud)至于颜色差异,您已将图像转换为 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)
...
Run Code Online (Sandbox Code Playgroud)免责声明:这些更改基于上面的 python 代码。对不在提供代码中的 python 代码进行的任何更改都会导致我的更改无效。