您的位置:首页 > 编程语言 > Python开发

python+opencv多进程实现识别魔方颜色,通过kociemba算法得出算法字符串并画图

2019-03-07 15:00 1501 查看

上一篇博客已经给大家讲了鼠标hsv获取魔方颜色阈值方法,具体见https://blog.csdn.net/qq_32107283/article/details/86774583
本次给大家讲讲识别的整套流程(其实是从github上扒下来的),看了一个寒假改了改,改成自己能用的,单进程运算六个面按照他的方法识别其实挺慢的,于是便做了一些改进用了多进程(进程池)的方法,创建六个进程,每个进程分别处理一个面,这样就快多了,原来需要三十秒左右,现在好的时候能到一两秒内,分享出来给大家共享。
先呈现一下效果图:
`d 可以看出,快的时候将近一秒,具体要看cpu了。(注:第一次刚一开始运行的时候会很慢,如果时间很长都没有出结果把它关掉,重新在运行一遍就好啦!)
关于多进程进程池的问题,前段时间冥思苦想,一开始用多线程思路,使用threading模块,具体网上都有参考例子,可以搜一下,例如:https://www.cnblogs.com/tyomcat/p/5486827.html,但是用多线程后出现一个致命问题,并不是想自己想的那样更快,反而慢了,原因应该是网上说的 GIL全局锁,在解释器解释执行 Python 代码时,先要得到这把锁,意味着,任何时候只可能有一个线程在执行代码,其它线程要想获得 CPU 执行代码指令,就必须先获得这把锁,如果锁被其它线程占用了,那么该线程就只能等待,直到占有该锁的线程释放锁才有执行代码指令的可能。
因此,这也就是为什么两个线程一起执行反而更加慢的原因,因为同一时刻,只有一个线程在运行,其它线程只能等待,即使是多核CPU,也没办法让多个线程「并行」地同时执行代码,只能是交替执行,因为多线程涉及到上下文切换、锁机制处理(获取锁,释放锁等),所以,多线程执行不快反慢。
在python里,如果想更多的利用多核cpu内存,还是更多利用多进程处理比较好。
https://blog.csdn.net/topleeyap/article/details/78981848这篇博客里有具体的实现多进程的几种方式,我用的是进程池的方式(Pool):

ps=Pool(6)
#cpu=ps.apply(colorMatch,args=(i,))      # 同步执行
up_cpu=ps.apply_async(colorMatch,args=('up',))  # 异步执行
right_cpu=ps.apply_async(colorMatch,args=('right',))
front_cpu=ps.apply_async(colorMatch,args=('front',))
down_cpu=ps.apply_async(colorMatch,args=('down',))
left_cpu=ps.apply_async(colorMatch,args=('left',))
back_cpu=ps.apply_async(colorMatch,args=('back',))

# 关闭进程池,停止接受其它进程
ps.close()
# 阻塞进程
ps.join()

我创建了六个进程:up_cpu、right_cpu、front_cpu、down_cpu、left_cpu、back_cpu
将拍摄的六个面照片依次传入颜色识别函数colorMatch(side):

def colorMatch(side):
cube_rgb = cv2.imread( side + '.jpg')

这套颜色识别方法,是对六种颜色分别计算出掩膜,将其加和后对整个面进行识别,

mask = red_erosion + green_erosion + yellow_erosion + blue_erosion + orange_erosion + white_erosion
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_50)#开运算分割色块
mask = cv2.erode(mask, kernel_50, iterations = 1)
res = cv2.bitwise_and(cube_hsv, cube_hsv, mask = mask)

并且通过开运算和腐蚀函数对图像进行色块分离:
效果图如下:(分别是第一个mask,第二个mask,第三个mask,第四个res图)
可以看出,随着处理,色块分离越来越明显。

中点色块颜色及坐标
def midpoint(x1,y1,x2,y2):
x_mid = int((x1 + x2)/2)
y_mid = h - int(((y1 + y2)/2))
color = res[y_mid, x_mid]
return ([int(color[0]), int(color[1]), int(color[2])])

此处函数为识别每个面hsv图中九个色块的中点颜色和坐标,供后面使用

# midpoint获得每个面九个色块的颜色
mid_1 = midpoint(x_min, y_max, (x_min + int(width/3)), (y_max - int(height/3)))
mid_2 = midpoint((x_min + int(width/3)), y_max, (x_min + int(width*2/3)), (y_max - int(height/3)))
mid_3 = midpoint((x_min + int(width*2/3)), y_max, x_max, (y_max - int(height/3)))
mid_4 = midpoint(x_min, (y_max - int(height/3)), (x_min + int(width/3)), (y_max - int(height*2/3)))
mid_5 = midpoint((x_min + int(width/3)), (y_max - int(height/3)), (x_min + int(width*2/3)), (y_max - int(height*2/3)))
mid_6 = midpoint((x_min + int(width*2/3)), (y_max - int(height/3)), x_max, (y_max - int(height*2/3)))
mid_7 = midpoint(x_min, (y_max - int(height*2/3)), (x_min + int(width/3)), y_min)
mid_8 = midpoint(x_min + int(width/3), (y_max - int(height*2/3)), (x_min + int(width*2/3)), y_min)
mid_9 = midpoint(x_min + int(width*2/3), (y_max - int(height*2/3)), x_max, y_min)
mids = [mid_1, mid_2, mid_3, mid_4, mid_5, mid_6, mid_7, mid_8, mid_9]

这是获得九个色块的颜色保存在mids列表中

s=''
for rgb in mids:#hsv
if ((0<=rgb[0]<=180 ) and (0<=rgb[1]<=50 ) and( 201<=rgb[2]<=255)):#白
s+='D'
elif ((3<=rgb[0]<=9 )and (115<=rgb[1]<=190 )and (195<=rgb[2] <=255)):#澄
s+='B'
elif ((95<=rgb[0] <=124) and (123<=rgb[1]<=253) and (109<=rgb[2]<=240)):#蓝
s+='L'
elif( (68<=rgb[0]<=82) and (140<=rgb[1]<=255) and (120<=rgb[2]<=245)):#绿
s+='R'
elif ((20<=rgb[0]<=34) and (125<=rgb[1]<=243) and(142 <=rgb[2]<=255)):#黄
s+='U'
elif ((170<=rgb[0]<=182)and (110<=rgb[1] <=240 )and (145<=rgb[2]<=255 )):#红
s+='F'
return(s)

此功能为将各种点色块hsv转化为字符串供给kociemba算法得到解法
注意主函数里

#cpu=ps.apply(colorMatch,args=(i,))      # 同步执行
up_cpu=ps.apply_async(colorMatch,args=('up',))  # 异步执行

注释的是同步执行,后面的是异步执行操作

通过kociemba.solve(get_str)函数就能直接得到kociemba算法还原魔方的字符串了

Tip:
这里有一个小细节,为了让操作员更能清楚的知道识别的颜色对不对,我画了一个draw窗口来显示魔方展开后的颜色样子:
这个本来想通过多线程来画图,原本想的是六个进程每识别完一个面就能在draw画布画出相应的色块,结果试验后发现我以为的多进程共用参数draw,实际上是调用了六次,并没有实现在同一个画布上画图功能。
冥思苦想想了两天,由一开始从网上查资料开辟共享内存或者Manager方法来实现,结果通通不行,突然想到可以把识别颜色后给出的字符串来转换为对应的颜色:

def drawcolor(get_str):#URFDLB 传入54个字符
#strcolor={'U':(0,255,255),'R':(0,255,0),'F':(0,0,255),'D':(255,255,255),'L':(255,191,0),'B':(0,128,255)}
#strcolor="URFDLB"
draw_str=[]
#BGR:
U=(0,255,255)
R=(0,255,0)
F=(0,0,255)
D=(255,255,255)
L=(255,191,0)
B=(0,128,255)
for key in get_str:#遍历54个字符中每个字符,i为字符

`
一开始想用字典,把每个字符对应的颜色输进去,结果实际调用的时候发现总是没法一起调用,思考了两天两夜才想到用这个for循环+列表就可以实现了,因为54个块的位置是固定的,所以可以把字符串每个字符依次填入到对应的色块中去,然后去寻找他所对应的颜色就ok啦!
辛辛苦苦写了这么多,希望能对大家应用有些启发~~~~
有什么建议可以在下方评论哦,具体程序见:
https://download.csdn.net/download/qq_32107283/11004596

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