How to identify and crop a rectangle from a picture using java - java

I am bit new to image processing. What i'm doing is recognize rectangular shapes (not overlapped) of a given image and create separate images by crop them out. So the output images should be without a border. I tried some examples but none of it did the trick. FYI: those horizontal rectangles are with black lined border in a white background. There are some symbols inside them.
does anyone have a clue or an similar example? regards on help

This is pseudo C code, but my idea is there.
 Main Structure
struct data {
float pixelsNb;
int currentX;
int currentY;
}
Main loop 
void mainLoop(){
void **imgData = getPixelsFromImage("toto.png");
struct dataRight, dataDown;
loopRight(&dataRight, imgData);
loopDown(&dataDown, imgData);
// now you data right struct contains the number of
//following black pixels to the right
// and you data down, same for the down side.
if (dataRight->pixelNb == dataDown->pixelNb) // not really, should be in %
printf("There's a square !");
}
void loopRight(struct data *dataCurrent, void **imgData){
if (imgData[dataCurrent->currentY][dataCurrent->currentX] == color(0x0)){
dataCurrent->pixelNb++;
dataCurrent->currentX++;
loopRight(dataCurrent, imgData);
}
}
void loopDown(struct data *dataCurrent, void **imgData){
if (imgData[dataCurrent->currentY][dataCurrent->currentX] == color(0x0)){
dataCurrent->pixelNb++;
dataCurrent->currentY++;
loopDown(dataCurrent, imgData);
}
}
}
This is really not accurate. Don't try to copy and past, it will fail.
But you have the idea here.
Also note that i only check the line in the up side, and the linde on the left side
XXXXXXXX
X o
X o
X o
X o
Xooooooo
X are checked, not o
The algo here is just to check if there's the same number of X on the left side and on the top side.
If it's the case, you have a square.
For sure, if you want to find a rectangle, you have to check down side and right side.
Then, it would be :
If there's the same amount on the left side + down side and on the top side + right side, then we have a rectangle.
That sort of algorythm should do the trick.

Related

Implementing Bilinear Interpolation on Android, with support to Alpha values

