opencv颜色过滤--------如何选择需要的颜色呢?

Posted 微卡智享

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv颜色过滤--------如何选择需要的颜色呢?相关的知识,希望对你有一定的参考价值。

文章转自  http://blog.csdn.net/jianjian1992/article/details/51274834


自已学了一个HSV的图片转换,用于图像处理方面


在日常生活中我们表示颜色的时候都喜欢用RGB模型进行表示,RGB分别代表了三原色:红色Red, 绿色Green,蓝色Blue。但是当我们想要从图片中选取某种颜色的时候,比如说红色,用RGB该怎么做?很难啊。

所以当涉及到颜色的时候我们通常都会将图片转化到hsv空间进行表示。这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)。

那么该如何选择我们需要的颜色呢?比如说红色,是否就只需要选择一个固定区间就可以了呢?

如果只考虑计算机画出来的颜色,那当然可以了,但是当我们推广到现实生活中的颜色的时候,我发现很多看上去是红色的色彩并不符合理论中的红色,比如说用手机拍照,用扫描仪扫描图片,色彩都是很容易失真,我们看上去是红色,但它已经不再是红色了。


里边的说明,它有如下结论

所以红色区域为

( (H >= 0  && H <= 10) || (H >= 125  && H <= 180)) && S >= 43 && V >= 46
 
   
   
 
  • 1

真是这样么?
让我们来做做实验吧。
我按照上面的规则进行过滤,满足规则的像素我设为白色,不满足的则为黑色。
苹果原图为:
opencv颜色过滤--------如何选择需要的颜色呢?
最后的结果为空:
opencv颜色过滤--------如何选择需要的颜色呢?
要求太严格了对吧,那我改成:

( (H >= 0  && H <= 10) || (H >= 125  && H <= 180) ) && S >= 43
 
   
   
 
  • 1

结果变为了
opencv颜色过滤--------如何选择需要的颜色呢?
效果真棒哦!

这张苹果图片是张好图片,接下来我们来见识一下其它的红色吧。
用同样的条件,下面处理一下这张淡淡的红色吧,图片是用扫描仪扫描得到:
(里边打印的信息被我涂掉了,但是处理的时候是没有的)
opencv颜色过滤--------如何选择需要的颜色呢?
得到的结果图很磕碜啊,线条基本上都断掉了,完全不能满足要求啊。
opencv颜色过滤--------如何选择需要的颜色呢?

那么该如何选择我们需求的阈值呢?
下面介绍一个颜色直方图方法,简单地说就是我们截取我们需要的颜色区域,之后统计一下它里边的颜色直方图,之后根据统计得到的信息划定阈值。
下面是颜色直方图所用代码。

颜色直方图

vector<MatND> getHSVHist(Mat &src){    //输入图片得是三通道彩色图片
    assert (!src.empty() && src.channels() == 3);    //rgb转hsv图像
    Mat hsv;
    cvtColor(src, hsv, CV_BGR2HSV);    //h的范围是0~180,所以选取30个bin
    //s和v的范围都是0~255,那就选择51个bin
    int hbins = 30;    int sbins = 51;    int vbins = 51;    int hHistSize[] = {hbins};    int sHistSize[] = {sbins};    int vHistSize[] = {vbins};    float hranges[] = {0, 180};    float sranges[] = {0, 255};    float vranges[] = {0, 255};    const float* hRanges[] = {hranges};    const float* sRanges[] = {sranges};    const float* vRanges[] = {vranges};    vector<MatND> hist;    int hChannels[] = {0};    int sChannels[] = {1};    int vChannels[] = {2};
    MatND hHist, sHist, vHist;
    calcHist(&hsv, 1, hChannels, Mat(), hHist, 1, hHistSize, hRanges);
    calcHist(&hsv, 1, sChannels, Mat(), sHist, 1, sHistSize, sRanges);
    calcHist(&hsv, 1, vChannels, Mat(), vHist, 1, vHistSize, vRanges);
    hist.push_back(hHist);
    hist.push_back(sHist);
    hist.push_back(vHist);
    normalize( hist[0], hist[0], 0, 1, NORM_MINMAX, -1, Mat() );
    normalize( hist[1], hist[1], 0, 1, NORM_MINMAX, -1, Mat() );
    normalize( hist[2], hist[2], 0, 1, NORM_MINMAX, -1, Mat() );    int i;  
    int start = -1, end = -1;    for(i = 0; i < 30; i++)  
    {  
        float value = hist[0].at<float>(i);        if (value  > 0)
        {            if (start == -1)
            {
                start = i;
                end = i;
            }            else
                end = i;            cout << "H Value" << i << ": " << value << endl;
        }        else
        {            if (start != -1)                cout <<"H:" <<start*6 <<"~"<<(end+1)*6-1<<endl;
            start = end = -1;
        }
    }  
    if (start != -1)        cout <<"H:" <<start*5 <<"~"<<(end+1)*5-1<<endl;

    start = -1, end = -1;    for(i = 0; i < 51; i++)  
    {  
        float value = hist[1].at<float>(i);        if (value  > 0)
        {            if (start == -1)
            {
                start = i;
                end = i;
            }            else
                end = i;            cout << "S Value" << i << ": " << value << endl;
        }        else
        {            if (start != -1)                cout <<"S:"<< start*5 <<"~"<<(end+1)*5-1<<endl;
            start = end = -1;
        }
    }  
    if (start != -1)        cout <<"S:" <<start*5 <<"~"<<(end+1)*5-1<<endl;

    start = -1, end = -1;    for(i = 0; i < 51; i++)  
    {  
        float value = hist[2].at<float>(i);        if (value  > 0)
        {            if (start == -1)
            {
                start = i;
                end = i;
            }            else
                end = i;            cout << "V Value" << i << ": " << value << endl;
        }        else
        {            if (start != -1)                cout <<"V:" <<start*5 <<"~"<<(end+1)*5-1<<endl;
            start = end = -1;
        }
    }  
    if (start != -1)        cout <<"V:" <<start*5 <<"~"<<(end+1)*5-1<<endl;    return hist;
}
 
   
   
 
  • 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

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 91

  • 92

  • 93

  • 94

  • 95

  • 96

  • 97

  • 98

  • 99

  • 100

  • 101

  • 102

  • 103

  • 104

  • 105

  • 106

  • 107

  • 108

  • 109

  • 110

  • 111

  • 112

  • 113

  • 114

  • 115

  • 116

  • 117

  • 118

