I have a Data-URL from an image file and have to pass it through to another function. Along this path from Data-URL to the BufferedImage it needs to be a byteArray.
my approach was the following:
String dataUrl;
byte[] imageData = dataUrl.getBytes();
// pass the byteArray along the path
// create BufferedImage from byteArray
BufferedImage inputImage = ImageIO.read(new ByteArrayInputStream(imageData));
// If the picture is null, then throw an unsupported image exception.
if (inputImage == null) {
throw new UnknownImageFormatException();
}
The problem is, it always throws the UnknownImageFormatException Exception, which means inputImage is null, which means, the ImageIO.read did not recognize the imagetype.
I've used ImageIO.getReaderFormatNames() to get the supported Filenames and got the following list:
Supported Formats:
jpg, BMP, bmp, JPG, jpeg, wbmp, png, JPEG, PNG, WBMP, GIF, gif
The dataURLs I try to pass are like: data:image/png;base64,... or data:image/jpg;base64,...
As far as I understand, those are in the supported filelist and therefor should be recognized.
What else could cause the inputImage to be null in this case? And more interesting, how do I solve it?
As the comments already said the image data is Base64 encoded. To retrieve the binary data you have to strip the type/encoding headers, then decode the Base64 content to binary data.
String encodingPrefix = "base64,";
int contentStartIndex = dataUrl.indexOf(encodingPrefix) + encodingPrefix.length();
byte[] imageData = Base64.decodeBase64(dataUrl.substring(contentStartIndex));
I use org.apache.commons.codec.binary.Base64 from apaches common-codec, other Base64 decoders should work as well.
The only one problem with RFC2397 string is its specification with everything before data but data: and , optional:
data:[<mediatype>][;base64],<data>
So pure Java 8 solution accounting this would be:
final int dataStartIndex = dataUrl.indexOf(",") + 1;
final String data = dataUrl.substring(dataStartIndex);
byte[] decoded = java.util.Base64.getDecoder().decode(data);
Of course dataStartIndex should be checked.
I think, a simple regex replace would be better and more conform to the RFC2397:
java.util.Base64.getDecoder().decode(b64DataString.replaceFirst("data:.+,", ""))
The RFC states that the data: and the , are the required prefixes for a data url, therefore it is wise to match for them.
Related
I want to compress a Bitmap (JPEG image) to quality 64, the result with be stored in a byte array. Previously I can achieve this with Bitmap#compress() method:
public static byte[] compressJPEG(Bitmap bmp, int quality) {
if (quality <= 0)
return new byte[0];
byte[] array = new byte[0];
try {
ByteArrayOutputStream bmpStream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, quality, bmpStream);
array = bmpStream.toByteArray();
bmpStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return array;
}
But the result byte array (with the same input image file) is different when I run this on different devices. (May be because it use nativeCompress() internally)
After checking, I find out the different is in Huffman Table(s), SOS & image data parts, see this:
Diff check between 2 image photos
So how can I compress JPEG image to specific quality level without using Bitmap#compress() method? Because I really want the result byte array to be similar in structure.
There is no concept of "quality level" in JPEG. It is a shorthand that some encoders use to select quantization tables. There are a number of variables in JPEG compression, including the type and breakdown of scans, samples, huffman table selection, and quantization table selection. If any of those are different in the encoder you use, you are going to get different output. It's the nature of the beast.
Displaying an image in base64 is rather simple:
<img src="data:image/png;base64,hexadecimal-code-for-image-here">
However, what I'm trying to do is to convert the hexadecimal value received and the save it in a png file so I get the desired picture from the code.
For instance: let's say I have the following code:
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QAAAAAAAD5
Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wEVDTYmJtINnAAAAcRJ
REFUOMuVlL9rU1EUx7/n3psmL6VoWqoJpNgI0Vg6uXSw0E0QHBzsUJDSoSKCuLro
KIL0D3Dr2N0gOHUpdWgrLW0EW5osakNT+4Oo7+X13XuPi4L2SZ73jGf43O/hfM4l
nKnC9TtZmStfIwKFn5bXW9vvDBxKnG2YdP7BzOzDtanp+6uBGLwFx1LxF7RtHvmI
tIaUsK7AWEJW6UgbC2MYxtjQFUijky/Hy5WROQIgBbgT6nyuUBo2hvGtVf8oVPrE
GCbDwM6Hjce71WcrXUfuv1C8lC9eHiMwlCCcGgs/1CBiFEuVShhZBKcGHQ309H0e
AtAd+PXwkFa36shkPCglIaXEcOE8jABqjQMQgEhrdAIfZCNKHFmlsr3C6yuzjaCD
tr56++m9GzfvPgEDr+efPzquVZco5QkhFcJ2cxuMoGtCHfk/EPkbvxsD+dJejyIA
jMzFkYZ+v7CJ8Pt/exrbMhFoaCCNYn8G2gpy9TTm4cF+U7xZ+QIAyHKbXD2NARtv
X7w6rk8sghmtrWrN1dPErbl6qpKArp4mAl09TRzZ1dPEhK6eKuff5JenzPinp87A
JE/JFZjyznm58sSVPzz96/R+AmVHLPIJpOvnAAAAAElFTkSuQmCC
Is there any way I can convert this code to image and then store in a png file in JAVA?
Transfer the base64 into byte array and than write it to a png file.
byte[] img = Base64.getDecoder().decode(imgBase64);
Files.write(Paths.get("my.png"), img); //As suggested by Joop Eggen
Decode base64 image to byte[] then using ImageIO to write to file
byte[] imgInBytes = Base64.getDecoder().decode(base64Img);
InputStream in = new ByteArrayInputStream(imgInBytes);
BufferedImage bufferedImage = ImageIO.read(in);
File png = new File("ImageAsPNG.png");
ImageIO.write(bufferedImage, "PNG", png);
I decoded an image (JPEG) with Android BitmapFactory class and it decoded fine with color format ARGB_8888.
Bitmap bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().toString()+"/camel.jpg",
new BitmapFactory.Options());
Log.d(TAG,"Color format of the bitmap : "+bitmap.getConfig());
I dumped the raw buffer from the Bitmap class
ByteBuffer buffer = ByteBuffer.allocate(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(buffer);
Log.d(TAG,"Color format of the bitmap : "+bitmap.getConfig());
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/buffer.raw");
outputStream.write(buffer.array());
outputStream.close();
}
catch (Exception e) {
e.printStackTrace();
}
and examined the dump in a Hex editor , it looks like the ordering of the color components is not in the order A,R,G and B.
Instead it looks like ordering is R,G,B and A.
As seen above the alpha (FF) is in the end of the set of four bytes for a pixel.Also to corroborate this I opened the image in 7yuv and image is displayed properly with color format RGBA and little-endian encoding.
My confusion is why is Android reports the format of the Bitmap as ARGB_8888 and actual byte ordering is R,G,B and A.
I was wondering this might be due to some endian mismatch , but in that case the whole ordering needs to be simply reversed (B,G,R and A).
Also I might be doing something wrong while dumping the bitmap to a raw buffer (and I am not very good at Java) , but I am not sure.
Thanks in advance!
I am trying to send a UIImage (taken from an iPhone's camera) to a Java Server and display it in a JFrame.
It might have something to do with the Encoding Option as the String in Xcode and Eclipse are slightly different. ex. the xCode string has "+" for spaces, but in Eclipse, it has " " for spaces.
In Objective-C
UIImage *image = info[UIImagePickerControllerEditedImage];
NSData *imageData = UIImagePNGRepresentation([temp objectForKey:#"photo"]);
NSString *base64StringOfImage = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
I then send this NSString in json to Java
In Java
byte[] imageBytes = Base64.decodeBase64(jsonPhoto.getString("photo"));
BufferedImage img = ImageIO.read(new ByteArrayInputStream(imageBytes));
I get this error on the BufferedImage img line:
javax.imageio.IIOException: Error reading PNG image data
If anyone can show me how to fix this, that would be great.
I figured it out.
byte[] imageBytes = Base64.decodeBase64(jsonPhoto.getString("photo").replace(' ', '+'));
-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!).