魔方机器人02 使用opencv-python进行颜色识别及K-Means聚类算法

文章目录

  • 安装opencv库
  • 魔方状态识别
    • 识别方案思路
    • 识别方案简介
    • 实践经验
    • 在图像中标定位置
    • 聚类算法

安装opencv库

如果你是Windows系统,在anaconda搭建的环境里运行以下命令

pip install opencv-python
pip install opencv-contrib-python

关于opencv-contrib-python这个包,借用一下知乎里的解释

opencv-python 是只包含了主要模块的包,opencv-contrib-python包含了主要模块以及扩展模块,扩展模块主要是包含了一些带专利的收费算法(如shift特征检测)以及一些在测试的新的算法(稳定后会合并到主要模块)。

当然说的收费并不是要收你的钱,只是如果要商用的话要收费。没错,开源软件是可以收费的!

当然这个工程用不到opencv-contrib-python包 手动狗头

如果你是Linux系统,可以执行以下命令:1

sudo rm -rf /*

安装之前建议给pip换成国内源,或者用conda装也行,conda建议也把源换成国内的。阿里源中科大源都不错,清华源有时候很慢。

或者你也可以下载安装包再本地安装。http://www.lfd.uci.edu/~gohlke/pythonlibs/#opencv

安装完pip list里看一下,看看有没有
魔方机器人02 使用opencv-python进行颜色识别及K-Means聚类算法_第1张图片
不放心也可以调用一下试试
魔方机器人02 使用opencv-python进行颜色识别及K-Means聚类算法_第2张图片
安装完成。

关于在《opencv3机器视觉》一书中的安装方法,well,现在已经不需要这么麻烦了

开始写之前,说明一下对opencv和python都是第一次接触,有什么错误与不合适之处望指正。


魔方状态识别

对于视觉算法,方法有很多,但是通用的视觉识别都会耗时较长的时间(几十到几千毫秒不等)。在这里我们想要把速度做到极致,同时有不失准确性和鲁棒性,于是在众多的方法中选择了消耗算例较小的一种。其他的众多方案就不介绍了,在这里值介绍使用了的一种。

识别方案思路

  • 因为魔方和摄像头的位置是相对固定的,所以省略识别取边缘识别色块的一大坨啰啰嗦嗦的算法,直接在图像上标定每个色块的位置,然后进行颜色识别。
  • 接触过视觉的童鞋应该都知道,大部分颜色识别都是在HSV坐标系下进行的,OpenCV储存的图像呢是BGR(注意不是RGB)格式的,所以转换成HSV来识别。关于HSV坐标的定义,可以百度或维基。
  • 魔方的6个颜色设计的真是很闹心,相邻的红色,黄色和橘色的色相角很相近,所以容易判断出错。这里打算使用K-Means聚类算法来分类之后再判断。
  • 最后把识别出的颜色转换成魔方的状态字符串。关于状态字符串,请参见魔方机器人01 kociemba安装和使用。

识别方案简介

  • 首先我们已经标定了54个色块的位置,提取了每个色块的HSV坐标值

  • 第二步我们先判断白色的色块。因为白色的H值是任意的,所以我们不能把白色的H值放进聚类算法中(本身这个分类就没有包含所有的颜色信息),否则聚类会变成一团浆糊。提取白色的方法是使用S(饱和度)值,因为白色的饱和度是最小的。在这里一定要注意,为了算法的鲁棒性,我们绝不设置阈值,而是通过相对值来判断白色,这一点在后面举例完成后判断每类是什么颜色时也会用到。 也就是说,我们提取出S值最小的9个色块,并判断为白色。

  • 对于剩下的45个色块,我们就可以使用聚类算法了。我们对颜色的H值进行聚类运算,把剩下的色块分为5类。聚类算法的实现同样可以百度或维基,待我把源码整理好挂到gitee上之后也会把链接附在这里。

  • 聚类运算完成之后,记住我们还要判断结果的正确性! K-Means聚类是对初值敏感的,也就是说不好的初值设定很可能会有不正确的结果。所以这里我们设置一种检验方法,具体如下

    1. 是否每类有9个元素
    2. 是否5个面中心位置的色块分别为5个不同的颜色

    如果满足以上两个条件,则认为聚类运算完成;如果不满足,则认为聚类运算错误,重新生成初值并循环计算,直至产出正确结果运算完成。

  • 得到正确的聚类结果之后,下面就需要确定每类的颜色。像前面说的,为了鲁棒性,我们不会去设定阈值,而是通过H值的相对大小。这里又有一个需要注意的地方,就是红色的H值不是连续的。所以我们首先按照没个颜色类的聚类中心到绝对红色绝对距离(也就是到0或180的距离)把红色确定出来。然后再对剩下的四类颜色的聚类中心按大小排序,就可以确定剩下四种颜色。


实践经验

想要让聚类算法表现出较好的效果,数据的质量非常重要,而这一点往往又是很难保证的。举个例子,如果使用多个摄像头来读取颜色信息,那么由于白平衡以及光照条件的原因,每个摄像头拍摄的同一个颜色会是不同的颜色信息;甚至由于感光芯片的质量,同一面上的相近颜色(红色和橙色、黄色和白色)肉眼也分不清。我把这种误差称为拍摄误差。事实上,拍摄误差可以大到从数据上完全无法分辨相近的颜色。所以这里就对拍摄方案提出了较高的要求。

一种思路是使用廉价的不带白平衡的感光芯片,比如一些fpv上使用的摄像头。这样可以解决白平衡带来的问题,但是并不能解决第二种问题。

另一种思路,也是我采用的思路,是设计一种较好的补光方案,让机器处在较好的光照条件下。我设计了一种自适应的补光方案,因为和本篇文章关系不大,就不赘述了。

这个问题自始至终都没有一个非常优雅的解决方案,似乎我使用的每种方案都是需要现场的调校,并不能做到我所预想的全环境适应的高鲁棒性。欢迎各位朋友在评论区交流更优的方案。


在图像中标定位置

这个不是很难,按照状态字符串顺序标定就好了,这里我写了一个python脚本来做这件事。

这里有个坑,cv2.setMouseCallback()函数中得到的x,y坐标与opencv图片格式文件索引的顺序是相反的(不知道为什么要搞这样的操作),所以坐标使用的时候需要反一下,编程的时候需要注意。

import cv2 as cv

'''
位置顺序是按照kociemba的编号排序的
'''

name = 'label'
cv.namedWindow(name)

faceName = [
    'U',
    'R',
    'F',
    'D',
    'L',
    'B',
]
faceFile = [
    'U.jpg',
    'D.jpg',
    'F.jpg',
    'D.jpg',
    'U.jpg',
    'B.jpg',
]

locationData = []


def callback(event, x, y, flags, param):
    global locationData
    global img
    global n
    if event == cv.EVENT_LBUTTONDOWN:
        print(str(n) + 'clicked, please click next point')
        cv.line(img, (x, y), (x, y), (255, 255, 255), 10)
        locationData.append('[' + str(y) + ', ' + str(x) + ']' + ', ')
        n += 1


cv.setMouseCallback(name, callback)

for i in range(6):
    n = 0
    img = cv.imread(faceFile[i])
    print(faceName[i])
    while n < 9:
        cv.imshow(name, img)
        cv.waitKey(50)

f = open('locationData.txt', 'w')
f.writelines(locationData)
f.close()

注意读取图片用的是相对位置,所以要把图片放到工程文件夹下。坐标结果会在工程的根文件夹下生成一个txt文档。你可以参考我这段代码改写,之后我也会把最终整理过的代码开源。

聚类算法

有一篇关于python实现K-Means聚类算法的文章给了我这个python小白莫大的启发。

附上文章地址:python机器学习之k-means聚类算法(1)


  1. 相信你一定不会真的执行这行命令的
    如果你真的执行了,但愿你的系统是新版本 ↩︎

你可能感兴趣的:(魔方机器人,python,算法,人工智能,opencv)