Android: image saved using copyPixelsFromBuffer is distorted - java

I am trying to make an image processing program in Android Studio and I have a problem concerning how to save a Bitmap.
My problem is about saving an image which comes from a ByteBuffer.
To show it here, I have done this: I load an image in a ByteBuffer and I try to save it, rescaled to 1014x1163 pixels. And the image I get is distorted, messy.
For example, here is an image I load:
And here is what I get in the image I save:
Here is my code:
imageSize = (int) (bmWidth*bmHeight*4);
ByteBuffer pixelsArray = ByteBuffer.allocateDirect(imageSize);
ByteBuffer outputArray = ByteBuffer.allocateDirect(imageSize);
workingBitmap.copyPixelsToBuffer(pixelsArray);
workingBitmap.copyPixelsToBuffer(outputArray);
int thiswidth = workingBitmap.getWidth();
int thisheight = workingBitmap.getHeight();
Bitmap copiedBitmap = Bitmap.createScaledBitmap(workingBitmap, Width, Height, false);
int thiswidth2 = copiedBitmap.getWidth();
int thisheight2 = copiedBitmap.getHeight();
outputArray.rewind();
copiedBitmap.copyPixelsFromBuffer(outputArray);
SaveJPG(copiedBitmap);
The function SaveJPG works fine because if I call SaveJPG(workingBitmap) it saves a normal image.
Here is the code with variables values during debugging:
I am wondering if the problem comes from the fact that the output resolution is not a multiple of 4. That's mandatory in my program: the output image resolution can be of any value (odd or even).
I have tried many different things (copying workingBitmap and resizing the copy for example).
No success. I don't know what the cause of the problem is.
Does anyone have some source code which can save an image of any resolution, stored in a ByteBuffer ?
Thanks in advance.

Try this
try (FileOutputStream out = new FileOutputStream(yourFileName)) {
yourBitmap.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap instance
// PNG is a lossless format, the compression factor (100) is ignored
} catch (IOException e) {
e.printStackTrace();
}
Or use this
MediaStore.Images.Media.insertImage(getContentResolver(), yourbitmap,
"File name", "description of the image");

I have found the cause of the problem. It was simply due to a problem in the width and height of the input image: I was using the width and height of the View which displays the input bitmap, instead of using the bitmap's width and height.

Related

Copying image and maintain orientation?

Edit
It turns out that the 2nd snippet is actually working but the images in question still show incorrectly in my IDE (IntelliJ IDEA) for some reason.
I am trying read an image, place a watermark and save it in a different folder and the below code does a good job, but it randomly orientates my images.
try {
final Image image = ImageIO.read(file);
int w = ((BufferedImage) image).getWidth();
int h = ((BufferedImage) image).getHeight();
final BufferedImage finalImage =
new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
//Graphics2D g = finalImage.createGraphics();
Graphics2D g = (Graphics2D) finalImage.getGraphics();
g.drawImage(image, 0, 0, null);
g.drawImage(watermark, 0, 0, null);
g.dispose();
File outputFile = new File("watermarked/" + folderName + "/" + file.getName());
outputFile.mkdirs();
ImageIO.write(finalImage, "jpg", outputFile);
} catch (IOException e) {
// TODO: notify client
e.printStackTrace();
}
After some reading I learned that ImageIO.read(...) does not maintain orientation or other "metadata" of the image it is processing. I also read about using the ImageReader to extract the metadata. According to the docs, using ImageReader.readall() should include the metadata in the returned IIOImage but I still end up with some of my images upside down. The below code demonstrates the copying without adding a watermark.
File out = new File("watermarked/" + folderName + "/" + file.getName());
out.getParentFile().mkdirs();
ImageInputStream input = ImageIO.createImageInputStream(file);
ImageOutputStream output = ImageIO.createImageOutputStream(out);
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
ImageReader reader = readers.next();
reader.setInput(input);
IIOImage image = reader.readAll(0, null);
// Should not be needed since readAll should already take care of it.
IIOMetadata metadata = reader.getImageMetadata(0);
image.setMetadata(metadata);
ImageWriter writer = ImageIO.getImageWriter(reader);
writer.setOutput(output);
writer.write(image);
System.out.println(writer.canReplaceImageMetadata(0)); // Returns false
writer.replaceImageMetadata(0, metadata); // Results in a "Unsupported write variant" error.
Both code snippets reside in a method that get passed a folderName as a string and the actual image file.
Edit
The above snippet works and the issue is something else. In my windows folder all my images made with a Galaxy S8 show in the correct orientation. But when I copy them to my project and open them in IntelliJ IDEA some are oriented differently. So I added sanselan as a dependency to get more insight in the meta data of the images and the images that get a different orientation in the IDE do indeed show a different orientation in the metadata. But why aren't they oriented like that in the windows folder, am I missing a metadata field or is windows storing additional data somewhere outside the image metadata?

