I'm currently making a method that converts a ppm file to a jpg, png, and bmp file. The way I did it is reading the content of a ppm file, creating a BufferedImage, and assigning each pixel from the ppm file to the corresponding pixel in the BufferedImage. My bmp and png files look correct. However, the jpg file looks completely different.
Below is my code:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import javax.imageio.ImageIO;
public class readPPMOutputOthers {
public static void main(String[] args) throws InterruptedException {
// read a ppm file
Scanner sc;
// if the file is not found, it will throw an exception
try {
sc = new Scanner(new FileInputStream("res/test2.ppm"));
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("File not found!");
}
// the file now is a StringBuilder
// read line by line to get information
StringBuilder builder = new StringBuilder();
while (sc.hasNextLine()) {
String s = sc.nextLine();
// ignore comment #
if (s.charAt(0) != '#') {
builder.append(s).append(System.lineSeparator());
}
}
// throw an exception if the raw file doesn't begin with P3
sc = new Scanner(builder.toString());
String token;
token = sc.next();
if (!token.equals("P3")) {
throw new IllegalArgumentException("Invalid PPM file: plain RAW file should begin with P3.");
}
// set the fields
// initial load image
int width = sc.nextInt();
int height = sc.nextInt();
int maxValue = sc.nextInt();
List<Integer> pixels = new ArrayList<>();
for (int i = 0; i < height; i ) {
for (int j = 0; j < width; j ) {
int r = sc.nextInt();
int g = sc.nextInt();
int b = sc.nextInt();
int rgb = r;
rgb = (rgb << 8) g;
rgb = (rgb << 8) b;
pixels.add(rgb);
}
}
// make a BufferedImage from pixels
BufferedImage outputImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int[] outputImagePixelData = ((DataBufferInt) outputImg.getRaster().getDataBuffer()).getData();
for (int i = 0; i < pixels.size(); i ) {
outputImagePixelData[i] = pixels.get(i);
}
try {
ImageIO.write(outputImg, "png",
new File("res/test.png"));
ImageIO.write(outputImg, "jpg",
new File("res/test2.jpg"));
ImageIO.write(outputImg, "bmp",
new File("res/test.bmp"));
} catch (IOException e) {
System.out.println("Exception occurred :" e.getMessage());
}
System.out.println("Images were written successfully.");
}
}
And in our case:
becomes
The brown color is a mixture of the original red, cyan magenta and the yellow colors (the brown color is "shared" by the 4 pixels).
- Note:
Chroma subsumpling is not considered as "compression", is the sense that it not performed as part of the JPEG compression stage.
We can't control the chroma subsumpling by setting the compression quality parameter.
Chroma subsumpling is referred as part of the "color format conversion" pre-processing stage - converting from RGB to YUV420 color format.
The commonly used JPEG color format is YUV420, but JPEG standard does support YUV444 Chroma subsumpling.
Enlarged:
I couldn't find an example for saving YUV444 JPEG in JAVA...
CodePudding user response:
To some degree the effect you describe is to be expected.
From https://en.wikipedia.org/wiki/JPEG:
JPEG is a commonly used method of lossy compression for digital images, particularly for those images produced by digital photography. The degree of compression can be adjusted, allowing a selectable tradeoff between storage size and image quality. JPEG typically achieves 10:1 compression with little perceptible loss in image quality.
Maybe when storing small files you can set the compression to be low and thus increase quality. See Setting jpg compression level with ImageIO in Java