Writing a byte array to a bmp java - java

As the title suggests, I'm attempting to write a byte array to a bmp file in Java. Currently, my program successfully writes data to the file location, however it appears to be missing data and cannot be opened because of it. Of the two functions listed below the goal is:
fromImage takes a grayscale bmp image byte data and converts each integer to a binary string, which is then stored in a LinkedList node. toImage takes that LinkedList, converts the binary strings back to integers and then writes the new byte array back to another file.
public static LinkedList<String> fromImage(BufferedImage img) {
LinkedList<String> new_buff = new LinkedList<String>();
//try{
//img = ImageIO.read(new File("img/lena.bmp"));
byte[] byte_buffer = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
for(byte b : byte_buffer){
String buffer;
buffer = Integer.toBinaryString(b & 255 | 256).substring(1);
new_buff.addLast(buffer);
//System.out.println(buffer);
}
//}catch(IOException e){}
System.out.println("Exiting fromImage");
return new_buff;
}
// Save a binary number as a BMP image
// Image input hardcoded atm
public static BufferedImage toImage(LinkedList<String> bi) {
BufferedImage img = null;
int b;
byte[] bytes = new byte[bi.size()];
for(int i = 0; i < bi.size(); i++){
String temp = bi.get(i);
b = Integer.parseInt(temp);
bytes[i] = (byte) b;
//System.out.println(i);
}
System.out.println("Exiting For loop");
try{
Files.write(Paths.get("img/encrypted.bmp"), bytes);
//img = ImageIO.read(new File("img/lena.bmp"));
//ImageIO.write(img, "bmp", new File("img/encrypted.bmp"));
//img = ImageIO.read(new File("img/encrypted.bmp"));
}catch(IOException e){}
System.out.println("Exiting toImage");
return img;
}
So ultimately, my question is - Where is the data I'm missing, why am I missing it, and what can I do to fix it?

BMP has a file structure.
Here, you are writing to a file named "encrypted.bmp", so I suppose your bytes are the encryption of something, and thus do not represent a valid bmp file.
You will have to comply to the BMP file structure, adding a header and footer so that your bytes are eg. the pixel part of the BMP file.
The easiest way to do that is by writing your image to a BufferedImage img and then use ImageIO.write(img, "BMP", new File("encrypted.bmp")).

I just want to turn Ekleog's answer into a usable code:
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
public static boolean arrayToBMP(byte[] pixelData, int width, int height, File outputFile) throws IOException {
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
img.setData(Raster.createRaster(img.getSampleModel(), new DataBufferByte(pixelData, pixelData.length), null));
return javax.imageio.ImageIO.write(img, "bmp", outputFile);
}

Related

Compress an image without specifying the type of image

This code is to compress a jpeg image, but if I want to compress an image without specifying the type of image, how can I do that? , How do I modify the code ?
File originalImage = new File("C:\\Users\\Super\\Desktop\\man.jpg");
File compressedImage = new File("C:\\Users\\Super\\Desktop\\compressedImage.jpg");
try{
compressJPEGImage(originalImage, compressedImage,0.5f );
System.out.println("Done!");
}
catch(IOException e){
}
}
public static void compressJPEGImage(File originalImage , File compressedImage , float
compressionQuality) throws IOException{
RenderedImage image = ImageIO.read(originalImage);
ImageWriter jpegwriter = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam jpegWriteParam=jpegwriter.getDefaultWriteParam();
jpegWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpegWriteParam.setCompressionQuality(compressionQuality);
try(ImageOutputStream output=ImageIO.createImageOutputStream(compressedImage)){
jpegwriter.setOutput(output);
IIOImage outputImage = new IIOImage(image,null,null);
jpegwriter.write(null,outputImage,jpegWriteParam);
}
jpegwriter.dispose();
}
Convert image to base64 or byte array of any type, then you can try for compressing it.

How to disable compression when writing JPEG in java?