Writing a GIF image over PNG image

I wonder if there is a way in java to put a gif image over png image at particular location (say at particular value of x,y). If so please help me through this.
This is the case :
I have a base Image which is of png type. and I have gif images of size 62*62. I wanted to put several such gif images on png image and I need to render the png image on front end at every 5 seconds..
To extract image from GIF file.. This save the first image into png file from GIF.
try {
ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next();
ImageInputStream stream = ImageIO.createImageInputStream(new File("c:/aaa.gif");
reader.setInput(stream);
int count = reader.getNumImages(true);
if(count>0){
BufferedImage frame = reader.read(0);
ImageIO.write(frame, "png", new File(filePath+fileName+".png"));
System.out.println("Donesss");
}
} catch (IOException ex) {
}

PDFBox draw black image from BufferedImage

I try to draw an image from a bufferedImage into a PDF using PDFBox but fails, and I get black images and Acrobat Reader warns whith errors like "Out of memory" (but PDF is display).
I use a bufferedImage because I need to draw a JavaFX Image object (with came from call to Funciones.crearImagenDesdeTexto(), is a function which converts a text into an Image) into PDF. Rest of images works well without using bufferedimage.
PDPixelMap img = null;
BufferedImage bi;
try {
//If item has id, I try to get image with that id (image it's shows OK on PDF)
img = new PDPixelMap(documento, read(getClass().getResourceAsStream("/com/img/" + item.getId() + ".png")));
}
catch (Exception e) {
//If item has not id or fails load image, I create image on the fly (which contains item name. This not work on PDF, shows black images)
bi = new BufferedImage(alto, ancho, BufferedImage.TYPE_INT_ARGB);
bi.createGraphics().drawImage(SwingFXUtils.fromFXImage(Funciones.crearImagenDesdeTexto(item.getNombre()), null), ancho, alto, null);
img = new PDPixelMap(documento, bi);
}
finally {
contenedor.drawXObject(img, x, y, alto, ancho);
}
NOTE: crearImagenDesdeTexto() returns a JavaFX Image Object that is create on the fly (I try this function in other parts of the program and works well, function is take from other stackOverflow response).
Your code is confusing, you have three "new PDJpeg" and one of them is in a catch (which should just handle the error). And what does "read()" do? Does it pass a stream or a BufferedImage? If it is a stream, then it is wrong, because PDJpeg is for JPEGs, not for PNG.
The second one
img = new PDJpeg(documento, (getClass().getResourceAsStream("/com/img/" + Byte.toString(item.getId()) + ".png")));
is definitively wrong for the same reason: PDJPeg is not for PNG files / streams.
If you want to create an image from a PNG file / stream, use PDPixelMap.
It is possible to create a PDJpeg object from a BufferedImage, but this is recommended only if the image wasn't encoded before. Because if you would read a BufferedImage from a JPEG, and then use PDJPeg for this, you'll have a slight loss of quality as the image is decoded and encoded again (JPEG is a "lossy" compression format).
If my advice doesn't help, please upload the JPEG file and the PDF somewhere.
Also make sure that you're using the latest version, which is 1.8.7.
Update after comments:
the parameters to createGraphics.drawImage() should be 0, 0 and not width, height. The two parameters are a location, not a size.
Finally, I find a solution (thanks also to Tilman Hausherr):
private void dibujarImagen(Item i, int x, int y, int alto, int ancho) throws IOException {
PDPixelMap img = null;
try {
img = new PDPixelMap(documento, read(getClass().getResourceAsStream("/com/img/" + i.getId() + ".png")));
}
catch (IllegalArgumentException e) {
img = new PDPixelMap(documento, SwingFXUtils.fromFXImage(Funciones.crearImagenDesdeTexto(i.getNombre()),null));
}
finally {
contenedor.drawXObject(img, x, y, alto, ancho);
}
}

Understand an gif is animated or not in JAVA

I have a gif image and I would like to be able to detect whether the gif is an animated gif or not using JAVA. This question is about detection rather than displaying the gif.
I see that MIME type of animated gif isn't different of static gif.
How I can do it?
You need an ImageReader. if size is 1 its not animated, everything above is animated.
Check this out:
public Snippet() {
File f = new File("test.gif");
ImageReader is = ImageIO.getImageReadersBySuffix("GIF").next();
ImageInputStream iis;
try {
iis = ImageIO.createImageInputStream(f);
is.setInput(iis);
int images= is.getNumImages(true);
} catch (IOException e) {
e.printStackTrace();
}
}

Converting PNG into JPEG

I'm having problems converting a simple PNG into a JPEG format.
I'm using the following code:
...
File png = new File(filePath);
try {
SeekableStream s = new FileSeekableStream(png);
PNGDecodeParam pngParams = new PNGDecodeParam();
ImageDecoder dec = ImageCodec.createImageDecoder("png", s, pngParams);
RenderedImage pngImage = dec.decodeAsRenderedImage();
JPEGEncodeParam jparam = new JPEGEncodeParam();
jparam.setQuality(0.50f); // e.g. 0.25f
File jpeg = new File("jpeg.jpeg");
FileOutputStream out = new FileOutputStream(jpeg);
ImageEncoder encoder = ImageCodec.createImageEncoder("JPEG", out, jparam);
encoder.encode(pngImage);
s.close();
} catch (IOException e) {
ok = false;
e.printStackTrace();
}
return ok;
}
...
I end up with an JAI exception ->
java.lang.RuntimeException: Only 1, or 3-band byte data may be written.
at com.sun.media.jai.codecimpl.JPEGImageEncoder.encode(JPEGImageEncoder.java:148) ...
Ran out of options. Any suggestion?
It might be easier to use ImageIO to read the PNG into a BufferedImage and write the image out in JPEG format.
Addendum: In this approach, the conversion is handled transparently by the writer's ImageTranscoder.
BufferedImage img = ImageIO.read(new File("image.png"));
ImageIO.write(img, "jpg", new File("image.jpg"));
you probably have alpha channel in the png that you need to get rid of before trying to write the jpg.
Create a new BufferedImage with type TYPE_INT_RGB (not TYPE_INT_ARGB), and then write your source image (pngImage) onto the new blank image.
Something like this (warning, not tested code):
BufferedImage newImage = new BufferedImage( pngImage.getWidth(), pngImage.getHeight(), BufferedImage.TYPE_INT_RGB);
newImage.createGraphics().drawImage( pngImage, 0, 0, Color.BLACK, null);
I also found that reading a PNG image into a BufferedImage with ImageIO (Java 6) and writing it out to a JPG "format name" corrupted the image. The image was there, but the colors looked "solarized" and almost inverted. The JPG file was much smaller than the PNG file for sure, so a lot of compression was done. I don't see how you might control the compression or color depth.
I had corrupted file after conversion with other solutions but this method worked for me:
public static void formatConverter(String pngFile, String jpgFile) {
try {
File input = new File(pngFile);
File output = new File(jpgFile);
BufferedImage image = ImageIO.read(input);
BufferedImage result = new BufferedImage(
image.getWidth(),
image.getHeight(),
BufferedImage.TYPE_INT_RGB);
result.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
ImageIO.write(result, "jpg", output);
} catch (IOException e) {
e.printStackTrace();
}
}
I suppse that JAI reads the PNG image with an indexed colour model and is only able to write 8-bit grayscale or 24-bit colour images as JPEG files.
If you are not required to use JAI for this task, you should be able to use ImageIO instead:
ImageIO.write(ImageIO.read(new File("in.png")), "JPEG", new File("out.jpg"));
I was getting the following message in a slightly different context. Getting rid of the alpha channel solved the problem
javax.imageio.IIOException: Sample size must be <= 8
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.write(JPEGImageWriter.java:435)
at javax.imageio.ImageWriter.write(ImageWriter.java:580)
at com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageWriter.write(Unknown Source)
at net.sf.basedb.util.ImageTools.tiffToJpg(ImageTools.java:98)
at net.sf.basedb.util.ImageTools.main(ImageTools.java:118)
see Converting transparent gif / png to jpeg using java
Have a look at the solution that redraws with the graphics environment posted by harmanjd. The solution with the DirectColorModel doesn't compile and should be wiped away. I don't have enough rep points to comment directly there.

Categories

Resources