Reputation: 1478
Currently I am developing an app, which will detect circles from photos. I have managed to write a code for this, but it either does false negatives or false positives, if I get phone a bit away from PC screen. How can I improve the result?I mean, there are lots of apps that detect small and unclear circles.
[Update]
I'm fiddling with values in GaussianBlur
and HoughCircles
. Changing
Imgproc.GaussianBlur(grayMat, grayMat, new Size(9, 9), 2, 2);
to Imgproc.GaussianBlur(grayMat, grayMat, new Size(9, 9), 9, 9);
and double param1 = 70, param2 = 72;
to double param1 = 50, param2 = 52;
improve the result, but not enough.
Mat mat = new Mat(bitmap.getWidth(), bitmap.getHeight(),
CvType.CV_8UC1);
Mat grayMat = new Mat(bitmap.getWidth(), bitmap.getHeight(),
CvType.CV_8UC1);
Utils.bitmapToMat(bitmap, mat);
int colorChannels = (mat.channels() == 3) ? Imgproc.COLOR_BGR2GRAY
: ((mat.channels() == 4) ? Imgproc.COLOR_BGRA2GRAY : 1);
Imgproc.cvtColor(mat, grayMat, colorChannels);
Imgproc.GaussianBlur(grayMat, grayMat, new Size(9, 9), 2, 2);
// accumulator value
double dp = 1.2d;
// minimum distance between the center coordinates of detected circles in pixels
double minDist = 100;
int minRadius = 0, maxRadius = 0;
double param1 = 70, param2 = 72;
Mat circles = new Mat(bitmap.getWidth(),
bitmap.getHeight(), CvType.CV_8UC1);
Imgproc.HoughCircles(grayMat, circles,
Imgproc.CV_HOUGH_GRADIENT, dp, minDist, param1,
param2, minRadius, maxRadius);
int numberOfCircles = 9;
if (numberOfCircles > circles.cols()){
numberOfCircles = circles.cols();
}
for (int i=0; i<numberOfCircles; i++) {
double[] circleCoordinates = circles.get(0, i);
if(circleCoordinates == null){
break;
}
int x = (int) circleCoordinates[0], y = (int) circleCoordinates[1];
Point center = new Point(x, y);
android.graphics.Point centerC = new android.graphics.Point(x, y);
int radius = (int) circleCoordinates[2];
Core.circle(mat, center, radius, new Scalar(0,
255, 0), 4);
Core.rectangle(mat, new Point(x - 5, y - 5),
new Point(x + 5, y + 5),
new Scalar(0, 128, 255), -1);
Thanks in advance.
Right now I use those A-shaped points to test the code, but I want to detect even smaller circles on the photo.
Upvotes: 6
Views: 1027
Reputation: 1478
I think, if you want to detect only white circles, you need to implement color detection. Not only it will greatly improve detection quality, but remove a lot of false positives and negatives. Using color detection is pretty simple, since it is already present in OpenCV. For this use Core.inRange
function. You can find more about it here. But probably the best thing for you would be follow this tutorial. It was written in Python, but it's understandable and you need to change only few lines to get it work for android. Hope this helps :)
Upvotes: 1
Reputation: 5363
Try this.
Mat input = new Mat();
Mat rgb = new Mat();
Mat output = new Mat();
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentImagePath, bitOpts);
input = Utils.bitmapToMat(bitmap);
FeatureDetector fast = FeatureDetector.create(FeatureDetector.FAST);
List<KeyPoint> keypoints = new ArrayList<KeyPoint>();
fast.detect(input, keypoints);
Imgproc.cvtColor(input, rgb, Imgproc.COLOR_RGBA2RGB);
Features2d.drawKeypoints(rgb, keypoints, rgb);
Imgproc.cvtColor(rgb, output, Imgproc.COLOR_RGB2RGBA);
Utils.matToBitmap(output, bitmap);
//SHOW IMAGE
mImageView.setImageBitmap(bitmap);
input.release();
rgb.release();
output.release();
Upvotes: 0
Reputation: 706
In general, making param1 and param2 smaller will relax the threshold for recognizing circles, so there will be more false positives [detecting a circle where there is none] and fewer false negatives [not detecting a circle where there is one].
Param2 sets the circle-detection threshold. If the number is smaller, more circles in general will be detected.
Param1 sets the sensitivity threshold for the Canny edge detector. If the number is smaller, fainter circles are more likely to be detected than with a bigger number.
As with anything in computer vision or machine learning, if it's not working reliably and you don't know why, try changing some parameters. :-D
The crash when there are fewer than nine circles comes from hardcoding the number of circles to nine. When there are fewer than nine circles, the program tries to access beyond the bounds of the Mat array.
I don't have a system to test some OpenCV at the moment, but I can write up some code. My only caveat is that you might need circles.rows
instead of circles.cols
. Edit: also, circles is null if it has 0 entries.
int numberOfCircles = 9;
if (circles == null) numberOfCircles = 0;
else if (numberOfCircles > circles.cols) numberOfCircles = circles.cols;
for (int i=0; i<numberOfCircles; i++) {
double[] circleCoordinates = circles.get(0, i);
...
Upvotes: 0