strgeon

Opencv-使用DFS确实区域是否杂色
前言之前在做比赛的时候因为有实物和色块,而两者之间的最明显的区别就是颜色不一样。而我们使用的是纯RGB进行颜色判断...
扫描右侧二维码阅读全文
18
2018/07

Opencv-使用DFS确实区域是否杂色

前言

之前在做比赛的时候因为有实物和色块,而两者之间的最明显的区别就是颜色不一样。
而我们使用的是纯RGB进行颜色判断,理论上结合HSV来一起判断效果会更好,但是本方法也优化的还行了,HSV之前用的时候不是很会用。

在比赛中颜色只有5种:黑、红、黄、绿、蓝,差不多如下图吧。按照纯色给出的图片因为打印机和摄像头问题会有一些误差。
原图


前方法思路

本来检测颜色的方法使用的是给定一个阈值范围,因为RGB值{R,G,B}(0≤R,G,B≤255)比较明显。但是由于摄像头的色差,最后出来的结果比较不理想。


方法思路

传入一个轮廓的中心点。然后从改点往四个方向进行DFS,判断下一个点和这个点之间各个通道的RGB值的差值,如果差值大于20,那么就说明是杂色。如果不是杂色,那么根据中心点的RGB三个通道的差值来进行判断。
找轮廓和中心点的方法就不写了。现在已经有中心点了。具体如图:

中心点


效果展示

物体的对应编号如下

//圆形、正方形、长方形、椭圆形4种(ID依次为1, 2, 3, 4)
//可乐罐、口香糖、方便桶面、饼干盒4种(ID依次为81, 82, 83, 84)
//黑、红、黄、绿、蓝5种(ID依次为1, 2, 3, 4, 5)
ID[2]:第一个为颜色,第二个为形状

我将dfs到的点在二值图上打亮,得到的结果可以看到扫描的区域比较理想。
DFS效果

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


详细代码

直接上代码了,thrImg是一个全局变量,用来拿到图的尺寸。这段代码是判断某个轮廓是否是杂色。

//---------检测是否纯色-----------
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)
{
    //对该点的颜色进行读取,存到watchi[3]中
    int watchi[3];
    for(int i=0;i<3;i++)
        watchi[i] = originMat.at<Vec3b>(y, x)[i];

//是否在二值图上显示dfs到的点
// cout<<x<<":"<<y<<endl<<"0:"<<watch[0]<<" 1:"<<watch[1]<<" 2:"<<watch[2]<<endl;
// cvSetReal2D(thrImg, y, x, 255.0);
// cvShowImage("colorJudge", thrImg);
// cvWaitKey();

    //进行判断,对watch值进行比较
    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)
{//传值按照x,y
    int ans=isColorPure(x,y,0);
    return ans;
}
int isColorPure(int x,int y,int depth)
{//传值按照x,y
    memset(vis,0,sizeof(vis));
    //对该点的颜色进行读取,存到watchi[3]中
    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);
// cvWaitKey();
    if(mix==1){
        return -1;
    }else{
        return getColor(y,x);
    }
}

得到某个点的颜色。首先根据差值来判断(用这个就能有返回值了,后面的基本没作用),如果判断不出来则根据范围判断。

int getColor(int x,int y)
{ //传值按照y,x
    // imshow("mat",originMat);
        // 源图像载入及判断
        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;
        }//BGR蓝绿红

        cvSetReal2D(thrImg, x, y, 255.0);
        cvShowImage("colorJudge", thrImg);
    // cvSet2D(originImg,x,y, cvScalar(0, 255, 0, 0)); //绘制来查看检测点的位置
    // cvShowImage("yanse", originImg);

        int colorRange=40;
        //通过BGR之间的差值来进行判断
        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;//蓝
        }
        //-------------以上通过差值进行判断-----------
// cvSetReal2D(thrImg, x, y, 255.0);
// cvShowImage("colorJudge", thrImg);
        // cvSet2D(originImg,x,y, cvScalar(0, 255, 0, 0)); //绘制来查看检测点的位置
        // cvShowImage("yanse", originImg);
        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差值也要感谢大佬的建议了。单纯范围判断效果不好。

Last modification:August 10th, 2018 at 03:57 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment