前言
之前在做比赛的时候因为有实物和色块,而两者之间的最明显的区别就是颜色不一样。
而我们使用的是纯RGB进行颜色判断,理论上结合HSV来一起判断效果会更好,但是本方法也优化的还行了,HSV之前用的时候不是很会用。
在比赛中颜色只有5种:黑、红、黄、绿、蓝,差不多如下图吧。按照纯色给出的图片因为打印机和摄像头问题会有一些误差。
前方法思路
本来检测颜色的方法使用的是给定一个阈值范围,因为RGB值{R,G,B}(0≤R,G,B≤255)比较明显。但是由于摄像头的色差,最后出来的结果比较不理想。
方法思路
传入一个轮廓的中心点。然后从改点往四个方向进行DFS,判断下一个点和这个点之间各个通道的RGB值的差值,如果差值大于20,那么就说明是杂色。如果不是杂色,那么根据中心点的RGB三个通道的差值来进行判断。
找轮廓和中心点的方法就不写了。现在已经有中心点了。具体如图:
效果展示
物体的对应编号如下
1 2 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是一个全局变量,用来拿到图的尺寸。这段代码是判断某个轮廓是否是杂色。
1 2 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); } }
|
得到某个点的颜色。首先根据差值来判断(用这个就能有返回值了,后面的基本没作用),如果判断不出来则根据范围判断。
1 2 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差值也要感谢大佬的建议了。单纯范围判断效果不好。