Background
I'm trying to figure out how to best convert some old Java code to C/C++ (here) of resizing a bitmap using Bilinear Interpolation.
For this, I've decided to also make the code usable on Android itself using Java (on Android), to check that it still works as I've left it, and maybe also allow others to use it.
The problem
Even though everything seem to work nicely, I can see some weird black artifacts when resizing. I'm almost sure why they appear:
notice the gray color on the left of the yellow box and on the right of the red one.
I'd assume the color should be between yellow and white for the first case, and between red and white on the second one.
However, I think the color I get is because of the alpha values aren't handled correctly (the white area is actually transparent).
That's because if I set real white color to the bitmap, I get the correct result.
The original code (that works on PC)
/** class for resizing imageData using the Bilinear Interpolation method */
public class BilinearInterpolation
{
/** the method for resizing the imageData using the Bilinear Interpolation algorithm */
public static void resize(final ImageData inputImageData,final ImageData newImageData,final int oldWidth,final int oldHeight,final int newWidth,final int newHeight)
{
// position of the top left pixel of the 4 pixels to use interpolation on
int xTopLeft,yTopLeft;
int x,y,lastTopLefty;
final float xRatio=(float)newWidth/(float)oldWidth,yratio=(float)newHeight/(float)oldHeight;
// Y color ratio to use on left and right pixels for interpolation
float ycRatio2=0,ycRatio1=0;
// pixel target in the src
float xt,yt;
// X color ratio to use on left and right pixels for interpolation
float xcRatio2=0,xcratio1=0;
// copy data from source image to RGB values:
RGB rgbTopLeft,rgbTopRight,rgbBottomLeft=null,rgbBottomRight=null,rgbTopMiddle=null,rgbBottomMiddle=null;
RGB[][] startingImageData;
startingImageData=new RGB[oldWidth][oldHeight];
for(x=0;x<oldWidth;++x)
for(y=0;y<oldHeight;++y)
{
rgbTopLeft=inputImageData.palette.getRGB(inputImageData.getPixel(x,y));
startingImageData[x][y]=new RGB(rgbTopLeft.red,rgbTopLeft.green,rgbTopLeft.blue);
}
// do the resizing:
for(x=0;x<newWidth;x++)
{
xTopLeft=(int)(xt=x/xRatio);
// when meeting the most right edge, move left a little
if(xTopLeft>=oldWidth-1)
xTopLeft--;
if(xt<=xTopLeft+1)
{
// we are between the left and right pixel
xcratio1=xt-xTopLeft;
// color ratio in favor of the right pixel color
xcRatio2=1-xcratio1;
}
for(y=0,lastTopLefty=Integer.MIN_VALUE;y<newHeight;y++)
{
yTopLeft=(int)(yt=y/yratio);
// when meeting the most bottom edge, move up a little
if(yTopLeft>=oldHeight-1)
yTopLeft--;
// we went down only one rectangle
if(lastTopLefty==yTopLeft-1)
{
rgbTopLeft=rgbBottomLeft;
rgbTopRight=rgbBottomRight;
rgbTopMiddle=rgbBottomMiddle;
rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
rgbBottomMiddle=new RGB((int)(rgbBottomLeft.red*xcRatio2+rgbBottomRight.red*xcratio1),(int)(rgbBottomLeft.green*xcRatio2+rgbBottomRight.green*xcratio1),(int)(rgbBottomLeft.blue*xcRatio2+rgbBottomRight.blue*xcratio1));
}
else if(lastTopLefty!=yTopLeft)
{
// we went to a totally different rectangle (happens in every loop start,and might happen more when making the picture smaller)
rgbTopLeft=startingImageData[xTopLeft][yTopLeft];
rgbTopRight=startingImageData[xTopLeft+1][yTopLeft];
rgbTopMiddle=new RGB((int)(rgbTopLeft.red*xcRatio2+rgbTopRight.red*xcratio1),(int)(rgbTopLeft.green*xcRatio2+rgbTopRight.green*xcratio1),(int)(rgbTopLeft.blue*xcRatio2+rgbTopRight.blue*xcratio1));
rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
rgbBottomMiddle=new RGB((int)(rgbBottomLeft.red*xcRatio2+rgbBottomRight.red*xcratio1),(int)(rgbBottomLeft.green*xcRatio2+rgbBottomRight.green*xcratio1),(int)(rgbBottomLeft.blue*xcRatio2+rgbBottomRight.blue*xcratio1));
}
lastTopLefty=yTopLeft;
if(yt<=yTopLeft+1)
{
// color ratio in favor of the bottom pixel color
ycRatio1=yt-yTopLeft;
ycRatio2=1-ycRatio1;
}
// prepared all pixels to look at, so finally set the new pixel data
newImageData.setPixel(x,y,inputImageData.palette.getPixel(new RGB((int)(rgbTopMiddle.red*ycRatio2+rgbBottomMiddle.red*ycRatio1),(int)(rgbTopMiddle.green*ycRatio2+rgbBottomMiddle.green*ycRatio1),(int)(rgbTopMiddle.blue*ycRatio2+rgbBottomMiddle.blue*ycRatio1))));
}
}
}
}
The converted code (for Android)
/** class for resizing imageData using the Bilinear Interpolation method */
public class BilinearInterpolation
{
/** the method for resizing the imageData using the Bilinear Interpolation algorithm */
public static void resize(final Bitmap input,final Bitmap output)
{
final int oldHeight=input.getHeight(),oldWidth=input.getWidth();
final int newHeight=output.getHeight(),newWidth=output.getWidth();
// position of the top left pixel of the 4 pixels to use interpolation on
int xTopLeft,yTopLeft;
int x,y,lastTopLefty;
final float xRatio=(float)newWidth/(float)oldWidth,yratio=(float)newHeight/(float)oldHeight;
// Y color ratio to use on left and right pixels for interpolation
float ycRatio2=0,ycRatio1=0;
// pixel target in the src
float xt,yt;
// X color ratio to use on left and right pixels for interpolation
float xcRatio2=0,xcratio1=0;
int rgbTopLeft=0,rgbTopRight=0,rgbBottomLeft=0,rgbBottomRight=0,rgbTopMiddle=0,rgbBottomMiddle=0;
// do the resizing:
for(x=0;x<newWidth;x++)
{
xTopLeft=(int)(xt=x/xRatio);
// when meeting the most right edge, move left a little
if(xTopLeft>=oldWidth-1)
xTopLeft--;
if(xt<=xTopLeft+1)
{
// we are between the left and right pixel
xcratio1=xt-xTopLeft;
// color ratio in favor of the right pixel color
xcRatio2=1-xcratio1;
}
for(y=0,lastTopLefty=Integer.MIN_VALUE;y<newHeight;y++)
{
yTopLeft=(int)(yt=y/yratio);
// when meeting the most bottom edge, move up a little
if(yTopLeft>=oldHeight-1)
yTopLeft--;
// we went down only one rectangle
if(lastTopLefty==yTopLeft-1)
{
rgbTopLeft=rgbBottomLeft;
rgbTopRight=rgbBottomRight;
rgbTopMiddle=rgbBottomMiddle;
rgbBottomLeft=input.getPixel(xTopLeft,yTopLeft+1);
rgbBottomRight=input.getPixel(xTopLeft+1,yTopLeft+1);
rgbBottomMiddle=Color.argb((int)(Color.alpha(rgbBottomLeft)*xcRatio2+Color.alpha(rgbBottomRight)*xcratio1),//
(int)(Color.red(rgbBottomLeft)*xcRatio2+Color.red(rgbBottomRight)*xcratio1),//
(int)(Color.green(rgbBottomLeft)*xcRatio2+Color.green(rgbBottomRight)*xcratio1),//
(int)(Color.blue(rgbBottomLeft)*xcRatio2+Color.blue(rgbBottomRight)*xcratio1));
}
else if(lastTopLefty!=yTopLeft)
{
// we went to a totally different rectangle (happens in every loop start,and might happen more when making the picture smaller)
rgbTopLeft=input.getPixel(xTopLeft,yTopLeft);
rgbTopRight=input.getPixel(xTopLeft+1,yTopLeft);
rgbTopMiddle=Color.argb((int)(Color.alpha(rgbTopLeft)*xcRatio2+Color.alpha(rgbTopRight)*xcratio1),//
(int)(Color.red(rgbTopLeft)*xcRatio2+Color.red(rgbTopRight)*xcratio1),//
(int)(Color.green(rgbTopLeft)*xcRatio2+Color.green(rgbTopRight)*xcratio1),//
(int)(Color.blue(rgbTopLeft)*xcRatio2+Color.blue(rgbTopRight)*xcratio1));
rgbBottomLeft=input.getPixel(xTopLeft,yTopLeft+1);
rgbBottomRight=input.getPixel(xTopLeft+1,yTopLeft+1);
rgbBottomMiddle=Color.argb((int)(Color.alpha(rgbBottomLeft)*xcRatio2+Color.alpha(rgbBottomRight)*xcratio1),//
(int)(Color.red(rgbBottomLeft)*xcRatio2+Color.red(rgbBottomRight)*xcratio1),//
(int)(Color.green(rgbBottomLeft)*xcRatio2+Color.green(rgbBottomRight)*xcratio1),//
(int)(Color.blue(rgbBottomLeft)*xcRatio2+Color.blue(rgbBottomRight)*xcratio1));
}
lastTopLefty=yTopLeft;
if(yt<=yTopLeft+1)
{
// color ratio in favor of the bottom pixel color
ycRatio1=yt-yTopLeft;
ycRatio2=1-ycRatio1;
}
// prepared all pixels to look at, so finally set the new pixel data
output.setPixel(x,y,Color.argb(//
(int)(Color.alpha(rgbTopMiddle)*ycRatio2+Color.alpha(rgbBottomMiddle)*ycRatio1),//
(int)(Color.red(rgbTopMiddle)*ycRatio2+Color.red(rgbBottomMiddle)*ycRatio1),//
(int)(Color.green(rgbTopMiddle)*ycRatio2+Color.green(rgbBottomMiddle)*ycRatio1),//
(int)(Color.blue(rgbTopMiddle)*ycRatio2+Color.blue(rgbBottomRight)*ycRatio1)));
}
}
}
}
The question:
How should I handle the alpha channel?
What should be done to the algorithm in this case?
Should I multiple the (combined) alpha channel (and divide by 255) on each pixel I output?
By the way, I know there is already a built in solution for this using the framework, but the purpose here is to learn from my mistakes, and also provide something that works well on the C/C++ solution I'm making.

