Pregunta

I'd like to detect an object using OpenCV that is distinctly different from other elements in the scene as it's gray. This is good because I can just run a test with R == G == B and it allows to be independent of luminosity, but doing it pixel by pixel is slow.

Is there a faster way to detect gray things? Maybe there's an OpenCV method that does the R == G == B test... cv2.inRange does color thresholding, it's not quite what I'm looking for.

¿Fue útil?

Solución

The fastest method I can find in Python is to use slicing to compare each channel. After a few test runs, this method is upwards of 200 times faster than two nested for-loops.

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

This will generate a binary image where gray objects are indicated by white pixels. If you do not need a binary image, but only a logical array where grey pixels are indicated by True values, this method gets even faster:

slices = np.bitwise_and(bg, gr)

Omitting the type cast and multiplication yields a method 500 times faster than nested loops.

Running this operation on this test image:

image with gray object

Gives the following result:

gray object detection mask

As you can see, the gray object is correctly detected.

Otros consejos

I'm surprised that such a simple check is slow, probably you are not coding it efficiently.

Here is a short piece of code that should do that for you. It optimal neither in speed nor in memory, but quite in number of lines of code :)

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

For the sake of it, here is a comparison with something that would be the fastest way to do it in my opinion (without parallelization)

#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);

}

Bench on an image:

Image resolution=3171x2179
Time using split: 0.06353s
Time using loop: 0.029044s
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top