Error using HoughCircles with 3-channel input
Asked Answered
S

2

5

Before detecting circles, I am replacing the red channel with the green channel. After replacing the channel, I pass it through a blur filter and then do a Hough transform to detect circles. But as I do this, I get a crappy error message:

OpenCV(3.4.1) Error: Assertion failed (!_image.empty() && _image.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3)) && (_image.isMat() || 

_image.isUMat())) in HoughCircles, file /io/opencv/modules/imgproc/src/hough.cpp, line 1659
Traceback (most recent call last):

  File "circle_light.py", line 44, in <module>
    param1=param1,param2=param2,minRadius=minRadius,maxRadius=maxRadius)

cv2.error: OpenCV(3.4.1) /io/opencv/modules/imgproc/src/hough.cpp:1659: error: (-215) !_image.empty() 

&& _image.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3)) && (_image.isMat() || _image.isUMat()) in function HoughCircles

I could not make any sense from it and thus could not understand what I could be doing incorrectly. Here is the snippet to what I did.

import cv2

img = cv2.imread("images/{}".format("img.png"), 1)

b,g,r = cv2.split(img)
img = cv2.merge([b,g,g])
img = cv2.GaussianBlur(img,(5,5),0)

minDist = 11
param1 = 20
param2 = 20
minRadius = 10
maxRadius = 20

circles = cv2.HoughCircles(
    img, cv2.HOUGH_GRADIENT, 1, minDist,
    param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius
)

The error happens when I call HoughCircles function. What is it that I might be doing incorrectly?

Steffi answered 26/1, 2019 at 16:42 Comment(1)
In case you are interested, I explain the error message in my answer and I provide a little bit more of information on why cv2.HoughCircles() uses single-channel inputs. The answer is long, but it is your fault: your question made me curious :)Nonintervention
N
5

About interpreting the error. It comes from hough.cpp#L1659:

CV_Assert(!_image.empty() && _image.type() == CV_8UC1 && (_image.isMat() || _image.isUMat()));

Breaking it down, all the following conditions have to be true:

  • !_image.empty(): the input image should not be empty;
  • _image.type() == CV_8UC1: the input image must be 8U (8-bit unsigned, np.uint8) and C1 (single-channel);
  • _image.isMat() || _image.isUMat(): check if the input is Mat or UMat (in Python, it has to be a numpy array);

Regarding your specific error message (error: (-215) !_image.empty() && _image.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3)) && (_image.isMat() || _image.isUMat())):


I'll try to complement @Mark Setchell's answer, simply because I was curious and I want to share :)

If you look at the documentation, cv2.HoughCircle() is part of the imgproc module (under the Feature Detection "submodule"). The documentation says that the only implemented method is the HOUGH_GRADIENT (aka 21HT, i.e., Two stage Hough Transform), and they point out to the reference paper "Comparative study of Hough Transform methods for circle finding" (1990) :). If you cannot access because of the paywall, you can access the 1989's version for free). In the paper, the authors comment:

The HT method of shape analysis uses a constraint equation relating points in a feature space to possible parameter values of the searched for shape. For each feature point, invariably edge points, votes are accumulated for all parameter combinations which satisfy the constraint. [...]

Later, they write:

If edge direction information is available, then one way to reduce the storage and computational demands of circle finding is to decompose the problem into two stages [...]

Therefore, if you want to stick to the 21HT, you basically need both edges and edge direction information. For instance, you could get the edge direction information via Sobel (e.g., dx and dy), and use these already computed dx and dy to get the edges using Canny. In fact, this is what the OpenCV implementation does. If you navigate to modules/imgproc/src/hough.cpp, you can see the Sobel+Sobel+Canny operations here.

So, what? Well, it means that if you have another method (or you want to propose a new one, why not?) that is able to return edges and edge direction information that are better suited for your case (maybe the colors have a different meaning in your case), then you can just replace these 3 lines (Sobel+Sobel+Canny) with your method and re-use the rest of the implementation (cool, huh?). If you are feeling inspired :), you can take a look at "A Short History of Color Edge Detection" and start from there.

