Ask Your Question
1

OpenCV CUDA - method that works like inRange()

asked 2016-04-01 02:41:46 -0600

Graver gravatar image

updated 2016-04-01 03:03:01 -0600

berak gravatar image

I have a image in BGR color space and I convert it in HSV color space. I want to find a method that works on GPU like inRange works on CPU to binarize my image and obtain a threshold. Afer the conversion in HSV colorspace I make a split in three channels. After that I apply threshold() on every channel and finaly call function merge().

The problem is that in the last step i don't get the binary image. The image is colored.

Here is my code

gpu::cvtColor(myFrame_device, myFrameHSV_device, COLOR_BGR2HSV);
gpu::GpuMat channels_device[3];
gpu::GpuMat channels_device_dest[3];
gpu::split(myFrameHSV_device, channels_device);
//Mat myFrameEqualizedHSV(myFrameHSV_device);
//threshold HSV
gpu::threshold(channels_device[0], channels_device_dest[0], 0, 225, THRESH_BINARY);
gpu::threshold(channels_device[1], channels_device_dest[1], 75, 225, THRESH_BINARY);
gpu::threshold(channels_device[2], channels_device_dest[2], 0, 225, THRESH_BINARY);
gpu::merge(channels_device_dest,3, threshold_device);
edit retag flag offensive close merge delete

3 answers

Sort by ยป oldest newest most voted
1

answered 2019-09-27 23:52:52 -0600

kdupreez gravatar image

updated 2019-09-27 23:58:09 -0600

Just an update to this code, as of today, the InRange function still does not exist for GPU.

The above code only does a threshold, i.e. it only evaluates a minimum floor value and ignores the ceiling..

There are two reasons, one is that the threshold function does not use a ceiling, only a floor.. the "maxval" parameter is actually what binary threshold wil use to replace the current value when threshold is met..

For example: a value of 23 with at a threshold of 50 and maxval of 100 will become 0. a value of 57 with a threshold of 50 and maxval of 100 will become 100 a value of 200 with a threshold of 50 and maxal of 100 will become 100

Meaning that any input value that falls above the threshold will be replaced with the maxval..

So - In order to really implement an "InRange" function you need to zero out values that are below your floor AND above your ceiling and then make everything within that range = maxval

The way to do this is to do use two thresholds, one for floor and one for ceiling and then only keep the data that falls between the two.

Like this:

//low and high ranges
cv::Scalar hsv_low(20, 70, 120);
cv::Scalar hsv_high(50, 200, 255);

//read source from file
cv::Mat src_img = cv::imread("myfile");

//upload img to gpu
cv::cuda::GpuMat src_img_gpu(src_img);

//all the parts to keep low and high thresholds
cv::cuda::GpuMat mat_parts[3];
cv::cuda::GpuMat mat_parts_low[3];
cv::cuda::GpuMat mat_parts_high[3];

//split into 3 channels H, S and V
cv::cuda::split(src_img_gpu, mat_parts);

//Use the inverse binary of upper threshold with bitwise_and to remove above limit values from lower threshold.

//find range between high and low values of H
cv::cuda::threshold(mat_parts[0], mat_parts_low[0], hsv_low[0], MAX_UCHAR, cv::THRESH_BINARY);
cv::cuda::threshold(mat_parts[0], mat_parts_high[0],  hsv_high[0], MAX_UCHAR, cv::THRESH_BINARY_INV);
cv::cuda::bitwise_and(mat_parts_high[0], mat_parts_low[0], mat_parts[0]);

//find range between high and low values of S
cv::cuda::threshold(mat_parts[1], mat_parts_low[1], hsv_low[1], MAX_UCHAR, cv::THRESH_BINARY);
cv::cuda::threshold(mat_parts[1], mat_parts_high[1],  hsv_high[1], MAX_UCHAR, cv::THRESH_BINARY_INV);
cv::cuda::bitwise_and(mat_parts_high[1], mat_parts_low[1], mat_parts[1]);

//find range between high and low values of V
cv::cuda::threshold(mat_parts[2], mat_parts_low[2], hsv_low[2], MAX_UCHAR, cv::THRESH_BINARY);
cv::cuda::threshold(mat_parts[2], mat_parts_high[2],  hsv_high[2], MAX_UCHAR, cv::THRESH_BINARY_INV);
cv::cuda::bitwise_and(mat_parts_high[2], mat_parts_low[2], mat_parts[2]);

cv::cuda::GpuMat tmp1, final_result;

//bitwise AND all channels.
cv::cuda::bitwise_and(mat_parts[0], mat_parts[1], tmp1);
cv::cuda::bitwise_and(tmp1, mat_parts[2], final_result);

cv::Mat img_final(final_result);
edit flag offensive delete link more
1

answered 2016-04-01 03:06:28 -0600

That is normal: merge creates a multi-channels image from multiple images. (see the doc here). The inRange function creates a binary mask, and I assume, according to your title, that is what you want to do. The inRange function does not exist (yet) in GPU, therefore, you have two options:

  1. Implement the GPU version of inRange (make a pull request and make the OpenCV community happy). Here is some info if you want to contribute to OpenCV.
  2. Download the merged image and to the inRange on CPU, eventually upload the result to GPU if you still need to process the image after that. This option is not suitable if you have to do some other process to the GPU after, as it could be slower than doing everything on CPU.
edit flag offensive delete link more

Comments

Hello!

I need only to detect some squares in a image. The squares are found with canny(). The problem is that i need to binarize a HSV image. I tried to make the sum of the channels but now i have some gray in image.

Graver gravatar imageGraver ( 2016-04-01 04:01:51 -0600 )edit

Hi ! I am currently working on the same thing as described above and could'nt really figure out what to do exactly , hence faced similiar issues. I just wanted to ask if anybody could finally get a proper working implentation of it or not as I need it for my project work too. Graver, were you able to do it? Please let me know , yes or no. Thanks!

sarthakahuja11 gravatar imagesarthakahuja11 ( 2016-09-29 14:17:10 -0600 )edit
0

answered 2017-08-20 23:10:05 -0600

As mentioned before "merge creates a multi-channels image ". The right approach would be using gpu::bitwise_and() function twice on the three threshold channels to get a binary single channel. I faced the same problem and came across your post and I assume many will face the same problem since there is no gpu::inrange() function. Here is the code I have implemented.

gpu::GpuMat imgpu;
gpu::GpuMat hsv;
gpu::GpuMat shsv[3];
gpu::GpuMat thresc[3];
gpu::GpuMat temp;
gpu::GpuMat thres;

//Transform image to HSV
gpu::cvtColor(imggpu, hsv, COLOR_BGR2HSV);

//Split HSV 3 channels
gpu::split(hsv, shsv);

//Threshold HSV channels
gpu::threshold(shsv[0], thresc[0], 45, 90, THRESH_BINARY);
gpu::threshold(shsv[1], thresc[1], 100, 225, THRESH_BINARY);
gpu::threshold(shsv[2], thresc[2], 20, 225, THRESH_BINARY);

//Bitwise AND the channels
gpu::bitwise_and(thresc[0], thresc[1],temp);
gpu::bitwise_and(temp, thresc[2], thres);
edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2016-04-01 02:41:46 -0600

Seen: 4,255 times

Last updated: Sep 27 '19