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

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

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

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

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

参照
http://www.xuebuyuan.com/2149290.html
里边的说明,它有如下结论
这里写图片描述

所以红色区域为

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

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

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

结果变为了
这里写图片描述
效果真棒哦!

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

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

颜色直方图

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;
}

比如说对于以下这幅图,我想要只保留图中的红色部分。
这里写图片描述

于是我在画图中打开它,然后选择了一小块红色区域裁剪并保存为新图片,如下图所示:
这里写图片描述
之后我对这一小块区域进行直方图测试,得到以下结果
这里写图片描述

颜色过滤结果

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

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];
                }

            }
        }
    }
}

得到如下结果:
这里写图片描述

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

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

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

这一回结果变为:
这里写图片描述

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

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

哈哈,这一回结果perfect啊!
这里写图片描述

蓝色的查找

在做实验的时候,我发现蓝色特别好找,好神奇的。
蓝色部分H主要是在100~130之间。
我对下面这张图做实验
这里写图片描述

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

这里写图片描述
说起来S还是蛮影响结果的,如果我S设小一些,那么结果就出现很多噪声了。
比如说S设置>=10,然后结果就变成了
这里写图片描述

黑色的去除

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

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

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

  • 32
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
使用Java OpenCV进行颜色过滤的步骤如下: 1. 加载图像:使用`Imgcodecs.imread()`函数加载图像。 ```java Mat image = Imgcodecs.imread("path/to/image.jpg"); ``` 2. 转换颜色空间:将图像从BGR颜色空间转换为HSV颜色空间。 ```java Mat hsvImage = new Mat(); Imgproc.cvtColor(image, hsvImage, Imgproc.COLOR_BGR2HSV); ``` 3. 定义颜色范围:根据需要过滤颜色定义颜色范围,例如,要过滤红色,可以定义红色的下限和上限。 ```java Scalar lowerColorRange = new Scalar(0, 100, 100); Scalar upperColorRange = new Scalar(10, 255, 255); ``` 4. 过滤颜色:使用`Core.inRange()`函数过滤颜色并生成掩码图像。 ```java Mat mask = new Mat(); Core.inRange(hsvImage, lowerColorRange, upperColorRange, mask); ``` 5. 应用掩码:使用`Core.bitwise_and()`函数将掩码应用于原始图像以获取过滤后的图像。 ```java Mat filteredImage = new Mat(); Core.bitwise_and(image, image, filteredImage, mask); ``` 完整代码如下所示: ```java import org.opencv.core.*; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import org.opencv.core.Core; public class ColorFilter { public static void main(String[] args) { // Load image Mat image = Imgcodecs.imread("path/to/image.jpg"); // Convert to HSV color space Mat hsvImage = new Mat(); Imgproc.cvtColor(image, hsvImage, Imgproc.COLOR_BGR2HSV); // Define color range Scalar lowerColorRange = new Scalar(0, 100, 100); Scalar upperColorRange = new Scalar(10, 255, 255); // Filter color Mat mask = new Mat(); Core.inRange(hsvImage, lowerColorRange, upperColorRange, mask); // Apply mask Mat filteredImage = new Mat(); Core.bitwise_and(image, image, filteredImage, mask); // Display filtered image Imgcodecs.imwrite("path/to/filtered_image.jpg", filteredImage); } } ``` 注意,在运行Java OpenCV代码之前,需要正确配置OpenCV库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值