How do I stop java from setting my BufferedImage to fully transparent? - java

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.

Related

get average image of a set of images in java

I have a set of meteorological RGB type BufferedImages. I want to get average image of them. By that, I mean get average value of each pixel and make a new image out of those values. What I tried is this:
public void getWaveImage(BufferedImage input1, BufferedImage input2){
// images are of same size that's why i'll use first one's width and height
int width = input1.getWidth(), height = input1.getHeight();
BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
int[] rgb1 = input1.getRGB(0, 0, width, height, new int[width * height], 0, width);
int[] rgb2 = input2.getRGB(0, 0, width, height, new int[width * height], 0, width);
for(int i=0; i<width; i++){
for(int j=0; j<height; j++){
int rgbIndex = i * width + j;
rgb1[rgbIndex] = (rgb1[rgbIndex] + rgb2[rgbIndex]) / 2;
}
}
output.setRGB(0, 0, width, height, rgb1, 0, width);
return output;
}
What am I doing wrong? Thank you in advance.
input1:
input2:
output:
You want the average of each component of the colour, average red, average green, average blue.
Instead you are averaging the whole int.
Color c1 = new Color(rgb1[rgbIndex]);
Color c2 = new Color(rgb2[rgbIndex]);
Color cA = new Color((c1.getRed() + c2.getRed())/2,
(c1.getGreen() + c2.getGreen())/2,
(c1.getBlue() + c2.getBlue())/2);
rgb1[rgbIndex] = cA.getRGB();
This may not be the most efficient due to creating so many objects, so a more direct approach is like so:
public static int average(int argb1, int argb2){
return (((argb1 & 0xFF) + (argb2 & 0xFF)) >> 1) | //b
(((argb1 >> 8 & 0xFF) + (argb2 >> 8 & 0xFF)) >> 1) << 8 | //g
(((argb1 >> 16 & 0xFF) + (argb2 >> 16 & 0xFF)) >> 1) << 16 | //r
(((argb1 >> 24 & 0xFF) + (argb2 >> 24 & 0xFF)) >> 1) << 24; //a
}
Usage:
rgb1[rgbIndex] = average(rgb1[rgbIndex], rgb2[rgbIndex]);
If you have:
int rgb1, rgb2; //the rgb value of a pixel in image 1 and 2 respectively
The "average" color would be:
int r = (r(rgb1) + r(rgb2)) / 2;
int g = (g(rgb1) + g(rgb2)) / 2;
int b = (b(rgb1) + b(rgb2)) / 2;
int rgb = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) << 0);
with the following "helper" methods:
private static int r(int rgb) { return (rgb >> 16) & 0xFF; }
private static int g(int rgb) { return (rgb >> 8) & 0xFF; }
private static int b(int rgb) { return (rgb >> 0) & 0xFF; }
Alternatively you can use the Color class if you don't want to deal with bitwise operations.
another solution can be to replace
rgb1[rgbIndex] = (rgb1[rgbIndex] + rgb2[rgbIndex]) / 2;
with
rgb1[rgbIndex] = ((rgb1[rgbIndex]>>1)&0x7f7f7f7f)+((rgb2[rgbIndex]>>1)&0x7f7f7f7f)+(rgb1[rgbIndex]&rgb2[rgbIndex]&0x01010101);
binary right shift to divide by 2, last member of the sum to handle the case of two odd numbers.

Converting 8 bit to 4 bit image

Can anyone see what the issue is when I try to convert my 8 bit image into an 4 bit image?
I am testing using the 8 bit image found here: http://laurashoe.com/2011/08/09/8-versus-16-bit-what-does-it-really-mean/
You can tell how the 4 bit image should look like but mine is almost purely black.
// get color of the image and convert to grayscale
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) & 0xF;
int g = (rgb >> 8) & 0xF;
int b = (rgb & 0xF);
int grayLevel = (int) (0.299*r+0.587*g+0.114*b);
int gray = (grayLevel << 16) + (grayLevel << 8) + grayLevel;
img.setRGB(x,y,gray);
}
}
You should use 0xFF not 0xF,as 0xF means only last four bits, wchich will tell you almost nothing about the color, since in RGB an color is 8 bit.
try if this work:
// get color of the image and convert to grayscale
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);
int grayLevel = (int) (0.299*r+0.587*g+0.114*b);
int gray = (grayLevel << 16) + (grayLevel << 8) + grayLevel;
img.setRGB(x,y,gray);
}
}
Since the code has been edited out from the question, here it is with the confirmed solution from the comments:
// get color of the image and convert to grayscale
for(int x = 0; x <img.getWidth(); x++) {
for(int y = 0; y < img.getHeight(); y++) {
int rgb = img.getRGB(x, y);
// get the upper 4 bits from each color component
int r = (rgb >> 20) & 0xF;
int g = (rgb >> 12) & 0xF;
int b = (rgb >> 4) & 0xF;
int grayLevel = (int) (0.299*r+0.587*g+0.114*b);
// use grayLevel value as the upper 4 bits of each color component of the new color
int gray = (grayLevel << 20) + (grayLevel << 12) + (grayLevel << 4);
img.setRGB(x,y,gray);
}
}
Note that the resulting image only looks like 4-bit grayscale, but still uses int as the RGB value.
8 bit image values are in range [0, 255] because pow(2, 8) = 256
To get 4 bit image values which will be in range [0, 15] as pow(2, 4) = 16,
we need to divide each pixel value by 16 -> range [0, 255] / 16 = range [0, 15].
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("crowd.jpeg")
#Convert the image to grayscale
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray_img, cmap='gray')
Grayscale image
bit_4 = np.divide(gray_img, 16).astype('uint8')
plt.imshow(bit_4, cmap='gray')
Bit4 image

How to compress a Pixmap / use cim files in android?

How can I compress a LibGDX Pixmap? I want to save it on the disk, but it uses about 4MB, which is way to much and takes like forever to save it.
final Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(x, y, w, h);
FileHandle screenshot = Gdx.files.local("something.png");
PixmapIO.writePNG(screenshot, pixmap);
I saw that there is a PixmapIO.writeCIM, which is quite fast and the output is quite small.
Am I able to display the something.cim file in Android? The docu says, that cim should only be used within libgdx. Arguing that this is the old documentation, maybe there is something new?
http://badlogicgames.com/forum/viewtopic.php?f=11&t=8947
int w = p.getWidth();
int h = p.getHeight();
int[] pixels = new int[w * h];
for (int y=0; y<h; y++) {
for (int x=0; x<w; x++) {
//convert RGBA to RGB
int value = p.getPixel(x, y);
int R = ((value & 0xff000000) >>> 24);
int G = ((value & 0x00ff0000) >>> 16);
int B = ((value & 0x0000ff00) >>> 8);
int A = ((value & 0x000000ff));
int i = x + (y * w);
pixels[ i ] = (A << 24) | (R << 16) | (G << 8) | B;
}
}
Bitmap b = Bitmap.createBitmap(pixels, w, h, Config.ARGB_8888);
b.compress(CompressFormat.JPEG, quality, handle.write(false));

Toggle between RGB channels

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);
}
}

Image to byte[] to Image

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");
}
}

Categories

Resources