package org.hipi.test;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import org.hipi.image.HipiImage;
import org.hipi.image.RasterImage;
import org.hipi.image.ByteImage;
import org.hipi.image.FloatImage;
import org.hipi.image.PixelArrayByte;
import org.hipi.image.PixelArrayFloat;
import org.hipi.image.PixelArray;
import org.hipi.image.HipiImageFactory;
import org.hipi.image.HipiImageHeader;
import org.hipi.image.io.ImageDecoder;
import org.hipi.image.io.ImageEncoder;
import org.hipi.image.io.JpegCodec;
import org.hipi.image.io.PpmCodec;
import org.hipi.util.ByteUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.ArrayUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.Ignore;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.ColorConvertOp;
import java.awt.color.ColorSpace;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.System;
import java.util.Scanner;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
public class JpegCodecTestCase {
@BeforeClass
public static void setup() throws IOException {
TestUtils.setupTmpDirectory();
}
private void printExifData(HipiImage image) {
// display EXIF data
for (String key : image.getAllExifData().keySet()) {
String value = image.getExifData(key);
System.out.println(key + " : " + value);
}
}
@Test
public void testTwelveMonkeysPlugIn() {
boolean foundTwelveMonkeys = false;
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
while (readers.hasNext()) {
ImageReader imageReader = readers.next();
// System.out.println("ImageReader: " + imageReader);
if (imageReader.toString().startsWith("com.twelvemonkeys.imageio.plugins.jpeg")) {
foundTwelveMonkeys = true;
}
}
assertTrue("FATAL ERROR: failed to locate TwelveMonkeys ImageIO plugins", foundTwelveMonkeys);
}
/**
* Test method for {@link hipi.image.io.JpegCodec#decodeHeader(java.io.InputStream)}.
*
* @throws IOException
*/
@Test
public void testDecodeHeader() throws IOException {
ImageDecoder decoder = JpegCodec.getInstance();
String[] fileName =
{"canon-ixus", "fujifilm-dx10", "fujifilm-finepix40i", "fujifilm-mx1700", "kodak-dc210",
"kodak-dc240", "nikon-e950", "olympus-c960", "ricoh-rdc5300", "sanyo-vpcg250",
"sanyo-vpcsx550", "sony-cybershot", "sony-d700", "fujifilm-x100s"};
String[] model =
{"Canon DIGITAL IXUS", "DX-10", "FinePix40i", "MX-1700ZOOM", "DC210 Zoom (V05.00)",
"KODAK DC240 ZOOM DIGITAL CAMERA", "E950", "C960Z,D460Z", "RDC-5300", "SR6", "SX113",
"CYBERSHOT", "DSC-D700", "X100S"};
int[] width = {640, 1024, 600, 640, 640, 640, 800, 640, 896, 640, 640, 640, 672, 3456};
int[] height = {480, 768, 450, 480, 480, 480, 600, 480, 600, 480, 480, 480, 512, 2304};
for (int i = 0; i < fileName.length; i++) {
String fname = "../testdata/jpeg-exif-test/" + fileName[i] + ".jpg";
FileInputStream fis = new FileInputStream(fname);
HipiImageHeader header = decoder.decodeHeader(fis, true);
assertNotNull("failed to decode header: " + fname, header);
assertEquals("exif model not correct: " + fname, model[i].trim(),
header.getExifData("Model").trim());
assertEquals("width not correct: " + fname, width[i], header.getWidth());
assertEquals("height not correct: " + fname, height[i], header.getHeight());
}
}
@Test
public void testSRGBConversions() throws IOException {
ImageDecoder jpegDecoder = JpegCodec.getInstance();
ImageEncoder ppmEncoder = PpmCodec.getInstance();
File[] cmykFiles = new File("../testdata/jpeg-cmyk").listFiles();
File[] rgbFiles = new File("../testdata/jpeg-rgb").listFiles();
File[] files = (File[])ArrayUtils.addAll(cmykFiles,rgbFiles);
for (File file : files) {
String ext = FilenameUtils.getExtension(file.getName());
if (file.isFile() && ext.equalsIgnoreCase("jpg")) {
String jpgPath = file.getPath();
String ppmPath = FilenameUtils.removeExtension(file.getPath()) + "_hipi.ppm";
System.out.println("Testing linear RGB color conversion for: " + jpgPath);
// Using FloatImage forces conversion from non-linear sRGB to linear RGB by default
FloatImage jpegImage = (FloatImage)jpegDecoder.decodeHeaderAndImage(
new FileInputStream(jpgPath), HipiImageFactory.getFloatImageFactory(), true);
assertNotNull(jpegImage);
BufferedImage javaImage = ImageIO.read(new FileInputStream(jpgPath));
assertNotNull(javaImage);
ColorSpace ics = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
ColorConvertOp cco = new ColorConvertOp(ics, null);
BufferedImage rgbImage = cco.filter(javaImage, null);
assertNotNull(rgbImage);
Raster raster = rgbImage.getData();
DataBuffer dataBuffer = raster.getDataBuffer();
int w = raster.getWidth();
int h = raster.getHeight();
assertEquals(w,jpegImage.getWidth());
assertEquals(h,jpegImage.getHeight());
assertEquals(3,raster.getNumBands());
ppmEncoder.encodeImage(jpegImage, new FileOutputStream(ppmPath));
System.out.println("wrote: " + ppmPath);
String truthPath = FilenameUtils.removeExtension(file.getPath()) + "_photoshop.ppm";
Runtime rt = Runtime.getRuntime();
String cmd = "compare -metric PSNR " + ppmPath + " " + truthPath + " " +
TestUtils.getTmpPath("psnr.png");
System.out.println(cmd);
Process pr = rt.exec(cmd);
Scanner scanner = new Scanner(new InputStreamReader(pr.getErrorStream()));
float psnr = scanner.hasNextFloat() ? scanner.nextFloat() : 0;
System.out.println("PSNR with respect to Photoshop ground truth: " + psnr);
assertTrue("PSNR is too low : " + psnr, psnr > 30);
}
}
}
@Test
public void testDecodeImage() throws IOException {
ImageDecoder jpegDecoder = JpegCodec.getInstance();
ImageDecoder ppmDecoder = PpmCodec.getInstance();
File[] cmykFiles = new File("../testdata/jpeg-cmyk").listFiles();
// File[] rgbFiles = new File("./testimages/jpeg-rgb").listFiles();
File[] rgbFiles = null;
File[] files = (File[])ArrayUtils.addAll(cmykFiles,rgbFiles);
for (int iter=0; iter<=1; iter++) {
for (File file : files) {
String ext = FilenameUtils.getExtension(file.getName());
if (file.isFile() && ext.equalsIgnoreCase("jpg")) {
String jpgPath = file.getPath();
String ppmPath = FilenameUtils.removeExtension(file.getPath()) + "_photoshop.ppm";
System.out.println("Testing JPEG decoder (" + (iter == 0 ? "ByteImage" : "FloatImage") + ") for: " + jpgPath);
FileInputStream fis = new FileInputStream(ppmPath);
RasterImage ppmImage = (RasterImage)ppmDecoder.decodeHeaderAndImage(fis, (iter == 0 ? HipiImageFactory.getByteImageFactory() : HipiImageFactory.getFloatImageFactory()), false);
assumeNotNull(ppmImage);
fis = new FileInputStream(jpgPath);
RasterImage jpegImage = (RasterImage)jpegDecoder.decodeHeaderAndImage(fis, (iter == 0 ? HipiImageFactory.getByteImageFactory() : HipiImageFactory.getFloatImageFactory()), true);
assumeNotNull(jpegImage);
float maxDiff = (iter == 0 ? 45.0f : 45.0f/255.0f);
if (!ppmImage.equalsWithTolerance((RasterImage)jpegImage, maxDiff)) { // allow 3 8-bit values difference to account for color space conversion
System.out.println(ppmImage);
System.out.println(jpegImage);
// Get pointers to pixel arrays
PixelArray ppmPA = ppmImage.getPixelArray();
PixelArray jpegPA = jpegImage.getPixelArray();
int w = ppmImage.getWidth();
int h = ppmImage.getHeight();
assertEquals(ppmImage.getNumBands(),3);
assertEquals(jpegImage.getNumBands(),3);
// Check that pixel data is equal.
for (int i = 0; i < w*h*3; i++) {
float diff = (iter == 0 ? Math.abs(ppmPA.getElem(i) - jpegPA.getElem(i)) : Math.abs(ppmPA.getElemFloat(i) - jpegPA.getElemFloat(i)));
if (diff > maxDiff) {
int j = (int)(i/3);
int x = j%w;
int y = (int)(j/w);
if (iter == 0) {
System.out.println(String.format("(%d,%d) PPM: %d %d %d | JPG: %d %d %d", x, y,
ppmPA.getElem(j*3+0), ppmPA.getElem(j*3+1), ppmPA.getElem(j*3+2),
jpegPA.getElem(j*3+0), jpegPA.getElem(j*3+1), jpegPA.getElem(j*3+2)));
} else {
System.out.println(String.format("(%d,%d) PPM: %.2f %.2f %.2f | JPG: %.2f %.2f %.2f", x, y,
ppmPA.getElemFloat(j*3+0), ppmPA.getElemFloat(j*3+1), ppmPA.getElemFloat(j*3+2),
jpegPA.getElemFloat(j*3+0), jpegPA.getElemFloat(j*3+1), jpegPA.getElemFloat(j*3+2)));
}
System.out.println(String.format("diff = %f [maxDiff = %f]", diff, maxDiff));
break;
}
}
fail("Found differences between decoded image and ground truth.");
}
}
}
}
}
@Test
public void testEncodeImage() throws IOException {
ImageDecoder ppmDecoder = PpmCodec.getInstance();
ImageEncoder jpegEncoder = JpegCodec.getInstance();
File[] files = new File("../testdata/jpeg-rgb").listFiles();
// Tests PPM decode and JPEG encode routines using ByteImage
for (File file : files) {
if (file.isFile() && file.getName().endsWith("_photoshop.ppm")) {
String ppmPath = file.getPath();
String jpgPath = TestUtils.getTmpPath("hipi_enc.jpg");
System.out.println("Testing JPEG encoder (ByteImage) for: " + ppmPath);
ByteImage image = (ByteImage)ppmDecoder.decodeHeaderAndImage(new FileInputStream(ppmPath),
HipiImageFactory.getByteImageFactory(), false);
jpegEncoder.encodeImage(image, new FileOutputStream(jpgPath));
Runtime rt = Runtime.getRuntime();
String cmd = "compare -metric PSNR " + ppmPath + " " + jpgPath + " /tmp/psnr.png";
System.out.println("cmd: " + cmd);
Process pr = rt.exec(cmd);
Scanner scanner = new Scanner(new InputStreamReader(pr.getErrorStream()));
float psnr = scanner.hasNextFloat() ? scanner.nextFloat() : 0;
System.out.println("PSNR: " + psnr);
assertTrue(ppmPath + " PSNR is too low : " + psnr, psnr > 30);
}
}
// Tests PPM decode and JPEG encode routines using FloatImage
for (File file : files) {
if (file.isFile() && file.getName().endsWith("_photoshop.ppm")) {
String ppmPath = file.getPath();
String jpgPath = TestUtils.getTmpPath("hipi_enc.jpg");
System.out.println("Testing JPEG encoder (FloatImage) for: " + ppmPath);
FloatImage image = (FloatImage)ppmDecoder.decodeHeaderAndImage(new FileInputStream(ppmPath),
HipiImageFactory.getFloatImageFactory(), false);
jpegEncoder.encodeImage(image, new FileOutputStream(jpgPath));
Runtime rt = Runtime.getRuntime();
String cmd = "compare -metric PSNR " + ppmPath + " " + jpgPath + " " +
TestUtils.getTmpPath("psnr.png");
System.out.println("cmd: " + cmd);
Process pr = rt.exec(cmd);
Scanner scanner = new Scanner(new InputStreamReader(pr.getErrorStream()));
float psnr = scanner.hasNextFloat() ? scanner.nextFloat() : 0;
System.out.println("PSNR: " + psnr);
assertTrue(ppmPath + " PSNR is too low : " + psnr, psnr > 30);
}
}
}
}