前言
之前在做比赛的时候因为有实物和色块,而两者之间的最明显的区别就是颜色不一样。
而我们使用的是纯RGB进行颜色判断,理论上结合HSV来一起判断效果会更好,但是本方法也优化的还行了,HSV之前用的时候不是很会用。
在比赛中颜色只有5种:黑、红、黄、绿、蓝,差不多如下图吧。按照纯色给出的图片因为打印机和摄像头问题会有一些误差。

前方法思路
本来检测颜色的方法使用的是给定一个阈值范围,因为RGB值{R,G,B}(0≤R,G,B≤255)比较明显。但是由于摄像头的色差,最后出来的结果比较不理想。
方法思路
传入一个轮廓的中心点。然后从改点往四个方向进行DFS,判断下一个点和这个点之间各个通道的RGB值的差值,如果差值大于20,那么就说明是杂色。如果不是杂色,那么根据中心点的RGB三个通道的差值来进行判断。
找轮廓和中心点的方法就不写了。现在已经有中心点了。具体如图:

效果展示
物体的对应编号如下
| 12
 3
 4
 
 | //圆形、正方形、长方形、椭圆形4种(ID依次为1, 2, 3, 4)//可乐罐、口香糖、方便桶面、饼干盒4种(ID依次为81, 82, 83, 84)
 //黑、红、黄、绿、蓝5种(ID依次为1, 2, 3, 4, 5)
 ID[2]:第一个为颜色,第二个为形状
 
 | 
我将dfs到的点在二值图上打亮,得到的结果可以看到扫描的区域比较理想。

经过调试参数之后准确地得到了最后结果

详细代码
直接上代码了,thrImg是一个全局变量,用来拿到图的尺寸。这段代码是判断某个轮廓是否是杂色。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 
 | int dir[4][2]={
 0,1,1,0,0,-1,-1,0
 };
 int check(int x,int y)
 {
 if(x>0&&x<=cvGetSize(thrImg).width&&y>0&&y<=cvGetSize(thrImg).height)
 return 1;
 return 0;
 }
 
 void dfs(int x,int y,int watch[3],int depth)
 {
 
 int watchi[3];
 for(int i=0;i<3;i++)
 watchi[i] = originMat.at<Vec3b>(y, x)[i];
 
 
 
 
 
 
 
 
 for(int i=0;i<3;i++)
 {
 if(abs(watch[i]-watchi[i])>20)
 mix=1;
 }
 for(int i=0;i<4;i++)
 {
 int dx=x+dir[i][0];
 int dy=y+dir[i][1];
 if(check(dx,dy)&&depth<9&&!vis[dy][dx]){
 vis[dy][dx]=1;
 dfs(dx,dy,watchi,depth+1);
 }
 }
 }
 
 int isColorPure(int x,int y)
 {
 int ans=isColorPure(x,y,0);
 return ans;
 }
 int isColorPure(int x,int y,int depth)
 {
 memset(vis,0,sizeof(vis));
 
 mix=0;
 int watchi[3];
 for(int i=0;i<3;i++)
 watchi[i] = originMat.at<Vec3b>(y, x)[i];
 
 vis[y][x]=1;
 dfs(x,y,watchi,depth);
 
 if(mix==1){
 return -1;
 }else{
 return getColor(y,x);
 }
 }
 
 | 
得到某个点的颜色。首先根据差值来判断(用这个就能有返回值了,后面的基本没作用),如果判断不出来则根据范围判断。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 
 | int getColor(int x,int y){
 
 
 if( !originMat.data )
 return -1;
 Mat tempImage = originMat.clone();
 int watch[3],flag[3];
 flag[0]=flag[1]=flag[2]=0;
 for(int i=0;i<3;i++)
 watch[i] = originMat.at<Vec3b>(x, y)[i];
 
 for(int i=0;i<3;i++){
 if(watch[i]>colorRecgnize)
 flag[i]=1;
 }
 
 cvSetReal2D(thrImg, x, y, 255.0);
 cvShowImage("colorJudge", thrImg);
 
 
 
 int colorRange=40;
 
 if(abs(watch[0]-watch[1]<colorRange)&&abs(watch[0]-watch[2])<colorRange&&abs(watch[1]-watch[2])<colorRange){
 return 1;
 }else if(watch[2]-watch[0]>colorRange&&watch[2]-watch[1]>colorRange){
 return 2;
 }else if(watch[2]-watch[0]>colorRange&&watch[1]-watch[0]>colorRange&&abs(watch[1]-watch[2])<colorRange){
 return 3;
 }else if(watch[1]-watch[0]>colorRange&&watch[1]-watch[2]>colorRange){
 return 4;
 }else if(watch[0]-watch[1]>colorRange&&watch[0]-watch[2]>colorRange){
 return 5;
 }
 
 
 
 
 
 if(flag[0]==0&&flag[1]==1&&flag[2]==1){
 return 3;
 }else if(flag[0]==1&&flag[1]==0&&flag[2]==0){
 return 5;
 }else if(flag[0]==0&&flag[1]==1&&flag[2]==0){
 return 4;
 }else if(flag[0]==0&&flag[1]==0&&flag[2]==1){
 return 2;
 }else if(flag[0]==0&&flag[1]==0&&flag[2]==0){
 return 1;
 }
 return 5;
 }
 
 | 
后记
dfs这个方法有点像区域生长吧,不过没写过后者,之前在群友的建议下使用的这个方法,效果挺好。后面的RGB差值也要感谢大佬的建议了。单纯范围判断效果不好。