Then, why do we need single-channel inputs? Well, basically because we need edges, and they are usually represented as single-channel images. In addition, the implementation only supports single-channel edges and edge direction information so far. However, most of these concepts could be extended to multi-channel inputs. I think that, because there are no generic solutions (probably these concepts change in a case-by-case basis) and very few people would benefit from, no one bothered to provide any implementation so far.

Sorry for the long answer. I know the TL;DR "the method requires single-channel input" is enough. I got curious and wanted to share =]

Nonintervention answered 26/1, 2019 at 21:3 Comment(0)
L
6

You can only call cv2.HoughCircles() on a single-channel (greyscale) image, your image has 3 channels.

Documentation.

Lithotrity answered 26/1, 2019 at 17:36 Comment(4)
What could be the reason behind this?Steffi
I guess people normally find edges first, which yields a greyscale image, then look for edges that make up circles.Lithotrity
@MarkSetchell It is important to note that the cv2.HoughCircle() will, internally, apply Sobel and Canny to get the edges and edge information needed by the 21HT algorithm. I provide some details (to complement your answer) on the second part of my answer. I hope you don't mind.Nonintervention
@Nonintervention I'm always happy to learn something new - so thank you.Lithotrity
N
5

About interpreting the error. It comes from hough.cpp#L1659:

CV_Assert(!_image.empty() && _image.type() == CV_8UC1 && (_image.isMat() || _image.isUMat()));

Breaking it down, all the following conditions have to be true:

  • !_image.empty(): the input image should not be empty;
  • _image.type() == CV_8UC1: the input image must be 8U (8-bit unsigned, np.uint8) and C1 (single-channel);
  • _image.isMat() || _image.isUMat(): check if the input is Mat or UMat (in Python, it has to be a numpy array);

Regarding your specific error message (error: (-215) !_image.empty() && _image.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3)) && (_image.isMat() || _image.isUMat())):


I'll try to complement @Mark Setchell's answer, simply because I was curious and I want to share :)

If you look at the documentation, cv2.HoughCircle() is part of the imgproc module (under the Feature Detection "submodule"). The documentation says that the only implemented method is the HOUGH_GRADIENT (aka 21HT, i.e., Two stage Hough Transform), and they point out to the reference paper "Comparative study of Hough Transform methods for circle finding" (1990) :). If you cannot access because of the paywall, you can access the 1989's version for free). In the paper, the authors comment:

The HT method of shape analysis uses a constraint equation relating points in a feature space to possible parameter values of the searched for shape. For each feature point, invariably edge points, votes are accumulated for all parameter combinations which satisfy the constraint. [...]

Later, they write:

If edge direction information is available, then one way to reduce the storage and computational demands of circle finding is to decompose the problem into two stages [...]

Therefore, if you want to stick to the 21HT, you basically need both edges and edge direction information. For instance, you could get the edge direction information via Sobel (e.g., dx and dy), and use these already computed dx and dy to get the edges using Canny. In fact, this is what the OpenCV implementation does. If you navigate to modules/imgproc/src/hough.cpp, you can see the Sobel+Sobel+Canny operations here.

So, what? Well, it means that if you have another method (or you want to propose a new one, why not?) that is able to return edges and edge direction information that are better suited for your case (maybe the colors have a different meaning in your case), then you can just replace these 3 lines (Sobel+Sobel+Canny) with your method and re-use the rest of the implementation (cool, huh?). If you are feeling inspired :), you can take a look at "A Short History of Color Edge Detection" and start from there.

Then, why do we need single-channel inputs? Well, basically because we need edges, and they are usually represented as single-channel images. In addition, the implementation only supports single-channel edges and edge direction information so far. However, most of these concepts could be extended to multi-channel inputs. I think that, because there are no generic solutions (probably these concepts change in a case-by-case basis) and very few people would benefit from, no one bothered to provide any implementation so far.

Sorry for the long answer. I know the TL;DR "the method requires single-channel input" is enough. I got curious and wanted to share =]

Nonintervention answered 26/1, 2019 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.