I have 4 points. For example...
A(1;1)
B(2;5)
C(4;4)
D(3;2)
How I can change RGB parameters in this rectangle (for all pixels)?
Like this:
double[] data = mat.get(x, y);
data[0] = data[0]+30;
data[1] = data[1]+20;
data[2] = data[2]+10;
mat.put(x, y, data);
Try something like that for implementing approach described in Dan MaĊĦek comment:
...
Bitmap sourceBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.<your_image>);
Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(sourceBitmap, sourceMat);
Mat maskMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC4);
Mat resultMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC4);
// create color, which added to sourceMat region (+100 - for red channel)
Scalar color = new Scalar(100, 0, 0, 255);
// or you can try Scalar color = new Scalar(10, 20, 30); as in your question
Point[] region = new Point[4];
// your coords multiplied by 50 for visualization convenience
region[0] = new Point(50, 50);
region[1] = new Point(100, 250);
region[2] = new Point(200, 200);
region[3] = new Point(150, 100);
List<MatOfPoint> contours = new ArrayList();
MatOfPoint regionMat = new MatOfPoint(region);
contours.add(regionMat);
// create mask
Imgproc.drawContours(maskMat, contours, 0, color, -1);
// apply mask to source
Core.add(maskMat, sourceMat, resultMat);
// just for visualisation
Bitmap bitmap = Bitmap.createBitmap(sourceMat.cols(), sourceMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(resultMat, bitmap);
<your_ImageView>.setImageBitmap(bitmap);
...
NB! This is just example of masking, not optimized.
Edited answer due to comment below:
This is OpenCV C++ code but you can easily port it to JAVA. Also my code assumes the points always represent a rectangle.
// read image
Mat image=imread("image.jpg",-1);
// region of interest, shape=rectangle
Point p1(50,50), p2(100,80);
Rect roi(p1.x,p1.y,p2.x,p2.y);
// vector hold channels
std::vector<Mat> channels(3);
// split original image to bgr channels
cv::split(image, channels);
// Mat to hold ROI
Mat extractedRoi;
//For channel B
extractedRoi = channels.at(0)(roi);
extractedRoi += 30;
//For channel G
extractedRoi = channels.at(1)(roi);
extractedRoi += 20;
//For channel R
extractedRoi = channels.at(2)(roi);
extractedRoi += 10;
// merge channels back together
cv::merge(channels, image);
Edit2: A faster approach.
Mat image=imread("/home/haseebullah/Pictures/S1.jpg",-1);
Point p1(50,50), p2(100,80);
Rect roi(p1.x,p1.y,p2.x,p2.y);
Mat extractedRoi;
extractedRoi = image(roi);
Scalar constants(30,20,10);
extractedRoi += constants
Related
I have been trying to remove the black background from the grabcut output using python opencv.
import numpy as np
import cv2
img = cv2.imread(r'myfile_1.png')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (1,1,665,344)
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
cv2.imshow('img',img)
cv2.imwrite('img.png',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Above code I had written to save the grabcut output. Please suggest, How I can remove the black background and make it transparent?
I have achieved this by using the following snippet.
import cv2
file_name = "grab.png"
src = cv2.imread(file_name, 1)
tmp = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
_,alpha = cv2.threshold(tmp,0,255,cv2.THRESH_BINARY)
b, g, r = cv2.split(src)
rgba = [b,g,r, alpha]
dst = cv2.merge(rgba,4)
cv2.imwrite("test.png", dst)
This is java code. After use grabcut, the result background is transparent.
public Bitmap removeBackground(Bitmap bitmap) {
//GrabCut part
Mat img = new Mat();
Utils.bitmapToMat(bitmap, img);
int r = img.rows();
int c = img.cols();
Point p1 = new Point(c / 100, r / 100);
Point p2 = new Point(c - c / 100, r - r / 100);
Rect rect = new Rect(p1, p2);
Mat mask = new Mat();
Mat fgdModel = new Mat();
Mat bgdModel = new Mat();
Mat imgC3 = new Mat();
Imgproc.cvtColor(img, imgC3, Imgproc.COLOR_RGBA2RGB);
Imgproc.grabCut(imgC3, mask, rect, bgdModel, fgdModel, 5, Imgproc.
GC_INIT_WITH_RECT);
Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(3.0));
Core.compare(mask, source/* GC_PR_FGD */, mask, Core.CMP_EQ);
//This is important. You must use Scalar(255,255, 255,255), not Scalar(255,255,255)
Mat foreground = new Mat(img.size(), CvType.CV_8UC3, new Scalar(255,
255, 255,255));
img.copyTo(foreground, mask);
// convert matrix to output bitmap
bitmap = Bitmap.createBitmap((int) foreground.size().width,
(int) foreground.size().height,
Bitmap.Config.ARGB_8888);
Utils.matToBitmap(foreground, bitmap);
return bitmap;
}
I'm trying to get this rectangle from that image:
Found this solution using OpenCV:
private Bitmap findRectangle(Bitmap src) throws Exception {
Mat imageMat = new Mat();
Utils.bitmapToMat(src, imageMat);
Mat imgSource=imageMat.clone();
Imgproc.cvtColor(imgSource, imageMat, Imgproc.COLOR_BGR2GRAY);
//find the contours
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(imageMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);
Imgproc.Canny(imageMat,imageMat,0,255);
Bitmap canny=Bitmap.createBitmap(imageMat.cols(),imageMat.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(imageMat,canny);
Imgproc.GaussianBlur(imageMat, imageMat, new org.opencv.core.Size(1, 1), 2, 2);
Bitmap blur=Bitmap.createBitmap(imageMat.cols(),imageMat.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(imageMat,blur);
MatOfPoint temp_contour = contours.get(0); //the largest is at the index 0 for starting point
for (int idx = 0; idx < contours.size(); idx++) {
temp_contour = contours.get(idx);
//check if this contour is a square
MatOfPoint2f new_mat = new MatOfPoint2f( temp_contour.toArray() );
int contourSize = (int)temp_contour.total();
MatOfPoint2f approxCurve_temp = new MatOfPoint2f();
Imgproc.approxPolyDP(new_mat, approxCurve_temp, contourSize*0.05, true);
if (approxCurve_temp.total() == 4) {
MatOfPoint points = new MatOfPoint( approxCurve_temp.toArray() );
Rect rect = Imgproc.boundingRect(points);
Imgproc.rectangle(imgSource, new Point(rect.x,rect.y), new Point(rect.x+rect.width,rect.y+rect.height), new Scalar(255, 0, 0, 255), 3);
}
}
Bitmap analyzed=Bitmap.createBitmap(imgSource.cols(),imgSource.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(imgSource,analyzed);
return analyzed;
}
The best i got was this:
The problem is that the rectangle isn't perfect, maybe find the white numbers inside of that can be a best option, but i don't know too much of OpenCV.
Original image:
This is a very simple C++ implementation which tries to search for the text box. The accuracy of the detection depends on three parameters:
The thresh value provided to cv::threshold function to convert gray image to binary.
The height/width ratio, since the height of the text box is relatively smaller than the width, and the area of the text box.
Mat img = imread("image.jpg",-1), gray, binary;
/*pre-processing steps*/
uchar thresh = 80;
cvtColor(img, gray, cv::COLOR_BGR2GRAY);
GaussianBlur(gray, gray, Size(7,7), 0);
// change the thresh value to fine tune this program for your images
threshold(gray, binary, thresh, 255, cv::THRESH_BINARY_INV);
/*contour searching*/
std::vector<std::vector<Point>> contours;
std::vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
/*Filtering contours based on height/width ratio and bounding box area*/
std::vector<Rect> boxes;
double box_ratio = 0.3;
int box_area = 20000;
for(auto& cnt : contours)
{
auto box = minAreaRect(cnt).boundingRect();
// we are searching for a rectangle which a has relatively large area,
// and the height is smaller than the width, so the
// height/width ratio should be small. Change the these two values for fine tuning
if((min(box.width,box.height)/double(max(box.width,box.height)) < box_ratio) && box.area() > box_area )
{
boxes.push_back(box);
}
}
Mat txt_box = img(boxes.at(0));
Here is the almost same solution on java:
private Bitmap findRoi(Bitmap sourceBitmap) {
Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CV_8UC3);
Utils.bitmapToMat(sourceBitmap, sourceMat);
Mat grayMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CV_8UC3);
Imgproc.cvtColor(sourceMat, grayMat, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(grayMat, grayMat, 125, 200, Imgproc.THRESH_BINARY);
// find contours
List<MatOfPoint> whiteContours = new ArrayList<>();
Rect largestRect = null;
Imgproc.findContours(grayMat, whiteContours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// find appropriate bounding rectangles
for (MatOfPoint contour : whiteContours) {
RotatedRect boundingRect = Imgproc.minAreaRect(new MatOfPoint2f(contour.toArray()));
double rectangleArea = boundingRect.size.area();
// test min ROI area in pixels
if (rectangleArea > 10000) {
Point rotated_rect_points[] = new Point[4];
boundingRect.points(rotated_rect_points);
Rect rect = Imgproc.boundingRect(new MatOfPoint(rotated_rect_points));
// test horizontal ROI orientation and aspect ratio
if (rect.width > 3 * rect.height) {
if (largestRect == null) {
largestRect = rect;
} else {
if (rect.width > largestRect.width) {
largestRect = rect;
}
}
}
}
}
Mat roiMat = new Mat(sourceMat, largestRect);
Bitmap bitmap = Bitmap.createBitmap(roiMat.cols(), roiMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(roiMat, bitmap);
return bitmap;
}
Also, you can use additional information: red number places on the right.
In the picture below, the application detected multiple "black" and drawn a bounding rectangle around them. Now I want to compare the rect3.tl().y values of each rectangle and only keep the lowest one, deleting the other bounding rectangles. But I'm not sure how to go about doing that.
Code:
Rect rectBlack = new Rect();
Bitmap roiBitmap = null;
Scalar green = new Scalar(0, 255, 0, 255);
Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(sourceBitmap, sourceMat);
Mat roiTmp = sourceMat.clone();
bitmapWidth = sourceBitmap.getWidth();
Log.e("bitmapWidth", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
final Mat hsvMat = new Mat();
sourceMat.copyTo(hsvMat);
// convert mat to HSV format for Core.inRange()
Imgproc.cvtColor(hsvMat, hsvMat, Imgproc.COLOR_RGB2HSV);
Scalar lowerb = new Scalar(85, 50, 40); // lower color border for BLUE
Scalar upperb = new Scalar(135, 255, 255); // upper color border for BLUE
Scalar lowerblack = new Scalar(0, 0, 0); // lower color border for BLACK
Scalar upperblack = new Scalar(180, 255, 40); // upper color border for BLACK
Scalar testRunL = new Scalar(60, 50, 40); // lower Green 83 100 51
Scalar testRunU = new Scalar(90, 255, 255); // upper Green
Core.inRange(hsvMat, lowerblack, upperblack, roiTmp); // select only blue pixels
// find contours
List<MatOfPoint> contours = new ArrayList<>();
List<RotatedRect> boundingRects = new ArrayList<>();
Imgproc.findContours(roiTmp, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// find appropriate bounding rectangles
for (MatOfPoint contour : contours) {
MatOfPoint2f areaPoints = new MatOfPoint2f(contour.toArray());
RotatedRect boundingRect = Imgproc.minAreaRect(areaPoints);
double rectangleArea = boundingRect.size.area();
// test min ROI area in pixels
if (rectangleArea > 1300 ) {
Point rotated_rect_points[] = new Point[4];
boundingRect.points(rotated_rect_points);
Rect rect3 = Imgproc.boundingRect(new MatOfPoint(rotated_rect_points));
// test horizontal ROI orientation
if (rect3.height > rect3.width) {
Log.e("w,h", String.valueOf(rect3.width)+ " h " + String.valueOf(rect3.height));
double w = rect3.width;
double h = rect3.height;
double ratio= h/w;
Log.e("h:w ratio", String.valueOf(ratio));
Log.e("Black Area", String.valueOf(rect3.area()));
Imgproc.rectangle(sourceMat, rect3.tl(), rect3.br(), green, 3);
rectBlack = rect3;
Log.e("blackArea", String.valueOf(rect3.area()));
xBlack = rect3.br().x;
xBlackCenter = (rect3.br().x + rect3.tl().x) / 2;
yBlack = rect3.br().y;//bottom
battHeight = (rect3.br().y - rect3.tl().y); //batt height in pixel
}
}
}
You can create list of rects:
List<Rect> rects = new ArrayList<>();
then in your for (MatOfPoint contour : contours) loop add each founded rectangle to that list:
// find appropriate bounding rectangles
for (MatOfPoint contour : contours) {
...
// test horizontal ROI orientation
if (rect3.height > rect3.width) {
...
rects.add(rect3)
}
}
then use method to find bottom-most rectangle, like that:
public static Rect getBottomMostRect(List<Rect> rects) {
Rect bottomMostRect = null;
if (rects != null && rects.size() >= 1) {
Rect rect;
double minY;
int ixMinY = 0;
rect = rects.get(ixMinY);
minY = rect.tl().y;
for (int ix = 1; ix < rects.size(); ix++) {
rect = rects.get(ix);
if (rect.tl().y < minY) {
minY = rect.tl().y;
ixMinY = ix;
}
}
bottomMostRect = rects.get(ixMinY);
}
return bottomMostRect;
}
and call it this way:
Rect bottomMostRect = getBottomMostRect(rects)
Or add getBottomMostRect() implementation directly into your for (MatOfPoint contour : contours) loop.
I am trying to implement some image processing using OpenCV and Java to extract a card out of an image.
Following is my approach:
Convert to BGR image
Convert to GRAY image
Apply GaussianBlur
Apply Canny Edge detection
Dilate
Find contours
Find the largest contour
Find corners of the largest contour using approxPolyDP
Getting a top-down view of the cropped image along the largest contour
At step no 8, I am facing some issues, as I am not getting the appropriate corners/vertices. Following sample images shows the scenario :
The original Image
After edge detection and dilation. (What is to be done to get appropriate edges?? Here I've got broken edges. Could not get Hough transform working)
After finding vertices. (shown in green)
Following is the code :
System.loadLibrary( Core.NATIVE_LIBRARY_NAME );
//load Image
File input = new File("card4.png");
BufferedImage image = ImageIO.read(input);
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
//put read image to Mat
mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3); //original Mat
mat.put(0, 0, data);
mat_f = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3); //for storing manipulated Mat
//conversion to grayscale, blurring and edge detection
Imgproc.cvtColor(mat, mat_f, Imgproc.COLOR_RGB2BGR);
Imgproc.cvtColor(mat_f, mat_f, Imgproc.COLOR_RGB2GRAY);
Imgproc.GaussianBlur(mat_f, mat_f, new Size(13,13), 0);
Imgproc.Canny(mat_f, mat_f, 300, 600, 5, true);
Imgproc.dilate(mat_f, mat_f, new Mat(), new Point(-1, -1), 2);
Imgcodecs.imwrite("D:\\JAVA\\Image_Proc\\CVTest1.jpg",mat_f);
//finding contours
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(mat_f, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
double maxArea=0;
int maxAreaIdx=0;
//finding largest contour
for (int idx = 0; idx != contours.size(); ++idx)
{
Mat contour = contours.get(idx);
double contourarea = Imgproc.contourArea(contour);
if (contourarea > maxArea)
{
maxArea = contourarea;
maxAreaIdx = idx;
}
}
//Rect rect = Imgproc.boundingRect(contours.get(maxAreaIdx));
//Imgproc.rectangle(mat, new Point(rect.x,rect.y), new Point(rect.x+rect.width,rect.y+rect.height),new Scalar(0,0,255),7);
// mat = mat.submat(rect.y, rect.y + rect.height, rect.x, rect.x + rect.width);
//Polygon approximation
MatOfPoint2f approxCurve = new MatOfPoint2f();
MatOfPoint2f oriCurve = new MatOfPoint2f(contours.get(maxAreaIdx).toArray());
Imgproc.approxPolyDP(oriCurve, approxCurve, 6.0, true);
//drawing red markers at vertices
Point [] array = approxCurve.toArray();
for(int i=0; i < array.length;i++) {
Imgproc.circle(mat, array[i], 2, new Scalar(0, 255, 0), 5);
}
Imgcodecs.imwrite("D:\\JAVA\\Image_Proc\\CVTest.jpg",mat);
Seeking help in getting the appropriate corner vertices...
Thanks in advance..
In order to archive the good result using your approach then your cards have to contain 4 corners. But i prefer to use the HoughLine approach for this task.
Step 1: Resize image for higher performance
Step 2: Edges detection
Transform the image into gray scale
Blur image to clear noises
Edge detection using Canny filters
You can use the dilation for make the white bigger for the next step
Step 3: Find card's corners
Find contour of image
From the list of contour get the largest contour
Get the convexHull of it
Use approxPolyDP to simplify the convex hull (this should give a quadrilateral)
From now you can draw contour to get the rectangle after restore scale
From the quadrilateral you can get the 4 corners.
Find Homography
Warp the input image using the computed homography matrix
Here is sample code in Java
// STEP 1: Resize input image to img_proc to reduce computation
double ratio = DOWNSCALE_IMAGE_SIZE / Math.max(frame.width(), frame.height());
Size downscaledSize = new Size(frame.width() * ratio, frame.height() * ratio);
Mat dst = new Mat(downscaledSize, frame.type());
Imgproc.resize(frame, dst, downscaledSize);
Mat grayImage = new Mat();
Mat detectedEdges = new Mat();
// STEP 2: convert to grayscale
Imgproc.cvtColor(dst, grayImage, Imgproc.COLOR_BGR2GRAY);
// STEP 3: try to filter text inside document
Imgproc.medianBlur(grayImage, detectedEdges, 9);
// STEP 4: Edge detection
Mat edges = new Mat();
// Imgproc.erode(edges, edges, new Mat());
// Imgproc.dilate(edges, edges, new Mat(), new Point(-1, -1), 1); // 1
// canny detector, with ratio of lower:upper threshold of 3:1
Imgproc.Canny(detectedEdges, edges, this.threshold.getValue(), this.threshold.getValue() * 3, 3, true);
// STEP 5: makes the object in white bigger to join nearby lines
Imgproc.dilate(edges, edges, new Mat(), new Point(-1, -1), 1); // 1
Image imageToShow = Utils.mat2Image(edges);
updateImageView(cannyFrame, imageToShow);
// STEP 6: Compute the contours
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(edges, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// STEP 7: Sort the contours by length and only keep the largest one
MatOfPoint largestContour = getMaxContour(contours);
// STEP 8: Generate the convex hull of this contour
Mat convexHullMask = Mat.zeros(frame.rows(), frame.cols(), frame.type());
MatOfInt hullInt = new MatOfInt();
Imgproc.convexHull(largestContour, hullInt);
MatOfPoint hullPoint = OpenCVUtil.getNewContourFromIndices(largestContour, hullInt);
// STEP 9: Use approxPolyDP to simplify the convex hull (this should give a quadrilateral)
MatOfPoint2f polygon = new MatOfPoint2f();
Imgproc.approxPolyDP(OpenCVUtil.convert(hullPoint), polygon, 20, true);
List<MatOfPoint> tmp = new ArrayList<>();
tmp.add(OpenCVUtil.convert(polygon));
restoreScaleMatOfPoint(tmp, ratio);
Imgproc.drawContours(convexHullMask, tmp, 0, new Scalar(25, 25, 255), 2);
// Image extractImageToShow = Utils.mat2Image(convexHullMask);
// updateImageView(extractFrame, extractImageToShow);
MatOfPoint2f finalCorners = new MatOfPoint2f();
Point[] tmpPoints = polygon.toArray();
for (Point point : tmpPoints) {
point.x = point.x / ratio;
point.y = point.y / ratio;
}
finalCorners.fromArray(tmpPoints);
boolean clockwise = true;
double currentThreshold = this.threshold.getValue();
if (finalCorners.toArray().length == 4) {
Size size = getRectangleSize(finalCorners);
Mat result = Mat.zeros(size, frame.type());
// STEP 10: Homography: Use findHomography to find the affine transformation of your paper sheet
Mat homography = new Mat();
MatOfPoint2f dstPoints = new MatOfPoint2f();
Point[] arrDstPoints = { new Point(result.cols(), result.rows()), new Point(0, result.rows()), new Point(0, 0), new Point(result.cols(), 0) };
dstPoints.fromArray(arrDstPoints);
homography = Calib3d.findHomography(finalCorners, dstPoints);
// STEP 11: Warp the input image using the computed homography matrix
Imgproc.warpPerspective(frame, result, homography, size);
}
I'm new to OpenCV, and I want to work on object detection to help my FRC robotics team. I'm trying to use an HSV filter and HoughCircles to find a tennis ball in a webcam image and draw a circle around it. Here is my code:
Mat currentFrame = new Mat();
Mat hsv = new Mat();
Mat threshImage = new Mat();
Mat circles = new Mat();
while (true) {
camera.read(currentFrame);
Imgproc.resize(currentFrame, currentFrame, new Size(WIDTH, HEIGHT));
Imgproc.cvtColor(currentFrame, hsv, Imgproc.COLOR_RGB2HSV);
hsvWindow.showImage(hsv);
Core.inRange(hsv, new Scalar(50, 100, 0), new Scalar(95, 255, 255), threshImage);
threshWindow.showImage(threshImage);
Imgproc.HoughCircles(threshImage, circles, Imgproc.CV_HOUGH_GRADIENT, 2, 100, 100, 100, 0, 500);
for (int i = 0; i < circles.cols(); i++) {
double[] vCircle = circles.get(0, i);
Point pt = new Point(Math.round(vCircle[0]), Math.round(vCircle[1]));
int radius = (int)Math.round(vCircle[2]);
Core.circle(currentFrame, pt, radius, new Scalar(255, 0, 0), 2);
}
drawWindow.showImage(currentFrame);
}
The original image, the hsv image, and the filtered image are in this album: http://imgur.com/a/hO8vs
When I run HoughCircles with the parameters here, it finds circles on the piano bench and toy rabbit, but not the tennis ball, which appears as a big white circle.
I fixed it! After some fiddling with the parameters of HoughCircles and blurring and thresholding the binary image it was finding it reliably, but the circle was jittery and inconsistent. So, I replaced HoughCircles with findContours, cycled through the contours looking for the largest one, and used minEnclosingCircle. Here is the code now:
Mat currentFrame = new Mat(), hsv = new Mat(), threshImage = new Mat();
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
while (true) {
camera.read(currentFrame);
Imgproc.resize(currentFrame, currentFrame, new Size(WIDTH, HEIGHT));
Imgproc.cvtColor(currentFrame, hsv, Imgproc.COLOR_RGB2HSV);
hsvWindow.showImage(hsv);
Core.inRange(hsv, new Scalar(50, 100, 50), new Scalar(95, 255, 255), threshImage);
Imgproc.blur(threshImage, threshImage, new Size(10, 10));
Imgproc.threshold(threshImage, threshImage, 150, 255, Imgproc.THRESH_BINARY);
threshWindow.showImage(threshImage);
Imgproc.findContours(threshImage, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
double maxArea = 0;
float[] radius = new float[1];
Point center = new Point();
for (int i = 0; i < contours.size(); i++) {
MatOfPoint c = contours.get(i);
if (Imgproc.contourArea(c) > maxArea) {
MatOfPoint2f c2f = new MatOfPoint2f(c.toArray());
Imgproc.minEnclosingCircle(c2f, center, radius);
}
}
Core.circle(currentFrame, center, (int)radius[0], new Scalar(255, 0, 0), 2);
drawWindow.showImage(currentFrame);
}
I know this might not be particularly helpful for people looking to use HoughCircles specifically, but it's a testament to the power of blurring binary images. If you're looking for a circle among many things, you look for contours and compare the contour area to the area of its enclosing circle.