/*_x000D_
 * Copyright (c) 2014, Netherlands Forensic Institute_x000D_
 * All rights reserved._x000D_
 */_x000D_
package nl.minvenj.nfi.prnu;_x000D_
_x000D_
import java.awt.color.CMMException;_x000D_
import java.awt.image.BufferedImage;_x000D_
import java.awt.image.ColorModel;_x000D_
import java.io.BufferedInputStream;_x000D_
import java.io.BufferedOutputStream;_x000D_
import java.io.File;_x000D_
import java.io.FileInputStream;_x000D_
import java.io.FileOutputStream;_x000D_
import java.io.IOException;_x000D_
import java.io.InputStream;_x000D_
import java.io.ObjectInputStream;_x000D_
import java.io.ObjectOutputStream;_x000D_
import java.io.OutputStream;_x000D_
_x000D_
import javax.imageio.ImageIO;_x000D_
_x000D_
import nl.minvenj.nfi.prnu.filter.FastNoiseFilter;_x000D_
import nl.minvenj.nfi.prnu.filter.ImageFilter;_x000D_
import nl.minvenj.nfi.prnu.filter.WienerFilter;_x000D_
import nl.minvenj.nfi.prnu.filter.ZeroMeanTotalFilter;_x000D_
_x000D_
public final class PrnuExtract {_x000D_
    static final File TESTDATA_FOLDER = new File("testdata");_x000D_
_x000D_
    static final File INPUT_FOLDER = new File(TESTDATA_FOLDER, "input");_x000D_
//    public static final File INPUT_FILE = new File(INPUT_FOLDER, "test.jpg");_x000D_
    public static final File INPUT_FILE = new File("/var/scratch/bwn200/Dresden/2748x3664/Kodak_M1063_4_12664.JPG");_x000D_
    static final File EXPECTED_PATTERN_FILE = new File(INPUT_FOLDER, "expected.pat");_x000D_
_x000D_
    static final File OUTPUT_FOLDER = new File(TESTDATA_FOLDER, "output");_x000D_
    static final File OUTPUT_FILE = new File(OUTPUT_FOLDER, "test.pat");_x000D_
_x000D_
    public static void main(final String[] args) throws IOException {_x000D_
        long start = System.currentTimeMillis();_x000D_
    	long end = 0;_x000D_
    	_x000D_
    	// Laad de input file in_x000D_
        final BufferedImage image = readImage(INPUT_FILE);_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Load image: " + (end-start) + " ms.");_x000D_
_x000D_
        // Zet de input file om in 3 matrices (rood, groen, blauw)_x000D_
        start = System.currentTimeMillis();_x000D_
        final float[][][] rgbArrays = convertImageToFloatArrays(image);_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Convert image:" + (end-start) + " ms.");_x000D_
_x000D_
        // Bereken van elke matrix het PRNU patroon (extractie stap)_x000D_
        start = System.currentTimeMillis();_x000D_
        for (int i = 0; i < 3; i++) {_x000D_
            extractImage(rgbArrays[i]);_x000D_
        }_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("PRNU extracted: " + (end-start) + " ms.");_x000D_
_x000D_
        // Schrijf het patroon weg als een Java object_x000D_
        writeJavaObject(rgbArrays, OUTPUT_FILE);_x000D_
_x000D_
        System.out.println("Pattern written");_x000D_
_x000D_
        // Controleer nu het gemaakte bestand_x000D_
        final float[][][] expectedPattern = (float[][][]) readJavaObject(EXPECTED_PATTERN_FILE);_x000D_
        final float[][][] actualPattern = (float[][][]) readJavaObject(OUTPUT_FILE);_x000D_
        for (int i = 0; i < 3; i++) {_x000D_
            // Het patroon zoals dat uit PRNU Compare komt, bevat een extra matrix voor transparantie. Deze moeten we overslaan (+1)!_x000D_
            compare2DArray(expectedPattern[i + 1], actualPattern[i], 0.0001f);_x000D_
        }_x000D_
_x000D_
        System.out.println("Validation completed");_x000D_
        _x000D_
        //This exit is inserted because the program will otherwise hang for a about a minute_x000D_
        //most likely explanation for this is the fact that the FFT library spawns a couple_x000D_
        //of threads which cannot be properly destroyed_x000D_
        System.exit(0);_x000D_
    }_x000D_
_x000D_
    private static BufferedImage readImage(final File file) throws IOException {_x000D_
        final InputStream fileInputStream = new FileInputStream(file);_x000D_
        try {_x000D_
            final BufferedImage image = ImageIO.read(new BufferedInputStream(fileInputStream));_x000D_
            if ((image != null) && (image.getWidth() >= 0) && (image.getHeight() >= 0)) {_x000D_
                return image;_x000D_
            }_x000D_
        }_x000D_
        catch (final CMMException e) {_x000D_
            // Image file is unsupported or corrupt_x000D_
        }_x000D_
        catch (final RuntimeException e) {_x000D_
            // Internal error processing image file_x000D_
        }_x000D_
        catch (final IOException e) {_x000D_
            // Error reading image from disk_x000D_
        }_x000D_
        finally {_x000D_
            fileInputStream.close();_x000D_
        }_x000D_
_x000D_
        // Image unreadable or too smalld array_x000D_
        return null;_x000D_
    }_x000D_
_x000D_
    private static float[][][] convertImageToFloatArrays(final BufferedImage image) {_x000D_
        final int width = image.getWidth();_x000D_
        final int height = image.getHeight();_x000D_
        final float[][][] pixels = new float[3][height][width];_x000D_
_x000D_
        final ColorModel colorModel = ColorModel.getRGBdefault();_x000D_
        for (int y = 0; y < height; y++) {_x000D_
            for (int x = 0; x < width; x++) {_x000D_
                final int pixel = image.getRGB(x, y); // aa bb gg rr_x000D_
                pixels[0][y][x] = colorModel.getRed(pixel);_x000D_
                pixels[1][y][x] = colorModel.getGreen(pixel);_x000D_
                pixels[2][y][x] = colorModel.getBlue(pixel);_x000D_
            }_x000D_
        }_x000D_
        return pixels;_x000D_
    }_x000D_
_x000D_
    private static void extractImage(final float[][] pixels) {_x000D_
        final int width = pixels[0].length;_x000D_
        final int height = pixels.length;_x000D_
_x000D_
        long start = System.currentTimeMillis();_x000D_
        long end = 0;_x000D_
_x000D_
        final ImageFilter fastNoiseFilter = new FastNoiseFilter(width, height);_x000D_
        fastNoiseFilter.apply(pixels);_x000D_
_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Fast Noise Filter: " + (end-start) + " ms.");_x000D_
_x000D_
        start = System.currentTimeMillis();_x000D_
        final ImageFilter zeroMeanTotalFilter = new ZeroMeanTotalFilter(width, height);_x000D_
        zeroMeanTotalFilter.apply(pixels);_x000D_
_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Zero Mean Filter: " + (end-start) + " ms.");_x000D_
_x000D_
        start = System.currentTimeMillis();_x000D_
        final ImageFilter wienerFilter = new WienerFilter(width, height);_x000D_
        wienerFilter.apply(pixels);_x000D_
_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Wiener Filter: " + (end-start) + " ms.");_x000D_
    }_x000D_
_x000D_
    public static Object readJavaObject(final File inputFile) throws IOException {_x000D_
        final ObjectInputStream inputStream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(inputFile)));_x000D_
        try {_x000D_
            return inputStream.readObject();_x000D_
        }_x000D_
        catch (final ClassNotFoundException e) {_x000D_
            throw new IOException("Cannot read pattern: " + inputFile.getAbsolutePath(), e);_x000D_
        }_x000D_
        finally {_x000D_
            inputStream.close();_x000D_
        }_x000D_
    }_x000D_
