I am trying to detect circles using android. I succeeded to implement the detect lines algorithm but nothing gets displayed when trying the draw hough circles algoritm.
Here is my code:
Mat thresholdImage = new Mat(getFrameHeight() + getFrameHeight() / 2, getFrameWidth(), CvType.CV_8UC1);
mYuv.put(0, 0, data);
Imgproc.cvtColor(mYuv, destination, Imgproc.COLOR_YUV420sp2RGB, 4);
Imgproc.cvtColor(destination, thresholdImage, Imgproc.COLOR_RGB2GRAY, 4);
Imgproc.GaussianBlur(thresholdImage, thresholdImage, new Size(9, 9), 2, 2 );
Mat circles = new Mat();
Imgproc.HoughCircles(thresholdImage, circles, Imgproc.CV_HOUGH_GRADIENT, 1d, (double)thresholdImage.height()/70, 200d, 100d);
Log.w("circles", circles.cols()+"");
for (int x = 0; x < circles.cols(); x++)
{
double vCircle[]=circles.get(0,x);
Point center=new Point(Math.round(vCircle[0]), Math.round(vCircle[1]));
int radius = (int)Math.round(vCircle[2]);
// draw the circle center
Core.circle(destination, center, 3,new Scalar(0,255,0), -1, 8, 0 );
// draw the circle outline
Core.circle( destination, center, radius, new Scalar(0,0,255), 3, 8, 0 );
}
You may have got this sorted by now, but a few things. I'd check your circles mat actually has some results; sometimes vCircle seems to come back null; try one of the other versions of HoughCircles:
iCannyUpperThreshold = 100;
iMinRadius = 20;
iMaxRadius = 400;
iAccumulator = 300;
Imgproc.HoughCircles(thresholdImage, circles, Imgproc.CV_HOUGH_GRADIENT,
2.0, thresholdImage.rows() / 8, iCannyUpperThreshold, iAccumulator,
iMinRadius, iMaxRadius);
if (circles.cols() > 0)
for (int x = 0; x < circles.cols(); x++)
{
double vCircle[] = circles.get(0,x);
if (vCircle == null)
break;
Point pt = new Point(Math.round(vCircle[0]), Math.round(vCircle[1]));
int radius = (int)Math.round(vCircle[2]);
// draw the found circle
Core.circle(destination, pt, radius, new Scalar(0,255,0), iLineThickness);
Core.circle(destination, pt, 3, new Scalar(0,0,255), iLineThickness);
}
(I swapped your code into mine, renamed some stuff and swapped it back, I think I've got it back so it works...)
B.
Related
I am trying to find all intersection points (their x and y values) based on 4 corner points that I always have and number of cells (in my case 9, so 9x9 matrix, sudoku puzzle).
My 4 corners are marked with green cross, and taged P1 to P4.
I tried to calculate it, and only managed to do it precisely for the first row.
double xDis = p2.x - p1.x;
double yDis = p2.y - p1.y;
double xW = xDis / 9;
double yH = yDis / 9;
for (int i = 0; i < 10; i++) {
Point point = new Point(p1.x + (i * xW), p1.y + (i * yH));
}
This code would work exactly as I expected it but only for the first row.
What am I missing here ? Is there some kind of algoritmh that already does this ? Any hints are welcome.
Note that I am using android with OpenCV library.
As written above in the comments, I ended up warping the image and then cutting it. It looks something like this
if (points != null) {
Point p1 = points[0];
Point p2 = points[1];
Point p3 = points[2];
Point p4 = points[3];
MatOfPoint2f src = new MatOfPoint2f(
p1,
p2,
p3,
p4);
drawMarker(frame, p1, new Scalar(255,0,0), 0, 20, 1);
drawMarker(frame, p2, new Scalar(255,0,0), 0, 20, 1);
drawMarker(frame, p3, new Scalar(255,0,0), 0, 20, 1);
drawMarker(frame, p4, new Scalar(255,0,0), 0, 20, 1);
double x = p2.x - p1.x;
double y = p3.y - p2.y;
MatOfPoint2f dst = new MatOfPoint2f(
new Point(0, 0),
new Point(x,0),
new Point(0,y),
new Point(x,y)
);
Mat warpMat = Imgproc.getPerspectiveTransform(src, dst);
//This is you new image as Mat
Mat destImage = new Mat();
Imgproc.warpPerspective(bw2, destImage, warpMat, new Size(x, y));
List<Mat> cells = getCells(destImage, destImage.width() / 9, destImage.height / 9);
}
private List<Mat> getCells(Mat m, int width, int height) {
Size cellSize = new Size(width, height);
List<Mat> cells = new ArrayList<>();
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
Rect rect = new Rect(new Point(col * width, row * height), cellSize);
Mat digit = new Mat(m, rect).clone();
cells.add(digit);
}
}
return cells;
}
You only do your calculation once, on the first row.
Put your for loop inside of another for loop and run it 10 times, and you should be good (Adding in whatever x,y translation happens as you traverse downwards in y).
As for if there is any automated way to do this, yes. I could suggest using Harris Corner Detection. I suspect using the right thresholds could get you only the thicker line corners. You could also try doing line detection and looking for intersections.
Also, this article may be helpful if you find you aren't finding good lines/corners. You can correct the shading from the lighting and get a good clean image to analyze.
I am working with OpenCV 3.0 for Android. I have an image in which i want to detect angle of hands inside circular dials. for that i am working on HoughLinesP to detect hands.
Here is the code.
Mat imgSource = new Mat(), imgCirclesOut = new Mat(),imgLinesOut=new Mat();
//grey opencv
Imgproc.cvtColor(Image, imgSource, Imgproc.COLOR_BGR2GRAY);
Imgproc.GaussianBlur( imgSource, imgSource, new Size(9, 9), 2, 2 );
int threshold = 0;
int minLineSize = 0;
int lineGap = 0;
Imgproc.HoughLinesP(imgSource, imgLinesOut, 1, Math.PI/180, threshold, minLineSize, lineGap);
for( int j = 0; i < imgLinesOut.cols(); i++ )
{
double[] vec=imgLinesOut.get(0,j);
Point pt1, pt2;
pt1=new Point(vec[0],vec[1]);
pt2=new Point(vec[2],vec[3]);
Imgproc.line( Image, pt1, pt2, new Scalar(0,0,255), 3, Core.LINE_AA,0);
}
But result is
What i need is the angle of hands in these circles. Any help regarding this issue is highly appreciated. Thanks in ADvance
Edit
I have updated my code with this
Mat imgSource = new Mat(), imgCirclesOut = new Mat(),imgLinesOut=new Mat();
Imgproc.GaussianBlur( Image, imgSource, new Size(5, 5), 2, 2 );
int threshold = 20;
int minLineSize = 0;
int lineGap = 10;
Imgproc.Canny(imgSource, imgSource, 70, 100);
Imgproc.HoughLinesP(imgSource, imgLinesOut, 1, Math.PI/180, threshold, minLineSize, lineGap);
for( int j = 0; j < imgLinesOut.cols(); j++ )
{
double[] vec=imgLinesOut.get(0,j);
Point pt1, pt2;
pt1=new Point(vec[0],vec[1]);
pt2=new Point(vec[2],vec[3]);
Imgproc.line( imgSource, pt1, pt2, new Scalar(0,0,255), 3, Core.LINE_AA,0);
}
as suggested by #Micka, there is no need of Graying image(I removed cvtcolor). I also decreased value of GuassianBlur Size to 5. I have added Canny on image too for edges.
Resulting blur image is
Detecting lines can be a problem in such small images, since you have to few points to fill the Hough accumulator properly.
I propose to use a different approach:
Segment each circle (dial)
Extract the largest dark blob (hand)
Below is a simple implementation of this idea. The code is in C++, but you can easily port to Java, or at least use as a reference.
#include "opencv2/opencv.hpp"
using namespace cv;
int main(int, char**)
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
Mat3b res;
cvtColor(img, res, COLOR_GRAY2BGR);
// Find dials
vector<Vec3f> circles;
HoughCircles(img, circles, CV_HOUGH_GRADIENT, 1, img.cols/10, 400, 40);
// For each dial
for (int i = 0; i < circles.size(); ++i)
{
// Segment the dial
Mat1b dial(img.size(), uchar(255));
Mat1b mask(img.size(), uchar(0));
circle(mask, Point(circles[i][0], circles[i][1]), circles[i][2], Scalar(255), CV_FILLED);
img.copyTo(dial, mask);
// Apply threshold and open
Mat1b bin;
threshold(dial, bin, 127, 255, THRESH_BINARY_INV);
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(5,5));
morphologyEx(bin, bin, MORPH_OPEN, kernel);
// Get min area rect
vector<Point> points;
findNonZero(bin, points);
RotatedRect r = minAreaRect(points);
// Draw min area rect
Point2f pts[4];
r.points(pts);
for (int j = 0; j < 4; ++j) {
line(res, pts[j], pts[(j + 1) % 4], Scalar(0, 255, 0), 1);
}
}
imshow("Result", res);
waitKey();
return 0;
}
Starting from this image:
I find hands here:
for( int j = 0; j < imgLinesOut.size(); j++ )
This will give the size of the vector.To iterate through that vector
I've tried so many solutions that it's possible that my code is a bit mixed up, but whatever I try, it just won't work.
Basically I made a map with Tiled, where my player can run around and bump into stuff. I want the whole map to be visible for the whole time (it's 20 by 15, 64 pixels a tile). The camera doesn't need to move around or follow the player, it has to stay still at the center of the map.
The problem is that the map only shows in the upper right corner of the screen. When I centered the camera to the map itself it messed up the collission detection, (bumping into trees while they were not visible & walking through visible trees). So what I want to do is center the map to 0,0 where my camera also is (at least I think..).
Another thing I'd like to accomplish is that the size of the map gets resized to match different mobile phones. Tried to accomplish this with the stretchviewport, but haven't been able to test this.
public class PlayScreen implements Screen {
TiledMap map;
OrthogonalTiledMapRenderer mapRenderer;
OrthographicCamera cam;
float unitScale = 1 / 64f;
OrthogonalTiledMapRenderer renderer = new OrthogonalTiledMapRenderer(map, unitScale);
Viewport viewport;
public void show() {
map = new TmxMapLoader().load("maps/map.tmx");
mapRenderer = new OrthogonalTiledMapRenderer(map);
cam = new OrthographicCamera(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
cam.setToOrtho(false);
viewport = new StretchViewport(1280, 960, cam);
bounds = new ArrayList<Rectangle>();
for(int i = 0; i < 20; i++){
for(int j = 0; j < 15; j++){
TiledMapTileLayer cur = (TiledMapTileLayer) map.getLayers().get(1);
Cell cell = new Cell();
Vector3 center = new Vector3(cur.getWidth() * cur.getTileWidth() / 2, cur.getHeight() * cur.getTileHeight() / 2, 0);
cam.position.set(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, 0);
cam.update();
if(cur.getCell(i,j) != null){ //null = first layer != --> if its not
cell = cur.getCell(i, j);
System.out.println(i + ", " + j + ", " + cell.getTile().getId());
bounds.add(new Rectangle(i * 64, j * 64, 64 , 64));
}
}
}
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
mapRenderer.setView(cam);
mapRenderer.render();
cam.position.set(0, 0, 0);
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
batch.draw(player.getCurrentFrame(), player.getPosition().x , player.getPosition().y);
player.update();
for(int i = 0; i < bounds.size(); i++){
if(bounds.get(i).overlaps(player.getBounds())){
int x = (int)bounds.get(i).x / 64;
int y = (int)bounds.get(i).y / 64;
TiledMapTileLayer cur = (TiledMapTileLayer)map.getLayers().get(1);
Cell cell = cur.getCell(x, y);
if(cell.getTile().getProperties().containsKey("blocked")){
System.out.println("bush");
}
player.reAdjust();
}
}
batch.end();
}
public void resize(int width, int height) {
viewport.update(width, height);
}
Nevermind, I deleted: cam.position.set(0, 0, 0); and everything seems to work just fine. Guess I already made some changes what caused it to work, just didn't see it cause this was still around.
im trying to draw multiple diagonal lines across the whole image (leaving a space between them) i've used this code to draw horizontal and vertical lines:
for (int z = 1; z < partToCrop; z++) {
Shape hLines = new Line2D.Float(0, cropInPartWidth*z, chunkWidth, cropInPartWidth*z);
Shape vLines = new Line2D.Float(cropInPartHeight*z, 0, cropInPartHeight*z, chunkHeight);
gr.draw(hLines); //gr is a BufferedImage
gr.draw(vLines);
}
where
int partToCrop = 5;
float cropInPartWidth = imgWidth / partToCrop;
float cropInPartHeight = imgHeight / partToCrop;
and works good. Now i need to draw multiple diagonal lines (i.e 4 diagonal lines) with 45° and -45° degrees of inclination across the whole image, hope you will help me.
Thanks.
Shape firstLine = new Line2D.Float(0, imgHeight, imgWidth, 0); // this line is from bottom left to top right
Shape secondLine = new Line2D.Float(0, 0, imgWidth, imgHeight); // this line is from top left to bot right
Actually this seems easier to me.
Assuming imgDim = imgHeight = imgWidth:
int spacing = 2;
for (int z = 1; z < imgDim; z = z + spacing)
{
Shape dLines = new Line2D.Float(0, z, z, 0);
gr.draw(dLines);
}
I can't get the circle vectors values from the OpenCV Mat in Android. I want to use this function:
HoughCircles(Mat image, Mat circles, int method, double dp, double minDist)
And then I want to show the circles that were found. Where I'm stuck is how to use the circles parameter in this function.
So, the question is: how can I get numbers of 3-element vectors and values of every element in vector from Mat type of OpenCV in Android?
Once you have your circles Mat
for (int i = 0; i < circles.cols(); i++)
{
double vCircle[] = circles.get(0,i);
double x = vCircle[0];
double y = vCircle[1];
double radius = vCircle[2];
}
Ideally you would want to use a vector<Vec3f> list to process the circles like this:
vector<Vec3f> circles;
// do HoughCircles...
for(size_t i = 0; i < circles.size(); i++)
{
Vec3f circle = circles[i];
Point2f center(circle[0] /* x */, circle[1] /* y */);
float radius = circle[2];
// use the circle...
}
EDIT : I tried the code just using a Mat, and it appears that the circle parameters are stored as a 1xN matrix with elements of type CV_32FC3, and where N is the number of circles detected. So, each column contains the (x, y, radius) vector you need.
Here is a sample I wrote in C++ showing this:
Mat circleImage = imread("circle.png", 0);
Mat circleDisp;
cvtColor(circleImage, circleDisp, CV_GRAY2RGB);
Mat circles;
HoughCircles(circleImage, circles, CV_HOUGH_GRADIENT, 2, circleImage.rows >> 2, 200, 100);
for( size_t i = 0; i < circles.cols; i++ )
{
Vec3f vCircle = circles.at<Vec3f>(i);
Point center(cvRound(vCircle[0]), cvRound(vCircle[1]));
int radius = cvRound(vCircle[2]);
// draw the circle center
circle( circleDisp, center, 3, Scalar(0,255,0), -1, 8, 0 );
// draw the circle outline
circle( circleDisp, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
namedWindow( "circles", 1 );
imshow( "circles", circleDisp );
waitKey();
Hope that helps!
Just cast your Mat to vector:
HoughCircles(Mat image, Mat circles, int method, double dp, double minDist);
vector<Vec3f> myCircles = (vector<Vec3f>)circles;
Or, simpler
HoughCircles(Mat image, vector<Vec3f>& circles,
int method, double dp, double minDist);
Note
This is only true for OpenCV 2.3.1.