红色镶边交通标志的颜色阈值

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

我遇到了检测红色对象的问题 ,答案是使用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,我尝试过,但我发现不可能进行按位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=H && H=S && S=V && V=H && H=S && S=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); Imgproc.cvtColor(rgb, hsv, Imgproc.COLOR_RGB2HSV); 

OpenCV C ++ API参考通常是最完整和最详细的,因此引用它永远不会受到伤害。 如果你看一下cv::imread你会注意到以下注释:

在彩色图像的情况下,解码图像将具有以BGR顺序存储的通道。

但是,在您的代码中,您将图像视为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!"); } } 

从样本输入图像中生成以下组合蒙版:

组合面具

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

蒙面的图像

我已经学习了OpenCV for C++/Python ,而且我现在正在学习OpenCV for Java ,并且发现这个问题a good practice for pixel loopa good practice for pixel loop

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


正如@DanMaš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=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) 

这是我的结果:

在此处输入图像描述