Java Slick2D Texture Issue

I was working on my game today and I found that the top of my trees have a weird texture problem where they overlap each other with a black box. It is only the top of the trees and the tops are split up into 9 blocks all with their own image. The 9 images are transparent, each is 32x32, and I've tried it a bunch of different ways with no luck. Does anyone know what the problem with the texture is? This isn't a generation question but an OpenGL/Slick2D question about textures. Here's a screenshot of the problem: Screenshot
EDIT: Here's a piece of the rendering code.
for (int x = (int) (World.instance.camera.getX() / Block.WIDTH); x < width; x++)
{
for (int y = (int) (World.instance.camera.getY() / Block.HEIGHT); y < height; y++)
{
try
{
if (blocks[x][y] != Block.AIR.getId())
{
g.drawImage(textureCache.get(blocks[x][y]), x * Block.WIDTH, y * Block.HEIGHT);
}
}
catch(Exception ex)
{
}
}
}
Looking at your code, it seems that you are only drawing a single image at each 32x32 square. So if tree A is in front of tree B, but tree A only partly fills a square, then tree A is the one listed in your blocks array and therefore retrieved from your "texture cache"; and not tree B. So tree A is all that is drawn.
To resolve this, your blocks structure would need to be three dimensional - basically, for each 32x32 square, you'd need some kind of "stack" of references to all the images whose corresponding object is found in that square. Then when you draw that square, draw all of the images in order, from the back to the front.