I want to compress JPEG to fixed file size (20480 bytes). Here is my code:
package io.github.baijifeilong.jpeg;
import lombok.SneakyThrows;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.FileImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
/**
* Created by BaiJiFeiLong#gmail.com at 2019/10/9 上午11:26
*/
public class JpegApp {
#SneakyThrows
public static void main(String[] args) {
BufferedImage inImage = ImageIO.read(new File("demo.jpg"));
BufferedImage outImage = new BufferedImage(143, 143, BufferedImage.TYPE_INT_RGB);
outImage.getGraphics().drawImage(inImage.getScaledInstance(143, 143, Image.SCALE_SMOOTH), 0, 0, null);
JPEGImageWriteParam jpegParams = new JPEGImageWriteParam(null);
jpegParams.setCompressionMode(ImageWriteParam.MODE_DISABLED);
ImageWriter imageWriter = ImageIO.getImageWritersByFormatName("jpg").next();
imageWriter.setOutput(new FileImageOutputStream(new File("demo-xxx.jpg")));
imageWriter.write(null, new IIOImage(outImage, null, null), jpegParams);
}
}
And the error occured:
Exception in thread "main" javax.imageio.IIOException: JPEG compression cannot be disabled
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeOnThread(JPEGImageWriter.java:580)
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.write(JPEGImageWriter.java:363)
at io.github.baijifeilong.jpeg.JpegApp.main(JpegApp.java:30)
Process finished with exit code 1
So how to disable JPEG compression? Or there be any method that can compress any image to a fixed file size with any compression?
As for the initial question, how to create non-compressed jpegs: one can't, for fundamental reasons. While I initially assumed that it is possible to write a non-compressing jpg encoder producing output that can be decoded with any existing decoder by manipulating the Huffman tree involved, I had to dismiss it. The Huffman encoding is just the last step of quite a pipeline of transformations, that can not be skipped. Custom Huffman trees may also break less sophisticated decoders.
For an answer that takes into consideration the requirement change made in comments (resize and compress any way you like, just give me the desired file size) one could reason this way:
The jpeg file specification defines an End of Image marker. So chances are, that patching zeros (or just anything perhaps) afterwards make no difference. An experiment patching some images up to a specific size showed that gimp, chrome, firefox and your JpegApp swallowed such an inflated file without complaint.
It would be rather complicated to create a compression that for any image compresses precisely to your size requirement (kind of: for image A you need a compression ratio of 0.7143, for Image B 0.9356633, for C 12.445 ...). There are attempts to predict image compression ratios based on raw image data, though.
So I'd propose just to resize/compress to any size < 20480 and then patch it:
calculate the scaling ratio based on the original jpg size and the
desired size, including a safety margin to account for the
inherently vague nature of the issue
resize the image with that ratio
patch missing bytes to match exactly the desired size
As outlined here
private static void scaleAndcompress(String fileNameIn, String fileNameOut, Long desiredSize) {
try {
long size = getSize(fileNameIn);
// calculate desired ratio for conversion to stay within size limit, including a safte maring (of 10%)
// to account for the vague nature of the procedure. note, that this will also scale up if needed
double desiredRatio = (desiredSize.floatValue() / size) * (1 - SAFETY_MARGIN);
BufferedImage inImg = ImageIO.read(new File(fileNameIn));
int widthOut = round(inImg.getWidth() * desiredRatio);
int heigthOut = round(inImg.getHeight() * desiredRatio);
BufferedImage outImg = new BufferedImage(widthOut, heigthOut, BufferedImage.TYPE_INT_RGB);
outImg.getGraphics().drawImage(inImg.getScaledInstance(widthOut, heigthOut, Image.SCALE_SMOOTH), 0, 0, null);
JPEGImageWriter imageWriter = (JPEGImageWriter) ImageIO.getImageWritersByFormatName("jpg").next();
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
imageWriter.setOutput(new MemoryCacheImageOutputStream(outBytes));
imageWriter.write(null, new IIOImage(outImg, null, null), new JPEGImageWriteParam(null));
if (outBytes.size() > desiredSize) {
throw new IllegalStateException(String.format("Excess output data size %d for image %s", outBytes.size(), fileNameIn));
}
System.out.println(String.format("patching %d bytes to %s", desiredSize - outBytes.size(), fileNameOut));
patch(desiredSize, outBytes);
try (FileOutputStream outFileStream = new FileOutputStream(new File(fileNameOut))) {
outBytes.writeTo(outFileStream);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void patch(Long desiredSize, ByteArrayOutputStream bytesOut) {
long patchSize = desiredSize - bytesOut.size();
for (long i = 0; i < patchSize; i++) {
bytesOut.write(0);
}
}
private static long getSize(String fileName) {
return (new File(fileName)).length();
}
private static int round(double f) {
return Math.toIntExact(Math.round(f));
}
A solution using Magick (sudo apt install imagemagick on Debian), maybe not work for some images. Thanks to #curiosa-g.
package io.github.baijifeilong.jpeg;
import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Scanner;
/**
* Created by BaiJiFeiLong#gmail.com at 2019/10/9 上午11:26
*/
public class JpegApp {
#SneakyThrows
private static InputStream compressJpeg(InputStream inputStream, int fileSize) {
File tmpFile = new File(String.format("tmp-%d.jpg", Thread.currentThread().getId()));
FileUtils.copyInputStreamToFile(inputStream, tmpFile);
Process process = Runtime.getRuntime().exec(String.format("mogrify -strip -resize 512 -define jpeg:extent=%d %s", fileSize, tmpFile.getName()));
try (Scanner scanner = new Scanner(process.getErrorStream()).useDelimiter("\\A")) {
if (process.waitFor() > 0) throw new RuntimeException(String.format("Mogrify Error \n### %s###", scanner.hasNext() ? scanner.next() : "Unknown"));
}
try (FileInputStream fileInputStream = new FileInputStream(tmpFile)) {
byte[] bytes = IOUtils.toByteArray(fileInputStream);
assert bytes.length <= fileSize;
byte[] newBytes = new byte[fileSize];
System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
Files.delete(Paths.get(tmpFile.getPath()));
return new ByteArrayInputStream(newBytes);
}
}
#SneakyThrows
public static void main(String[] args) {
InputStream inputStream = compressJpeg(new FileInputStream("big.jpg"), 40 * 1024);
IOUtils.copy(inputStream, new FileOutputStream("40KB.jpg"));
System.out.println(40 * 1024);
System.out.println(new File("40KB.jpg").length());
}
}
And the output:
40960
40960

Convert an ImageIcon to a Base64 String and back to an ImageIcon without saving to disk?

I'm trying to store an imageIcon bas a Base64 String.
This is what I have so far:
public ImageIcon getImageIcon() {
if(imageIcon == null || imageIcon.isEmpty()){
return null;
} else {
try {
byte[] btDataFile = Base64.decodeBase64(imageIcon);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(btDataFile));
return new ImageIcon(image);
} catch (IOException ex) {
System.out.println(ex.getLocalizedMessage());
return null;
}
}
}
public void setImageIcon(ImageIcon imageIconIn) {
imageIcon = Base64.encodeBase64String(imageToByteArray(imageIconIn));
}
public static byte[] imageToByteArray(ImageIcon imageIn) {
try {
BufferedImage image = new BufferedImage(imageIn.getIconWidth(), imageIn.getIconHeight(),BufferedImage.TYPE_INT_RGB);
ByteArrayOutputStream b = new ByteArrayOutputStream();
// FIX
Graphics g;
g = image.createGraphics();
imageIn.paintIcon(null, g, 0,0);
// END FIX
ImageIO.write(image, "jpg", b );
g.dispose();
return b.toByteArray();
} catch (IOException ex) {
System.out.println(ex.getLocalizedMessage());
return null;
}
}
I get a black rectangle instead of the image.
I'm using Java 1.8 on Ubuntu 16.04.
What am I doing wrong?
Thanks for your help.
******************************** . FIXED . ******************************
I found a working solution and updated the above code.
******************************** EDIT *********************************
Added g.dispose() after painting icon.
This code creates a brand new BufferedImage, with width & height same as the given image.
BufferedImage image = new BufferedImage(
imageIn.getIconWidth(),
imageIn.getIconHeight(),
BufferedImage.TYPE_INT_RGB);
Note that the image is empty. No content has been written to it. The bytes will all be zero, and RGB 0x000000 is black.
Then, you are writing the bytes of this black image to your ByteArrayOutputStream.
ByteArrayOutputStream b = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", b );
return b.toByteArray();
Of course, when you convert that byte buffer back to an image, it will be black.
You will want to draw/copy imageIn into your new image before you write out the bytes.
But if you don't mind using whatever the current image's format is, you could just write out that image instead of converting it to TYPE_INT_RGB...
Image image = imageIn.getImage();
// write image to ByteArrayOutputStream

Java. Broken image from URL

I am trying to load this image from URL, but receive image like this.
Code:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
URL url = new URL("http://s.developers.org.ua/img/announces/java_1.jpg");
BufferedInputStream in = new BufferedInputStream(url.openStream());
byte[] b = new byte[512];
while (in.read(b)!=-1)
out.write(b);
Image img = ImageIO.read(new ByteArrayInputStream(out.toByteArray()));
g2.drawImage(img, 0, 0, getWidth(), getHeight(), null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Don't read images inside the paintComponent method, it will make your application appear sluggish, as the method is executed on the event dispatcher thread (EDT). Also, it will be re-read, whenever your component is re-painted, meaning you'll download the image over and over. Instead, read it up front, or in a separate thread (ie. use a SwingWorker), and only invoke g.drawImage(...) from inside the paintComponent method.
The reason for the broken image is your byte copying code, where you don't pay attention to how many bytes are read (as long as the value isn't -1), but instead unconditionally copy 512 bytes. However, you don't need to do that here, you can simply pass the stream to ImageIO.read, like this, making the code simpler and more readable:
URL url = new URL("http://s.developers.org.ua/img/announces/java_1.jpg");
try (BufferedInputStream in = new BufferedInputStream(url.openStream())) {
BufferedImage img = ImageIO.read(in);
}
Adding the extra try (try-with-resources) block makes sure your stream is also properly closed to avoid resource leaks.
For completeness, to fix the byte copying code, the correct version would be:
// ... as above ...
byte[] b = new byte[512];
int bytesRead; // Keep track of the number of bytes read into 'b'
while ((bytesRead = in.read(b)) != -1)
out.write(b, 0, bytesRead);
I don't know if this is the only problem, but you might write more than you get.
I suggest that you change your writing code to:
int len;
while ((len=in.read(b))!=-1)
out.write(b, 0, len);
Otherwise, if the last buffer is not exactly 512 bytes long, you'll write too much
I have some code copy file from URL to Local.. So far the result is same like actual source. Just do some modification maybe can help to solved it.
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import org.apache.commons.io.FilenameUtils;
import javax.imageio.ImageIO;
public class ImagesUrlToImagesLocal {
public ArrayList<String> getIt(ArrayList<String> urlFile)
{
ArrayList<String> strResult = new ArrayList<String>();
Image imagesUrl = null;
String baseName = null;
String extension = null;
File outputfile = null;
try {
for (int i = 0; i < urlFile.size(); i++)
{
URL url = new URL(urlFile.get(i));
baseName = FilenameUtils.getBaseName(urlFile.get(i));
extension = FilenameUtils.getExtension(urlFile.get(i));
imagesUrl = ImageIO.read(url);
BufferedImage image = (BufferedImage) imagesUrl;
outputfile = new File("temp_images/" + baseName + "." + extension);
ImageIO.write(image, extension, outputfile);
strResult.add("temp_images/" + baseName + "." + extension);
}
} catch (Exception e) {
e.printStackTrace();
}
return strResult;
}
}

My batch jpg resizer works with color images, but grayscale ones become washed out

I've been having a problem with my Java program. It's for resizing images. You drop it into a folder and run it, and it creates a new folder with the resized images. It works great on color, but it has a problem with grayscale. The images are converted, but they become lighter and more washed out, as if someone has messed with the curves or levels. All the input files and output files are sRGB color space jpegs, saved in RGB color mode. I have thousands of 50 megapixel film scans I'm trying to convert down to 15 megapixels or less. Any help or ideas anyone could offer would be most appreciated. The programs full code is below, it's about 130 lines. I have a feeling the problem may be in the toBufferedImage function but I'm lost as to what it could be.
package jpegresize;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.io.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
System.out.println("JPEGResize running . . .");
int max_side = 4096;
float quality = 0.9f;
if(args.length == 0) System.out.println("No maximum side resolution or compression quality arguments given, using default values.\nUsage: java -jar JPEGResize.jar <maximum side resolution in pixels> <quality 0 to 100 percent>");
if(args.length >= 1) max_side = Integer.parseInt(args[0]);
if(args.length >= 2) quality = Float.parseFloat(args[1]) / 100.0f;
System.out.println("Maximum side resolution: " + max_side);
System.out.println("Compression quality: " + (quality * 100) + "%");
File folder = new File(".");
File[] listOfFiles = folder.listFiles(new JPEGFilter());
for(int i = 0; i < listOfFiles.length; i++) {
System.out.println("Processing " + listOfFiles[i].getName() + " . . .");
resizeFile(listOfFiles[i].getName(), max_side, quality);
System.out.println("Saved /resized/" + listOfFiles[i].getName());
}
System.out.println("Operations complete.");
}
public static void resizeFile(String filename, int max_side, float quality) {
try
{
BufferedImage input_img = ImageIO.read(new File(filename));
double aspect_ratio = ((double)input_img.getWidth()) / ((double)input_img.getHeight());
int width, height;
if(input_img.getWidth() >= input_img.getHeight()) {
width = max_side;
height = (int)(((double)max_side) / aspect_ratio);
}
else {
width = (int)(((double)max_side) * aspect_ratio);
height = max_side;
}
Image scaled_img = input_img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage output_img = toBufferedImage(scaled_img);
Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter)iter.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(quality);
File doesDirExist = new File("resized/");
if(!doesDirExist.exists())
new File("resized").mkdir();
File file = new File("resized/" + filename);
FileImageOutputStream output = new FileImageOutputStream(file);
writer.setOutput(output);
IIOImage image = new IIOImage(output_img, null, null);
writer.write(null, image, iwp);
writer.dispose();
}
catch (IOException e)
{
e.printStackTrace();
}
}
// This method returns a buffered image with the contents of an image
public static BufferedImage toBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage)image;
}
// This code ensures that all the pixels in the image are loaded
image = new ImageIcon(image).getImage();
// Create a buffered image with a format that's compatible with the screen
BufferedImage bimage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
// Determine the type of transparency of the new buffered image
int transparency = Transparency.OPAQUE;
// Create the buffered image
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
bimage = gc.createCompatibleImage(
image.getWidth(null), image.getHeight(null), transparency);
} catch (HeadlessException e) {
// The system does not have a screen
}
if (bimage == null) {
// Create a buffered image using the default color model
int type = BufferedImage.TYPE_INT_RGB;
bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
}
// Copy image to buffered image
Graphics g = bimage.createGraphics();
// Paint the image onto the buffered image
g.drawImage(image, 0, 0, null);
g.dispose();
return bimage;
}
}
class JPEGFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(".jpg")) || (name.toLowerCase().endsWith(".jpeg"));
}
}
If jdk's classes and methods are buggy, report the bug to oracle (oh! I wish I could go on saying to SUN..).
And, while the next release will correct the bug ;), try some work arounds, scaling image by yourself like proposed here.
Regards,
Stéphane
In your code, you assume jpeg are encoded in RGB, but that's not always the case. It's also possible to encode 8 bit gray scaled jpeg. So I suggest that you try this when building your BufferedImage, replace :
BufferedImage.TYPE_INT_RGB;
by
BufferedImage.TYPE_BYTE_GRAY;
and see if it works for those images.
If so, then you still have to find out a way to determine the encoding type to automatically change the type of BufferedImage color encoding to use, but you will be one stop closer.
Regards,
Stéphane

Categories

Resources