I'm implementing a virtual world for an autonomous machine. The machine has to be able to detect the center coordinates of different colored ellipses in the screen.
The image is given to the machine in an width x height array, with ints for the colors (for example white = 0, red = 8, blue = 9 etc).
Also if the ellipses are behind eachother you do not have to calculate the excact middle point.
I was thinking of some sort of sweepline algorithm? Which will keep the first time it sees a color and when it exits it gets the average between those two? and then do it horizontally and vertically. Then you have the middle coordinates.
Would this be efficient? Any other ideas?
Thanks in advance
Related
I couldn't find any satisfying answer on that topic. I want to make a program that will get snapshots from camera above the pool table and detect balls. I am using OpenCV and Java. My algorithm now is basically:
blurring image -> converting RGB to HSV -> splitting into 3 planes -> using Canny() on H plane -> using HoughCircles() method to detect balls
This algorithm detects balls quite well, it has problem with two balls only (green and blue, because background of the table is green). But I want to go one step further and:
Detect if the ball belongs to stripes or solids
Set an ID of every ball, stripes would have for example 1-7 and solids 8-14, every ball would have unique ID that doesn't change during the game
Do you have any idea how to implement task #1? My idea is to use inRange() function, but then I'd have to prepare a mask for every ball that detects that one ball in specified range of colors, and do this detection for every ball, am I right? Thanks for sharing your opinions.
#Edit: Here I give you some samples of how my algorithm works. I changed some parameters because I wanted to detect everything, and now it works worse, but it still works with quite nice accuracy. I`ll give you three samples of original image from camera, image where I detect balls (undistorted, with some filters) and image with detected balls.
Recommendation:
If you can mask out the pixels corresponding to a ball, the following method should work to differentiate striped/solid balls based on their associated pixels:
Desaturate the ball pixels and threshold them at some brightness p.
Count the number of white pixels and total pixels within the ball area.
Threshold on counts: if the proportion of white pixels is greater than some threshold q, classify it as a striped ball. Otherwise, it's a solid ball.
(The idea being that the stripes are white, and always at least partially visible, so striped balls will have a higher proportion of white pixels).
Sample Testing:
Here's an example of this applied (by hand, with p = 0.7) to some of the balls in the unrectified image, with final % white pixels on the right.
It looks like a classification threshold of q = 0.1 (minimum 10% white pixels to be a striped ball) will distinguish the two groups, although it would be ideal to tune the thresholds based on more data.
If you run into issues with shadowed balls using this method, you also can try rescaling each ball's brightnesses before thresholding (so that the brightnesses span the full range 0, 1), which should make the method less dependent on the absolute brightness.
While working on Projectiles I thought that it would be a good idea to rotate the sprite as well, to make it look nicer.
I am currently using a 1-Dimensional Array, and the sprite's width and height can and will vary, so it makes it a bit more difficult for me to figure out on how to do this correctly.
I will be honest and straight out say it: I have absolutely no idea on how to do this. There have been a few searches that I have done to try to find some stuff, and there were some things out there, but the best I found was this:
DreamInCode ~ Rotating a 1-dimensional Array of Pixels
This method works fine, but only for square Sprites. I would also like to apply this for non-square (rectangular) Sprites. How could I set it up so that rectangular sprites can be rotated?
Currently, I'm attempting to make a laser, and it would look much better if it didn't only go along a vertical or horizontal axis.
You need to recalculate the coordinate points of your image (take a look here). You've to do a matrix product of every point of your sprite (x, y) for the rotation matrix, to get the new point in the space x' and y'.
You can assume that the bottom left (or the bottom up, depends on your system coordinate orientation) of your sprite is at (x,y) = (0,0)
And you should recalculate the color too (because if you have a pure red pixel surrounded by blue pixel at (x,y)=(10,5) when you rotate it can move for example to (x, y)=(8.33, 7.1) that it's not a real pixel position because pixel haven't float coordinate. So the pixel at real position (x, y)=(8, 7) will be not anymore pure red, but a red with a small percentage of blue)... but one thing for time.
It's easier than you think: you only have to copy the original rectangular sprites centered into bigger square ones with transparent background. .png files have that option and I think you may use them.
This is a seemingly simple game mechanic that I've been trying to figure out how to do.
To try and explain I will describe a idea (problem):
Basically we say there's a vertical line that is centered in the
screen.
We have a sprite object that changes it's horizontal velocity to
dodge missiles, however in doing that the object would just drift
away.
How can I add a strong gravity force to the horizontal "center line"
of my screen so that my sprite will "fall" back into it every time it
boosts its velocity outwards?
I could post my source code but it wouldn't be too helpful to solving the question in this particular situation.
I've searched around for days trying to figure this out so any help especially with code examples would be very helpful!
I've programmed this type of thing in the past. Gravity (in physics) is an acceleration, so
1) if the sprite is to the right of the line you subtract from its horizontal velocity every 1/n seconds, and
2) if the sprite is to the left of the line you add to its horizontal velocity every 1/n seconds.
Experiment with adding/subtracting a constant, or with adding/subtracting a number that increases the farther the sprite is from the center line.
Either way you do it, that's going to create a pendulum effect. You'll also have to add a dampening factor if you don't want that. One simple approach is that if the sprite is headed away from the center line, the value you add/subtract is larger than if the sprite is heading back towards the center line. So the "gravity" that pulls the sprite to a stop is greater than the gravitational acceleration that brings the sprite back to the center line.
As you are using libgdx you should also use camera. So you don't have to calculate verything in pixels. So for example you say my screen is 16 worldunits width and 9 world units height (16/9 aspect ratio). So you can say the center of gravity is in the center of that 16, so at 8.5 if i am not wrong. Now you can say: if (player.center.x < 8.5f) { player.xSpeed += GRAVITY_HORIZONTAL } and if (player.center.x > 8.5) { player.xSpeed -= GRAVITY_HORIZONTAL }. In this case the gravity is a constant value. But as #BrettFromLA said you can also let the value grow if the distance to the center grows.
I'm using Java Graphics2D to generate this map with some sort of tinted red overlay over it. As you can see, the overlay gets cut off along the image boundary on the left side:-
After demo'ing this to my project stakeholders, what they want is for this overlay to clip along the map boundary with some consistent padding around it. The simple reason for this is to give users the idea that the overlay extends outside the map.
So, my initial thought was to perform a "zoom and shift", by creating another larger map that serves as a "cookie cutter", here's my simplified code:-
// polygon of the map
Polygon minnesotaPolygon = ...;
// convert polygon to area
Area minnesotaArea = new Area();
minnesotaArea.add(new Area(minnesotaPolygon));
// this represents the whole image
Area wholeImageArea = new Area(new Rectangle(mapWidth, mapHeight));
// zoom in by 8%
double zoom = 1.08;
// performing "zoom and shift"
Rectangle bound = minnesotaArea.getBounds();
AffineTransform affineTransform = new AffineTransform(g.getTransform());
affineTransform.translate(-((bound.getWidth() * zoom) - bound.getWidth()) / 2,
-((bound.getHeight() * zoom) - bound.getHeight()) / 2);
affineTransform.scale(zoom, zoom);
minnesotaArea.transform(affineTransform);
// using it as a cookie cutter
wholeImageArea.subtract(minnesotaArea);
g.setColor(Color.GREEN);
g.fill(wholeImageArea);
The reason I'm filling the outside part with green is to allow me to see if the cookie cutter is implemented properly. Here's the result:-
As you can see, "zoom and shift" doesn't work in this case. There is absolutely no padding at the bottom right. Then, I realized that this technique will not work for irregular shape, like the map... and it only works on simpler shapes like square, circle, etc.
What I want is to create consistent padding/margin around the map before clipping the rest off. To make sure you understand what I'm saying here, I photoshopped this image below (albeit, poorly done) to explain what I'm trying to accomplish here:-
I'm not sure how to proceed from here, and I hope you guys can give me some guidance on this.
Thanks.
I'll just explain the logic, as I don't have time to write the code myself. The short answer is that you should step through each pixel of the map image and if any pixels in the surrounding area (i.e. a certain distance away) are considered "land" then you register the current pixel as part of the padding area.
For the long answer, here are 9 steps to achieve your goal.
1. Decide on the size of the padding. Let's say 6 pixels.
2. Create an image of the map in monochrome (black is "water", white is "land"). Leave a margin of at least 6 pixels around the edge. This is the input image: (it isn't to scale)
3. Create an image of a circle which is 11 pixels in diameter (11 = 6*2-1). Again, black is empty/transparent, white is solid. This is the hit-area image:
4. Create a third picture which is all black (to start with). Make it the same size as the input image. It will be used as the output image.
5. Iterate each pixel of the input image.
6. At that pixel overlay the hit-area image (only do this virtually, via calculation), so that the center of the hit-area (the white circle) is over the current input image pixel.
7. Now iterate each pixel of the hit-area image.
8. If the any white pixel of the hit-area image intersects a white pixel of the input image then draw a white pixel (where the center of the circle is) into the output image.
9. Go to step 5.
Admittedly, from step 6 onward it isn't so simple, but it should be fairly easy to implement. Hopefully you understand the logic. If my explanation is too confusing (sorry) then I could spend some time and write the full solution (in Javascript, C# or Haskell).
I have a program that needs to take in a photo taken by an iphone (or any kind of decent camera) of a 7x10 grid with a thick black boarder around the edges. This image can be received rotated to the right or to the left (there's no need to worry about skew). I have an image of the grid in its original state already, but I need to get the picture that I'm taking in and rotate it to its "perfect/original" state.
Idea 1: Performance Hog/Bad Results
Threshold the picture that I receive and the perfect grid Image I already have. Compare each pixel for 0 rotation, get a total score, and save it. Do do this rotating the image of increments by 1 to 359. The lowest score is the rotation we need to get the picture back to its original state.
Idea 2: Still Unsure How To Go About Doing This
Threshold the picture that I receive and the perfect grid Image I already have. Draw a a line through the center of the picture vertically and horizontally. Find the rotation based on the black pixel count that the vertical and horizontal line passed through. This would require some sort of Trigonometry that I'm not to great with understanding.
Does anyone have any other ideas for getting this working?
Any help for pointing me in the right direction would be greatly appreciated!
Thanks!
Instead of drawing one horizontal and one vertical line, draw instead two horizontal lines (say, each at a third of the picture). Only look at the left halves of these lines and calculate how many black pixels there are on the path of each (a1 and a2). You also have to keep track of the distance between the two red lines, so the number of pixels d.
Using this notation in the figure above, your desired angle is:
alpha=atan2((a2-a1),d)
and a counterclockwise rotation by alpha will bring the white portion of the picture into proper alignment.