Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert any image to JPG?

I am receiving a MultipartFile Spring object from rest controller. I am trying to convert any inage file to JPG image but I just need the byte array to save it on mongoDb

I found this code to do that

public boolean convertImageToJPG(InputStream attachedFile) {
    try {
        BufferedImage inputImage = ImageIO.read(attachedFile);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        boolean result = ImageIO.write(inputImage, "jpg", byteArrayOutputStream);
        
        return result;
        
    } catch (IOException e) {
        
        System.err.println("Error " + e);
        
    }
    return false;
}

But result as a false with not error, so ImageIO.write is not working

Also I found this to do the same but using File object, I don't want to create the file on directory, I just need the byte array

public static boolean convertFormat(String inputImagePath,
        String outputImagePath, String formatName) throws IOException {
        FileInputStream inputStream = new FileInputStream(inputImagePath);
        FileOutputStream outputStream = new FileOutputStream(outputImagePath);
         
        // reads input image from file
        BufferedImage inputImage = ImageIO.read(inputStream);
         
        // writes to the output image in specified format
        boolean result = ImageIO.write(inputImage, formatName, outputStream);
         
        // needs to close the streams
        outputStream.close();
        inputStream.close();
         
        return result;
    }

Testing

public class TestImageConverter {
 
    public static void main(String[] args) {
        String inputImage = "D:/Photo/Pic1.jpg";
        String oututImage = "D:/Photo/Pic1.png";
        String formatName = "PNG";
        try {
            boolean result = ImageConverter.convertFormat(inputImage,
                    oututImage, formatName);
            if (result) {
                System.out.println("Image converted successfully.");
            } else {
                System.out.println("Could not convert image.");
            }
        } catch (IOException ex) {
            System.out.println("Error during converting image.");
            ex.printStackTrace();
        }
    }
}

How can I solve my problem?

like image 823
TuGordoBello Avatar asked Oct 15 '22 02:10

TuGordoBello


1 Answers

UPDATED SOLUTION (alternative with no need for Raster and ColorModel)

It had indeed bothered me that my older solution (see below) still required Rasters and ColorModels. I got challenged on my solution, so I spent some more time looking for alternatives. So the best thing I could come up with now is the following:

try {
    final FileInputStream fileInputStream = new FileInputStream("dice.png");
    final BufferedImage image = ImageIO.read(fileInputStream);
    fileInputStream.close(); // ImageIO.read does not close the input stream

    final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
    convertedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);

    final FileOutputStream fileOutputStream = new FileOutputStream("dice-test.jpg");
    final boolean canWrite = ImageIO.write(convertedImage, "jpg", fileOutputStream);
    fileOutputStream.close(); // ImageIO.write does not close the output stream

    if (!canWrite) {
        throw new IllegalStateException("Failed to write image.");
    }
} catch (IOException e) {
    e.printStackTrace();
}

I ended up with a copy of the BufferedImage as I did before. It does more or less the same thing, but you can actually reuse the ColorModel and Raster more easily.

drawImage() seems to take care of most of what I did before manually. And since it is standard java library code all the way, it seems indeed to be a better way.

Note that you end up with an Image of type BufferedImage.TYPE_INT_RGB. While it seems to work for the types jpg, png, and gif, I am not sure what will happen to other file formats or files with a different storage ColorModel - information might be lost (e.g. 4 color-channels to 3). For the mentioned types we don't need an alpha channel, even if we convert from gif or jpg to png (it will be Color.WHITE).



OLD SOLUTION

I was not happy with my first design and also it did not quite work the way it should have.

Therefore, I have created one from scratch. I ended up with a little converter for sRGB files. You can convert from png to jpg and vice versa (Edit: Added gif support also). If you want to handle other types feel free to extend this further. You can more or less add it the same way. It might work for other file types as well, but I have not tested them yet. Luckily, it seems that sRGB is quite common though.

Tbh. I have no idea how many combinations and variants (color palettes, precision, quality, b/w, etc.) you can produce or which common properties they share.

Maybe this is good enough for you. Maybe not. At least it was a nice exercise for me.

This solution is by no means perfect. The results looked okay-ish. The file-type conversion worked and the file-size is also smaller than the png.

try {
    final String fileName = "dice.png";
    final BufferedImage inputImage = ImageIO.read(new FileInputStream(fileName));
    final boolean isSRGB = inputImage.getColorModel().getColorSpace().isCS_sRGB();
    final String outputFormat = "gif";
    if (!isSRGB) {
        throw new IllegalArgumentException("Please provide an image that supports sRGB.");
    }
    final WritableRaster raster = createRaster(inputImage);
    final ColorModel colorModel = createColorModel(inputImage);
    final BufferedImage outputImage = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
    final String outputFileName = fileName + "-converted." + outputFormat;
    final boolean writeResult = ImageIO.write(outputImage, outputFormat, new FileOutputStream(outputFileName));
    if (!writeResult) {
        throw new IllegalStateException("Could not convert file: " + fileName + " to format: " + outputFormat);
    }
    System.out.println(">> Created file: " + outputFileName);
} catch (Exception e) {
    e.printStackTrace();
}
@NotNull
public static ColorModel createColorModel(@NotNull BufferedImage bufferedImage) {
    Objects.requireNonNull(bufferedImage);
    final int type = bufferedImage.getType();
    boolean isAlphaPremultiplied = false;
    int transparency = Transparency.OPAQUE;
    if (type == BufferedImage.TYPE_3BYTE_BGR) {
        isAlphaPremultiplied = true;
    }
    return new ComponentColorModel(
            ColorModel.getRGBdefault().getColorSpace(),
            false, isAlphaPremultiplied, transparency,
            bufferedImage.getData().getDataBuffer().getDataType()
    );
}

@NotNull
public static WritableRaster createRaster(@NotNull BufferedImage bufferedImage) {
    Objects.requireNonNull(bufferedImage);
    final int type = bufferedImage.getType();
    final int width = bufferedImage.getWidth();
    final int height = bufferedImage.getHeight();
    final int pixelStride = 3;
    int[] offset = new int[]{0, 1, 2};
    DataBufferByte dataBufferByte;

    if (type == BufferedImage.TYPE_4BYTE_ABGR || type == BufferedImage.TYPE_BYTE_INDEXED) {
        int dataIndex = 0;
        final byte[] data = new byte[height * width * pixelStride];
        final int bitmask = 0xff;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                final int rgb = bufferedImage.getRGB(x, y);
                final int blue = bitmask & rgb;
                final int green = bitmask & (rgb >> 8);
                final int red = bitmask & (rgb >> 16);
                if (rgb == 0) {
                    data[dataIndex++] = (byte) bitmask;
                    data[dataIndex++] = (byte) bitmask;
                    data[dataIndex++] = (byte) bitmask;
                } else {
                    data[dataIndex++] = (byte) red;
                    data[dataIndex++] = (byte) green;
                    data[dataIndex++] = (byte) blue;
                }
            }
        }
        dataBufferByte = new DataBufferByte(data, data.length);
    } else if (type == BufferedImage.TYPE_3BYTE_BGR) {
        dataBufferByte = (DataBufferByte) bufferedImage.getRaster().getDataBuffer();
        offset = new int[]{2, 1, 0};
    } else {
        throw new IllegalArgumentException("Cannot create raster for unsupported image type.");
    }

    return Raster.createInterleavedRaster(
            dataBufferByte, width, height,
            pixelStride * width, pixelStride,
            offset,
            null
    );
}

image-conversion-examples

EDIT: Added support for gif.

like image 147
F. Müller Avatar answered Nov 15 '22 12:11

F. Müller