_x000D_
    private static void writeJavaObject(final Object object, final File outputFile) throws IOException {_x000D_
        final OutputStream outputStream = new FileOutputStream(outputFile);_x000D_
        try {_x000D_
            final ObjectOutputStream objectOutputStream = new ObjectOutputStream(new BufferedOutputStream(outputStream));_x000D_
            objectOutputStream.writeObject(object);_x000D_
            objectOutputStream.close();_x000D_
        }_x000D_
        finally {_x000D_
            outputStream.close();_x000D_
        }_x000D_
    }_x000D_
_x000D_
    private static boolean compare2DArray(final float[][] expected, final float[][] actual, final float delta) {_x000D_
        for (int i = 0; i < expected.length; i++) {_x000D_
            for (int j = 0; j < expected[i].length; j++) {_x000D_
                if (Math.abs(actual[i][j] - expected[i][j]) > delta) {_x000D_
                    System.err.println("de waarde op " + i + "," + j + " is " + actual[i][j] + " maar had moeten zijn " + expected[i][j]);_x000D_
                    return false;_x000D_
                }_x000D_
            }_x000D_
        }_x000D_
        return true;_x000D_
    }_x000D_
_x000D_
}_x000D_

// Laad de input file in_x000D_
/*_x000D_
 * Copyright (c) 2014, Netherlands Forensic Institute_x000D_
 * All rights reserved._x000D_
 */_x000D_
