How do I save a BufferedImage to a pgm file? - java

I looked through the internet but I could not find an answer. I have a pgm file which I use as a BufferedImage to do a convolution (I use JAI for that) but I am having trouble in saving it back to a pgm file.
So far I used following code to save:
JAI.create("filestore", newImage, outputFileName);
With that I get a pgm file but when I open the image IfranView tells me that it is a TIF file with incorrect extension. What do I need to change?
I appreciate any help! Please provide code examples if possible. Thanks everyone.
Kind regards,
Staniel

BufferedImage bufferedImage = ImageIO.read(new File("input file directory...image.png"));
ImageIO.write(bufferedImage, "pgm", new File("output file directory.....image.pgm"));
This should take a buffered image (jpeg, png...etc) and convert it properly to a pgm.
EDIT: The JAI Plugin which allows for .pgm files to be used with ImageIO can be found at http://www.oracle.com/technetwork/java/index.html

Here's an example I found. Not tested.
// Create the OutputStream.
OutputStream out = new FileOutputStream(fileToWriteTo);
// Create the ParameterBlock.
PNMEncodeParam param = new PNMEncodeParam();
param.setRaw(true.equals("raw"));
//Create the PNM image encoder.
ImageEncoder encoder = ImageCodec.createImageEncoder("PNM", out, param);
See Writing PNM Files.

I found the answer. I already added the external JAI imageio to my library.
ImageIO.write(bufferedImage, "pnm", new File("output file directory.....image.pgm"));
Instead of "pgm" it should say "pnm". The new file will automatically have the pgm extension.

Maybe late, but I just wrote one. A simple PGM writer taking a 2d-double array with values in range [0.0,1.0].
public static void WritePGM(string fileName, double[,] bitmap)
{
var width = bitmap.GetLength(0);
var height = bitmap.GetLength(1);
var header = "P5\n" + width + " " + height + "\n255\n";
var writer = new BinaryWriter(new FileStream(fileName, FileMode.Create));
writer.Write(System.Text.Encoding.ASCII.GetBytes(header));
for (var j = 0; j < height; j++)
{
for (var i = width-1; i >= 0; i--)
{
var c = (byte)(System.Math.Max(System.Math.Min(1.0, bitmap[i, j]), 0.0) * 255.0);
writer.Write(c);
}
}
writer.Close();
}

Related

Is there a way to get Java to read a PNG as TYPE_INT_RGB / TYPE_INT_ARGB?

When I read a PNG image in Java using javax.imageio.ImageIO.read(), the resulting BufferedImage is of TYPE_3BYTE_BGR or TYPE_4BYTE_ABGR depending on transparency.
I'm processing very large images (64+ megapixels), and need them in TYPE_INT_RGB / TYPE_INT_ARGB format, which requires an expensive and very-large-chunk-of-memory hogging repainting of the image onto a new image in the correct format, which is causing OOMs.
It would be much better if I could somehow persuade ImageIO to read the image in the desired format from the get-go - is there any way of doing that? Thanks!
Yes, it is possible to read into a predefined type of BufferedImage, given that the type is supported by and compatible with the reader plugin. Most often the TYPE_#BYTE_* types are compatible with the TYPE_INT_* types, and this is the case for the standard PNGImageReader.
To make it work, you need access to the ImageReader directly, and use the read method that takes an ImageReadParam to control the type of image. It's possible to read into a pre-allocated image by using the ImageReadParam.setDestination(..) method, or to just specify the type of image and let the reader plugin allocate it for you by using ImageReadParam.setDestinationType(..) like I will show below.
Here's a short stand-alone code sample that shows how to read into a specific image type:
public static void main(String[] args) throws IOException {
File input = new File(args[0]);
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
// Find a suitable reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (!readers.hasNext()) {
throw new IIOException("No reader for " + input);
}
ImageReader reader = readers.next();
try {
reader.setInput(stream);
// Query the reader for types and select the best match
ImageTypeSpecifier intPackedType = getIntPackedType(reader);
System.out.println("intPackedType = " + intPackedType);
// Pass the type to the reader using read param
ImageReadParam param = reader.getDefaultReadParam();
param.setDestinationType(intPackedType);
// Finally read the image
BufferedImage image = reader.read(0, param);
System.out.println("image = " + image);
}
finally {
reader.dispose();
}
}
}
private static ImageTypeSpecifier getIntPackedType(ImageReader reader) throws IOException {
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
while (types.hasNext()) {
ImageTypeSpecifier spec = types.next();
switch (spec.getBufferedImageType()) {
case BufferedImage.TYPE_INT_RGB:
case BufferedImage.TYPE_INT_ARGB:
return spec;
default:
// continue searching
}
}
return null;
}
Sample output from one of my runs using a PNG as input:
intPackedType = javax.imageio.ImageTypeSpecifier$Packed#707084ba
image = BufferedImage#45ff54e6: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 100 height = 100 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0
Where type = 2 means BufferedImage.TYPE_INT_ARGB.

