python+opencv多进程实现识别魔方颜色,通过kociemba算法得出算法字符串并画图
上一篇博客已经给大家讲了鼠标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
- python opencv 实现Reinhard颜色迁移算法
- 新手想用opencv python做颜色识别,然后通过树莓派将信号输出到单片机中
- OpenCV图像识别:车牌定位算法源码,Python语言实现
- kNN算法python实现和简单数字识别的方法
- 【人脸识别】人脸验证算法Joint Bayesian详解及实现(Python版)
- LeetCode探索模块初级算法字符串章节python3代码实现
- opencv2用反投影实现标定颜色的识别
- 【机器学习算法实现】kNN算法__手写识别——基于Python和NumPy函数库
- 基于python3 OpenCV3实现静态图片人脸识别
- python下实现人脸识别(opencv2)
- 【人脸识别】人脸验证算法Joint Bayesian详解及实现(Python版)
- 静态图片人脸识别 OpenCV3,Python3实现
- python实现识别手写数字 python图像识别算法
- Python实现基于KNN算法的笔迹识别功能详解
- python实现kNN算法识别手写体数字
- git是一种分布式代码管理工具,git通过树的形式记录文件的更改历史,比如: base'<--base<--A<--A' ^ | --- B<--B' 小米工程师常常需要寻找两个分支最近的分割点,即base.假设git 树是多叉树,请实现一个算法,计算git树上任意两点的最近分割点。 (假设git树节点数为n,用邻接矩阵的形式表示git树:字符串数组matrix包含n个字符串,每个字符串由字符'0
- Python实现knn算法手写数字识别
- Linux系统下利用OpenCV实现人脸检测和基于LBPH算法的人脸识别
- OpenCV + Python 实现人脸识别
- Python实现一些简单的算法(4)—将一个字符串转成驼峰数