package nl.minvenj.nfi.prnu;_x000D_
_x000D_
import java.awt.color.CMMException;_x000D_
import java.awt.image.BufferedImage;_x000D_
import java.awt.image.ColorModel;_x000D_
import java.io.BufferedInputStream;_x000D_
import java.io.BufferedOutputStream;_x000D_
import java.io.File;_x000D_
import java.io.FileInputStream;_x000D_
import java.io.FileOutputStream;_x000D_
import java.io.IOException;_x000D_
import java.io.InputStream;_x000D_
import java.io.ObjectInputStream;_x000D_
import java.io.ObjectOutputStream;_x000D_
import java.io.OutputStream;_x000D_
_x000D_
import javax.imageio.ImageIO;_x000D_
_x000D_
import nl.minvenj.nfi.prnu.filter.FastNoiseFilter;_x000D_
import nl.minvenj.nfi.prnu.filter.ImageFilter;_x000D_
import nl.minvenj.nfi.prnu.filter.WienerFilter;_x000D_
import nl.minvenj.nfi.prnu.filter.ZeroMeanTotalFilter;_x000D_
_x000D_
public final class PrnuExtract {_x000D_
    static final File TESTDATA_FOLDER = new File("testdata");_x000D_
_x000D_
    static final File INPUT_FOLDER = new File(TESTDATA_FOLDER, "input");_x000D_
//    public static final File INPUT_FILE = new File(INPUT_FOLDER, "test.jpg");_x000D_
    public static final File INPUT_FILE = new File("/var/scratch/bwn200/Dresden/2748x3664/Kodak_M1063_4_12664.JPG");_x000D_
    static final File EXPECTED_PATTERN_FILE = new File(INPUT_FOLDER, "expected.pat");_x000D_
_x000D_
    static final File OUTPUT_FOLDER = new File(TESTDATA_FOLDER, "output");_x000D_
    static final File OUTPUT_FILE = new File(OUTPUT_FOLDER, "test.pat");_x000D_
_x000D_
    public static void main(final String[] args) throws IOException {_x000D_
        long start = System.currentTimeMillis();_x000D_
    	long end = 0;_x000D_
    	_x000D_
    	// Laad de<SUF>
        final BufferedImage image = readImage(INPUT_FILE);_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Load image: " + (end-start) + " ms.");_x000D_
_x000D_
        // Zet de input file om in 3 matrices (rood, groen, blauw)_x000D_
        start = System.currentTimeMillis();_x000D_
        final float[][][] rgbArrays = convertImageToFloatArrays(image);_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Convert image:" + (end-start) + " ms.");_x000D_
_x000D_
        // Bereken van elke matrix het PRNU patroon (extractie stap)_x000D_
        start = System.currentTimeMillis();_x000D_
        for (int i = 0; i < 3; i++) {_x000D_
            extractImage(rgbArrays[i]);_x000D_
        }_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("PRNU extracted: " + (end-start) + " ms.");_x000D_
_x000D_
        // Schrijf het patroon weg als een Java object_x000D_
        writeJavaObject(rgbArrays, OUTPUT_FILE);_x000D_
_x000D_
        System.out.println("Pattern written");_x000D_
_x000D_
        // Controleer nu het gemaakte bestand_x000D_
        final float[][][] expectedPattern = (float[][][]) readJavaObject(EXPECTED_PATTERN_FILE);_x000D_
        final float[][][] actualPattern = (float[][][]) readJavaObject(OUTPUT_FILE);_x000D_
        for (int i = 0; i < 3; i++) {_x000D_
            // Het patroon zoals dat uit PRNU Compare komt, bevat een extra matrix voor transparantie. Deze moeten we overslaan (+1)!_x000D_
            compare2DArray(expectedPattern[i + 1], actualPattern[i], 0.0001f);_x000D_
        }_x000D_
_x000D_
        System.out.println("Validation completed");_x000D_
        _x000D_
        //This exit is inserted because the program will otherwise hang for a about a minute_x000D_
        //most likely explanation for this is the fact that the FFT library spawns a couple_x000D_
        //of threads which cannot be properly destroyed_x000D_
        System.exit(0);_x000D_
    }_x000D_
_x000D_
    private static BufferedImage readImage(final File file) throws IOException {_x000D_
        final InputStream fileInputStream = new FileInputStream(file);_x000D_
        try {_x000D_
            final BufferedImage image = ImageIO.read(new BufferedInputStream(fileInputStream));_x000D_
            if ((image != null) && (image.getWidth() >= 0) && (image.getHeight() >= 0)) {_x000D_
                return image;_x000D_
            }_x000D_
        }_x000D_
        catch (final CMMException e) {_x000D_
            // Image file is unsupported or corrupt_x000D_
        }_x000D_
        catch (final RuntimeException e) {_x000D_
            // Internal error processing image file_x000D_
        }_x000D_
        catch (final IOException e) {_x000D_
            // Error reading image from disk_x000D_
        }_x000D_
        finally {_x000D_
            fileInputStream.close();_x000D_
        }_x000D_
_x000D_
        // Image unreadable or too smalld array_x000D_
        return null;_x000D_
    }_x000D_
_x000D_
    private static float[][][] convertImageToFloatArrays(final BufferedImage image) {_x000D_
        final int width = image.getWidth();_x000D_
        final int height = image.getHeight();_x000D_
        final float[][][] pixels = new float[3][height][width];_x000D_
_x000D_
        final ColorModel colorModel = ColorModel.getRGBdefault();_x000D_
        for (int y = 0; y < height; y++) {_x000D_
            for (int x = 0; x < width; x++) {_x000D_
                final int pixel = image.getRGB(x, y); // aa bb gg rr_x000D_
                pixels[0][y][x] = colorModel.getRed(pixel);_x000D_
                pixels[1][y][x] = colorModel.getGreen(pixel);_x000D_
                pixels[2][y][x] = colorModel.getBlue(pixel);_x000D_
            }_x000D_
        }_x000D_
        return pixels;_x000D_
    }_x000D_
_x000D_
    private static void extractImage(final float[][] pixels) {_x000D_
        final int width = pixels[0].length;_x000D_
        final int height = pixels.length;_x000D_
_x000D_
        long start = System.currentTimeMillis();_x000D_
        long end = 0;_x000D_
_x000D_
        final ImageFilter fastNoiseFilter = new FastNoiseFilter(width, height);_x000D_
        fastNoiseFilter.apply(pixels);_x000D_
_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Fast Noise Filter: " + (end-start) + " ms.");_x000D_
_x000D_
        start = System.currentTimeMillis();_x000D_
        final ImageFilter zeroMeanTotalFilter = new ZeroMeanTotalFilter(width, height);_x000D_
        zeroMeanTotalFilter.apply(pixels);_x000D_
_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Zero Mean Filter: " + (end-start) + " ms.");_x000D_
_x000D_
        start = System.currentTimeMillis();_x000D_
        final ImageFilter wienerFilter = new WienerFilter(width, height);_x000D_
        wienerFilter.apply(pixels);_x000D_
_x000D_
        end = System.currentTimeMillis();_x000D_
        System.out.println("Wiener Filter: " + (end-start) + " ms.");_x000D_
    }_x000D_
_x000D_
    public static Object readJavaObject(final File inputFile) throws IOException {_x000D_
        final ObjectInputStream inputStream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(inputFile)));_x000D_
        try {_x000D_
            return inputStream.readObject();_x000D_
        }_x000D_
        catch (final ClassNotFoundException e) {_x000D_
            throw new IOException("Cannot read pattern: " + inputFile.getAbsolutePath(), e);_x000D_
        }_x000D_
        finally {_x000D_
            inputStream.close();_x000D_
        }_x000D_
    }_x000D_
_x000D_
    private static void writeJavaObject(final Object object, final File outputFile) throws IOException {_x000D_
        final OutputStream outputStream = new FileOutputStream(outputFile);_x000D_
        try {_x000D_
            final ObjectOutputStream objectOutputStream = new ObjectOutputStream(new BufferedOutputStream(outputStream));_x000D_
            objectOutputStream.writeObject(object);_x000D_
            objectOutputStream.close();_x000D_
        }_x000D_
        finally {_x000D_
            outputStream.close();_x000D_
        }_x000D_
    }_x000D_
_x000D_
    private static boolean compare2DArray(final float[][] expected, final float[][] actual, final float delta) {_x000D_
        for (int i = 0; i < expected.length; i++) {_x000D_
            for (int j = 0; j < expected[i].length; j++) {_x000D_
                if (Math.abs(actual[i][j] - expected[i][j]) > delta) {_x000D_
                    System.err.println("de waarde op " + i + "," + j + " is " + actual[i][j] + " maar had moeten zijn " + expected[i][j]);_x000D_
                    return false;_x000D_
                }_x000D_
            }_x000D_
        }_x000D_
        return true;_x000D_
    }_x000D_
_x000D_
}_x000D_
