I am trying to write a code where I get a png/jpeg image. If it is a png image, I want to check if it's background is 100% transparent. If yes, I want to add white background using image magick.
Currently I use image magick's "identify -format %A new.png" which returns true or false based on transparency.
However, is there any way to find out 100 percent background transparency using image magick or java code?
You could iterate over each pixel in the image and check whether the most significant byte (which is the alpha channel) is zero (as explained here).
Do it somehow like this:
public static boolean isFullyAlpha(File f) throws IOException {
BufferedImage img = ImageIO.read(f);
for(int y = 0; y < img.getHeight(); y++) {
for(int x = 0; x < img.getWidth(); x++) {
if(((img.getRGB(x, y) >> 24) & 0xFF) != 0) {
return false;
}
}
}
return true;
}
Related
I'm attempting to convert a .csv file containing grayscale values to an image using BufferedImage.
The csv is read into pixArray[] initially, in which all values are doubles.
I am attempting to use BufferedImage to create a 100x100px output image with the code
BufferedImage image = new BufferedImage(width,height,BufferedImage.
TYPE_BYTE_GRAY);
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
image.setRGB(x, y, (int)Math.round(pixArray[y]));
}
}
File file_out = new File("output.png");
try {
ImageIO.write(image, "png", file_out);
} catch (IOException e) {
e.printStackTrace();
}
but all I have as output is a 100x100 black square.
I've tried alternatives to TYPE_BYTE_GRAY with no success, as well as the png format for outout, and can't find what is producing this error.
It should be
int g = (int)Math.round(pixArray[y]);
image.setRGB(x,y,new Color(g,g,g).getRGB());
What your current code is doing is setting the alpha to the pixel value but leaving the color components all zero.
Posting an alternative solution. While Jim's answer is correct and works, it is also one of the slowest* ways to put sample values into a gray scale BufferedImage.
A BufferedImage with TYPE_BYTE_GRAY don't need all the conversion to and from RGB colors. To put the gray values directly into the image, do it through the image's raster:
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
WritableRaster raster = image.getRaster();
for (int y = 0; y < height; y++) {
int value = (int) Math.round(pixArray[y])
for (int x = 0; x < width; x++) {
raster.setSample(x, y, 0, value);
}
}
*) Slow because of creating excessive throw-away Color instances, but mostly due to color space conversion to/from sRGB color space. Probably not very noticeable in a 100x100 image, but if you try 1000x1000 or larger, you will notice.
PS: I also re-arranged the loops to loop over x in the inner loop. This is normally faster, especially when reading values, due to data locality and caching in modern CPUs. In your case, it matters mostly because you only need to compute (round, cast) the value for each row.
I'm attempting to convert an image to black and white only (not grey scale).
I've used this:
BufferedImage blackAndWhiteImage = new BufferedImage(
dWidth.intValue(),
dHeight.intValue(),
BufferedImage.TYPE_BYTE_BINARY);
Graphics2D graphics = blackAndWhiteImage.createGraphics();
graphics.drawImage(colourImage, 0, 0, null);
return blackAndWhiteImage;
Everything fine, until I decided to try out brighter colors, like the Google logo for example:
and it came out with this:
Then I tried first to pass trough grey scale first using:
BufferedImage blackAndWhiteImage2 = new BufferedImage(
dWidth.intValue(),
dHeight.intValue(),
BufferedImage.TYPE_USHORT_GRAY);
And it seemed to have saved the Blue color, but not the brightest (in this case yellow), and as you may see it decreased in quality:
Any suggestions are much appreciated; I believe what I am seeking for is to convert every colour to Black except White (which would be the background color), this is already done when applying TYPE_BYTE_BINARY removing the alpha channel.
EDIT:
Maybe I have not explained very clear:
the final image has to have White background **1
every other color has to be converted to Black
**1 - there are some cases where the image is actually White on Black..which is annoying (whiteOnBlackExample) as it complicates a lot this process, and I will leave this later on, as priority now is to convert "normal" images.
What I did was, first strip out the alpha channel if it exists -> therefore convert the alpha channel to White; then convert every other color to Black
If you use JavaFX you can use the ColorAdjust effect with brightness of -1 (minimum), which makes all the (non-white) colors black:
public class Main extends Application {
Image image = new Image("https://i.stack.imgur.com/UPmqE.png");
#Override
public void start(Stage primaryStage) {
ImageView colorView = new ImageView(image);
ImageView bhView = new ImageView(image);
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setBrightness(-1);
bhView.setEffect(colorAdjust);
primaryStage.setScene(new Scene(new VBox(colorView, bhView)));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
These Effects are optimized so they are probably faster than what you would achieve by applying them manually.
Edit
Since your requirements are that
any pixel which is not opaque should be transformed to white, and
any pixel which is not white should be transformed to black,
the predesigned effects won't suit you as far as I can tell - they are too specific. You can do pixel by pixel manipulation:
WritableImage writableImage = new WritableImage(image.getPixelReader(), (int) image.getWidth(), (int) image.getHeight());
PixelWriter pixelWriter = writableImage.getPixelWriter();
PixelReader pixelReader = writableImage.getPixelReader();
for (int i = 0; i < writableImage.getHeight(); i++) {
for (int j = 0; j < writableImage.getWidth(); j++) {
Color c = pixelReader.getColor(j, i);
if (c.getOpacity() < 1) {
pixelWriter.setColor(j, i, Color.WHITE);
}
if (c.getRed() > 0 || c.getGreen() > 0 || c.getBlue() > 0) {
pixelWriter.setColor(j, i, Color.BLACK);
}
}
}
ImageView imageView = new ImageView(writableImage);
Note that the order in which you apply the rules matter. A transparent non-white pixel will turn white if you apply 1 and then 2, but if you apply 2 and then 1 it will end up black. This is because the predefined WHITE and BLACK colors are opaque. You can manually set the red, green and blue values while not changing the alpha value instead. It all depends on your exact requirements.
Remember that due to lossy compression of some file formats you might not find true white in them at all, but a value which is close to true white and your eye won't be able to tell the difference.
Here is the example from my comment. At first open the input image and create a new one for output.
BufferedImage myColorImage = ImageIO.read(fileInput);
BufferedImage myBWImage = new BufferedImage(myColorImage.getWidth(), myColorImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Then iterate through all the pixels and compare rgb values with a threshold:
for (int x = 0; x < myColorImage.getWidth(); x++)
for (int y = 0; y < myColorImage.getHeight(); y++)
if (rgbToGray(myColorImage.getRGB(x, y), MODE.AVERAGE) > threshold)
myBWImage.setRGB(x, y, 0);
else
myBWImage.setRGB(x, y, 0xffffff);
Here is rgbToGray method implementation to compare with threshold:
private static int rgbToGray(int rgb, MODE mode) {
// split rgb integer into R, G and B components
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
int gray;
// Select mode
switch (mode) {
case LIGHTNESS:
gray = Math.round((Math.max(r, Math.max(g, b)) + Math.min(r, Math.min(g, b))) / 2);
break;
case LUMINOSITY:
gray = Math.round(0.21f * r + 0.72f * g + 0.07f * b);
break;
case AVERAGE:
default:
gray = Math.round((r + g + b) / 3);
break;
}
return gray;
}
An utility enum:
private enum MODE {
LIGHTNESS, AVERAGE, LUMINOSITY
}
I got the following result:
Note: for your google image even threshold = 1 is suitable, for other images you should pick another values from range [0..255]. For photos, most likely, more appropriate values are about 100-150. MODE also will affect the final result.
I have one task in Libgdx:
Change color of image for example triangle,star, heart and others shapes.
All shapes are given in png with transparent background.
I'm doing this with Pixmap, checking every pixel if it is not-transparent fill pixel with needed color.
Here is the code:
for (int y = 0; y < pixmap.getHeight(); y++) {
for (int x = 0; x < pixmap.getWidth(); x++) {
Color color = new Color();
Color.rgba8888ToColor(color, pixmap.getPixel(x, y));
if(color.r != 1 || color.b != 1 && color.g != 1){
pixmap.setColor(setColor);
pixmap.fillRectangle(x, y, 1, 1);
}
}
}
Is there any other way to do this?
Because method below works too long.
You can certainly speed up the way you're doing it, because right now for every pixel in the image you are instantiating a new Color object and converting the pixel components into separate floats. And then the GC will have to take time to clear up all those Color objects you are generating. Those extra intermediate steps are unnecessary.
Also, you only need to call pixmap.setColor one time (although that is fairly trivial). And you can use drawPixel instead of fillRectangle to more efficiently draw a single pixel.
static final int R = 0xFF000000;
static final int G = 0x00FF0000;
static final int B = 0x0000FF00;
pixmap.setColor(setColor);
for (int y = 0; y < pixmap.getHeight(); y++) {
for (int x = 0; x < pixmap.getWidth(); x++) {
int pixel = pixmap.getPixel(x, y);
if((pixel & R) != R || (pixel & B) != B && (pixel & G) != G){
pixmap.drawPixel(x, y);
}
}
}
(By the way, did you mean to check red or blue and green? Seems like odd criteria unless you only want to change the color if the original color is pure yellow, cyan, or white.)
If you are merely drawing the images as Textures, then there is no need to be operating on the Pixmaps like this. You could make your source image white and tint the image when drawing it with SpriteBatch, for example, and this would have no impact on performance.
Support library provides utilities to tint drawable.
// create a drawable from the bitmap
BitmapDrawable tintedDrawable = DrawableCompat.wrap(new BitmapDrawable(getResources(), pixmap));
// Apply a Tint, it will color all non-transparent pixel
DrawableCompat.setTint(setColor);
// Draw it back on a bitmap
Bitmap b = Bitmap.createBitmap(pixmap.getWidth(), pixmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
tintedDrawable.setBounds(0, 0, pixmap.getWidth(), pixmap.getHeight());
tintedDrawable.draw(c);
If you just need to show these pictures with a specific color in your application you can simply do it with setColorFilter
ImageView ivEx = (ImageView) findViewById(R.id.ivEx);
int color = Color.parseColor("your color's code");
ivEx.setColorFilter(color);
How to get the color of the image icon.png using java
Actually I have a servlet for that i wll be sending image using multipart file transfer,
now the server should respond back the color of the icon file,
Here the image file has a single color;
Assuming you have the path to the image file:
Color getImageColor(File imagePath) {
BufferedImage image = ImageIO.read(imagePath);
int color = image.getRGB(0, 0);
for (int r = 0; r < image.getHeight(); r += 1) {
for (int c = 0; c < image.getWidth(); c += 1) {
if (image.getRGB(c, r) != color) {
throw new IllegalArgumentException("Image: " + imagePath + " is not a solid color.");
}
}
}
return new Color(color);
}
This code assumes that the image really does only have a single color and pulls the first pixel only.
The loop is there to make sure the entire image is one color. There are many ways to handle that situation, of course.
You can loop the BufferedImage (two loops - one from 0 to width, and one from 0 to height), and get the call getRgb(x, y). Then count each different value. You can use a Map for that (key = color, value = number of occurences). Note that this will give you counts of how often each color occurs in the image.
I'm going to use the getRGB method of BufferedImage. I want to check the pixels of an image and see which of them have transparency (in general the pixels I will have that are transparent will be totaly transparent). How can I get it from the int that getRGB returns?
BufferedImage img = ....
public boolean isTransparent( int x, int y ) {
int pixel = img.getRGB(x,y);
if( (pixel>>24) == 0x00 ) {
return true;
}
return false;
}
Of course img has to be in the correct format TYPE_4BYTE_ABGR or some format that supports alpha channels else if will always be opaque (ie 0xff).
the correct shift to get alpha value in an int is with >>> due to sign bit.
example:
int alpha1 = (pixel1 & 0xff000000) >>> 24;