I have this program to detect objects in a binarized BufferedImage, which the image is a multiple choice answer sheet.
I'm trying to use 4-Connectivity as in to detect each object (answer on the sheet).
So far what I have in hand as source is these:
http://en.wikipedia.org/wiki/Connected-component_labeling
http://homepages.inf.ed.ac.uk/rbf/HIPR2/label.htm
and I have came up with this, following instructions from Wikipedia:
if(getPixel(image, x, y) != 0){
if(getPixel(image, x-1, y) !=0){
System.out.println("we are in the same region");
region[x][y] = region[x-1][y];
}
else if(getPixel(image, x-1, y) !=0 && getPixel(image, x, y-1) !=0){
System.out.println("North and West pixels belong to the same region and must be merged");
region[x][y] = Math.min(region[x-1][y], region[x][y-1]);
}
else if( getPixel(image, x-1, y) ==0 && getPixel(image, x, y-1) !=0){
System.out.println("Assign the label of the North pixel to the current pixel");
region[x][y] = region[x][y-1];
}
else if(getPixel(image, x-1, y) ==0 && getPixel(image, x, y-1) ==0){
System.out.println("Create a new label id and assign it to the current pixel");
cpt++;
region[x][y] = cpt;
}
But the problem is it creates 51 regions! and it only prints a couple of top pixels of each object (not all pixels).
Can anyone please help me to find what the problem is and how can I detect my objects?
I would appreciate any help.
You probably get a lot of regions because you do not seem to merge equal labels. There is no code for storing equal labels in your code snippet. The algorithm is a two-pass algorithm where the first pass assigns labels and the second pass merges equal labels.
Here are the condition checks quoted from the Wikipedia page:
Conditions to check:
Does the pixel to the left (West) have the same value as the current pixel?
Yes – We are in the same region. Assign the same label to the current pixel
No – Check next condition
Do both pixels to the North and West of the current pixel have the same value as the current pixel but not the same label?
Yes – We know that the North and West pixels belong to the same region and must be merged. Assign the current pixel the minimum of the North and West labels, and record their equivalence relationship
No – Check next condition
Does the pixel to the left (West) have a different value and the one to the North the same value as the current pixel?
Yes – Assign the label of the North pixel to the current pixel
No – Check next condition
Do the pixel's North and West neighbors have different pixel values than current pixel?
Yes – Create a new label id and assign it to the current pixel
Your second condition check,
else if(getPixel(image, x-1, y) !=0 && getPixel(image, x, y-1) !=0)
does not check if the left pixel and up pixel have different labels.
Furthermore, just like supersam654 mentions in the comment, the first else if will never be called. It seems like condition check (1) and (2) on the Wikipedia page should be in the opposite order. That is, first check if the left and up pixel have the same value as the current pixel but not the same label. Then, if that check fails, check if the left pixel has the same value as the current.
So try the following:
Add the label condition to your second condition check.
Switch the order of your first two condition checks.
Keep track of equal labels (i.e., which labels represent the same region).
Do a second pass over the image and merge equal labels.
I hope this helps.
While I'm not entirely sure that this answers your question, I would modify your code to look like this:
if(getPixel(image, x, y) != 0){
if(getPixel(image, x-1, y) !=0){
if(getPixel(image, x, y-1) !=0) {
System.out.println("North and West pixels belong to the same region and must be merged");
region[x][y] = Math.min(region[x-1][y], region[x][y-1]);
} else {
System.out.println("we are in the same region");
region[x][y] = region[x-1][y];
}
} else if(getPixel(image, x-1, y) ==0) {
if(getPixel(image, x, y-1) !=0) {
System.out.println("Assign the label of the North pixel to the current pixel");
region[x][y] = region[x][y-1];
} else if (getPixel(image, x, y-1) ==0) {
System.out.println("Create a new label id and assign it to the current pixel");
cpt++;
region[x][y] = cpt;
}
}
Related
I've got a program that can hexagonal tiles using images that are loaded into it. It creates these tiles images by replacing any pixels outside the hexagon with transparent pixels using a function called inHex(). This works.
Now I want to create a border on the inner edge of the hexagon. So I modified a copy of inHex(), changing the &&s to ||s, and adding two lines for the flat edges. The problem is that only the upper borders get drawn.
I've tried changing the order of the statements, and replacing all of the ors with separate if statements, no dice. Using System.out.print line in the loop I know hexEdge() doesn't return true when it should except for the upper lines.
public boolean hexEdge(int x, int y)
{
/**
* returns true if the coordinates are inside the hexagon bounded by the width and height of the tile
*/
double slope = (tileHeight/2.0)/(tileWidth);
boolean inside=false;
//check in acordance to sides
if (x<(tileWidth/2 +1) )
{
inside=
( y == (int)(0.25*tileHeight-slope*x) ) ||//this works
( y == (int)(0.75*tileHeight+slope*x) ) ||
(x==0 && y>(int)(0.25*tileHeight) && y<=(int)(0.25*tileHeight) );
}else {
int x2=x-tileWidth/2;
inside =
(y == (int)(0+slope*x2) )||//this works
(y == (int)(tileHeight -slope*x2 ) )||
(x==tileWidth-1 && y>(int)(0.25*tileHeight) && y<=(int)(0.25*tileHeight) );
}
return inside;
}
I have a set of values say
LatLong1=(lon=74.663085,lat=22.67578)
LatLong2=(lon=80.663085,lat=28.67578)
These are the latitude and longitude values of a rectangular bounded region. LatLong1 is the left and bottom boundary values and LatLong2 is the right and top boundary values.
Now I need to check if some object lies within this boundary at current time.
If I have the object's position with 75.67 and 26.89 as latitude longitude respectively. How do I check whether these values lies within the above mentioned LatLong1 , LatLong2 values?
I've to guess your objects for which pertenency you need to check is a rentangle too, with values:
LatLong1=(lon=x0,lat=y0)
LatLong2=(lon=x1,lat=y1)
If this is the case, and both graphical structures are tied to the same origin of coordenates, the conditions to check is:
if( (x0>=lon0 && x1<=lon1 ) && (y0>=lat0 && y1<=lat1 ) )
return true;
else return false;
If you need to check if a point is inside a rectangle, being the coordinates of the point (x0,y0):
if( (x0>=lon0 && x0<=lon1 ) && (y0>=lat0 && y0<=lat1 ) )
return true;
else return false;
I don't know openlayer. But isn't it like finding whether a point (x,y) is in a rectangle whose lower left is (x1,y1) and top right is (x2, y2)?
In that case (x,y) is in the rectangle, if (x>x1 && x<x2 && y>y1 && y<y2)
You could create a rectangle. With your two points you can compute the upper left corner and the dimension.
Rectangle rect = new Rectangle(upperLeftCorner, dimension);
You want to check if a point (x,y) lies in the rectangle:
Point p = new Point(x,y);
rect.contains(p);
I've got a ball that I can move around on a map consisting of equally sized tiles. The player should not be able to walk over the tiles that are darker and have a black border. I've got a multidimensional array of the tiles that I use to check which tiles are solid.
I would like the player to slide against the wall if he is moving both horizontally and vertically into it. The problem is that if he does that he sticks to the wall. I managed to get it working perfectly on each axis, but separately. Here is my code for the horizontal collision checking:
if (vx < 0) {
// checks for solid tiles left of the player
if (level.isBlocked(i, j) || level.isBlocked(i, jj)) {
x = side * (i + 1); // moves player to left side of tile
vx = 0;
}
} else if (vx > 0) {
// checks for solid tiles right of the player
if (level.isBlocked(ii, j) || level.isBlocked(ii, jj)) {
x = (ii * side) - getWidth(); // moves player to right side of tile
vx = 0;
}
}
The level.isBlocked() method checks if that index of the array is occupied by a solid tile. The i and j variables is which index in the array the player's top right corner is located on. The ii and jj variables is which index in the array the player's bottom right corner is located on.
This works fine, but then if I add the same chunk of code beneath but replacing x with y, vx with vy and so on the problem occurs. So I can add either the horizontal or vertical collision handling and it works, but not at the same time. I've seen a few articles explaining I have to separate them or something, but I didn't understand much of them. How can I check collision on both axes and keep the sliding effect?
I finally got it to work. Angelatlarge's answer was helpful in understanding the problem, but I decided to start from scratch. I ended up first calculating the new x and y position and storing them in separate variables. Then I checked the tile under the middle left of the player and the same with the middle right. I then set a boolean to true if the player was standing on a tile because of his horizontal speed. If there was no collision I set the real x variable to the new one I calculated earlier. I then repeated the same thing for the vertical collision.
This is for the horizontal checking:
float newX = x + vx * delta;
boolean xCollision = false;
if (vx < 0) {
int i = level.toIndex(x);
int j = level.toIndex(y + getHeight() / 2);
xCollision = level.isBlocked(i, j);
} else if (vx > 0) {
int i = level.toIndex(x + getWidth());
int j = level.toIndex(y + getHeight() / 2);
xCollision = level.isBlocked(i, j);
}
if (!xCollision) x = newX;
The problem is that with the setup you have, given a block and the player position, and also given the fact that they overlap, you don't know whether the player collided with a vertical or a horizontal wall of the block. So see this more clearly consider the following block and two collision paths
The top path will collide with the left wall, and requires a vx=0; (cessation of horizontal movement), while the bottom path collides with the bottom wall and will require vy=0;, or stopping of the vertical movement.
I think in order to do the kind of collision detection you want, you will want to compute intersections of the player path and the walls of the blocks, not just checking whether the player overlaps a block. You could hack the desired behavior by computing the overlapping rectange of the player rectangle and the block rectangle. Consider the following situation:
where the red seqare represents your player. The fact that the overlap rectangle (the small rectangle occupied where the player is on top of the block) is more wide than it is tall suggests that it was the vertical collision that happened, not a horizontal. This is not foolproof, however. And it still requires you to be able to access the shape of the block, rather than just stesting if a part of the player rectangle overlaps a block.
I'm trying to make a Minesweeper-like game in Java and I've got most of it to work. Something I need help with is FloodFill - http://en.wikipedia.org/wiki/Flood_fill.
Can someone explain how it works? I've looked online but I don't really understand the explanation so I thought it would be easier to ask here.
In my Minesweeper I have:
JButton[] btn = new JButton[100]//buttons being clicked and displaying the values/bombs
int[] mines = new int[100];//int array holding the values for each button.
The grid is a 10x10 grid so say the button you clicked was btn[14],
btn[4] // north of btn[14](14-10)
btn[24] // south of btn[14](14+10)
btn[13] // west of btn[14](14-1)
btn[15] // east of btn[14](14+1)
So back to the question, could someone explain it to me?
EDIT:
I changed my code to be 2D so instead of the above one it is now
btn[1][4]//row one, column 4
When the button is clicked, i want it to check a variable called mines[][] which has the values and if the value is equal to 0(around the initial clicked) it changes the BG
btn[x][y].setBackground(Color.GRAY);
Its a recursive algorithm. You start at some start position in a 2D Grid [x,y], then look in all directions and fill them if you can. If (x,y) can't be filled, return.
void floodFill( int x, int y ) {
if ( btn( x, y ) isFillable ) {
fillBtn(x,y);
floodFill( x+1, y );
floodFill( x-1, y );
floodFill( x, y-1 );
floodFill( x, y+1 );
} else {
return;
}
}
(ommited check for boundaries of grid)
I guess you are mainly asking about floodfill.
Actually it is a simple and common recursive algorithm. It can solve whatever your data structure is 1D or 2D.
For 2D version, #RMoeller has given the answer.
For your previous 1D declaration, it is also similar like this:
void floodFill( int pos ) {
if ( btn( pos ) isFillable ) {
fillBtn(pos);
floodFill( pos+1 );
floodFill( pos-1 );
floodFill( pos+10 );
floodFill( pos-10 );
} else {
return;
}
}
One thing you should remember is that floodfill, and almost all recursive algorithms, need to check boundaries. Otherwise you may get into an infinite loop or get a wrong result.
In above example (1D version), you should check whether:
pos >= 1 && pos <= 100
Similar to 2D version which is to check:
x >= 1 && x <= 10 && y>=1 && y <=10
Hope this helps.
I'm trying to think of a way to efficiently and neatly determine whether a valid move is being made with a bishop in chess.
The piece will be moved from srcX,srcY to dstX,dstY
This is part of one of my ideas:
if(srcX < dstX && srcY < dstY) {
// Moving towards the top right of the board
// Determine the decrease in X coordinate
int deltaX = dstX-srcX;
// If the move is valid, the Y coordinate will have decreased by the same number as X
int validY = dstY-deltaX;
if(validY == srcY) {
validMove = true;
}
}
but it's going to be a bit long winded, doing that for ever corner.. Can anyone think of a nicer way?
I would break it up into two steps.
1) Is it a valid destination?
2) Are there obstructions?
The first is easy to calculate. Since a bishop can only move diagonals the deltaX and deltaY must be equal.
So, if( abs(srcX-dstX) == abs(srcY-dstY) )
That rules out logically invalid moves.
Then it is a simple matter iterating through the positions in between as you have done to check for obstructions.
If it's a diagonal the x and y move shoudl be the same, so...
return Math.abs(srcx - dstx) == Math.abs(srcy - dsty);
The move is valid if:
Destx-Desty = SourceX - SourceY OR
16 - DestX- DestY = SourceX - SourceY