Saving multi-layered TIFF images without losing layer information in Java - java

I'm trying to save tiff images uploaded via browser using Spring and JAI ImageIO. I can save the image using the code below but the problem is that the saved image doesn't have the layers(the ones that tiff images consist of) which it has before uploading.
Are there any other parameters to ensure that the saved image also has the layers?
private void saveTiffImage(byte[] bytes, String uuid) throws Exception {
SeekableStream stream = new ByteArraySeekableStream(bytes);
String[] names = ImageCodec.getDecoderNames(stream);
ImageDecoder dec =
ImageCodec.createImageDecoder(names[0], stream, null);
RenderedImage im = dec.decodeAsRenderedImage();
String fileName = uuid + ".tif";
com.sun.media.jai.codec.TIFFEncodeParam params = new com.sun.media.jai.codec.TIFFEncodeParam();
params.setCompression(com.sun.media.jai.codec.TIFFEncodeParam.COMPRESSION_PACKBITS);
FileOutputStream os = new FileOutputStream(IMG_LOCATION + fileName);
javax.media.jai.JAI.create("filestore", im, IMG_LOCATION + fileName, "TIFF", params);
os.flush();
os.close();
}

I've found the solution using the answer here : How to combine two or many tiff image files in to one multipage tiff image in JAVA.
private Object[] saveTiffImage(byte[] bytes, String uuid) throws Exception {
SeekableStream stream = new ByteArraySeekableStream(bytes);
String[] names = ImageCodec.getDecoderNames(stream);
ImageDecoder dec =
ImageCodec.createImageDecoder(names[0], stream, null);
// Here we get the other pages.
Vector vector = new Vector();
int pageCount = dec.getNumPages();
for (int i = 1; i < pageCount; i++) {
RenderedImage im = dec.decodeAsRenderedImage(i);
vector.add(im);
}
String fileName = uuid + ".tif";
com.sun.media.jai.codec.TIFFEncodeParam params = new com.sun.media.jai.codec.TIFFEncodeParam();
params.setCompression(com.sun.media.jai.codec.TIFFEncodeParam.COMPRESSION_PACKBITS);
// Then set here
params.setExtraImages(vector.iterator());
// This is the first page
RenderedImage im = dec.decodeAsRenderedImage(0);
FileOutputStream os = new FileOutputStream(IMG_LOCATION + fileName);
javax.media.jai.JAI.create("filestore", im, IMG_LOCATION + fileName, "TIFF", params);
os.flush();
os.close();
}

Related

How to keep compressing a image with ImageWriter in JAVA?

I need to compress every image that is larger than 500 kbytes.
So, i'm trying to create a code that will test every compression quality until i get <= 500 kb, then i'll have the best quality with the lowest length.
My biggest problem here is that writer method from ImageWriter appends my new image to the old. So, if i have a 600kb image and write a new one with low quality, i'll have 600kb + probably 500kb (size of the new image) in the same .jpg file and with a low quality.
My code:
public byte[] imageCompressor(String filePath, String newFileName, String formatName) throws IOException {
File input = new File(filePath);
BufferedImage image = ImageIO.read(input);
File compressedImageFile = new File(newFileName);
String compressedWithFormat = compressedImageFile + "." + formatName;
OutputStream os = new FileOutputStream(compressedWithFormat);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatName);
ImageWriter writer = (ImageWriter) writers.next();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(os);
writer.setOutput(ImageIO.createImageOutputStream(baos));
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writer.write(null, new IIOImage(image, null, null), param);
float contador = 1f;
while (imageSizeChecker(compressedWithFormat) > 500) {
param.setCompressionQuality(contador -= 0.09f); // Change the quality value you prefer
writer.write(null, new IIOImage(image, null, null), param);
System.out.println(imageSizeChecker(compressedWithFormat));
writer.dispose();
}
writer.dispose();
return baos.toByteArray();
}
public Long imageSizeChecker(String filePath) {
File insertFile = new File(filePath);
Long fileSize = insertFile.length() / 1024;
return fileSize;
}
Also, i want to return the image in a byte array (as it follows), and i'm trying to send the image file on the parameter as a byte array (replacing String filepath for byte[] filepath)
You keep writing multiple images to the same ByteArrayOutputStream. To reset the output stream and discard the data between each image, you can use:
baos.reset();
There are several other problems -- such as inspecting a file you never write -- but if you're finding that appended data is the major problem, this seems to be why.

