I'm trying to detect the center of a circle. I try to do this with cvHoughCircle. But I can't seem to get it working properly .
The only thing that can vary is the size of the circle.
I try detecting the circle by doing :
circle = cvHoughCircles(imgThreshold, storage, CV_HOUGH_GRADIENT, 1,
(double)imgThreshold.height()/20, 200, 20, 0, 0);
imgThreshold is the b/w image you can see here. The resolution of the image is in fact 1280*1024.
Can anyone tell me what I am doing wrong.
Instead of using cvHoughCircle it is possible to solve this problem with a bit of math:
CvMoments moments = new CvMoments();
cvMoments(imgThreshold, moments, 1);
double moment10 = cvGetSpatialMoment(moments, 1, 0);
double moment01 = cvGetSpatialMoment(moments,0,1);
double area = cvGetCentralMoment(moments, 0, 0);
int posX = 0;
int posY = 0;
int lastX = posX;
int lastY = posY;
posX = (int) (moment10/area);
posY = (int) (moment01/area);
cvCircle(iplRgbImage, new CvPoint(posX,posY), 3, CvScalar.GREEN, -1, 8, 0);
source = http://aishack.in/tutorials/tracking-colored-objects-in-opencv/
If the circle is complete and filled and not occluded by other shapes, you can use findContours() and then find the center of the contour.
use cvBlob
https://code.google.com/p/cvblob/
Concerning Hough transform it can detect circles by identifying pixels that belongs to a circle periphery. More precisely given a binary (thresholded) image containing ie white pixels along a cyclic path, the hough circle transform will detect the circle. So the image to feed the algorithm should be binary and thresholded but in your example must be the thresholded example of an edge filter (ex Sobel) rather than a solid filled circle.
I can not tell a right way of "fitting" a circle on the above image, but the centroid of the blob extracted with connected components is a good and fast way to go.
Related
I am working on a 2D java game engine using AWT canvas as a basis. Part of this game engine is that it needs to have hitboxes with collision. Not just the built in rectangles (tried that system already) but I need my own Hitbox class because I need more functionality. So I made one, supports circular and 4-sided polygon shaped hitboxes. The way the hitbox class is setup is that it uses four coordinate points to serve as the 4 corner vertices that connect to form a rectangle. Lines are draw connecting the points and these are the lines that are used to detect intersections with other hitboxes. But I now have a problem: rotation.
There are two possibilities for a box hitbox, it can just be four coordinate points, or it can be 4 coordinate points attached to a gameobject. The difference is that the former is just 4 coordinates based on 0,0 as the ordin while the attached to gameobject stores offsets in the coordinates rather than raw location data, so (-100,-100) for example represents the location of the host gameobject but 100 pixels to the left, and 100 pixels up.
Online I found a formula for rotating points about the origin. Since Gameobject based hitboxes were centered around a particular point, I figured that would be the best option to try it on. This code runs each tick to update a player character's hitbox
//creates a rectangle hitbox around this gameobject
int width = getWidth();
int height = getHeight();
Coordinate[] verts = new Coordinate[4]; //corners of hitbox. topLeft, topRight, bottomLeft, bottomRight
verts[0] = new Coordinate(-width / 2, -height / 2);
verts[1] = new Coordinate(width / 2, -height / 2);
verts[2] = new Coordinate(-width / 2, height / 2);
verts[3] = new Coordinate(width / 2, height / 2);
//now go through each coordinate and adjust it for rotation
for(Coordinate c : verts){
if(!name.startsWith("Player"))return; //this is here so only the player character is tested
double theta = Math.toRadians(rotation);
c.x = (int)(c.x*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(c.x*Math.sin(theta)+c.y*Math.cos(theta));
}
getHitbox().vertices = verts;
I appologize for poor video quality but this is what the results of the above are: https://www.youtube.com/watch?v=dF5k-Yb4hvE
All related classes are found here: https://github.com/joey101937/2DTemplate/tree/master/src/Framework
edit: The desired effect is for the box outline to follow the character in a circle while maintaining aspect ratio as seen here: https://www.youtube.com/watch?v=HlvXQrfazhA . The current system uses the code above, the effect of which can be seen above in the previous video link. How should I modify the four 2D coordinates to maintain relative aspect ratio throughout a rotation about a point?
current rotation system is the following:
x = x*Cos(theta) - y *Sin(theta)
y = x*Sin(theta) + y *Cos(theta)
where theta is degree of rotation in raidians
You made classic mistake:
c.x = (int)(c.x*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(c.x*Math.sin(theta)+c.y*Math.cos(theta));
In the second line you use modified value of c.x. Just remember tempx = c.x
before calculations and use it.
tempx = c.x;
c.x = (int)(tempx*Math.cos(theta)-c.y*Math.sin(theta));
c.y = (int)(tempx*Math.sin(theta)+c.y*Math.cos(theta));
Another issue: rounding coordinates after each rotation causes distortions and shrinking after some rotations. It would be wise to store coordinates in floats and round them only for output, or remember starting values and apply rotation by accumulated angle to them.
I'm using opencv in Java to try to detect circles (iris, and pupil) in images with eyes, but I didn't get the expected results.
Here is my code
// convert source image to gray
org.opencv.imgproc.Imgproc.cvtColor(mRgba, imgCny, Imgproc.COLOR_BGR2GRAY);
//fliter
org.opencv.imgproc.Imgproc.blur(imgCny, imgCny, new Size(3, 3));
//apply canny
org.opencv.imgproc.Imgproc.Canny(imgCny, imgCny, 10, 30);
//apply Hough circle
Mat circles = new Mat();
Point pt;
org.opencv.imgproc.Imgproc.HoughCircles(imgCny, circles, Imgproc.CV_HOUGH_GRADIENT, imgCny.rows() / 4, 2, 200, 100, 0, 0);
//draw the found circles
for (int i = 0; i < circles.cols(); i++) {
double vCircle[] = circles.get(0, i);
pt = new Point((int) Math.round((vCircle[0])), (int) Math.round((vCircle[1])));
int radius = (int) Math.round(vCircle[2]);
Core.circle(mRgba, pt, radius, new Scalar(0, 0, 255), 3);
}
the original image
canny result
I don't know what is the problem. Whether the problem is in the parameters of the found circle function or something else.
Has anyone faced such problem or knows how to fix it?
There is no way that the Hough transform will detect THE circle you want in this canny result! There are too many edges. You must clean the image first.
Start with black (the pupil, iris inner part) and white detection. These two zones will delimitate the ROI.
Moreover, I would also try to perform a skin detection (simple threshold into HSV color space. It will eliminate 90% of the research area.
When trying to program a game using Box2D, I ran into a problem with Box2D. I filled in pixel numbers for the lengths of the the textures and sprites to create a box around it. Everything was at the right place, but for some reason everything went very slowly. By looking on the internet I found out that if you didn't convert pixels to meters box2d might handle shapes as very large objects. this seemed to be a logical cause of everything moving slowly.
I found similar questions on this site, but the answers didn't really seem to help out. in most of the cases the solution was to make methods to convert the pixel numbers to meters using a scaling factor. I tried this out, but everything got misplaced and had wrong sizes. this seemed logical to me since the numbers where changed but had the same meaning.
I was wondering if there is a way to make the pixels mean less meters, so everything whould be at the same place with the same (pixel) size, but mean less meters.
If you have a different way which you think might help, I whould also like to hear it..
Here is the code i use to create the camera
width = Gdx.graphics.getWidth() / 5;
height = Gdx.graphics.getHeight() / 5;
camera = new OrthographicCamera(width, height);
camera.setToOrtho(false, 1628, 440);
camera.update();
This is the method I use to create an object:
public void Create(float X, float Y, float Width, float Height, float density, float friction, float restitution, World world){
//Method to create an item
width = Width;
height = Height;
polygonDef = new BodyDef();
polygonDef.type = BodyType.DynamicBody;
polygonDef.position.set(X + (Width / 2f), Y + (Height / 2f));
polygonBody = world.createBody(polygonDef);
polygonShape = new PolygonShape();
polygonShape.setAsBox(Width / 2f, Height / 2f);
polygonFixture = new FixtureDef();
polygonFixture.shape = polygonShape;
polygonFixture.density = density;
polygonFixture.friction = friction;
polygonFixture.restitution = restitution;
polygonBody.createFixture(polygonFixture);
}
To create an item, in this case a table, I use the following:
Table = new Item();
Table.Create(372f, 60f, 152f, 96f, 1.0f, 0.2f, 0.2f, world);
The Sprites are drawn on the item by using the following method:
public void drawSprite(Sprite sprite){
polygonBody.setUserData(sprite);
Utils.batch.begin();
if(polygonBody.getUserData() instanceof Sprite){
Sprite Sprite = (Sprite) polygonBody.getUserData();
Sprite.setPosition(polygonBody.getPosition().x - Sprite.getWidth() / 2, polygonBody.getPosition().y - Sprite.getHeight() / 2);
Sprite.setRotation(polygonBody.getAngle() * MathUtils.radiansToDegrees);
Sprite.draw(Utils.batch);
}
Utils.batch.end();
}
The sprites also have pixel sizes.
Using this methods it displays the images at the right places, but everything moves slowly.
I was wondering how or if I whould have to change this to make the objects move correctly, and / or mean less. Thanks in advance.
Box2D is an entirely independent of the graphics library that you use. It doesn't have any notion of sprites and textures. What you read online is correct, you'll have to convert pixels to metres, as Box2D works with metres(the standard unit for distance).
For example, if you drew a sprite of size 100x100 pixels, that's the size of the sprite that you want the user to see on the screen. In real world the size of the object should be in metres and not in pixels - so if you say 1px = 1m, then that'll map the sprite to a gigantic 100x100 meter object. In Box2D, large world objects will slow down calculations. So what you need to do is map the 100 pixels to a smaller number of meters, say, 1 meter - thus 100x100px sprite will be represented in Box2D world by a 1x1 meter object.
Box2D doesn't work well with very small numbers and very large numbers. So keep it in between, say between 0.5 and 100, to have good performance.
EDIT:
Ok. Now I get your question.
Don't code to pixels. Its as simple as that. I know it'll take some time to understand this(it took for me). But once you get the hang of it, its straight forward.
Instead of pixels, use a unit, say, you call it meter.
So we decide our viewport should be say 6mx5m.
So initialization is
Constants.VIEWPORT_WIDTH = 6;
Constants.VIEWPORT_HEIGHT = 5;
...
void init() {
camera = new OrthographicCamera(Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
camera.position.set(Constants.VIEWPORT_WIDTH/2, Constants.VIEWPORT_HEIGHT/2, 0);
camera.update();
}
Once you know the actual width and height, you call the following function in order to maintain aspect ratio:
public void resize(int width, int height) {
camera.viewportHeight = (Constants.VIEWPORT_WIDTH / width) * height;
camera.update();
}
resize() can be called anytime you change your screen size(eg: when you screen orientation changes). resize() takes the actual width and height (320x480 etc), which is the pixel value.
Now you specify you sprite sizes, their positions etc. in this new world of size 6x5. You can forget pixels. The minimum size of the sprite that'll fill the screen will be 6x5.
You can now use the same unit with Box2D. Since the new dimensions will be smaller, it won't be a problem for Box2D. If I remember correctly Box2D doesn't have any unit. We just call it meter for convenience sake.
Now you might ask where you specify the dimensions of the window. It depends on the platform. Following code shows a 320x480 windowed desktop game:
public class Main {
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "my-game";
cfg.useGL20 = false;
cfg.width = 480;
cfg.height = 320;
new LwjglApplication(new MyGame(), cfg);
}
}
Our camera will intelligently map the 6x5 viewport to 480x320.
Im developing simple game. I have cca. 50 rectangles arranged in 10 columns and 5 rows. It wasn't problem to put them somehow to fit the whole screen. But when I rotate the canvas, let's say about 7° angle, the old coordinates does't fit in the new position of the coordinates. In constructor I already create and define the position of that rectangles, in onDraw method I'm drawing this rectangles (of course there are aready rotated) bud I need some method that colliding with the current rectangle. I tried to use something like this (i did rotation around the center point of the screen)
int newx = (int) ((x * Math.cos(ROTATE_ANGLE) - (y * Math.sin(ROTATE_ANGLE))) + width / 2);
int newy = (int) ((y * Math.cos(ROTATE_ANGLE) + (x * Math.sin(ROTATE_ANGLE))) + height / 2);
but it doesn't works (becuase it gives me absolute wrong new coordinates). x and y are coordinates of the touch that I'm trying to calculate new position in manner of rotation. ROTATE_ANGLE is the angle of rotation the screen.
Does anybody know how to solve this problem, I already go thorough many articles, wiki, wolframalpha categories but not luck. Maybe I just need some link to understand problem more.
Thank you
You use a rotation matrix.
Matrix mat = new Matrix(); //mat is identity
mat.postRotate(ROTATE_ANGLE); //mat is a rotation matrix of ROTATE_ANGLE degrees
float point[] = {10.0, 20.0}; //create a new float array representing the point (10, 20)
mat.mapPoints(point); //rotate the point by the requested amount
Ok, find the solution.
First it is important to convert from angle to radian
Then I personly need to negate that radian value.
That's all, this solution is correct
If I have a canvas, on which I draw a Bitmap like this:
canvas.drawBitmap(bmLargeImage, srcRect, destRect, paint);
and I scale the bitmap:
canvas.scale(1.5f, 1.5f, 450, 250);
I want to get the position of the Bitmap after the scale. If the position before scale was (0, 0), after scale there is a offset and I need that offset.. how can I get it?
Thanks and sorry for the simple question, newbie here...
Ok lets try to work out the best formula for this
canvas.scale(scaleX, scaleY, pivotX, pivotY);
if (scaleX >= 1){
objectNewX = objectOldX + (objectOldX - pivotX)*(scaleX - 1);
}else{
objectNewX = objectOldX - (objectOldX - pivotX)*(1 - scaleX);
}
The same for objectNewY. The new width and height of the bitmap would of course be the multiple of the old size and scale.
I believe the cleanest Solution would be to use the underlying transformation Matrix of the Canvas you are manipulating.
In Android there is the canvas.getMatrix(Matrix cmt) method available which will yield it. The transformation matrix will transform any point in world space you throw at into screen coordinates. Just use the matrix.mapPoints(float[] points) and you will be fine.
FYI, you can easily do it the other way around too. If you want to know what screen coordinate maps to which point in world space, e.g. for tapping; the inverse matrix can be used for that. It can be obtained via the matrix.invert(Matrix out) method. Use its mapPoints() for the coordinate mapping then.
Here are the official docs:
mapPoints(), invert(), getMatrix()
If you'd like know the corners of your screen relative to your original canvas, you can use canvas.getClipBounds(). This returns a Rect with edge coordinates relative to your original canvas. For instance, if you start off with a canvas size of 320 x 480 and call
canvas.scale(2, 2, getWidth()/2, getHeight()/2);
and then
canvas.getClipBounds();
you will have a Rect (call this rect) where
rect.top == 120
rect.bottom == 360
rect.left == 80
rect.right == 240