比如说对于以下这幅图,我想要只保留图中的红色部分。
opencv颜色过滤--------如何选择需要的颜色呢?

于是我在画图中打开它,然后选择了一小块红色区域裁剪并保存为新图片,如下图所示:
opencv颜色过滤--------如何选择需要的颜色呢?
之后我对这一小块区域进行直方图测试,得到以下结果
opencv颜色过滤--------如何选择需要的颜色呢?

颜色过滤结果

我按照上面的结果进行实验,颜色过滤代码如下:

void filteredRed(const Mat &inputImage, Mat &resultGray, Mat &resultColor){    Mat hsvImage;    cvtColor(inputImage, hsvImage, CV_BGR2HSV);    resultGray = Mat(hsvImage.rows, hsvImage.cols,CV_8U,cv::Scalar(255));      resultColor = Mat(hsvImage.rows, hsvImage.cols,CV_8UC3,cv::Scalar(255, 255, 255));    double H=0.0,S=0.0,V=0.0;      for(int i=0;i<hsvImage.rows;i++)    {        for(int j=0;j<hsvImage.cols;j++)        {            H=hsvImage.at<Vec3b>(i,j)[0];            S=hsvImage.at<Vec3b>(i,j)[1];            V=hsvImage.at<Vec3b>(i,j)[2];            if((S >= 70 && S<155) || (S >= 35 && S<65))            {                      if((  H>=0 && H < 24) && V >= 215)                {                    resultGray.at<uchar>(i,j)=0;                    resultColor.at<Vec3b>(i, j)[0] = inputImage.at<Vec3b>(i, j)[0];                    resultColor.at<Vec3b>(i, j)[1] = inputImage.at<Vec3b>(i, j)[1];                    resultColor.at<Vec3b>(i, j)[2] = inputImage.at<Vec3b>(i, j)[2];                }            }        }    } }
 
   
   
 
  • 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

得到如下结果:
opencv颜色过滤--------如何选择需要的颜色呢?

感觉红章这里过滤得太多了哦,那就继续调整吧,上面的结果告诉我们大致在哪一部分区间进行调整。

接着再截取那些过滤得不是特别好的地方进行实验,过滤条件修改为:

    S >= 35    &&    ((  H>=0 && H < 24) || H >= 140)
 
   
   
 
  • 1

  • 2

  • 3

这一回结果变为:
opencv颜色过滤--------如何选择需要的颜色呢?

这次结果在红章这部分很好,但是下面的线好像处理得不太好呢。
所以继续放宽条件。

    S >= 15    &&    ((  H>=0 && H < 24) || H >= 140)
 
   
   
 
  • 1

  • 2

  • 3

哈哈,这一回结果perfect啊!
opencv颜色过滤--------如何选择需要的颜色呢?

蓝色的查找

在做实验的时候,我发现蓝色特别好找,好神奇的。
蓝色部分H主要是在100~130之间。
我对下面这张图做实验
opencv颜色过滤--------如何选择需要的颜色呢?

    S >= 40    &&    (  H>=100 && H <= 130)
 
   
   
 
  • 1

  • 2

  • 3

opencv颜色过滤--------如何选择需要的颜色呢?
说起来S还是蛮影响结果的,如果我S设小一些,那么结果就出现很多噪声了。
比如说S设置>=10,然后结果就变成了
opencv颜色过滤--------如何选择需要的颜色呢?

黑色的去除

说起来黑色真心难去掉哦,我试了下发现黑色几乎遍布H的全部区域,如果用S和V作为限制条件,又不能取得太高了,要不然会把自己要的部分也给去掉。超级难哦。
比如对于下面这张图,我首先用S>35作为条件进行过滤。
opencv颜色过滤--------如何选择需要的颜色呢?
结果如下:

可以看到图片上边缘部分还是有些奇怪的边留下,此时再把V加上,条件变为S>35&&V>190,得到结果如下,哈哈,把那条边给去掉啦:

我给出的条件是根据我这张图定的,具体问题具体分析,阈值的调整还是需要自己根据手里的图片慢慢来选择。


以上是关于opencv颜色过滤--------如何选择需要的颜色呢?的主要内容,如果未能解决你的问题,请参考以下文章

opencv之颜色过滤只留下图片中的红色区域

如何在OpenCV中为InRange阈值选择颜色的最佳HSV值

使用`cv::inRange` (OpenCV) 为颜色检测选择正确的 HSV 上下边界

opencv如何读取多边形区域内的像素值?

检测图像中的一团颜色

如何使用 OpenCV 减少图像中的颜色数量?