I must do n rounds of row pixel sorting of a buffered image, looping through each row and comparing the brightness of the current pixel to the one to the left of the current pixel. If the brightness of the current is less than the one to the left, I need to swap the colors of the pixels. This is my current code.
BufferedImage result = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int m = 0; m<n;m++){
for (int y=0;y<img.getHeight();y++){
for(int x =1; x<img.getWidth();x++){
int temp = img.getRGB(x-1,y);
int temp2 = img.getRGB(x, y);
int gr = (int)brightness(temp);
int gr2 = (int)brightness(temp2);
if(gr2<gr){
result.setRGB(x, y, rgbColour(temp2,temp2,temp2));
result.setRGB(x-1, y, rgbColour(temp,temp,temp));
}
}
}
}
The methods I've been given are as follows.
public static int getRed(int rgb) { return (rgb >> 16) & 0xff; }
public static int getGreen(int rgb) { return (rgb >> 8) & 0xff; }
public static int getBlue(int rgb) { return rgb & 0xff; }
public static int rgbColour(int r, int g, int b) {
return (r << 16) | (g << 8) | b;
}
public static double brightness(int rgb) {
int r = getRed(rgb);
int g = getGreen(rgb);
int b = getBlue(rgb);
return 0.21*r + 0.72*g + 0.07*b;
}
public static BufferedImage convertToGrayscale(BufferedImage img) {
BufferedImage result = new BufferedImage(
img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB
);
for(int x = 0; x < img.getWidth(); x++) {
for(int y = 0; y < img.getHeight(); y++) {
int col = img.getRGB(x, y);
int gr = (int)brightness(col);
result.setRGB(x, y, rgbColour(gr, gr, gr));
}
}
return result;
}
You are not switching the values properly after the comparison:
for(int x =1; x<img.getWidth();x++){
int temp = img.getRGB(x-1,y);
int temp2 = img.getRGB(x, y);
...
if (gr2 < gr) {
result.setRGB(x, y, rgbColour(temp2,temp2,temp2)); // same as in img
result.setRGB(x-1, y, rgbColour(temp,temp,temp)); // same as in img
}
}
Try this if-clause instead:
...
if (gr2 < gr) { // swap values in result
result.setRGB(x-1, y, rgbColour(temp2,temp2,temp2));
result.setRGB(x, y, rgbColour(temp,temp,temp));
} else { // keep values in result same as in img
result.setRGB(x, y, rgbColour(temp2,temp2,temp2));
result.setRGB(x-1, y, rgbColour(temp,temp,temp));
}
Related
Recently, our teacher gave us the task to convert a colorful image to a 1-bit image using Java. After a little experimentation I had the following result:
BufferedImage image = ...
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int clr = image.getRGB(x, y);
int r = (clr & 0x00ff0000) >> 16;
int g = (clr & 0x0000ff00) >> 8;
int b = clr & 0x000000ff;
double mono = 0.2126*r + 0.7152*g + 0.0722*b;
int c = mono < 128 ? 1 : 0;
//Adding to image buffer
buffer.add(c);
}
}
Well, it works but a lot of details are unfortunately lost. Here is a comparison:
Original:
Output:
What I want: (HQ: https://i.stack.imgur.com/vlEAE.png)
I was considering adding dithering to my converter, but I haven't found a working way yet, let alone any pseudo code.
Can anyone help me?
Edit:
So I created a DitheringUtils-class:
import java.awt.Color;
import java.awt.image.BufferedImage;
public class DitheringUtils {
public static BufferedImage dithering(BufferedImage image) {
Color3i[] palette = new Color3i[] {
new Color3i(0, 0, 0),
new Color3i(255, 255, 255)
};
int width = image.getWidth();
int height = image.getHeight();
Color3i[][] buffer = new Color3i[height][width];
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
buffer[y][x] = new Color3i(image.getRGB(x, y));
}
}
for(int y=0; y<image.getHeight();y++) {
for(int x=0; x<image.getWidth();x++) {
Color3i old = buffer[y][x];
Color3i nem = findClosestPaletteColor(old, palette);
image.setRGB(x, y, nem.toColor().getRGB());
Color3i error = old.sub(nem);
if (x+1 < width) buffer[y ][x+1] = buffer[y ][x+1].add(error.mul(7./16));
if (x-1>=0 && y+1<height) buffer[y+1][x-1] = buffer[y+1][x-1].add(error.mul(3./16));
if (y+1 < height) buffer[y+1][x ] = buffer[y+1][x ].add(error.mul(5./16));
if (x+1<width && y+1<height) buffer[y+1][x+1] = buffer[y+1][x+1].add(error.mul(1./16));
}
}
return image;
}
private static Color3i findClosestPaletteColor(Color3i match, Color3i[] palette) {
Color3i closest = palette[0];
for(Color3i color : palette) {
if(color.diff(match) < closest.diff(match)) {
closest = color;
}
}
return closest;
}
}
class Color3i {
private int r, g, b;
public Color3i(int c) {
Color color = new Color(c);
this.r = color.getRed();
this.g = color.getGreen();
this.b = color.getBlue();
}
public Color3i(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
public Color3i add(Color3i o) {
return new Color3i(r + o.r, g + o.g, b + o.b);
}
public Color3i sub(Color3i o) {
return new Color3i(r - o.r, g - o.g, b - o.b);
}
public Color3i mul(double d) {
return new Color3i((int) (d * r), (int) (d * g), (int) (d * b));
}
public int diff(Color3i o) {
return Math.abs(r - o.r) + Math.abs(g - o.g) + Math.abs(b - o.b);
}
public int toRGB() {
return toColor().getRGB();
}
public Color toColor() {
return new Color(clamp(r), clamp(g), clamp(b));
}
public int clamp(int c) {
return Math.max(0, Math.min(255, c));
}
}
And changed my function to this:
for (int y = 0; y < dithImage.getHeight(); ++y) {
for (int x = 0; x < dithImage.getWidth(); ++x) {
final int clr = dithImage.getRGB(x, y);
final int r = (clr & 0xFF0000) >> 16;
final int g = (clr & 0xFF00) >> 8;
final int b = clr & 0xFF;
if(382.5>(r+g+b)) {
buffer.add(0);
} else {
buffer.add(1);
}
}
}
But the output ends up looking... strange?
I really don't get why there are such waves.
I finally got it working! I improved the diff function and changed if(382.5>(r+g+b)) to if(765==(r+g+b)).
My DitheringUtils-class:
import java.awt.Color;
import java.awt.image.BufferedImage;
public class DitheringUtils {
public static BufferedImage dithering(BufferedImage image) {
Color3i[] palette = new Color3i[] {
new Color3i(0, 0, 0),
new Color3i(255, 255, 255)
};
int width = image.getWidth();
int height = image.getHeight();
Color3i[][] buffer = new Color3i[height][width];
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
buffer[y][x] = new Color3i(image.getRGB(x, y));
}
}
for(int y=0; y<image.getHeight();y++) {
for(int x=0; x<image.getWidth();x++) {
Color3i old = buffer[y][x];
Color3i nem = findClosestPaletteColor(old, palette);
image.setRGB(x, y, nem.toColor().getRGB());
Color3i error = old.sub(nem);
if (x+1 < width) buffer[y ][x+1] = buffer[y ][x+1].add(error.mul(7./16));
if (x-1>=0 && y+1<height) buffer[y+1][x-1] = buffer[y+1][x-1].add(error.mul(3./16));
if (y+1 < height) buffer[y+1][x ] = buffer[y+1][x ].add(error.mul(5./16));
if (x+1<width && y+1<height) buffer[y+1][x+1] = buffer[y+1][x+1].add(error.mul(1./16));
}
}
return image;
}
private static Color3i findClosestPaletteColor(Color3i match, Color3i[] palette) {
Color3i closest = palette[0];
for(Color3i color : palette) {
if(color.diff(match) < closest.diff(match)) {
closest = color;
}
}
return closest;
}
}
class Color3i {
private int r, g, b;
public Color3i(int c) {
Color color = new Color(c);
this.r = color.getRed();
this.g = color.getGreen();
this.b = color.getBlue();
}
public Color3i(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
public Color3i add(Color3i o) {
return new Color3i(r + o.r, g + o.g, b + o.b);
}
public Color3i sub(Color3i o) {
return new Color3i(r - o.r, g - o.g, b - o.b);
}
public Color3i mul(double d) {
return new Color3i((int) (d * r), (int) (d * g), (int) (d * b));
}
public int diff(Color3i o) {
int Rdiff = o.r - r;
int Gdiff = o.g - g;
int Bdiff = o.b - b;
int distanceSquared = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
return distanceSquared;
}
public int toRGB() {
return toColor().getRGB();
}
public Color toColor() {
return new Color(clamp(r), clamp(g), clamp(b));
}
public int clamp(int c) {
return Math.max(0, Math.min(255, c));
}
}
The final writing function:
for (int y = 0; y < dithImage.getHeight(); ++y) {
for (int x = 0; x < dithImage.getWidth(); ++x) {
final int clr = dithImage.getRGB(x, y);
final int r = (clr & 0xFF0000) >> 16;
final int g = (clr & 0xFF00) >> 8;
final int b = clr & 0xFF;
if(765==(r+g+b)) {
buffer.add(0);
} else {
buffer.add(1);
}
}
}
Thanks everyone!
BufferedImage image = ...
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color color = new Color(image.getRGB(x, y));
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
int mono = (red+green+blue)/255;
//Adding to image buffer
int col = (0 << 24) | (mono << 16) | (mono << 8) | mono;
image.setRGB(x,y,col);
}
}
Try this out
What you were doing wrong was, instead of trying to convert the picture to grayscale you were trying to convert to black and white.
I am working on a 2D platform game and I have a sprite sheet which includes the sprites of tiles and blocks.
I noticed that there was a pink-ish background behind the transparent sprites so I thought that Java wasn't loading the sprites as PNG and I tried to re-draw the sprite on a new bufferedImage, pixel by pixel checking if the pixel was R=255, G=63, B=52 but unfortunately, the code wasn't able to detect that either and at this point I have no more options left to try.
I made sure that the "pink" color values are correct by using a color picker.
original spritesheet (transparent):
The class that loads the sprite(s) is:
public class SpriteSheet {
private BufferedImage image;
public SpriteSheet(BufferedImage image) {
this.image = image;
}
public BufferedImage grabImage(int col, int row, int width, int height) {
BufferedImage alpha = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
BufferedImage img = image.getSubimage(
(col * width) - width,
(row * height) - height,
width,
height);
int w = img.getWidth();
int h = img.getHeight();
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
int pixel = img.getRGB(x, y);
int red, green, blue;
red = (pixel >> 16) & 0xff;
green = (pixel >> 8) & 0xff;
blue = (pixel) & 0xff;
if(red == 255 && green == 63 && blue == 52)
alpha.setRGB(x, y, new Color(0, 0, 0, 0).getRGB());
else
alpha.setRGB(x, y, pixel);
}
}
return alpha;
}
}
the class that loads the sprite sheet is:
public class Texture {
SpriteSheet bs, ss;
private BufferedImage block_sheet = null;
public BufferedImage[] block = new BufferedImage[3];
public Texture() {
BufferedImageLoader loader = new BufferedImageLoader();
try {
block_sheet = loader.loadImage("/tiles.png");
} catch(Exception e) {
e.printStackTrace();
}
bs = new SpriteSheet(block_sheet);
getTextures();
}
private void getTextures() {
block[0] = bs.grabImage(1, 1, 32, 32);
block[1] = bs.grabImage(2, 1, 32, 32);
block[2] = bs.grabImage(4, 1, 32, 32);
}
}
How do I get rid of the pink-ish background and keep transparency?
I dont understand why you're using subImage.
try {
BufferedImage img = ImageIO.read(new File("D:/image.png"));
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
Color pixelcolor = new Color(img.getRGB(i, j));
int r = pixelcolor.getRed();
int g = pixelcolor.getGreen();
int b = pixelcolor.getBlue();
if (r == 255 && g == 63 && b == 52) {
int rgb = new Color(255, 255, 255).getRGB();
img.setRGB(i, j, rgb);
}
}
}
ImageIO.write(img, "png", new File("D:/transparent.png"));
} catch (Exception e) {
System.err.println(e.getMessage());
}
cough, It worked all along, I had forgotten to disable the test blocks which was representing the blocks. Realized this after some time.
So the transparency was working fine. I just saw the rectangle i was drawing behind it.
I'm trying to create a program that, when selecting an image, reverses the colors of the image.
But when I run the code, my BufferedImage changes the RGB I assigned earlier.
I leave you the code that reverses the image.
image is a static BufferedImage.
public static void saveImage(File input, File output) throws IOException{
image = ImageIO.read(input);
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
boolean isTransparent = isTransparent(x, y);
if (!isTransparent) {
Color color = new Color(image.getRGB(x, y));
int r = 255 - color.getRed();
int g = 255 - color.getGreen();
int b = 255 - color.getBlue();
color = new Color(r, g, b);
int rgb = color.getRGB();
image.setRGB(x, y, rgb);
System.out.println(rgb+" --> "+image.getRGB(x, y));
}
}
}
ImageIO.write(image, "png", output);
}
public static boolean isTransparent(int x, int y) {
int pixel = image.getRGB(x, y);
return (pixel >> 24) == 0x00;
}
I've put all the black pixels of the image in an array and I want them to get the color of their left neighbor. I run the code without errors but the result is not really what I'm expecting.
Where those black stripes comes form? I was expecting it to be all red.
Here's my code and results.
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.*;
public class ImageTest {
public static BufferedImage Threshold(BufferedImage img) {
int height = img.getHeight();
int width = img.getWidth();
BufferedImage finalImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
int r = 0;
int g = 0;
int b = 0;
List<Integer> blackpixels = new ArrayList<Integer>();
for (int x = 0; x < width; x++) {
try {
for (int y = 0; y < height; y++) {
//Get RGB values of pixels
int rgb = img.getRGB(x, y);
r = ImageTest.getRed(rgb);
g = ImageTest.getGreen(rgb);
b = ImageTest.getBlue(rgb);
int leftLoc = (x-1) + y*width;
if ((r < 5) && (g < 5) && (b < 5)) {
blackpixels.add(rgb);
Integer[] simpleArray = new Integer[ blackpixels.size() ];
System.out.print(simpleArray.length);
int pix = 0;
while(pix < simpleArray.length) {
r = leftLoc;
pix = pix +1;
}
}
finalImage.setRGB(x,y,ImageTest.mixColor(r, g,b));
}
}
catch (Exception e) {
e.getMessage();
}
}
return finalImage;
}
private static int mixColor(int red, int g, int b) {
return red<<16|g<<8|b;
}
public static int getRed(int rgb) {
return (rgb & 0x00ff0000) >> 16;
}
public static int getGreen(int rgb) {
return (rgb & 0x0000ff00) >> 8;
}
public static int getBlue(int rgb) {
return (rgb & 0x000000ff) >> 0;
}
}
The following might work.
The main change is that it first collects the locations of ALL dark pixels, then goes over them to assign the colour from their left neighbours.
import java.awt.image.BufferedImage;
import java.util.*;
public class BlackRedImage
{
public static BufferedImage Threshold( BufferedImage img )
{
int height = img.getHeight();
int width = img.getWidth();
BufferedImage finalImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
List<Integer> blackpixels = new ArrayList<Integer>();
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
int rgb = img.getRGB(x, y); // Get the pixel in question
int r = BlackRedImage.getRed(rgb);
int g = BlackRedImage.getGreen(rgb);
int b = BlackRedImage.getBlue(rgb);
if ( (r < 5) && (g < 5) && (b < 5) )
{ // record location of any "black" pixels found
blackpixels.add(x + (y * width));
}
finalImage.setRGB(x, y, rgb);
}
}
// Now loop through all "black" pixels, setting them to the colour found to their left
for ( int blackPixelLocation: blackpixels )
{
if ( blackPixelLocation % width == 0 )
{ // these pixels are on the left most edge, therefore they do not have a left neighbour!
continue;
}
int y = blackPixelLocation / width;
int x = blackPixelLocation - (width * y);
int rgb = img.getRGB(x - 1, y); // Get the pixel to the left of the "black" pixel
System.out.println("x = " + x + ", y = " + y + ", rgb = " + rgb);
finalImage.setRGB(x, y, rgb);
}
return finalImage;
}
private static int mixColor( int red, int g, int b )
{
return red << 16 | g << 8 | b;
}
public static int getRed( int rgb )
{
return (rgb & 0x00ff0000) >> 16;
}
public static int getGreen( int rgb )
{
return (rgb & 0x0000ff00) >> 8;
}
public static int getBlue( int rgb )
{
return (rgb & 0x000000ff) >> 0;
}
}
EDIT: Here is a simpler version (doesn't collect the black pixels)
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.*;
public class ColourMove
{
public static BufferedImage Threshold( BufferedImage img )
{
int width = img.getWidth();
int height = img.getHeight();
BufferedImage finalImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
for ( int x = 1; x < width; x++ ) // Start at 1 as the left most edge doesn't have a left neighbour
{
for ( int y = 0; y < height; y++ )
{
Color colour = new Color(img.getRGB(x, y));
int red = colour.getRed();
int green = colour.getGreen();
int blue = colour.getBlue();
if ( (red < 5) && (green < 5) && (blue < 5) )
{ // Encountered a "black" pixel, now replace it with it's left neighbour
finalImage.setRGB(x, y, img.getRGB(x - 1, y));
}
else
{ // Non-black pixel
finalImage.setRGB(x, y, colour.getRGB());
}
}
}
return finalImage;
}
}
This question already has an answer here:
How to scan the screen for a specific color/image in java?
(1 answer)
Closed 9 years ago.
I'm not sure where to start on this one, but is there a way I can use Java to scan an image row by row for a specific color, and pass all of the positions into and ArrayList?
Can you? yes. Here's how:
ArrayList<Point> list = new ArrayList<Point>();
BufferedImage bi= ImageIO.read(img); //Reads in the image
//Color you are searching for
int color= 0xFF00FF00; //Green in this example
for (int x=0;x<width;x++)
for (int y=0;y<height;y++)
if(bi.getRGB(x,y)==color)
list.add(new Point(x,y));
Try using a PixelGrabber. It accepts an Image or ImageProducer.
Here's an example adapted from the documentation:
public void handleSinglePixel(int x, int y, int pixel) {
int alpha = (pixel >> 24) & 0xff;
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel ) & 0xff;
// Deal with the pixel as necessary...
}
public void handlePixels(Image img, int x, int y, int w, int h) {
int[] pixels = new int[w * h];
PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w);
try {
pg.grabPixels();
} catch (InterruptedException e) {
System.err.println("interrupted waiting for pixels!");
return;
}
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
return;
}
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
handleSinglePixel(x+i, y+j, pixels[j * w + i]);
}
}
}
In your case, you would have:
public void handleSinglePixel(int x, int y, int pixel) {
int target = 0xFFABCDEF; // or whatever
if (pixel == target) {
myArrayList.add(new java.awt.Point(x, y));
}
}