Sharing multiple files with Gluon ShareService (image and txt)

We want to know how we can share multiple files (image and txt file) with the Gluon ShareService. Especially how to share an image which was previously taken and stored (in gallery) with the PictureService.
But we need to create a file first with the path and image name. Unfortunately, the PictureService saves the image with the image title consisting of date and time at the moment the picture was taken.
We tried to get the image name with the loadImageFromGallery method but this returns void and opens the recent-screen.
Here what we've tried to share an image:
public void sharePicture() {
Services.get(PicturesService.class).ifPresent(picturesService -> {
Image image = picturesService.loadImageFromGallery().get();
File file= new File("Pictures", image.toString());
Services.get(ShareService.class).ifPresent(service -> {
service.share("image/jpg", file);
});
});
}
How can we store the image where we want with a title we want?
How can we share a file and an image together?
You are on the right track, combining different services from Charm Down, in order to select an image from the gallery and share it.
There is a major problem in this approach, though: You can't convert easily a JavaFX Image into a File.
So far the PicturesService returns only a JavaFX Image, and not a File, so we need a way to save that image into a file that we can read and share.
And the process is not easy since on mobile we don't have SwingUtilities.
The initial approach of using a PixelReader to read the image and get a byte array doesn't really work, as it will give you a big raw file that can't be read or share.
I've used this solution that makes use of a PNG encoder to get the byte array of a png from a JavaFX image:
PngEncoderFX encoder = new PngEncoderFX(image, true);
byte[] bytes = encoder.pngEncode();
Then I'll save that byte array into a file in the public storage folder (so it can be shared), that I can retrieve using the `StorageService:
private File getImageFile(Image image) {
if (image == null) {
return null;
}
// 1. Encode image to png
PngEncoderFX encoder = new PngEncoderFX(image, true);
byte[] bytes = encoder.pngEncode();
// 2.Write byte array to a file in public storage
File root = Services.get(StorageService.class)
.flatMap(storage -> storage.getPublicStorage("Pictures"))
.orElse(null);
if (root != null) {
File file = new File(root, "Image-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuuMMdd-HHmmss")) + ".png");
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(bytes);
return file;
} catch (IOException ex) {
System.out.println("Error: " + ex);
}
}
return null;
}
Now, you can call the PicturesService, retrieve the image, save it to the file and finally share it:
Services.get(PicturesService.class).ifPresent(pictures -> {
// 1. Retrieve picture from gallery
pictures.loadImageFromGallery().ifPresent(image -> {
// 2. Convert image to file
File imageFile = getImageFile(image);
// 3. Share file
if (imageFile != null) {
Services.get(ShareService.class).ifPresent(share -> {
share.share("image/png", imageFile);
});
}
});
});
Note that you may run into memory issues if you try to encode big images.
Anyway, all the process could be simplified if the PicturesService will return a file in the first place. If you want to file an issue, you can do it here.
EDIT
A possible solution to avoid memory issues, and to reduce the size of the shared file, and based on this solution, is scaling down the original image, if it exceeds certain size, like it is already done in the iOS implementation of the PicturesService:
private Image scaleImage(Image source) {
// Possible limit based on memory limitations
double maxResolution = 1280;
double width = source.getWidth();
double height = source.getHeight();
double targetWidth = width;
double targetHeight = height;
if (width > maxResolution || height > maxResolution) {
double ratio = width/height;
if (ratio > 1) {
targetWidth = maxResolution;
targetHeight = targetWidth/ ratio;
}
else {
targetHeight = maxResolution;
targetWidth = targetHeight * ratio;
}
}
ImageView imageView = new ImageView(source);
imageView.setPreserveRatio(true);
imageView.setFitWidth(targetWidth);
imageView.setFitHeight(targetHeight);
return imageView.snapshot(null, null);
}
This method can be used now in getImageFile():
// 1 Scale image to avoid memory issues
Image scaledImage = scaleImage(image);
// 2. Encode image to png
PngEncoderFX encoder = new PngEncoderFX(scaledImage, true);
byte[] bytes = encoder.pngEncode();
// 3. Write byte array to a file in public storage
...

