【问题标题】:Color thresholding of red rimmed traffic signs红边交通标志的颜色阈值
【发布时间】:2018-10-11 10:09:25
【问题描述】:

我想检测所有红边交通标志(三角形和圆形)。该算法必须高效且稳健,才能在现实世界的情况下工作,因此我决定使用 HSV 空间,因为它是光不变的。

我遇到了这个检测红色物体的question,答案是将此值范围用于 HSV:代码在 C++ 中:

inRange(hsv, Scalar(0, 70, 50), Scalar(10, 255, 255), mask1);
inRange(hsv, Scalar(170, 70, 50), Scalar(180, 255, 255), mask2);

Mat1b mask = mask1 | mask2;

由于我使用的是 Java 的 OpenCV,我尝试了 that,但我发现无法进行按位操作 OR

所以我尝试手动实现它而不是使用 OpenCV。我还尝试了提供的相同红色值范围,遗憾的是结果很糟糕:

这是我的代码

Mat hsv = new Mat();
Mat rgb = Highgui.imread(scene, Highgui.CV_LOAD_IMAGE_COLOR);
Imgproc.cvtColor(rgb, hsv, Imgproc.COLOR_RGB2HSV);
Mat thresh = new Mat(hsv.size(), CvType.CV_8UC1);


for(int x=0;x<hsv.rows();x++){
    for(int y=0;y<hsv.cols();y++)
    {
        double[] data = hsv.get(x,y);

        double H = data[0];
        double S = data[1];
        double V = data[2];
        if((( 0.0>=H && H<=10.0) && (70.0>=S && S<=255.0) && (50.0>=V && V<=255.0)) || (( 170.0>=H && H<=180.0) && (70.0>=S && S<=255.0) && (50.0>=V && V<=255.0)) ) {

            thresh.put(x,y, 255);
        }
        else
        {
            thresh.put(x,y, 0);
        }
    }
}

这是阈值化前后的结果

谁能给我正确的价值观?

【问题讨论】:

  • Mat rgb = Highgui.imread(scene, Highgui.CV_LOAD_IMAGE_COLOR); 是的,那不是 RGB,那是 BGR。
  • 天哪,它有效 :)),TBH 太愚蠢了,很多人都没有注意到 BGRRGB,我还以为它们是同一回事。您能否回答这个问题以便我可以接受,还请提及它们之间是否存在重大差异。
  • 好了,让 JDK 运行(距离我上次编写 Java 已经 15 年左右)并测试了代码——似乎也可以使用 bitwise_or。 |好吧,我在opencv 上阅读了很多问题,这是我观察到的常见错误之一,所以这是我首先要寻找的东西之一。
  • 谢谢你:)))
  • 顺便说一句,HSV 色彩空间不是光不变的!显然,如果你有彩色光,颜色和你的阈值行为可能会改变!

标签: java c++ opencv image-processing


【解决方案1】:

关键的错误就在开头:

Mat rgb = Highgui.imread(scene, Highgui.CV_LOAD_IMAGE_COLOR);
Imgproc.cvtColor(rgb, hsv, Imgproc.COLOR_RGB2HSV);

OpenCV C++ API 参考通常是最完整和最详细的,因此参考它永远不会有坏处。如果您查看cv::imread,您会注意到以下注释:

在彩色图像的情况下,解码后的图像将具有按B G R顺序存储的通道。

但是,在您的代码中,您将图像视为 RGB,即交换蓝色和红色。这对您的算法来说是致命的——您正在寻找红色的东西,但任何红色的东西实际上都是蓝色的。

解决方法很简单——将rgb 重命名为bgr(以避免误导变量名)并将转换代码更改为Imgproc.COLOR_BGR2HSV

我相信您之前与 bitwise_or 的问题只是此错误的另一个症状。 (我真的看不出它不起作用的原因)。

参见以下示例(使用 OpenCV 3.4.0):

import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.core.Core;

public class test
{
    public static void main(String[] args)
    {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

        Mat image = Imgcodecs.imread("test.jpg", Imgcodecs.CV_LOAD_IMAGE_COLOR);
        if ((image == null) || image.empty()) {
            System.out.println("Failed to load input image.");
            System.exit(-1);
        }

        Mat image_hsv = new Mat();
        Imgproc.cvtColor(image, image_hsv, Imgproc.COLOR_BGR2HSV);

        Mat mask1 = new Mat();
        Mat mask2 = new Mat();
        Core.inRange(image_hsv, new Scalar(0, 70, 50), new Scalar(10, 255, 255), mask1);
        Core.inRange(image_hsv, new Scalar(170, 70, 50), new Scalar(180, 255, 255), mask2);

        Mat mask_combined = new Mat();
        Core.bitwise_or(mask1, mask2, mask_combined);

        Mat image_masked = new Mat();
        Core.bitwise_and(image, image, image_masked, mask_combined);

        Imgcodecs.imwrite("test-mask.jpg", mask_combined);        
        Imgcodecs.imwrite("test-masked.jpg", image_masked);

        System.out.println("Done!");
    }
}

从您的示例输入图像中生成以下组合掩码:

如果我们在原始图像上使用这个掩码,我们可以看到我们确实得到了红色位:

【讨论】:

    【解决方案2】:

    学了OpenCV for C++/Python,这几天在学OpenCV for Java,发现这个问题是a good practice for pixel loop

    我用的是OpenCV 4.0.0-pre,所以有些功能可能在其他modules/packages/headers...


    正如@Dan Mašek 建议的那样,您应该使用COLOR_BGR2HSV 转换图像。除此之外,我还发现您编写了错误的 hsv 范围。

    对于这段 C++ 代码:

    inRange(hsv, Scalar(0, 70, 50), Scalar(10, 255, 255), mask1);
    inRange(hsv, Scalar(170, 70, 50), Scalar(180, 255, 255), mask2);
    
    Mat1b mask = mask1 | mask2;
    

    您在 Java 中的范围条件:

    (( 0.0>=H && H<=10.0) && (70.0>=S && S<=255.0) && (50.0>=V && V<=255.0)) ||
    (( 170.0>=H && H<=180.0) && (70.0>=S && S<=255.0) && (50.0>=V && V<=255.0)) 
    

    应该是:

    (( 0.0<=H && H<=10.0) && (70.0<=S && S<=255.0) && (50.0<=V && V<=255.0)) ||
    (( 170.0<=H && H<=180.0) && (70.0<=S && S<=255.0) && (50.0<=V && V<=255.0)) 
    

    或者这个:

    ///!(1) Create mask by loop with condition
    (( 0.0<=H && H<=10.0) || ( 170.0<=H && H<=180.0)) && (70.0<=S && S<=255.0) && (50.0<=V && V<=255.0)
    

    或者这个:

    ///!(2) Create mask by calling API
    Mat mask1 = new Mat();
    Mat mask2 = new Mat();
    Core.inRange(hsv, new Scalar(0, 70, 50), new Scalar(10, 255, 255), mask1);
    Core.inRange(hsv, new Scalar(170, 70, 50), new Scalar(180, 255, 255), mask2);
    
    Mat mask= new Mat();
    Core.bitwise_or(mask1, mask2, mask);
    

    在 Java 中:

    //! 2018.05.08 18:50:59 CST
    //! 2018.05.08 20:53:48 CST
    import org.opencv.imgcodecs.Imgcodecs;
    import org.opencv.imgproc.Imgproc;
    import org.opencv.core.*;
    import java.util.*;
    
    public class test
    {
        public static void main(String[] args)
        {
            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
            Mat img = Imgcodecs.imread("test.jpg", Imgcodecs.CV_LOAD_IMAGE_COLOR);
            if ((img == null) || img.empty()) {
                System.out.println("Failed to load input img.");
                System.exit(-1);
    
            }
            Mat hsv = new Mat();
            Imgproc.cvtColor(img, hsv, Imgproc.COLOR_BGR2HSV);
    
            ///! (1) Create the mask by loop
            Mat mask = new Mat(hsv.size(), CvType.CV_8UC1, new Scalar(0,0,0));
            for(int i=0;i<hsv.rows();++i){
                for(int j=0;j<hsv.cols();++j){
                    double[] data = hsv.get(i,j);
                    double H = data[0];
                    double S = data[1];
                    double V = data[2];
                    //mask[np.where(((h<10) | ((h>=170) & (h<=180)) ) & ((s>=70) & (s<=255)) & ((v>=50) & (v<=255)) )] = 255
                    if((( 0.0<=H && H<=10.0) || ( 170.0<=H && H<=180.0)) && (70.0<=S && S<=255.0) && (50.0<=V && V<=255.0)) {
                        mask.put(i,j, 255);
                    }
                }
            }
    
            ///!(2) Create mask by calling API
            Mat mask1 = new Mat();
            Mat mask2 = new Mat();
            Core.inRange(hsv, new Scalar(0, 70, 50), new Scalar(10, 255, 255), mask1);
            Core.inRange(hsv, new Scalar(170, 70, 50), new Scalar(180, 255, 255), mask2);
    
            Mat mask_combined = new Mat();
            Core.bitwise_or(mask1, mask2, mask_combined);
    
            ///! Get the masked
            Mat masked = new Mat();
            Core.bitwise_and(img, img, masked, mask);
            //Core.bitwise_and(img, img, masked, mask_combined);
    
            Imgcodecs.imwrite("test_mask.jpg", mask);
            Imgcodecs.imwrite("test_masked.jpg", masked);
        }
    }
    

    在Python中可以写成:

    import cv2
    import numpy as np
    
    img = cv2.imread("test.jpg")
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h,s,v = cv2.split(hsv)
    
    ## (1) by hand 
    mask = np.zeros_like(s)
    mask[np.where(((h<10) | ((h>=170) & (h<=180)) ) & ((s>=70) & (s<=255)) & ((v>=50) & (v<=255)) )] = 255
    
    ## (2) call api 
    mask1 = cv2.inRange(hsv, (0, 70, 50), (10, 255, 255))
    mask2 = cv2.inRange(hsv, (170, 70, 50), (180, 255, 255))
    mask = cv2.bitwise_or(mask1, mask2)
    
    masked = cv2.bitwise_and(img, img, mask=mask)
    

    这是我的结果:

    【讨论】:

      猜你喜欢
      • 2016-11-15
      • 2019-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多