How to do you make a click area be only part of a non rectangular part of an image?

I am working with images only and the dimensions of the window that I am using to view my application may be different on different systems. I have a mouse action listener that is listening for clicks on the main view of my program. I have a rounded rectangle that looks like a button. I want to make it so that way the mouse action listener only listens to the area of the rounded rectangle rather than the entire image on all systems. Like the title says, not the entire image has content, in particular, the corners don't look like they are part of the image, so I don't want to allow the user to be able to click on parts of the image without content and get the same result as if they clicked on the part with content.
My image looks similar to this
(source: youthedesigner.com)
So I only want the program to do something if the user clicks on the button inside the image rather than the nice stuff around the button.
This is what I have right now to listen to clicks:
#Override
public void mouseClicked(MouseEvent e) {
for(int i = 0; i <= 200; i++) {
if(e.getY() >= 100+i && e.getY() <= 300) {
if(e.getX() >= 10+100-Math.pow(10000-(Math.pow((i-100),2.0)),.5)) && e.getX() <= 10+400-Math.pow(10000-(Math.pow((i-100),2.0)),.5))) {
// do stuff
i = 201;
}
}
}
}
The math equation I am using in my code looks like 110-(10000-(y-100)^2)^(1/2)), which, if graphed, would look like an open parenthesis, and 410+(10000-(y-100)^2)^(1/2)), which would look like a close parenthesis 400 units away from the first graph.
The code works fine on my system, but on other systems, it doesn't work at all and I am curious how I could move the location I am listening to to correspond to how the image is scaled.
Thank you very much for any help you can provide.
The for-loop is superfluous.
You could ensure that pixels outside the button (.png) have some transparency, and then check for the alpha color component.
In this case you could add a Rect and look for that:
private boolean insideButton(Rectangle buttonRect, Point mousePt) {
if (buttonRect.contains(mousePt)) {
int r = buttonRect.height() / 2;
if (mousePt.x < r) {
// Left circle with O at (r, r)
int xFromO = r - mousePt.x;
int yFromO = r - mousePt.y;
if (xFromO * xFromO + yFromO * yFromO > r * r) {
return false; // Outside circle
}
}
if (mousePt.x > buttonRect.right - r) {
// Right circle:
...
}
return true;
}
return false;
}
So, I used Joop's answer to solve my problem. His answer wasn't quite what I was looking for, but it gave me the idea I needed to solve my problem. The solution I came to was:
private boolean insideButton(Rectangle buttonRect, Point mousePt) {
if (buttonRect.contains(mousePt)) {
int r = (int)buttonRect.getHeight()/2; // radius of either of the circles that make up the sides of the rectangle
if(mousePt.x <= buttonRect.getWidth()/2) { // if it is on the left of the button
Point center = new Point((int)buttonRect.getX()+r, (int)buttonRect.getY()+r); // the center of the circle on the left
double lengthToPoint = Math.pow(Math.pow(mousePt.x-center.x, 2)+Math.pow(mousePt.y-center.y, 2), 1.0/2); // length from center to the point that the user clicked at
if(lengthToPoint > r && mousePt.x < center.x) { // if it is to the left of the center and out of the circle
return false;
} else {
return true;
}
} else { // if it is on the right, the rest of the code is just about the same as the left circle
Point center = new Point((int)buttonRect.getWidth()-r, (int)buttonRect.getY()+r);
double lengthToPoint = Math.pow(Math.pow(mousePt.x-center.x, 2)+Math.pow(mousePt.y-center.y, 2), 1.0/2);
if(lengthToPoint > r && mousePt.x > center.x) {
return false;
} else {
return true;
}
}
} else {
return false;
}
}
I know it is goes a little overboard with calculations and inefficient, but I wanted to present it this way to show a better idea of how my solution works.
I can think of at least two ways.
The first is to produce a mask image (black and white), where (for example) white would indicate the clickable area. Basically, you could compare the pixel color of the mask based in click pick point of the original image.
The other way would be to build a image map, basically using something like a Shape API to allow for non-rectangular shapes. This would allow to use Shape#contains to determine if the mouse clicked inside it or not
In either case, you need to take into account the x/y position of the original image