Convert/display byte array to bmp/jpeg image

First of all, im asking this specific question, because i've already read many examples around this topic, but none of them really helped.
My problem is that i'd like to convert and display a jpg image. I don't have the original image, it is on a server. The data comes through a stream (socket) and it is given in a byte array. In this byte array, every 4 bytes represents information about a pixel. I managed to get the RGBs and convert/save them into a bmp image, however i couldn't find a working solution to make a jpg.
On the other hand, i have a BMP :) how can i display it in a JLabel or JPanel? I also read abou imageicon ImageIO etc. but it doesn't work for me. I use ScheduleExecutorSystem btw. Maybe that is the problem?
Here's the encoder code (Copyright (C) 2013 Philipp C. Heckel ):
public static void encodeToBitmap(byte[] srcBytes, OutputStream destStream)
throws IOException {
int imageWidth = 1024;
int imageHeight = 1080;
int imageBytes = imageWidth * imageHeight * 3;
int filesizeBytes = imageBytes + BMP_SIZE_HEADER;
byte[] header = BMP_HEADER.clone(); // Clone bitmap header template, and
// overwrite with fields
header = writeIntLE(header, BMP_OFFSET_FILESIZE_BYTES, filesizeBytes);
header = writeIntLE(header, BMP_OFFSET_IMAGE_WIDTH, imageWidth);
header = writeIntLE(header, BMP_OFFSET_IMAGE_HEIGHT, imageHeight);
header = writeIntLE(header, BMP_OFFSET_IMAGE_DATA_BYTES, 0);
header = writeIntLE(header, BMP_OFFSET_PAYLOAD_LENGTH,
(int) srcBytes.length);
// WRITE TO STREAM
// Add payload
destStream.write(header, 0, header.length);
for (int offset = imageBytes - imageWidth * 3; offset >= 0; offset -= imageWidth * 3) {
for (int i = 0; i < (imageWidth) * 3; i++) {
destStream.write(srcBytes[offset + i]);
}
}
destStream.close();
}
And the code, how i use it:
BitmapEncoder.encodeToBitmap(RGBvalues, new FileOutputStream("path to file"));
RGBvalues - bytes of 3, with the RGB values (i don't say!! :P )
image width and Height is fix for debug purpose
I have a JPanel and a JLabel within, and i added a default ImageIcon to the JLabel (a jpg image), and it works fine.
After i saved the image, i try :
ImageIcon icon = new ImageIcon("path to file");
pictureLabel.setIcon(icon);
It only makes the default disappear. Am i forgetting to set something?
Is this the fastest way, to do it?
The ImageIcon class only directly supports reading PNG, GIF and JPEG types. You need to use ImageIO to read your bitmap into a generic Image object (which is supported by ImageIcon) and then pass that to ImageIcon. Here's how I'd modify your code to work:
File imgFile = new File("path to file");
Image image = javax.imageio.ImageIO.read(imgFile);
ImageIcon icon = new ImageIcon(image);
pictureLabel.setIcon(icon);
Note that you can also use ImageIO to convert your bitmap to JPEG format.

How to combine two or many tiff image files in to one multipage tiff image in JAVA

I have 5 single page tiff images.
I want to combine all these 5 tiff images in to one multipage tiff image.
I am using Java Advanced Imaging API.
I have read the JAI API documentation and tutorials given by SUN.
I am new to JAI. I know the basic core java.
I dont understand those documentation and turorial by SUN.
So friends Please tell me how to combine 5 tiff image file in to one multipage tiff image.
Please give me some guidence on above topic.
I have been searching internet for above topic but not getting any single clue.
I hope you have the computer memory to do this. TIFF image files are large.
You're correct in that you need to use the Java Advanced Imaging (JAI) API to do this.
First, you have to convert the TIFF images to a java.awt.image.BufferedImage. Here's some code that will probably work. I haven't tested this code.
BufferedImage image[] = new BufferedImage[numImages];
for (int i = 0; i < numImages; i++) {
SeekableStream ss = new FileSeekableStream(input_dir + file[i]);
ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", ss, null);
PlanarImage op = new NullOpImage(decoder.decodeAsRenderedImage(0), null, null, OpImage.OP_IO_BOUND);
image[i] = op.getAsBufferedImage();
}
Then, you convert the BufferedImage array back into a multiple TIFF image. I haven't tested this code either.
TIFFEncodeParam params = new TIFFEncodeParam();
OutputStream out = new FileOutputStream(output_dir + image_name + ".tif");
ImageEncoder encoder = ImageCodec.createImageEncoder("tiff", out, params);
Vector vector = new Vector();
for (int i = 0; i < numImages; i++) {
vector.add(image[i]);
}
params.setExtraImages(vector.listIterator(1)); // this may need a check to avoid IndexOutOfBoundsException when vector is empty
encoder.encode(image[0]);
out.close();

Convert PNG to bitonal TIFF

-Edit-
FYI.. I am converting b&w documents scanned in as greyscale or color.
1)The first solution worked, it just reversed black & white (black background, white text). It also took nearly 10 minutes.
2)The JAI solution in the 2nd answer didn't work for me. I tried it before posting here.
Has anyone worked with other libraries free or pay that handle image manipulation well?
-Original-
I am trying to convert an PNG to a bitonal TIFF using Java ImageIO. Has anyone had any luck doing this? I have got it to convert from PNG to TIFF. I am not sure if I need to convert the BufferedImage (PNG) that I read in or convert on the TIFF as I write it. I have searched and searched but nothing seems to work? Does anyone have an suggestions where to look?
Here is the code that converts...
public static void test() throws IOException {
String fileName = "4848970_1";
// String fileName = "color";
String inFileType = ".PNG";
String outFileType = ".TIFF";
File fInputFile = new File("I:/HPF/UU/" + fileName + inFileType);
InputStream fis = new BufferedInputStream(new FileInputStream(fInputFile));
ImageReaderSpi spi = new PNGImageReaderSpi();
ImageReader reader = spi.createReaderInstance();
ImageInputStream iis = ImageIO.createImageInputStream(fis);
reader.setInput(iis, true);
BufferedImage bi = reader.read(0);
int[] xi = bi.getSampleModel().getSampleSize();
for (int i : xi) {
System.out.println("bitsize " + i);
}
ImageWriterSpi tiffspi = new TIFFImageWriterSpi();
TIFFImageWriter writer = (TIFFImageWriter) tiffspi.createWriterInstance();
// TIFFImageWriteParam param = (TIFFImageWriteParam) writer.getDefaultWriteParam();
TIFFImageWriteParam param = new TIFFImageWriteParam(Locale.US);
String[] strings = param.getCompressionTypes();
for (String string : strings) {
System.out.println(string);
}
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionType("LZW");
File fOutputFile = new File("I:\\HPF\\UU\\" + fileName + outFileType);
OutputStream fos = new BufferedOutputStream(new FileOutputStream(fOutputFile));
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
writer.setOutput(ios);
writer.write(null, new IIOImage(bi, null, null), param);
ios.flush();
writer.dispose();
ios.close();
}
I have tried changing the compression to type "CCITT T.6" as that appears to be what I want, but I get an error " Bits per sample must be 1 for T6 compression! " Any suggestion would be appreciated.
Most likely, you want something like this to convert to 1 bit before you save to TIFF with CCITT compression.
To expound a little bit - be aware that converting from other bit depths to 1 bit is non-trivial. You are doing a data reduction operation and there are dozens of domain specific solutions which vary greatly in output quality (blind threshold, adaptive threshold, dithering, local threshold, global threshold and so on). None of them are particularly good at all image types (adaptive threshold is pretty good for documents, but lousy for photographs, for example).
As plinth said, you have to do the conversion, Java won't do it magically for you...
If the PNG image is already black & white (as it seems, looking at your comment), using a threshold is probably the best solution.
Somebody seems to have the same problem: HELP: how to compress the tiff. A solution is given on the thread (untested!).

Categories