I want to know how to construct the following C++ statement in OpenCV using JavaCV:
float* p = (float*)cvGetSeqElem(circles, i);
int radius = cvRound(p[2]);
To get the Radius of a circle detected using cvHoughCircles(). Obviously Java doesn't use pointer so I have no idea how to do this in Java. The code I have so far so you can see it context:
lines = cvHoughCircles(frame2, storage, CV_HOUGH_GRADIENT, 1, 50, 300, 60, 10, 600);
for (int i = 0; i < lines.total(); i++) {
//Would like the code to go here
CvPoint2D32f point = new CvPoint2D32f(cvGetSeqElem(lines, i));
cvCircle(src, cvPoint((int)point.x(), (int)point.y()), 3, CvScalar.WHITE, -1, 8, 0);
Point p = new Point((int)point.x(), (int)point.y());
points.add(p);
}
JavaCPP maps C/C++ arrays/pointers to Pointer objects, so we can access it in the same way as in C/C++, i.e.:
FloatPointer p = new FloatPointer(cvGetSeqElem(circles, i));
int radius = Math.round(p.get(2));
Related
I have a captured image, the image consists of a table. I want to crop the table out of that image.
This is a sample image.
Can someone suggest what can be done?
I have to use it in android.
Use a hough transform to find the lines in the image.
OpenCV can easily do this and has java bindings. See the tutorial on this page on how to do something very similar.
https://docs.opencv.org/3.4.1/d9/db0/tutorial_hough_lines.html
Here is the java code provided in the tutorial:
import org.opencv.core.*;
import org.opencv.core.Point;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
class HoughLinesRun {
public void run(String[] args) {
// Declare the output variables
Mat dst = new Mat(), cdst = new Mat(), cdstP;
String default_file = "../../../../data/sudoku.png";
String filename = ((args.length > 0) ? args[0] : default_file);
// Load an image
Mat src = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
// Check if image is loaded fine
if( src.empty() ) {
System.out.println("Error opening image!");
System.out.println("Program Arguments: [image_name -- default "
+ default_file +"] \n");
System.exit(-1);
}
// Edge detection
Imgproc.Canny(src, dst, 50, 200, 3, false);
// Copy edges to the images that will display the results in BGR
Imgproc.cvtColor(dst, cdst, Imgproc.COLOR_GRAY2BGR);
cdstP = cdst.clone();
// Standard Hough Line Transform
Mat lines = new Mat(); // will hold the results of the detection
Imgproc.HoughLines(dst, lines, 1, Math.PI/180, 150); // runs the actual detection
// Draw the lines
for (int x = 0; x < lines.rows(); x++) {
double rho = lines.get(x, 0)[0],
theta = lines.get(x, 0)[1];
double a = Math.cos(theta), b = Math.sin(theta);
double x0 = a*rho, y0 = b*rho;
Point pt1 = new Point(Math.round(x0 + 1000*(-b)), Math.round(y0 + 1000*(a)));
Point pt2 = new Point(Math.round(x0 - 1000*(-b)), Math.round(y0 - 1000*(a)));
Imgproc.line(cdst, pt1, pt2, new Scalar(0, 0, 255), 3, Imgproc.LINE_AA, 0);
}
// Probabilistic Line Transform
Mat linesP = new Mat(); // will hold the results of the detection
Imgproc.HoughLinesP(dst, linesP, 1, Math.PI/180, 50, 50, 10); // runs the actual detection
// Draw the lines
for (int x = 0; x < linesP.rows(); x++) {
double[] l = linesP.get(x, 0);
Imgproc.line(cdstP, new Point(l[0], l[1]), new Point(l[2], l[3]), new Scalar(0, 0, 255), 3, Imgproc.LINE_AA, 0);
}
// Show results
HighGui.imshow("Source", src);
HighGui.imshow("Detected Lines (in red) - Standard Hough Line Transform", cdst);
HighGui.imshow("Detected Lines (in red) - Probabilistic Line Transform", cdstP);
// Wait and Exit
HighGui.waitKey();
System.exit(0);
}
}
public class HoughLines {
public static void main(String[] args) {
// Load the native library.
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
new HoughLinesRun().run(args);
}
}
Lines or LinesP will contain the found lines. Instead of drawing them (as in the example) you will want to manipulate them a little further.
Sort the found lines by slope.
The two largest clusters will be horizontal lines and then vertical lines.
For the horizontal lines calculate and sort by the y intercept.
The largest y intercept describes the top of the table.
The smallest y intercept is the bottom of the table.
For the vertical lines calculate and sort by the x intercept.
The largest x intercept is the right side of the table.
The smallest x intercept is the left side of the table.
You'll now have the coordinates of the four table corners and can do standard image manipulation to crop/rotate etc. OpenCV can help you will this step too.
Convert your image to grayscale.
Threshold your image to drop noise.
Find the minimum area rect of the non-blank pixels.
In python the code would look like:
import cv2
import numpy as np
img = cv2.imread('table.jpg')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 222, 255, cv2.THRESH_BINARY )
# write out the thresholded image to debug the 222 value
cv2.imwrite("thresh.png", thresh)
indices = np.where(thresh != 255)
coords = np.array([(b,a) for a, b in zip(*(indices[0], indices[1]))])
# coords = cv2.convexHull(coords)
rect = cv2.minAreaRect(coords)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
cv2.imwrite("box.png", img)
For me this produces the following image.
If your image didn't have the red squares it would be a tighter fit.
I ran into a strange issue with the polygon class from javafx (java 8).
When I apply a set translate, rotate or scale on the polygon instance it is correctly moving the polygon around on my shape. The problem is, the points in the getPoints() method stay the same.
I started now to create my own methods and moving around the points and resetting them, the methods do what they should, but is it the right way?
Here an example:
private void translatePoints(double translateX, double translateY) {
List<Double> newPoints = new ArrayList<>();
for (int i = 0; i < getPoints().size(); i += 2) {
newPoints.add(getPoints().get(i) + translateX);
newPoints.add(getPoints().get(i + 1) + translateY);
}
getPoints().clear();
getPoints().addAll(newPoints);
}
Is there a way to get the translated, rotated and scaled points after a couple of operations?
Or do I have to implement them all separatly?
Take a look at the subclasses of Transform (Affine, Rotate, Scale, Shear and Translate). They allow you to transform points stored in a double[] array using the transform2DPoints method.
double[] points = new double[] {
0, 0,
0, 1,
1, 1,
1, 0
};
Rotate rot = new Rotate(45, 0.5, 0.5);
Translate t = new Translate(5, 7);
Scale sc = new Scale(3, 3);
for (Transform transform : Arrays.asList(rot, t, sc)) {
transform.transform2DPoints(points, 0, points, 0, 4);
}
System.out.println(Arrays.toString(points));
this way you need to take care of determining the pivot point of transforms where this is relevant on your own.
You could also get resulting transform for a node using Node.getLocalToParentTransform.
double[] points = polygon.getPoints().stream().mapToDouble(Number::doubleValue).toArray();
polygon.getLocalToParentTransform().transform2DPoints(points, 0, points, 0, points.length/2);
System.out.println(Arrays.toString(points));
I'm trying to use the JavaCV implementation of the HoughCircles method, but I'm having some issues with the parameters.
Here is my code:
Mat currentImageGray = tgtFrag.getImage().clone();
Mat detectedCircles = new Mat();
HoughCircles(currentImageGray, detectedCircles, CV_HOUGH_GRADIENT, 1, 2, 254, 25, tgtFrag.getImage().rows() / 4, 0 );
if (detectedCircles != null && !detectedCircles.empty()) {
// TO DO:
// Print the center and the raidus of the detected circles.
}
First of all, the results of the detection (second argment of the HoughCircles) are given as a Mat (detectedCircles).
I'd like to process the detectedCircles Mat and somehow print the center and the radius of the circle on the console. My attempts have failed so far: I've been trying to iterate detectedCircles using a FloatBufferIndexer, could be the right direction, but I didn't succeed yet, anyone can help?
Please notice the following:
I'm using JavaCV, not openCV.
I'm using JavaCV HoughCircles, not cvHoughCircles (a solution using cvHoughCircles would be also ok though).
I'm using the latest version of JavaCV, i.e. 1.0 (July 2015).
I've only been able to use the JavaCV cvHoughCircles method, no idea about how to use the HoughCircles method though. Here is my adaptation of your code.
// Get the source Mat.
Mat myImage = tgtFrag.getImage();
IplImage currentImageGray = new IplImage(myImage);
CvMemStorage mStorage = CvMemStorage.create();
CvSeq detectedCircles = cvHoughCircles(currentImageGray, mStorage, CV_HOUGH_GRADIENT, 1, 2, 254, 25, tgtFrag.getImage().rows() / 4, 0);
if (detectedCircles != null && detectedCircles.total() > 0) {
for (int i = 0; i < detectedCircles.total(); i++) {
CvPoint3D32f curCircle = new CvPoint3D32f(cvGetSeqElem(detectedCircles, i));
int curRadius = Math.round(curCircle.z());
Point curCenter = new Point(Math.round(curCircle.x()), Math.round(curCircle.y()));
System.out.println(curCenter);
System.out.println(curRadius);
}
}
Even though this does not directly solve your problem, I hope this may help.
at the moment Iam working with Android Opencv. Iam Using the featureDetector with the Method FAST to find KeyPoints on an 240 x 320 Image.
//create a keypoint mat
MatOfKeyPoint keyPoints = new MatOfKeyPoint();
//create feature detector
FeatureDetector fd = FeatureDetector.create(FeatureDetector.FAST);
//detect feature points
fd.detect(image, keyPoints);
//return feature points
return keyPoints;
Next I will extract the points from the KeyPoints after Matching them:
DMatch[] matchesArray = matches.toArray();
Vector<Point> pointsVec1 = new Vector<Point>();
Vector<Point> pointsVec2 = new Vector<Point>();
for(int i = 0; i < matchesArray.length; i++)
{
pointsVec1.add(keyPoints1.toArray()[matchesArray[i].queryIdx].pt);
pointsVec2.add(keyPoints2.toArray()[matchesArray[i].trainIdx].pt);
}
points1.fromList(pointsVec1);
points2.fromList(pointsVec2);
Later I try to get the pixel color of these points, doing this:
Point[] pointsArray = points.toArray();
Vector<byte[]> colorVector = new Vector<byte[]>();
for(int i = 0; i< pointsArray.length; i++)
{
Point point = pointsArray[i];
byte[] color = new byte[4];
image.get((int) point.x, (int) point.y, color);
colorVector.add(color);
}
return colorVector;
Iam still wondering because there are Points out of border of the Image. For example I found this point by debugging: (308.0, 16.0). By an 240 x 340 Image this point is not in the image. There are more Point of these. I checked already the featureExtractor and the Point is already containing there. So i get a color of R = 0, G = 0, B = 0.
So my question is:
Where does this Points come from?
Have I to filter them on my own or is there something like Threshhold solution in the function?
Or I have to switch x and y or change the conversion of the Points?
In the end it is a small problem but i cant explain myself where doese they come from!
So thanks for your help!
Make sure you have not switch x <-> y or row <-> col.
I'm trying to detect the positions of billiards balls on a table from an image taken at a perspective angle. I'm using the getPerspectiveTransform() method to find the transformation matrix and I want to apply that to only the circles I detect using HoughCircles. I'm trying to go from a rather large trapezoidal shape to a smaller rectangular shape. I don't want to do the transformation on the image first and then find the HoughCircles because the image gets too warped for houghcircles to provide useful results.
Here's my code:
CvMat mmat = cvCreateMat(3,3,CV_32FC1);
double srcX1 = 462;
double srcX2 = 978;
double srcX3 = 1440;
double srcX4 = 0;
double srcY = 241;
double srcHeight = 772;
double dstX = 56.8;
double dstY = 33.5;
double dstWidth = 262.4;
double dstHeight = 447.3;
CvSeq seq = cvHoughCircles(newGray, circles, CV_HOUGH_GRADIENT, 2.1d, (double)newGray.height()/40, 85d, 65d, 5, 50);
JavaCV.getPerspectiveTransform(new double[]{srcX1, srcY, srcX2,srcY, srcX3, srcHeight, srcX4, srcHeight},
new double[]{dstX, dstY, dstWidth, dstY, dstWidth, dstHeight, dstX, dstHeight}, mmat);
cvWarpPerspective(seq, seq, mmat);
for(int j=0; j<seq.total(); j++){
CvPoint3D32f point = new CvPoint3D32f(cvGetSeqElem(seq, j));
float xyr[] = {point.x(),point.y(),point.z()};
CvPoint center = new CvPoint(Math.round(xyr[0]), Math.round(xyr[1]));
int radius = Math.round(xyr[2]);
cvCircle(gray, center, 3, CvScalar.GREEN, -1, 8, 0);
cvCircle(gray, center, radius, CvScalar.BLUE, 3, 8, 0);
}
The problem is I get this error on the warpPerspective() method:
error: (-215) seq->total > 0 && CV_ELEM_SIZE(seq->flags) == seq->elem_size in function cv::Mat cv::cvarrToMat(const CvArr*, bool, bool, int)
Also I guess it's worth mentioning that I'm using JavaCV, in case the method calls look a bit different than what you're used to. Thanks for any help.
Answer:
the problem with what you want to do (besides the obvious, opencv wont let you) is that the radius cant really be warped correctly. AFAIK the xy coordinates are pretty easy to calculate x'=((m00x+m01y+m02)/(m20x+m21y+m22)) y'=((m10x+m11y+m12)/(m20x+m21y_m22)) when m is the transformation matrix. the radius you can hack by transforming all the points of the original circle and then find the max distance between x'y' and those points (atleast if the radius in the warped image is expected to cover all those points)
btw, mIJx = m(i,j)*x (just to clarify)
End Answer.
Everything i write is according to the c++ version, i've never used JavaCV but from what i could see its just a wrapper that calls the native c++ lib.
CvSeq is a sequance data structure that behaves like a linked list.
the assert your application crushes at is
CV_Assert(seq->total > 0 && CV_ELEM_SIZE(seq->flags) == seq->elem_size);
which means that either your seq instance is empty (total is the number of elements in the sequence) or somehow the inner seq flags are corrupted.
I'd recommend that you'd check the total member of your CvSeq, and the cvHoughCircles call.
all of this occurs before the actual implementation of cvWarpPerspective (its the first line in the implementation, that only converts your CvSeq to cv::Mat).. so its not the warping but what you're doing before that.
anyway, to understand whats wrong with cvHoughCircles we'll need more info about the creation of newGray and circles.
here is an example i've found on the javaCV page (Link)
IplImage gray = cvCreateImage( cvSize( img.width, img.height ), IPL_DEPTH_8U, 1 );
cvCvtColor( img, gray, CV_RGB2GRAY );
// smooth it, otherwise a lot of false circles may be detected
cvSmooth(gray,gray,CV_GAUSSIAN,9,9,2,2);
CvMemStorage circles = CvMemStorage.create();
CvSeq seq = cvHoughCircles(gray, circles.getPointer(), CV_HOUGH_GRADIENT,
2, img.height/4, 100, 100, 0, 0);
for(int i=0; i<seq.total; i++){
float xyr[] = cvGetSeqElem(seq,i).getFloatArray(0, 3);
CvPoint center = new CvPoint(Math.round(xyr[0]), Math.round(xyr[1]));
int radius = Math.round(xyr[2]);
cvCircle(img, center.byValue(), 3, CvScalar.GREEN, -1, 8, 0);
cvCircle(img, center.byValue(), radius, CvScalar.BLUE, 3, 8, 0);
}
from what i've seen in the implementation of cvHoughCircles, the answer is saved in the circles buff and at the end they create from it the CvSeq to return, so if you've allocated the circles buff wrong, it wont work.
EDIT:
as you can see, the CvSeq instance in case of the return from cvHoughCircles is a list of point-values, that is probably why the assertion failed. you cannot convert this CvSeq into a cv::Mat.. because its just not a cv::Mat. to get only the circles returned from cvHoughCircles in an cv::Mat instance, you'll need to create a new cv::Mat instance and than draw onto it all the circles in the CvSeq - as seen in the provided example above.
than the warping will work (you'll have a cv::Mat instance, and that is what the function expect - a cv::Mat as the only element in the CvSeq)
END EDIT
here is the c++ reference for CvSeq
and if you want to fiddle with the source code than
cvarrToMat is in matrix.cpp
CV_ELEM_SIZE is in types_c.h
cvWarpPerspective is in imgwarp.cpp
cvHoughCircles is in hough.cpp
I hope that will help.
BTW, your next error will probably be:
cv::warpPerspective in the C++ OpencCv asserts that dst.data != src.data
thus
cvWarpPerspective(seq, seq, mmat);
wont work cause your source mat and destination mat referencing the same data.
Not all the functions in OpenCV (and image processing in general) work in-situ (because there is no in-situ algorithm or because its slower then the other version eg. transpose of an n*n mat will work in-situ, but n*m where n!=m will be harder to do in-situ and might be slower)
you cant assume the using the src matrix as the dst will work.