PDFBox: extracting images from pdf to inputstream

I am using PDFBox to extract the images from my pdf (which contains only jpg's).
Since I will save those images inside my database, I would like to directly convert each image to an inputstream object first without placing the file temporary on my file sysem. I am facing difficulties with this however. I think it has to do because of the use of image.getPDFStream().createInputStream() as I did in the following example:
while (imageIter.hasNext()) {
String key = (String) imageIter.next();
PDXObjectImage image = (PDXObjectImage) images.get(key);
FileOutputStream output = new FileOutputStream(new File(
"C:\\Users\\Anton\\Documents\\lol\\test.jpg"));
InputStream is = image.getPDStream().createInputStream(); //this gives me a corrupt file
byte[] buffer = new byte[1024];
while (is.read(buffer) > 0) {
output.write(buffer);
}
}
However this works:
while (iter.hasNext()) {
PDPage page = (PDPage) iter.next();
PDResources resources = page.getResources();
Map<String, PDXObject> images = resources.getXObjects();
if (images != null) {
Iterator<?> imageIter = images.keySet().iterator();
while (imageIter.hasNext()) {
String key = (String) imageIter.next();
PDXObjectImage image = (PDXObjectImage) images.get(key);
image.write2file(new File("C:\\Users\\Anton\\Documents\\lol\\test.jpg")); //this works however
}
}
}
Any idea how I can convert each PDXObjectImage (or any other object I can get) to an inputstream?
In PDFBox 1.8, the easiest way is to use write2OutputStream(), so your first code block would now look like this:
while (imageIter.hasNext()) {
String key = (String) imageIter.next();
PDXObjectImage image = (PDXObjectImage) images.get(key);
FileOutputStream output = new FileOutputStream(new File(
"C:\\Users\\Anton\\Documents\\lol\\test.jpg"));
image.write2OutputStream(output);
}
advanced solution, as long as you're really sure you have only JPEGs that display properly, i.e. have no unusual colorspace:
while (imageIter.hasNext()) {
String key = (String) imageIter.next();
PDXObjectImage image = (PDXObjectImage) images.get(key);
FileOutputStream output = new FileOutputStream(new File(
"C:\\Users\\Anton\\Documents\\lol\\test.jpg"));
InputStream is = image.getPDStream().getPartiallyFilteredStream(DCT_FILTERS);
byte[] buffer = new byte[1024];
while (is.read(buffer) > 0) {
output.write(buffer);
}
}
The second solution removes all filters except the DCT (= JPEG) filter. Some older PDFs have several filters, e.g. ascii85 and DCT.
Now even if you created the image with JPEGs, you don't know what your PDF creation software did. One way to find out what type of image it is, is to check what class it is (use instanceof):
- PDPixelMap => PNG
- PDJpeg => JPEG
- PDCcitt => TIF
Another way is to use image.getSuffix().
PDXObjectImage has method write2OutputStream(OutputStream out) from which you can then get either byte array out of output stream.
Check How to convert OutputStream to InputStream? for converting OutputStream to InputStream.
If you are using PDFBox 2.0.0 or above
PDDocument document = PDDocument.load(new File("filePath")); //filePath is the path to your .pdf
PDFRenderer pdfRenderer = new PDFRenderer(document);
for(int i=0; i<document.getPages().getCount(); i++){
BufferedImage bim = pdfRenderer.renderImage(i, 1.0f, ImageType.RGB); //Get bufferedImage for page "i" with scale 1
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(bim, "jpg", os);
InputStream is = new ByteArrayInputStream(os.toByteArray());
//Do whatever you need with the inputstream
}
document.close()

Writing a contact's photo into a vcf file, android

I am using a code in which a list of all contacts is shown. When I select a contact from the list, details of the contact are shown and saved in a .vcf file (in proper vcard format), working fine. when I select a contact which has a photo as well, it shows the photo in imageView, but I don't know how to write the photo in vcf file.
I have used these lines,
Uri photoUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI,Integer.parseInt(item));
Bitmap photoBitmap;
ContentResolver cr = getContentResolver();
InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(cr, photoUri);
photoBitmap = BitmapFactory.decodeStream(is);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
photoBitmap.compress(CompressFormat.JPEG, 100 , bos);
byte[] bitmapdata = bos.toByteArray();
imageEncoded = Base64.encodeToString(bitmapdata,Base64.DEFAULT);
String content = "BEGIN:VCARD\nVERSION:3.0\nCLASS:PUBLIC\nPRODID:-//class_vcard from TroyWolf.com//NONSGML Version 1//EN\nFN:"+contactName+"\nTEL;TYPE=cell,voice:"+number+"\nPHOTO;TYPE=JPEG;ENCODING=BASE64:"+imageEncoded+"\nTZ:+0000\nEND:VCARD";
But I am getting error while reading the contact("Failed to parse vCard for unexpected reason, Invalid line:")
Can anyone please help me solve the problem!
Try changing the value of the ENCODING parameter from BASE64 to B. B is the correct value to use in 3.0 vCards.
Also, the correct newline sequence for vCards is \r\n, not \n.
You might be interested in using a vCard library to generate your vCard. ez-vcard is one such library (disclaimer: I am the author).
VCard vcard = new VCard();
vcard.setClassification("PUBLIC");
vcard.setProdId("-//class_vcard from TroyWolf.com//NONSGML Version 1//EN");
vcard.setFormattedName(contactName);
TelephoneType tel = vcard.addTelephoneNumber(number);
tel.addType(TelephoneTypeParameter.CELL);
tel.addType(TelephoneTypeParameter.VOICE);
PhotoType photo = new PhotoType(bitmapdata, ImageTypeParameter.JPEG);
vcard.addPhoto(photo);
vcard.setTimezone(new TimezoneType(0, 0));
String content = Ezvcard.write(vcard).version(VCardVersion.V3_0).prodId(false).go();
This is a really late post, but I couldn't find any working solution on StackOverflow without using the library, so I thought it would be good to share my findings.
Changing the encoding parameter and correcting a newline sequence weren't enough to construct .vcf file with a photo. I also had to remove the line breaks after encoding to base64.
Sample code to convert Uri to base64 format (replace imageStream initialization as needed).
// string you can use to write to vcf file (3.0 vCards)
String.format("PHOTO;ENCODING=B;TYPE=JPEG: ,%s\r\n", convertUriToBase64(context, photoUri));
private String convertUriToBase64(Context context, String photoUri) {
InputStream imageStream = null;
try {
imageStream = context.getContentResolver().openInputStream(Uri.parse(photoUri));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
byte[] bitmapData = bos.toByteArray();
// line break has to be removed, so it is on the same line as PHOTO
return Base64.encodeToString(bitmapData, Base64.DEFAULT).replaceAll("\n", "");
}
Here's what I did to get a photo to send as part of the vCard...
Important things to consider:
1) Equipment: ZTE Axon 7 running Android 6.01
2) Couldn't get vCard 3.0 or 4.0 to function properly, could only use vCard 2.1
File vcfFile = new File(DisplayContactActivity.this.getExternalFilesDir(null), "generated.vcf");
try
{
/**Only Version 2.1 worked for me with or without PHOTO**/
FileWriter fw = new FileWriter(vcfFile);
fw.write("BEGIN:VCARD\r\n");
fw.write("VERSION:2.1\r\n");
fw.write("FN:" + "Contact 7" + "\r\n");
/*Getting the name of the File as I had saved it*/
String file_name = ("current_contact_image" + CONTACT_ID);
/*Get the bitmap that we stored in a File*/
Bitmap bitmap = getContactImage(file_name);
/*Convert bitmap to Base64*/
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b = baos.toByteArray();
String image_encoded = Base64.encodeToString(b, Base64.DEFAULT);
/*Write the encoded version of image to vCard 2.1, NOTICE that no determining whether the image is GIF or JPEG is needed*/
fw.write("PHOTO;ENCODING=BASE64:" + image_encoded + "\r\n");
/*Write some other stuff to the vCard also just trying to give whoever needs this a starting point*/
fw.write("TEL;Primary:" + "(586) 268-3437" + "\r\n");
fw.write("TEL;OTHER:" + "(313) 313-4545" + "\r\n");
fw.write("ADR;OTHER:" + "12345 AnyLane Dr." + "\r\n");
fw.write("ADR;OTHER:" + "54321 AnyPlace Av." + "\r\n");
fw.write("EMAIL;OTHER:" + "email#yahoo.com" + "\r\n");
fw.write("EMAIL;OTHER:" + "email#wowway.com" + "\r\n");
fw.write("END:VCARD\r\n");
fw.close();
Intent i = new Intent();
i.setAction(android.content.Intent.ACTION_VIEW);
i.setDataAndType(Uri.fromFile(vcfFile), "text/x-vcard");
startActivity(i);
}
catch (Exception e)
{
Log.i(TAG, "Exception: " + e);
}
public Bitmap getContactImage(String file_name)
{
Log.i(TAG, "Running getContactImage() with file name: " + file_name);
Bitmap thumbnail = null;
//------------------------------------------------------------------------------------------
try
{
File filePath = getBaseContext().getFileStreamPath(file_name);
FileInputStream fis = new FileInputStream(filePath);
thumbnail = BitmapFactory.decodeStream(fis);
}
catch (Exception e) //Use scaled_bitmap_for_storage instead of the current_contact_image file name
{
/*Error getting user-selected image, set boolean to get default image*/
Log.i(TAG, "Exception while running getContactImage() for file name: " + file_name + " with error message: " + e);
}
//------------------------------------------------------------------------------------------
return thumbnail;
}

