java - 红边交通标志的颜色阈值
问题描述
我想检测所有红边交通标志(三角形和圆形)。该算法必须高效且稳健,才能在现实世界的情况下工作,所以我决定使用 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<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);
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!");
}
}
从您的示例输入图像中生成以下组合掩码:
如果我们在原始图像上使用这个掩码,我们可以看到我们确实得到了红色位:
推荐阅读
- c++ - 这段代码不能正常工作,为什么它对我来说没有任何意义。有谁猜出问题可能是什么?
- javascript - 服务工作者可以在提取处理程序中使用 waitUntil 处理并发请求吗?
- html - 为什么当我更改数字时我的媒体查询不起作用?
- sockets - 如何了解如何与网络设备通信
- javascript - 如何在 Firebase 事务中循环?
- python - 如何使用循环加快计算速度?Python
- java - 在 Flutter 中使用等效的 Java Cipher
- palantir-foundry - 您如何在 Foundry Contour 中得出星期几?
- css - CSS Grid:让最后一个自动放置的孩子填满剩余的列
- reactjs - 如何使用 Jest 模拟第三方 React 组件?