I am trying to save a JPEG image from a URL to a file with Java.
URL: http://150.214.222.100//axis-cgi/mjpg/video.cgi?resolution=640x480&compression=1&duration=1&timeout=&dummy=garb
I tried the following:
1)
Image image = fetchImage(urlNorthView);
saveImage2Disk(image);
public static Image fetchImage( URL u ) throws MalformedURLException, IOException {
Toolkit tk = Toolkit.getDefaultToolkit();
return tk.createImage(u );
}
private void saveImage2Disk(Image Image) throws IOException{
File outputFile = new File("urlNorthView"+Calendar.getInstance().getTimeInMillis()+".jpg");
BufferedImage bufferedImage = new BufferedImage(Image.getWidth(null),Image.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = bufferedImage.createGraphics();
g2.drawImage(Image, null, null);
ImageIO.write(bufferedImage, "jpg", outputFile);
}
=> Exception:"Width (-1) and height (-1) cannot be <= 0"
2)
inputStream2Disk((InputStream) urlNorthView.getContent());
private void inputStream2Disk(InputStream in) throws Exception{
File outputFile = new File("urlNorthView"+Calendar.getInstance().getTimeInMillis()+".jpg");
OutputStream out=new FileOutputStream(outputFile);
byte buf[]=new byte[1024];
int len;
while((len=in.read(buf))>0)
out.write(buf,0,len);
out.close();
in.close();
}
The file is somehow broken. When I open it with Kate, I can read:
--myboundary Content-Type: image/jpeg Content-Length: 38256 ....
There should not be any text in a binary file.
What could the problem be?
For some reason, the http response body when requesting that image contains a mime part (mime parts are useful for putting multiple files into a single response). In this response, there is only one mime part, so it is mostly useless.
There is code in the javax.mail package that you might be able to use to parse this properly if you want, but it's not a very good api, imho.
Alternatively, there are a bunch of ways you could hackishly fix this in code yourself. Since there's only one mime part, you can just throw away data from the beginning of your input stream until you see two newline characters in a row (bytes equal to 10). That should work since mime headers are supposed to be 7-bit ascii, iirc, so there's no character encoding to worry about.
Here is some sample code:
URLConnection conn = urlNorthView.openConnection();
InputStream in = conn.getInputStream();
String contentType = conn.getHeaderField("Content-Type");
if (!"image/jpeg".equals(contentType)) {
// hack: assuming it's mime if not a raw image
int one = in.read();
if (one == -1) {
// stop??
}
int two = in.read();
while (two != -1 && !(two == 10 && one == 10)) {
one = two;
two = in.read();
}
}
// if it was mime, we've stripped off the mime headers
// and should now get the image
inputStream2Disk(in);
Edit: crap, instead of two \n, you'll see two \r\n, or the bytes 0x0d, 0x0a, 0x0d, 0x0a. Throwing away data until you see this pattern is left as an exercise to the reader ;)
Try the following method for converting an Image to BufferedImage
private static BufferedImage getBufferedImage(Image img, int imageType) {
if (img instanceof BufferedImage) {
return (BufferedImage) img;
}
BufferedImage bi = new BufferedImage(img.getWidth(null), img
.getHeight(null), imageType);
Graphics2D g = (Graphics2D) bi.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(img, 0, 0, null);
g.dispose();
return bi;
}
(where imageType is one of the BufferedImage declared constants. Most likely TYPE_INT_RGB.
Otherwise your first approach is fine.
I'd recommend the first (high-level) approach over the second (low-level).
Related
I have a ReadableByteChannel which contains an image (either obtained from an URL or a file). I write the Channel finally into a File with a code like
final FileOutputStream fileOutputStream = new FileOutputStream(outImageName);
fileOutputStream.getChannel().transferFrom(imageByteChannel, 0, Long.MAX_VALUE);
fileOutputStream.close();
Since it is unclear if the image is a png or a jpeg or ... I want to make sure and save it as png. I know I can use the ImageIO.write(buffImg, outImageName, "png");
But somehow this requires that the buffImg is a RenderedImage which raises the question how to obtain it?
Is there a simpler solution than reading the file from the file system with ImageIO and than write it as png? Can I convert it directly within the memory?
Plus an additional question: Is there a way to tell ImageIO to get rid of the AlphaChannel (=transparent)?
If you can, I suggest getting the InputStream or the underlying File or URL objects instead of a ReadableByteChannel, as ImageIO.read(..) supports all those types as input directly.
If not possible, you can use Channels.newInputStream(..) to get an InputStream from the byte channel, to pass on to ImageIO.read(...). There's no need to write to a file first.
Code:
ReadableByteChannel channel = ...; // You already have this
BufferedImage image = ImageIO.read(Channels.newInputStream(channel))
To get rid of any unwanted transparency, you could do:
BufferedImage opaque = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g = opaque.createGraphics();
try {
g.setColor(Color.WHITE); // Or any other bg color you like
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.drawImage(image, 0, 0, null);
}
finally {
g.dispose();
}
You can now write the image in PNG format, either to a file or stream:
if (!ImageIO.write(opaque, "PNG", new File(outputFile)) {
// TODO: Handle odd case where the image could not be written
}
I have a function generateImageOutput below to write BufferedImage to jpeg file.
public boolean generateImageOutput(BufferedImage image, String filename){
//The image is written to the file by the writer
File file = new File( projectFolder+"/data/"+filename+".jpg");
//Iterator containing all ImageWriter (JPEG)
Iterator encoder = ImageIO.getImageWritersByFormatName("JPEG");
ImageWriter writer = (ImageWriter) encoder.next();
//Compression parameter (best quality)
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(1.0f);
//Try to write the image
try{
ImageOutputStream outputStream = ImageIO.createImageOutputStream(file);
writer.setOutput(outputStream);
writer.write(null, new IIOImage(image, null, null), param);
outputStream.flush();
writer.dispose();
outputStream.close();
}catch(IOException e){
e.printStackTrace();
System.out.println(e.toString());
return false;
}
return true;
}
It works for some, but it fails for a BufferedImage converted from base64 string:
String encodedString = JSON.parseObject(string).getString("image");
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
ByteArrayInputStream bis = new ByteArrayInputStream(decodedBytes);
buffered_image = ImageIO.read(bis);
When writing the above buffered_image to jpeg using generateImageOutput, it raises exception :
javax.imageio.IIOException: Bogus input colorspace
at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeImage(Native Method)
at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeOnThread(JPEGImageWriter.java:1007)
at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageWriter.write(JPEGImageWriter.java:371)
The string encodedString has no issue, I have sucessfully converted it to an image online.
How can I resolve the exception ?
According to #J.doe Alpha channel must be remove. I have a code that removes the Alpha channel. The code below was to determine whether or not the image has alpha channel. If the image has alpha channel it will create an image without alpha channel.
private static BufferedImage removeAlphaChannel(BufferedImage img) {
if (!img.getColorModel().hasAlpha()) {
return img;
}
BufferedImage target = createImage(img.getWidth(), img.getHeight(), false);
Graphics2D g = target.createGraphics();
// g.setColor(new Color(color, false));
g.fillRect(0, 0, img.getWidth(), img.getHeight());
g.drawImage(img, 0, 0, null);
g.dispose();
return target;
}
private static BufferedImage createImage(int width, int height, boolean hasAlpha) {
return new BufferedImage(width, height, hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
}
Finally I find that the reason is the image has an ALPHA channel.
Looks like JPEG doesn't handle the alpha channel correctly in this context. For me saving the image as PNG did the trick.
public void actionPerformed(java.awt.event.ActionEvent evt) {
Connection cn = null;
Object source = evt.getSource();
JFileChooser filechooser= new JFileChooser();
filechooser.setDialogTitle("Choose Your File");
filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
int returnval=filechooser.showOpenDialog(this);
if(returnval==JFileChooser.APPROVE_OPTION)
{
File file = filechooser.getSelectedFile();
BufferedImage bi;
try
{
bi=ImageIO.read(file);
lbl_movieCover.setIcon(new ImageIcon(bi));
}
catch(IOException e)
{
}
//this.pack();
}
Above is my code for choosing the image and displaying the image to JLabel. My problem is that, I don't know how to convert it to byte[] so I could save it to my database. By the way, I'm using MySQL for my database. If you guys know how to do it, please let me know.
Use ImageIO.write to write the image through a ByteArrayOutputStream, for example
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
ImageIO.write(bi, "png", baos);
} finally {
try {
baos.close();
} catch (Exception e) {
}
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
You can then use the resulting byte[] array or ByteArrayInputStream and pass this to the setBlob method of a PreparedStatement
You could use a ByteArrayOutputStream and ImageIO to write an image to a byte array, like this:
static byte[] imageToByteArray(BufferedImage image) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", stream);
} catch(IOException e) {
// This *shouldn't* happen with a ByteArrayOutputStream, but if it
// somehow does happen, then we don't want to just ignore it
throw new RuntimeException(e);
}
return stream.toByteArray();
// ByteArrayOutputStreams don't need to be closed (the documentation says so)
}
You can use this method-
/**
* #param userSpaceImage
* #return byte array of supplied image
*/
public byte[] getByteData(BufferedImage userSpaceImage) {
WritableRaster raster = userSpaceImage.getRaster();
DataBufferByte buffer = (DataBufferByte) raster.getDataBuffer();
return buffer.getData();
}
Use it like-
//file from filechooser
BufferedImage originalImage = ImageIO.read(file);
byte image[] = getByteData(originalImage);
Note that if image type is that of int e.g. BufferedImage.TYPE_INT_RGB then
you will get cast exception. Following method can be used to convert to suitable type-
/**
* this method convert supplied image to suitable type
* it is needed because we need bytes of array so TYPE_INT images must be
* converted to BYTE_BGR or so
* #param originalImage loaded from file-chooser
* #return
*/
public BufferedImage convertImage(BufferedImage originalImage) {
int newImageType = originalImage.getType();
/**
* Converting int to byte since byte array is needed later to modify
* the image
*/
if (newImageType == BufferedImage.TYPE_INT_RGB
|| newImageType == BufferedImage.TYPE_INT_BGR) {
newImageType = BufferedImage.TYPE_3BYTE_BGR;
} else if (newImageType == BufferedImage.TYPE_INT_ARGB) {
newImageType = BufferedImage.TYPE_4BYTE_ABGR;
} else if (newImageType == BufferedImage.TYPE_INT_ARGB_PRE) {
newImageType = BufferedImage.TYPE_4BYTE_ABGR_PRE;
}
BufferedImage newImage = new BufferedImage(originalImage.getWidth(),
originalImage.getHeight(), newImageType);
Graphics g = newImage.getGraphics();
g.drawImage(originalImage, 0, 0, null);
g.dispose();
return newImage;
}
While both #MadProgrammer's and #immibis' answers are technically correct and answers your question, you don't really want to copy an image file by decoding it, and re-encoding it. This is because it's slower, but more importantly, you will lose quality for certain image formats (most notably JPEG) and any metadata associated with the image will be lost (this last part could of course be intentional, but there are better ways to do this without ruining the image quality).
So, instead, do as #immibis seems to hint at in his comment, just open a FileInputStream and read the bytes directly from the file, and into the database. You should be able to open an OutputStream to the blob in your database as well, so you can save some memory (a good thing, if your files are large) by not reading the entire file contents into a byte array, before writing.
Something like:
File file = filechooser.getSelectedFile();
// ... show image in label as before (but please, handle the IOException)
InputStream input = new FileInputStream(file);
try {
Blob blob = ...; // Get from JDBC connection
OutputStream output = blob.setBinaryStream(0);
try {
FileUtils.copy(input, output);
}
finally {
output.close();
}
}
finally {
input.close();
}
FileUtils.copy can be implemented as:
public void copy(final InputStream in, final OutputStream out) {
byte[] buffer = new byte[1024];
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
// Flush out stream, to write any remaining buffered data
out.flush();
}
I am trying to read an image in Java and access the pixels via the raster. However I get an NPE from the Raster, how do I access it?
Here is what I am doing:
public static void main(String[] args) throws Exception
{
IIOImage iioImage = Image.readImage(Main.class.getResourceAsStream("/annalisa-licciardi.png"));
System.out.println(iioImage.getRaster().getHeight());
}
readImage is implemented as follows:
public static IIOImage readImage(ImageInputStream stream) throws IOException
{
if (stream == null)
throw new IllegalArgumentException("stream == null!");
Iterator iterator = ImageIO.getImageReaders(stream);
if (!iterator.hasNext())
return null;
ImageReader imageReader = (ImageReader) iterator.next();
ImageReadParam param = imageReader.getDefaultReadParam();
imageReader.setInput(stream,true,true);
IIOImage iioImage = imageReader.readAll(0,param);
stream.close();
imageReader.dispose();
return iioImage;
}
public static IIOImage readImage(InputStream inputStream) throws IOException
{
return readImage(ImageIO.createImageInputStream(inputStream));
}
How do I get the raster?
ImageReader.readAll(...) doesn't work that way.
Form the JavaDoc:
Reads the image indexed by imageIndex and returns an IIOImage containing the image, thumbnails, and associated image metadata, using a supplied ImageReadParam.
The actual BufferedImage referenced by the returned IIOImage will be chosen using the algorithm defined by the getDestination method.
Also note that an IIOImage can only hold either a BufferedImage or a Raster. Not both. readAll(...) will return an IIOImage that holds a BufferedImage. So, basically, what you are trying to achieve won't work.
But as #Marco13 says in the comments, it's trivial to get the Raster from the BufferedImage once you have loaded it.
BufferedImage image = ImageIO.read(input);
WritableRaster raster = image.getRaster();
To get the pixels as int ARGB values, you don't need the Raster, you can always get it from the BufferedImage directly:
int[] pixels = new int[w * h];
image.getRGB(0, 0, w, h, pixels, 0, w);
These values will be normalized and in the sRGB color space. And it will work, regardless of the actual sample format in the image.
However, if (and only if) your raster (or its backing DataBuffer, really) is already containing pixels in int ARGB (pixel packed) format, you can access them in this way, which is faster (as it requires no conversion):
int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
In many cases though, images will be in 3 or 4 byte RGB/BGR or RGBA/ABGR (pixel interleaved) form.
You can then get the pixels directly like this:
byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
You need to loop through the values and convert to int packed form, if that is what you prefer.
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