Rotate an object right or left 90 degrees and move forward on a grid

I have the following scenario for which i need to create a java program. Any ideas how to implement this:
A small robot which can move on a grid, controlled by simple commands.
In this example, it will move on a 5x5 grid with a simple co-ordinate system - 0,0 is the bottom left, 4,4 is the top right.
We can control the robot by sending it a string consisting of commands, such as the following: RFLFFRF
R means rotate right 90 degrees, L rotate left 90 degrees, and F means move forward one square in the direction the Rover is currently facing. The Rover starts at 0,0 facing North ("up" the grid).
I need to write a program which will accept strings of commands and return the grid position of the obot after those commands
Write a robot class containing a position and an orientation, then implement the three commands (R, L and F) on it. Finally, write code to parse the command sequence and run the commands. Create a new robot, run the command sequence on it and output the position when you're done.
Function:
public int Robot(int input1,int input2,String input3,String input4)
{
int flag=0;
String s[]=input3.spilt("-");
int x=Integer.parseInt(s[0]);
int y=Integer.parseInt(s[1]);
String curface=s[2];
for(int i=0;i0){x--;}else{flag=1;break;}break;
case "E":if(x0){y--;}else{flag=1;break;};
}
}
}
String out="";
if(flag==0)
{
out=""+x+"-"+y+"-"+curface;
}
else
{
out=""+x+"-"+y+"-"+curface+"-ER";
=
}
return out;
}

StackOverflowError with a specific algorithm to color a closed shape

