I have been working on a nonogram solver in Java, and all of my algorithm works fine, but I have been struggling with the visualization a little bit.
During the execution of the algorithm, I have access to two "solution-arrays". One is of type int[][], and contains values -1 for "certainly white", 0 for "uncertain" and 1 for "certainly black". Another array is of type float[][] which contains values between 0 and 1, here 0 is for certainly white, 1 is for certainly black and a value of .2 indicates it is more likely for the cell to be white than it to be black.
Since I've recently switched from PHP to Java programming (without proper introduction), I don't know much about visualizing this array properly. Of course I have first tried to simply print the first type of array to the console with characters like X, . and ?, but this is far from nice. I then found something about BufferedImage's, and I created the following function (for the float[][], the int[][] is similar):
public void toImage(int w, int h, float[][] solution) throws IOException {
int[] data = toImage1(w, h, solution);
BufferedImage img = toImage2(data, w, h);
toImage3(img);
}
public int[] toImage1(int w, int h, float[][] solution) throws IOException {
int[] data = new int[w * h];
int i = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int a = y / 100;
int b = x / 100;
int z = (int) (255 * Math.sqrt(1 - solution[a][b]));
if (solution[a][b] == 1 && ((((y % 100 >= 10 && y % 100 <= 15) || (y % 100 >= 85 && y % 100 <= 90)) && x % 100 >= 10 && x % 100 <= 90) || (((x % 100 >= 10 && x % 100 <= 15) || (x % 100 >= 85 && x % 100 <= 90)) && y % 100 >= 10 && y % 100 <= 90))) {
z = 100;
} else if (solution[a][b] == 0 && ((((y % 100 >= 10 && y % 100 <= 15) || (y % 100 >= 85 && y % 100 <= 90)) && x % 100 >= 10 && x % 100 <= 90) || (((x % 100 >= 10 && x % 100 <= 15) || (x % 100 >= 85 && x % 100 <= 90)) && y % 100 >= 10 && y % 100 <= 90))) {
z = 230;
}
data[i++] = z << 16 | z << 8 | z;
}
}
return data;
}
public BufferedImage toImage2(int[] data, int w, int h) {
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
img.setRGB(0, 0, w, h, data, 0, w);
return img;
}
public void toImage3(BufferedImage img) throws IOException {
File f = new File("Nonogram.png");
ImageIO.write(img, "PNG", f);
}
Here, w and h are supposed to be the amount of cells in each column resp. row multiplied by 100 (I want each cell to be represented by a block of 100x100 pixels).
I then also want an extra gray box in cells that are certain, that's what the if and else if are for.
This works beautifully, and creates images like this:
However, I run into two problems:
This function is super slow. After profiling the execution, I see 90% of the execution time goes to this function. After breaking down my function into two bits, as suggested in the comments, I get the following profile:
Instead of writing to a .png file, I would like a JFrame to display my image, but (again since I missed my proper introduction to Java), JFrame's don't seem to be my best friends and I can't seem to work out how to use them.
Is there a possibility to fill entire 100x100 px cells at once? Is there a way to not have to create the entire BufferedImage each time over, but just modify the bits that have changed in another method? Should I use something else than BufferedImage? What elements does my code need, could someone code out an example method, or the needed code snippets?
Okay, so it looks like writing to a file is not actually your biggest problem, it looks like your biggest problem is that you're dumping the pixels individually. Here are some things I might do:
Make a smaller image. 100x100 is a lot. Why not 20x20? You can always zoom in with an image editor.
Skip the int[] step entirely, and write to the BufferedImage directly.
Use bufferedImage.setRGB(startX, startY, w, h, rgbArray, offset, scansize) as you've been doing, but only for the section of the image you're drawing in bulk.
Do everything based on the values of a and b (as opposed to w and h, including and especially the loops, see point 4.
Fill the the boxes completely solidly, then add the inner rectangle by overwriting those lines separately. All these complicated if checks are killing your performance. Do it branchless (no if statements), and it will run much faster.
Put the code that's inside the for loops into a separate method that makes it more clear. Call it something like drawSingleBox.
Remember, more methods with good names make it easier to follow what's going on. writeImageToFile is preferred over toImage3. convertArrayToImage is preferred over toImage2.
Also, you asked about how to put an image as the background of a JFrame; once you have your fully drawn BufferedImage object, you can use the information in JFrame background image to do that part of it.
Related
So I made a game and I want the enemies to bounce off the wall when they hit in a random x and y speed. However, somehow these little buggers are still getting out of the window. Most the time it works but every like 10ish times it will sneak out the border and I can't figure out how.
#Override
public void tick()
{
x += speedX;
y += speedY;
Random r = new Random();
//BUGS
if(y <= 0 || y >= Game.HEIGHT - 48) //This is correct size of the window edges
{
if(speedY <= 0)
speedY = (r.nextInt(8) + 1);
else
speedY = -(r.nextInt(8) + 1);
}
if(x <= 0 || x >= Game.WIDTH - 32) //This is correct size of the window edges
{
if(speedX <= 0)
speedX = (r.nextInt(8) + 1);
else
speedX = -(r.nextInt(8) + 1);
}
Issues:
Don't re-create Random as it's wasteful and sometimes dangerous. Better to create one Random object and assign it to an instance field of the class, and use it throughout.
Avoid "magic numbers". So instead of y >= Game.HEIGHT - 48, do y >= Game.HEIGHT - WINDOW_EDGES (or something similar)
Don't swap speed as you're doing but instead check for y <= 0 or y >= Game.HEIGHT -SOME_CONSTANT separately, and gear the results based on this finding to avoid getting caught in a speed "trap". This is your main problem in fact.
e.g.,
if (y <= 0) {
speedY = r.nextInt(8) + 1; // change 8 to a constant
} else if (y >= Game.HEIGHT - SOME_CONSTANT) {
speedY = -(r.nextInt(8) + 1);
}
Same for x and speedX
Regarding:
2) I would like to do that but since I have multiple object sizes, I have to change the edges.
Then each object should have a method that returns its edge size (or whatever property is needed), and you should use this, again, not magic numbers
3) I tried swapping and they just shot off the screen.
I don't know what you mean by this or what specific code changes you may have made.
If still stuck, consider creating and posting a valid Minimal Reproducible Example
I have these code to reduce image noise:
for (int x = 0; x < bitmap.getWidth(); x++) {
for (int y = 0; y < bitmap.getHeight(); y++) {
// get one pixel color
int pixel = processedBitmap.getPixel(x, y);
// retrieve color of RGB
int R = Color.red(pixel);
int G = Color.green(pixel);
int B = Color.blue(pixel);
// convert into single value
R = G = B = (int) (0.299 * R + 0.587 * G + 0.114 * B);
// convert to black and white + remove noise
if (R > 162 && G > 162 && B > 162)
bitmap.setPixel(x, y, Color.WHITE);
else if (R < 162 && G < 162 && B < 162)
bitmap.setPixel(x, y, Color.BLACK);
}
}
But the time takes very long to generate the outcome. Is there any other way to optimize these code to make it faster?
Don't use getPixel. Get the image data as an array and use math to access the correct pixel. Write the math such that the fewest multiplications possible are used. Same for setPixel.
Don't use Color.red(), Color.green(), etc. Use masking, its more efficient than a function call.
Even better, drop into the NDK and do this in C. Image manipulation in Java is generally less than optimal.
I'm currently working on a platformer game in java, and I'm having trouble figuring out why this decreases performance so much. Only the textures in view of the camera are rendered, and I've even tried clearing all objects outside the camera's view, so that the array was almost empty, and I still was unable to get a good framerate. When I comment out the call to this method, the game runs at 300 FPS, but when I run it, even when I remove everything afterwords, I still only get 40 FPS. This is not an issue with rendering, as I have tested this thoroughly. Any feedback would be much appreciated. Here is the code:
public void buildTerrain(BufferedImage bi) {
// this method will take an image and build a level based on it.
int width = bi.getWidth();
int height = bi.getHeight();
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
int pixel = bi.getRGB(x, y);
int r = (pixel >> 16) & 0xff;
int g = (pixel >> 8) & 0xff;
int b = (pixel) & 0xff;
if(r == 255 &&
g == 255 &&
b == 255)
h.addObject(new Block(x*32, y*32,
ID.blockStone,GameState.level1, tex));
if(r == 0 &&
g == 0 &&
b == 255){
p.setX(x*32);
p.setY(y*32);
p.setHeight(64);
}
}
}
}
references:
h is a Handler object, witch contains a method addObject(GameObject)
Block extends GameObject
p is a Player, witch also extends GameObject.
EDIT: this code is not called in a loop, it is ran once at the beginning of each level to load the terrain. All the AddObject() method does is add the Blocks to an array where then are then iterated over in the tick() and render() methods. Only objects in the scope of the camera are rendered, and the tick() method of blocks is empty.
Could you try:
if(0xffffff00 == (pixel & 0xffffff00))
h.addObject(new Block(x*32, y*32,
ID.blockStone,GameState.level1, tex));
if(0x0000ff00 == (pixel & 0x0000ff00)){
p.setX(x*32);
p.setY(y*32);
p.setHeight(64);
}
Because I don't understand the need of decomposing (r, g, b) for each pixel while you can do that using a binary & (0xffffff00, it might be 0x00ffffff).
in your code, you do width*height*(3 shift + 3 and + 3 equals + 3 equals) operations.
in my code, you do width*height*2*(and + test) operations.
I am using the following code currently to loop through pixels in an image and return the coordinates of the pixel with RGB values the same as defined in the if statement:
outerloop:
for (int y = 0; y < image.getHeight(); y = y + 1) {
for (int x = 0; x < image.getWidth(); x = x + 1) {
Color mycolor = new Color(image.getRGB(x, y));
int red = mycolor.getRed();
int green = mycolor.getGreen();
int blue = mycolor.getBlue();
if (red == 183 & green == 86 & blue == 182){
System.out.println(x,y);
break outerloop;
}
}
}
The problem now is that the RGB values vary very slightly every time in the application so I am trying to add a sort of "tolerance" to the currently constant RGB values. For example, in one case Red could be 185, green could be 89 and blue for example could be the same (182).
I understand I could just define all the conditions using the OR (||) function within the if statement but as this would require a lot of code is there any simpler solution? For example defining a positive tolerance as a constant and looping through all combinations of the RGB values within this tolerance?
It will be very slow to loop through all permutations of the colors within tolerance: let's say that you have a tolerance of +/- 5, that would require checking 1331 different colors (11 reds * 11 greens * 11 blues).
It is much faster just to change your condition red == 183 to something like Math.abs(red - 183) < tolerance or (red >= 183 - tolerance || red <= 183 + tolerance) (similarly for other channels).
Rather than check that your values are explicitly equal to a list of numbers, you'll be happier to check that they are within a certain range. You could do this with something like (180<x & x<185), but it's a little cleaner to use absolute value:
int TOLERANCE = 3;
boolean in_range(int value, int setpt) {
return abs(value-setpt) <= TOLERANCE;
}
And then in your loop, your conditional would look like:
int R_SETPT = 183;
int G_SETPT = 86;
int B_SETPT = 182;
if (in_range(red, R_SETPT) &
in_range(green, G_SETPT) &
in_range(blue, B_SETPT)) {
// etc.
how to implements low pass filter, i have:
BufferedImage img;
int width = img.getWidth();
int height = img.getHeight();
int L = (int) (f * Math.min(width, height));
for (int y = 0 ; y < height ; y++) {
for (int x = 0 ; x < width ; x++) {
if (x >= width / 2 - L && x <= width / 2 + L && y >= -L + height / 2 && y <= L + height / 2) {
img.setRGB(x, y, 0);
}
else {}
}
}
but firstly i should transform image but how?
Your code as written would just set the pixels near the edge of the image to black. If you did this in the frequency domain you would have a low-pass filter, because the pixels near the edge of the image would be the high-frequency components and so setting them to 0 would leave just the low-frequency components. To operate in the frequency domain you need to apply a Fourier transform. However you need to take care about where in the transformed image the low-frequency components end up, as different implementations of a Fourier transform may put the low-frequency components either in the center of the transformed image or at one of the corners.