Create a barcode image in java using jasperreports, without saving the image on disk

I need to create a barcode image in java using jasperreports, currently I'm doing this saving the image file on disk, but I need to do it without saving the image on disk. I need to create the barcode image in memory an then send it to iReport as a parameter.
This is what I have done:
Map<String, Object> parameters = new HashMap<String, Object>();
String imagePath = "\\\\netw\\barCode.jpg";
parameters.put("rutaCodigoBarrasVal", imagePath);
Barcode barCode = BarcodeFactory.createPDF417("1234567890");
barCode.setDrawingText(false);
barCode.setBarHeight(33);
barCode.setBarWidth(207);
FileOutputStream fOS = new FileOutputStream(imagePath);
BarcodeImageHandler.writeJPEG(barCode, fOS);
fOS.close();
What can I do?
You should first try to write the Barcode into a byte array or InputStream, looking at your library documentation.
JasperReports supports passing an image as a InputStream parameter, and draw that in the report.
InputStream imageStream = ...;
parametros.put("image", imageStream );
From JasperReports, receive that parameter as java.io.InputStream, then draw it with an image widget and the following properties:
Image Expression: $P{image}
Expression Class: java.io.InputStream
I hope it helps.
Finally this is what I did using barcode4j library:
ByteArrayOutputStream os = new ByteArrayOutputStream();
PDF417Bean barCode = new PDF417Bean();
boolean antiAlias = false;
int orientation = 0;
int dpi = 300;
BitmapCanvasProvider canvas = new BitmapCanvasProvider(dpi, BufferedImage.TYPE_BYTE_BINARY, antiAlias, orientation);
BarcodeDimension dim = new BarcodeDimension(207, 42);
canvas.establishDimensions(dim);
barCode.setColumns(7);
barCode.generateBarcode(canvas, codeToConvert);
canvas.finish();
String mime = MimeTypes.MIME_JPEG;
os = new ByteArrayOutputStream();
final BitmapEncoder encoder = BitmapEncoderRegistry.getInstance(mime);
encoder.encode(canvas.getBufferedImage(), os, mime, dpi);
fis = new ByteArrayInputStream(os.toByteArray());

