我想检测所有红色镶边交通标志(三角形和圆形)。该算法必须高效且稳健,才能在实际情况下工作,所以我决定使用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);
}
}
}
以下是阈值处理之前和之后的结果
有人能为我提供正确的价值吗?
答案 0 :(得分:2)
关键错误恰好在一开始就是:
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!");
}
}
从样本输入图像中生成以下组合蒙版:
如果我们在原始图像上使用此蒙版,我们可以看到我们确实得到了红色位:
答案 1 :(得分:0)
我已经学会了OpenCV for C++/Python
,而我现在正在学习OpenCV for Java
,并且发现这个问题是a good practice for pixel loop
。
我使用OpenCV 4.0.0-pre
,因此某些功能可能在其他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<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)
这是我的结果: