使用OpenCV检测灰色物体

5 投票
2 回答
4171 浏览
提问于 2025-04-18 06:40

我想用OpenCV来检测一个在场景中与其他元素明显不同的物体,因为它是灰色的。这很好,因为我可以通过检查红色、绿色和蓝色的值是否相等(R == G == B)来进行测试,这样就不受亮度的影响,但逐个像素检查的速度太慢了。

有没有更快的方法来检测灰色的东西?也许OpenCV中有一个方法可以直接做R == G == B的测试……cv2.inRange是用来做颜色阈值处理的,但这不是我想要的。

2 个回答

1

我很惊讶,像这样简单的检查居然会这么慢,可能是你的代码写得不够高效。

这里有一段简短的代码,可以帮你完成这个任务。虽然在速度和内存使用上都不是最优的,但代码行数倒是很少 :)

std::vector<cv::Mat> planes;
cv::split(image, planes);
cv::Mat mask = planes[0] == planes[1];
mask &= planes[1] == planes[2];

为了说明这个问题,这里有一个对比,展示我认为最快的方法(不使用并行处理)

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <iostream>
#include <vector>

#include <sys/time.h> //gettimeofday

static
double
P_ellapsedTime(struct timeval t0, struct timeval t1)
{
  //return ellapsed time in seconds
  return (t1.tv_sec-t0.tv_sec)*1.0 + (t1.tv_usec-t0.tv_usec)/1000000.0;
}



int
main(int argc, char* argv[])
{


  struct timeval t0, t1;
  cv::Mat image = cv::imread(argv[1]);
  assert(image.type() == CV_8UC3);
  std::vector<cv::Mat> planes;
  std::cout << "Image resolution=" << image.rows << "x" << image.cols << std::endl;
  gettimeofday(&t0, NULL);
  cv::split(image, planes);
  cv::Mat mask = planes[0] == planes[1];
  mask &= planes[1] == planes[2];
  gettimeofday(&t1, NULL);
  std::cout << "Time using split: " << P_ellapsedTime(t0, t1) << "s" << std::endl;

  cv::Mat mask2 = cv::Mat::zeros(image.size(), CV_8U);
  unsigned char *imgBuf = image.data;
  unsigned char *maskBuf = mask2.data;
  gettimeofday(&t0, NULL);
  for (; imgBuf != image.dataend; imgBuf += 3, maskBuf++)
    *maskBuf = (imgBuf[0] == imgBuf[1] && imgBuf[1] == imgBuf[2]) ? 255 : 0;
  gettimeofday(&t1, NULL);
  std::cout << "Time using loop: " << P_ellapsedTime(t0, t1) << "s" << std::endl;

  cv::namedWindow("orig", 0);
  cv::imshow("orig", image);
  cv::namedWindow("mask", 0);
  cv::imshow("mask", mask);
  cv::namedWindow("mask2", 0);
  cv::imshow("mask2", mask2);
  cv::waitKey(0);

}

在一张图片上进行基准测试:

Image resolution=3171x2179
Time using split: 0.06353s
Time using loop: 0.029044s
9

在Python中,我找到的最快方法是使用切片来比较每个通道。经过几次测试,这种方法比用两个嵌套的for循环快了200倍以上。

bg = im[:,:,0] == im[:,:,1] # B == G
gr = im[:,:,1] == im[:,:,2] # G == R
slices = np.bitwise_and(bg, gr, dtype= np.uint8) * 255

这样会生成一个二值图像,其中灰色物体用白色像素表示。如果你不需要二值图像,只想要一个逻辑数组,灰色像素用True值表示,这种方法会更快:

slices = np.bitwise_and(bg, gr)

省略类型转换和乘法后,这种方法比嵌套循环快了500倍。

在这个测试图像上运行这个操作:

image with gray object

得到的结果是:

gray object detection mask

如你所见,灰色物体被正确检测出来了。

撰写回答