Convert an image to binary data (0s and 1s) in java

I want to read an image from a url and convert it into binary data. Please help me..
byte[] data = null;
ByteArrayOutputStream bas = null;
try {
URL u = new URL(
"http://www.eso.org/public/archives/images/screen/eso0844a.jpg");
HttpURLConnection con1 = (HttpURLConnection) u.openConnection();
con1.setAllowUserInteraction(true);
con1.setRequestMethod("GET");
con1.connect();
InputStream is = con1.getInputStream();
BufferedImage imgToServe = null;
if (is != null) {
imgToServe = ImageIO.read(is);
}
bas = new ByteArrayOutputStream();
ImageIO.write(imgToServe, "jpg", bas);
File f = new File("C:\\img.jpg");
ImageIO.write(imgToServe, "jpg", f);
data = bas.toByteArray();
String str = "";
for (int i = 0; i < data.length; i++) {
str = str + toBinary(data[i]);
}
System.out.println(str);
} catch (HTTPException he) {
} catch (IOException ioe) {
}
}
private static String toBinary(byte b) {
StringBuilder sb = new StringBuilder("00000000");
for (int bit = 0; bit < 8; bit++) {
if (((b >> bit) & 1) > 0) {
sb.setCharAt(7 - bit, '1');
}
}
return (sb.toString());
}
If you're reading the image from a URL, it will already be in a binary format. Just download the data and ignore the fact that it's an image. The code which is involved in download it won't care, after all. Assuming you want to write it to a file or something similar, just open the URLConnection and open the FileOutputStream, and repeatedly read from the input stream from the web, writing the data you've read to the output stream.
If that's not what you were after, please clarify the question.
EDIT: If you really want to get the data as individual bits (which seems somewhat odd to me) you should separate the problem in two:
Downloading the data (see above; if you don't need it on disk, consider writing to a ByteArrayOutputStream)
Converting arbitrary binary data (a byte array or an input stream) into 0s and 1s
How you tackle the latter task will depend on what you actually want to do with the bits. What's the real aim here?
You can use the standard ImageIO for this. The read method takes a URL and retrieves it to an Image. Then you can use the write method to write it to a File or like in this case a ByteArrayOutputStream which outputs the image to a in-memory buffer.
public static void main(String[] args) throws Exception {
// read "any" type of image (in this case a png file)
BufferedImage image = ImageIO.read(new URL("http://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png"));
// write it to byte array in-memory (jpg format)
ByteArrayOutputStream b = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", b);
// do whatever with the array...
byte[] jpgByteArray = b.toByteArray();
// convert it to a String with 0s and 1s
StringBuilder sb = new StringBuilder();
for (byte by : jpgByteArray)
sb.append(Integer.toBinaryString(by & 0xFF));
System.out.println(sb.toString());
}
Load the image from path/url into BufferedImage
public static Raster loadImageRaster(String file_path) throws IOException
{
File input = new File(file_path);
BufferedImage buf_image = ImageIO.read(input);
buf_image = binarizeImage(buf_image);
return buf_image.getData(); //return raster
}
Make a Binary Type BufferedImage from the original BufferedImage
public static BufferedImage binarizeImage(BufferedImage img_param)
{
BufferedImage image = new BufferedImage(
img_param.getWidth(),
img_param.getHeight(),
BufferedImage.TYPE_BYTE_BINARY
);
Graphics g = image.getGraphics();
g.drawImage(img_param, 0, 0, null);
g.dispose();
return image;
}
Convert the BufferedImage to Raster so that you can manipulate it pixel by pixel
imageRaster.getSample(x, y, 0)
Raster.getSample(x,y, channel) will return 0s or 1s.
channel = 0 for TYPE_BYTE_BINARY images

Categories

Resources