I'm trying to do some image processing using with Java.
As a start, before doing any filters or anything, I'm doing a convert process on my image to a byte array, then convert it back to an image and save it to see how that goes.
I'm not getting the output image as the input one, there is some lost information/data, which causing the output to look different in colors.
Please tell me what is the problem; what I am missing.
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Ola {
BufferedImage img = null;
public void loadImage() {
try {
img = ImageIO.read(new File("/home/a/Pictures/Tux-vegeta.png"));
} catch (IOException e) {
System.out.println("image(s) could not load correctly, try changing the path");
}
}
public byte[] convertToArray() {
int w = img.getWidth();
int h = img.getHeight();
int bands = img.getSampleModel().getNumBands();
System.out.print(bands);
if (bands != 4) {
System.out.println("The image does not have 4 color bands");
}
byte bytes[] = new byte[4 * w * h];
int index = 0;
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
int pixel = img.getRGB(x, y);
int alpha = (pixel >> 24) & 0xFF;
int red = (pixel >> 16) & 0xFF;
int green = (pixel >> 8) & 0xFF;
int blue = pixel & 0xFF;
bytes[index++] = (byte) alpha;
bytes[index++] = (byte) red;
bytes[index++] = (byte) green;
bytes[index++] = (byte) blue;
}
}
return bytes;
}
public void convertToImage(byte[] bytes) {
try {
int w = 300;
int h = 300;
int index = 0;
BufferedImage resultPNG = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j ++) {
int pixel = (bytes[index] << 24) | (bytes[index + 1] << 16) | (bytes[index + 2] << 8) | (bytes[index + 3]);
resultPNG.setRGB(j, i, pixel);
index += 4;
}
}
File outputImage = new File("/home/a![enter image description here][1]/Pictures/test.png");
ImageIO.write(resultPNG, "png", outputImage);
} catch (IOException e) {
System.out.println("image write error");
}
}
public static void main(String[] args) {
Ola ola = new Ola();
ola.loadImage();
ola.convertToImage(ola.convertToArray());
}
}
what you are missing is turning your signed byte back to unsigned:
change your line
int pixel = (bytes[index] << 24) | (bytes[index + 1] << 16) | (bytes[index + 2] << 8) | (bytes[index + 3]);
to the following:
int pixel = ((bytes[index] & 0xFF) << 24) | ((bytes[index + 1] & 0xFF) << 16) | ((bytes[index + 2] & 0xFF) << 8) | (bytes[index + 3] & 0xFF);
Since you are wanting the alpha channel your destination should be using TYPE_INT_ARGB instead of TYPE_INT_RGB, using RGB will cause the buffered image to ignore the alpha byte.
Since PNGs do not load into the TYPE_INT_ARGB color model you can use a graphics object to draw the loaded bufferedimage into a bufferedimage object created with TYPE_INT_ARGB.
public void loadImage() {
try {
BufferedImage tempimg = ImageIO.read(new File("/home/a/Pictures/Tux-vegeta.png"));
img = new BufferedImage(300, 300, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.drawImage(tempimg,null,0,0);
} catch (IOException e) {
System.out.println("image(s) could not load correctly, try changing the path");
}
}
Related
This is the new Composite class for a Multiply(Overlay) effect made by Kristopher Ives(Howto perform a MULTIPLY composite effect using Graphics2D). As far as I can tell he's been inactive for quite some time. Every time I run the Main class all I get is "Expected integer sample type" exception which is thrown when:
'(r.getSampleModel().getDataType() != DataBuffer.TYPE_INT)'
import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
public class MultiplyComposite implements Composite, CompositeContext {
protected void checkRaster(Raster r) {
if (r.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
throw new IllegalStateException("Expected integer sample type");
}
}
#Override
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
checkRaster(src);
checkRaster(dstIn);
checkRaster(dstOut);
int width = Math.min(src.getWidth(), dstIn.getWidth());
int height = Math.min(src.getHeight(), dstIn.getHeight());
int x, y;
int[] srcPixels = new int[width];
int[] dstPixels = new int[width];
for (y=0; y < height; y++) {
src.getDataElements(0, y, width, 1, srcPixels);
dstIn.getDataElements(0, y, width, 1, dstPixels);
for (x=0; x < width; x++) {
dstPixels[x] = mixPixel(srcPixels[x], dstPixels[x]);
}
dstOut.setDataElements(0, y, width, 1, dstPixels);
}
}
private static int mixPixel(int x, int y) {
int xb = (x) & 0xFF;
int yb = (y) & 0xFF;
int b = (xb * yb) / 255;
int xg = (x >> 8) & 0xFF;
int yg = (y >> 8) & 0xFF;
int g = (xg * yg) / 255;
int xr = (x >> 16) & 0xFF;
int yr = (y >> 16) & 0xFF;
int r = (xr * yr) / 255;
int xa = (x >> 24) & 0xFF;
int ya = (y >> 24) & 0xFF;
int a = Math.min(255, xa + ya);
return (b) | (g << 8) | (r << 16) | (a << 24);
}
#Override
public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints) {
return this;
}
#Override
public void dispose() {
}
public static final MultiplyComposite Multiply = new MultiplyComposite();
}
This is my code for blending the two photos together which are BufferedImage types. The Overlay is provided from a folder and converted to a buffered image in a separate method which you can find at the bottom, and the SS is provided by a screenshot that is run through another class to make it greyscale and is then converted to a BufferedImage and is returned(You can find that at the bottom too).
// BLENDING THE PHOTOS TOGETHER
public static BufferedImage photosBlender(BufferedImage SS, BufferedImage Overlay) {
try {
BufferedImage base = SS;
BufferedImage overlay = Overlay;
Graphics2D g2d = base.createGraphics();
g2d.setComposite(MultiplyComposite.Multiply);
int x = (base.getWidth() - overlay.getWidth()) / 2;
int y = (base.getHeight() - overlay.getHeight()) / 2;
g2d.drawImage(overlay, x, y, null);
g2d.dispose();
File f = new File("resources/OutputImages/OutputBlended.png");
ImageIO.write((RenderedImage) base, "png", f);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
GETTING THE OVERLAY: the args is provided by a random selection method for selecting random overlay files provided in resources
public static BufferedImage OverlayProcess(String args) {
BufferedImage OverlayInput = null;
JFrame OverlayFrame = null;
try {
OverlayInput = ImageIO.read(new File(args));
ImageIcon screenShotIcon = new ImageIcon(OverlayInput);
//To Display selected Overlay for Testing purposes
/*OverlayFrame = new JFrame();
OverlayFrame.setLayout(new FlowLayout());
OverlayFrame.setSize(1500, 800);
JLabel lbl = new JLabel();
lbl.setIcon(screenShotIcon);
OverlayFrame.add(lbl);
OverlayFrame.setVisible(true);
OverlayFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);*/
} catch (IOException e) {
}
return OverlayInput;
}
GREYSCALING THE SS
public class GreyscaleTheImage {
public static BufferedImage main(String args) throws IOException {
BufferedImage img = null;
File f = null;
//read image
try {
f = new File(args);
img = ImageIO.read(f);
} catch (IOException e) {
System.out.println(e);
}
//get image width and height
int width = img.getWidth();
int height = img.getHeight();
//convert to grayscale
int x = 0;
int y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
int p = img.getRGB(x, y);
int a = (p >> 24) & 0xff;
int r = (p >> 16) & 0xff;
int g = (p >> 8) & 0xff;
int b = p & 0xff;
//calculate average
int avg = (r + g + b) / 3;
//replace RGB value with avg
p = (a << 24) | (avg << 16) | (avg << 8) | avg;
img.setRGB(x, y, p);
}
}
BufferedImage finalImage = null;
try {
finalImage = img;
} catch (Exception e) {
System.out.println(e);
}
//write image
try {
f = new File("resources/OutputImages/Output.png");
ImageIO.write(img, "png", f);
} catch (IOException e) {
System.out.println(e);
}
return finalImage;
}//main() ends here
}//class ends here
I've already tried changing the Raster of the image but it's just too much for my current skill level. I simply can't understand what sampleModels are or what it means to have a DataType of 3. I have a suspicion that it might have something to do with the image possibly having something other than a 32 bit "integer" buffer (whatever that means), because of Kristopher's resources saying that's all it will work with.
I made a new method that converts the image to ARGB, thank you to Harald K for the help he left in the comment section. The ARGB image works with the method I was having issues with.
I need to determinate whether the given image is blank or it has the same pixel values please find the below code. here I want to set a tolerance. I don't want to pass top, bottom, left and right 20 pixels to this logic. please help!
for (String pic : Finallist) {
BufferedImage image = ImageIO.read(new File(pic));
final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
final boolean hasAlphaChannel = image.getAlphaRaster() != null;
boolean blankImage=true;
int[][] result = new int[height][width];
if (hasAlphaChannel) {
final int pixelLength = 4;
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
argb += ((int) pixels[pixel + 1] & 0xff); // blue
argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green
argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red
result[row][col] = argb;
if(result[row][col]!=result[0][0]) {
blankImage=false;
}
col++;
if (col == width) {
col = 0;
row++;
}
}
} else {
final int pixelLength = 3;
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += -16777216; // 255 alpha
argb += ((int) pixels[pixel] & 0xff); // blue
argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green
argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red
result[row][col] = argb;
if(result[row][col]!=result[0][0]) {
blankImage=false;
}
col++;
if (col == width) {
col = 0;
row++;
}
}
}
if(blankImage==true) {
try {
System.out.println("Blank image found and its deleted");
File f = new File(pic);
f.delete();
} catch(Exception e) {
System.out.println("Exception"+e);
}
}else {
FinalListWithOutBlank.add(pic);
}
}
I want every thing on the air!! so that my code performance will not be in the pain.. I just want to skip those pixel to reach out into this logic..
Copy the desired area of interest into another image with
BufferedImage imageForEvaluation = image.getSubimage(x, y, width, height);
and use this image for your logic.
I'm trying to access each pixel, manipulate it then save it back to the system. But the resulting image is always flipped and rotated, why is this?
Here is my code for input:
BufferedImage input_image=ImageIO.read(new File("F:\\sophie4.png"));
int result[][] = convertTo2DWithoutUsingGetRGB(input_image);
private static int[][] convertTo2DWithoutUsingGetRGB(BufferedImage image) {
final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
final boolean hasAlphaChannel = image.getAlphaRaster() != null;
int[][] result = new int[height][width];
if (hasAlphaChannel) {
final int pixelLength = 4;
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
argb += ((int) pixels[pixel + 1] & 0xff); // blue
argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green
argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red
result[row][col] = argb;
col++;
if (col == width) {
col = 0;
row++;
}
}
} else {
final int pixelLength = 3;
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += -16777216; // 255 alpha
argb += ((int) pixels[pixel] & 0xff); // blue
argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green
argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red
result[row][col] = argb;
col++;
if (col == width) {
col = 0;
row++;
}
}
}
return result;
}
That works, i get the pixels, but even without processing the pixels, if i output the image, its always flipped.. Here is my output code:
BufferedImage image = new BufferedImage(result.length, result[0].length, BufferedImage.TYPE_INT_RGB);
for (int row = 0; row < result.length; row ++) {
for (int col = 0; col < result[row].length; col++) {
image.setRGB(row, col, result[row][col]);
}
}
File ImageFile = new File("path");
try {
ImageIO.write(image, "png", ImageFile);
} catch (IOException e) {
e.printStackTrace();
}
You can see the input and output image below
You're getting confused (or at least I am), because you're resulting array is height by width (not width x height which makes more sense to me), so, instead of...
BufferedImage image = new BufferedImage(result.length, result[0].length, BufferedImage.TYPE_INT_RGB);
it should be...
BufferedImage image = new BufferedImage(result[0].length, result.length, BufferedImage.TYPE_INT_RGB);
and
image.setRGB(row, col, result[row][col]);
should be
image.setRGB(col, row, result[row][col]); // See why that's consfusing
I am trying to create an image editor in java, but when I run the code, the output image is fully transparent.
Here is my code for Main.java:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Main {
public static void main(String[] args) {
BufferedImage image = null;
try {
image = ImageIO.read(new File("strawberry.png"));
} catch (IOException e) {
System.out.println(e);
}
new Negative(image);
File outputfile = new File("saved.png");
try {
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
System.out.println(e);
}
}
}
And here is my code for Negative.java:
import java.awt.image.BufferedImage;
public class Negative {
public Negative(BufferedImage img) {
for (int x = 0; x < img.getWidth(); ++x) {
for (int y = 0; y < img.getHeight(); ++y) {
int rgb = img.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb & 0xFF);
r = 255 - r;
g = 255 - g;
b = 255 - b;
int newColour = (r << 16) + (g << 8) + (b << 4);
img.setRGB(x, y, newColour);
}
}
}
}
If anyone could help I would be very grateful.
Problem
What they call RGB color is in fact ARGB, 8 bits for each.
Alpha is given in the highest 8 bits, 0 for transparent to 255 for fully opaque.
This is what TYPE_INT_ARGB means in the javadoc for BufferedImage.setRGB() :
The pixel is assumed to be in the default RGB color model, TYPE_INT_ARGB, and default sRGB color space.
Solution
For a fully opaque image, add a 255 alpha value:
int newColour = (0xff << 24) + (r << 16) + (g << 8) + (b << 4);
Alternatively, you can take the original alpha of your image if you extract it too:
int rgb = img.getRGB(x, y);
int alpha = (rgb >>> 24);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb & 0xFF);
int newColour = (alpha << 24) + (r << 16) + (g << 8) + (b << 4);
There is one more component of a color: alpha channel. It is stored in 24-31 bits of a number. If it is set to 0, the image is transparent. So you need to set 24-31 bits of newColor to 1 to make it opaque.
I need to toggle on/off RGB channels of an image, but I am stuck and my code is buggy.
Can you help me figure out how to do this the right way? This is my code:
The function channels is called when 1 of 3 checkboxes has changed its state and provides the arguments which are true == selected
public void channels(boolean red, boolean green, boolean blue) {
if (this.img != null) {// checks if the image is set
char r = 0xFF, g = 0xFF, b = 0xFF;
if (red == false) {
r = 0x00;
}
if (green == false) {
g = 0x00;
}
if (blue == false) {
b = 0x00;
}
BufferedImage tmp = new BufferedImage(
img.getWidth(),
img.getHeight(),
BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
int rgb = img.getRGB(i, j);
int red = (rgb >> 16) & r;
int green = (rgb >> 8) & g;
int blue = (rgb >> 0) & b;
int gbr = (red << 16) | (green << 8) | blue;// EDITED
tmp.setRGB(i, j, gbr);
}
}
img = tmp;
repaint();
} else {
//show error
}
}
Thank you for your help!
How about this optimized version, with a lot less bit shifting?
public void channels(boolean showRed, boolean showGreen, boolean showBlue) {
if (this.origImg!= null) {// checks if the image is set
int channelMask = 0xff << 24 | (showRed ? 0xff : 0) << 16 | (showGreen ? 0xff : 0) << 8 | (showBlue ? 0xff : 0);
BufferedImage tmp = new BufferedImage(origImg.getWidth(), origImg.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < origImg.getWidth(); i++) {
for (int j = 0; j < origImg.getHeight(); j++) {
int rgb = origImg.getRGB(i, j);
tmp.setRGB(i, j, rgb & channelMask);
}
}
img = tmp;
repaint();
} else {
//show error
}
}
A faster approach yet, would probably be to use a channeled Raster, or at least a Raster configuration that allows band sub-sampling (see Raster.createChild(...) method, especially the last parameter).
LookupOp, as mentioned by #trashgod is also a good idea, and probably faster than the getRGB()/setRGB() approach.
It looks like you're shifting in the bits wrong. Shouldn't it be: int gbr = (red << 16) | (green << 8) | blue;? You basically want to shift back in the same order as how you shifted out to begin with.
Also, once you have cleared the corresponding colour, there's no way for you to get it back. You'll need to store a copy of the original image somewhere. When it's time to turn the channel back on, simply copy the original pixel from the original image back.
Assuming that you have the original image stored somewhere as origImg, I would modify your for loop so that if the channel is toggled on, copy from the original image.
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
int rgb = img.getRGB(i, j);
int origRGB = origImg.getRGB(i, j);
int redPixel = red ? (origRGB >> 16) & r : (rgb >> 16) & r;
int greenPixel = green ? (origRGB >> 8) & g : (rgb >> 8) & g;
int bluePixel = blue ? origRGB & b : rgb & b;
int gbr = (redPixel << 16) | (greenPixel << 8) | bluePixel;
tmp.setRGB(i, j, gbr);
}
}