My assignment is to implement an algorithm to color a closed shape starting from a given (x,y) coordinate and "spread" via recursive calls untill it reaches the borders of the shape. So far this is what I've come up with:
private void color(int x, int y) {
g2d.draw(new Line2D.Double(x, y, x, y));
if (!robot.getPixelColor(x - 1, y).equals(Color.BLACK) &&
!robot.getPixelColor(x - 1, y).equals(Color.RED)) {
color(x - 1, y);
} else if (!robot.getPixelColor(x + 1, y).equals(Color.BLACK) &&
!robot.getPixelColor(x - 1, y).equals(Color.RED)) {
color(x + 1, y);
} else if (!robot.getPixelColor(x, y - 1).equals(Color.BLACK) &&
!robot.getPixelColor(x - 1, y).equals(Color.RED)) {
color(x, y - 1);
} else if (!robot.getPixelColor(x, y + 1).equals(Color.BLACK) &&
!robot.getPixelColor(x - 1, y).equals(Color.RED)) {
color(x, y + 1);
}
}
The Robot class' getPixelColor is the only way I found to get the color of a given pixel (as far as I know another would be getRGB, but that only works on Image objects). To my understanding this should work, as the outer lines of the shape are definitely black, and the initial x and y values come from a MouseListener, so they are inside the shape, however I get the following error:
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at sun.java2d.pipe.BufferedContext.validateContext(BufferedContext.java:110)
at sun.java2d.d3d.D3DRenderer.validateContextAA(D3DRenderer.java:42)
at sun.java2d.pipe.BufferedRenderPipe$AAParallelogramPipe.fillParallelogram(BufferedRenderPipe.java:445)
at sun.java2d.pipe.PixelToParallelogramConverter.drawGeneralLine(PixelToParallelogramConverter.java:264)
at sun.java2d.pipe.PixelToParallelogramConverter.draw(PixelToParallelogramConverter.java:121)
at sun.java2d.SunGraphics2D.draw(SunGraphics2D.java:2336)
at dline.DrawingSpace.color(DrawingSpace.java:87)
at dline.DrawingSpace.color(DrawingSpace.java:93)
at dline.DrawingSpace.color(DrawingSpace.java:90)
at dline.DrawingSpace.color(DrawingSpace.java:93)
at dline.DrawingSpace.color(DrawingSpace.java:90)
(drawingSpace is a sub-class of JPanel)
The teacher did tell us that this is memory consuming, however it's supposed to be a working algorithm, so I'm doing something wrong, obviously. Any help would be much appriciated, thank you.
You can try to increase the Stack size: How to increase the Java stack size?
Probably you have a bug in your algorithm, or the shape is too big. What helps if you 'draw' your algorithm on a piece of graph paper. That way you can check your algorithm.
I'm guessing that you're backtracking onto previously visited pixels. The pixel you just drew probably won't be visible to robot until after you return from color, so it will not appear red from the previous painting.
Do you have a reference to the java.awt.Shape? A much simpler way than using the robot would be to use Shape.contains(Point) to see whether it's in the shape you're supposed to draw.
The basic algorithm either way is depth-first traveral. To do a DFS when there are possible cycles, you can record the pixels you've already drawn.
//java.awt.Point
Set<Point> paintedPixels = new HashSet<Point>();
private void color(int x, int y) {
if ( paintedPixels.contains(new Point(x, y)) ) {
//already painted
return;
}
paintedPixels.add(new Point(x, y));
//...
}
Now, this could still result in a very deep search. You might consider instead using a non-recursive breadth-first traveral. See the Wikipedia article on Flood Fill.
The problem with implementing this as a recursive algorithm is that it has (for bigger images) a very high recursion depth.
In Java (and most other imperative programming languages, too) the maximal recursion depth is limited by the amount of stack space for each thread, since it must keep a stack frame for each method invocation there.
You may try smaller images first, and try to increase the stack size with the -xss parameter.
Edit: As pointed out by Mark, the Robot will not get any pixels until your drawing is complete, since often your drawing is double-buffered (i.e. the Swing engine lets you paint first on an image, and draws then the complete image to the screen).
Also, you are not converting between device (screen) and user (component) coordinates for the lookup.
You wrote:
The Robot class' getPixelColor is the only way I found to get the color of a given pixel (as far as I know another would be getRGB, but that only works on Image objects).
So, why don't you use an Image object? Fill your shape while drawing on the Image, and then draw the whole image at once to the screen.
And your method can be made much more readable if you transfer the "is already painted" test inside the recursive call:
private void color(int x, int y) {
// getPixel invokes something in the image - or replace it here.
Color org = getPixel(x,y);
if (org.equals(Color.BLACK)) {
// reached the border
return;
}
if (org.equals(Color.RED)) {
// already painted before
return;
}
g2d.draw(new Line2D.Double(x, y, x, y));
color(x-1, y);
color(x+1, y);
color(x, y-1);
color(x, y-1);
}

Categories

Resources