com.jiminger.image.ImageFile.java Source code

Java tutorial

Introduction

Here is the source code for com.jiminger.image.ImageFile.java

Source

/***********************************************************************
 * Legacy Film to DVD Project
 * Copyright (C) 2005 James F. Carroll
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 ****************************************************************************/

package com.jiminger.image;

import static org.opencv.imgcodecs.Imgcodecs.IMREAD_UNCHANGED;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jiminger.image.CvRaster.Closer;

public class ImageFile {
    private static final Logger LOGGER = LoggerFactory.getLogger(ImageFile.class);

    static {
        CvRaster.initOpenCv();
    }

    public static BufferedImage readBufferedImageFromFile(final String filename) throws IOException {
        LOGGER.trace("Reading image from {}", filename);
        final File f = new File(filename);
        if (!f.exists())
            throw new FileNotFoundException(filename);
        BufferedImage ret = ImageIO.read(f);
        if (ret == null) {
            LOGGER.info("Failed to read '{}' using ImageIO", filename);
            try (Closer closer = new Closer()) {
                final Mat mat = Imgcodecs.imread(filename, IMREAD_UNCHANGED);
                if (mat == null)
                    throw new IllegalArgumentException("Can't read '" + filename
                            + "' as an image. No codec available in either ImageIO or OpenCv");
                if (filename.endsWith(".jp2") && CvType.channels(mat.channels()) > 1)
                    Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2BGR);
                ret = Utils.mat2Img(mat);
            }
        }
        LOGGER.trace("Read {} from {}", ret, filename);
        return ret;
    }

    public static CvRaster readMatFromFile(final String filename) throws IOException {
        return readMatFromFile(filename, null);
    }

    public static CvRaster readMatFromFile(final String filename, final Closer closer) throws IOException {
        LOGGER.trace("Reading image from {}", filename);
        final File f = new File(filename);
        if (!f.exists())
            throw new FileNotFoundException(filename);

        final CvRaster ret;

        try (Closer cx = new Closer()) {
            final Mat mat = Imgcodecs.imread(filename, IMREAD_UNCHANGED);
            if (mat == null) {
                LOGGER.debug("Failed to read '" + filename + "' using OpenCV");
                ret = Utils.img2CvRaster(ImageIO.read(f));
            } else {
                if (filename.endsWith(".jp2") && CvType.channels(mat.channels()) > 1)
                    Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2BGR);
                ret = CvRaster.move(mat, closer);
            }
        }
        LOGGER.trace("Read {} from {}", ret, filename);
        return ret;
    }

    public static BufferedImage convert(final Image im, final int type) {
        if (im instanceof BufferedImage)
            return (BufferedImage) im;
        final BufferedImage bi = new BufferedImage(im.getWidth(null), im.getHeight(null), type);
        final Graphics bg = bi.getGraphics();
        bg.drawImage(im, 0, 0, null);
        bg.dispose();
        return bi;
    }

    public static void writeImageFile(final BufferedImage ri, final String filename) throws IOException {
        if (!doWrite(ri, filename)) {
            LOGGER.debug("Failed to write '" + filename + "' using ImageIO");
            if (!doWrite(Utils.img2CvRaster(ri), filename))
                throw new IllegalArgumentException("Failed to write");
        }
    }

    public static void writeImageFile(final CvRaster ri, final String filename) throws IOException {
        if (!doWrite(ri, filename)) {
            LOGGER.debug("Failed to write '" + filename + "' using OpenCV");
            final BufferedImage bi = ri.matOp(m -> Utils.mat2Img(m));
            if (!doWrite(bi, filename))
                throw new IllegalArgumentException("Failed to write");
        }
    }

    public static void writeImageFile(final Mat ri, final String filename) throws IOException {
        try (CvRaster raster = CvRaster.manageShallowCopy(ri)) {
            writeImageFile(raster, filename);
        }
    }

    private static boolean doWrite(final BufferedImage ri, final String filename) throws IOException {
        LOGGER.trace("Writing image {} to {}", ri, filename);
        final int dotindex = filename.lastIndexOf(".");
        if (dotindex < 0)
            throw new IOException("No extention on " + filename);
        final String ext = filename.substring(dotindex + 1);

        final File f = new File(filename).getCanonicalFile();
        final File p = f.getParentFile();
        // make sure the output directory exists.
        p.mkdirs();
        final Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix(ext);
        boolean wrote = false;
        while (iter.hasNext()) {
            final ImageWriter writer = iter.next(); // grab the first one
            try (final ImageOutputStream ios = ImageIO.createImageOutputStream(f);) {
                final ImageWriteParam param = writer.getDefaultWriteParam();

                writer.setOutput(ios);

                writer.write(null, new IIOImage(ri, null, null), param);
            }
            wrote = true;
        }
        return wrote;
    }

    private static boolean doWrite(final CvRaster ri, final String filename) throws IOException {
        LOGGER.trace("Writing image {} to {}", ri, filename);
        return ri.matOp(m -> (Boolean) Imgcodecs.imwrite(filename, m));
    }

    private static double scale(final int width, final int height, final ImageDestinationDefinition dest) {
        double scale = -1.0;
        if (dest.maxh != -1) {
            if (height > dest.maxh)
                // see what we need to scale to make the height the same.
                scale = ((double) dest.maxh) / ((double) height);
        }

        if (dest.maxw != -1) {
            final int adjwidth = (scale >= 0.0) ? (int) Math.round(scale * width) : width;
            if (adjwidth > dest.maxw) {
                scale = ((double) dest.maxw) / ((double) adjwidth);
            }
        }

        if (dest.maxe != -1) {
            final int adjedge = width > height ? (scale >= 0.0 ? (int) Math.round(scale * width) : width)
                    : (scale >= 0.0 ? (int) Math.round(scale * height) : height);
            if (adjedge > dest.maxe) {
                scale = ((double) (dest.maxe)) / ((double) adjedge);
            }
        }
        return scale;
    }

    public static void transcode(BufferedImage bi, final ImageDestinationDefinition dest) throws IOException {
        if (infile != null && infile.equalsIgnoreCase(dest.outfile))
            throw new IOException("Can't overwrite original file durring transcode (" + infile + ").");

        if (dest.maxw != -1 || dest.maxh != -1 || dest.maxe != -1) {
            final int width = bi.getWidth();
            final int height = bi.getHeight();

            final double scale = scale(width, height, dest);

            if (scale >= 0.0) {

                final int newwidth = (int) Math.round(scale * (width));
                final int newheight = (int) Math.round(scale * (height));

                bi = convert(bi.getScaledInstance(newwidth, newheight, BufferedImage.SCALE_DEFAULT), bi.getType());
            }
        }

        writeImageFile(bi, dest.outfile);
    }

    // public static void transcode(final CvRaster bi, final ImageDestinationDefinition dest) throws IOException {
    // if (infile != null && infile.equalsIgnoreCase(dest.outfile))
    // throw new IOException("Can't overwrite original file durring transcode (" + infile + ").");
    //
    // if (dest.maxw != -1 || dest.maxh != -1 || dest.maxe != -1) {
    // final int width = bi.cols;
    // final int height = bi.rows;
    //
    // double scale = -1.0;
    // if (dest.maxh != -1) {
    // if (height > dest.maxh)
    // // see what we need to scale to make the height the same.
    // scale = ((double) dest.maxh) / ((double) height);
    // }
    //
    // if (dest.maxw != -1) {
    // final int adjwidth = (scale >= 0.0) ? (int) Math.round(scale * width) : width;
    // if (adjwidth > dest.maxw) {
    // scale = ((double) dest.maxw) / ((double) adjwidth);
    // }
    // }
    //
    // if (dest.maxe != -1) {
    // final int adjedge = width > height ? (scale >= 0.0 ? (int) Math.round(scale * width) : width)
    // : (scale >= 0.0 ? (int) Math.round(scale * height) : height);
    // if (adjedge > dest.maxe) {
    // scale = ((double) (dest.maxe)) / ((double) adjedge);
    // }
    // }
    //
    // if (scale >= 0.0) {
    // final int newwidth = (int) Math.round(scale * (width));
    // final int newheight = (int) Math.round(scale * (height));
    //
    // Imgproc.resize(bi.mat, bi.mat, new Size(newwidth, newheight), 0, 0, Imgproc.INTER_LINEAR);
    // }
    // }
    //
    // writeImageFile(bi, dest.outfile);
    // }

    public static class ImageDestinationDefinition {
        public String outfile = null;
        public int maxw = -1;
        public int maxh = -1;
        public int maxe = -1;
        public boolean verify = false;

        public void set() {
        }
    }

    public static String infile = null;

    public static void main(final String[] args) throws IOException {
        final List<ImageDestinationDefinition> dests = commandLine(args);
        if (dests == null || dests.size() == 0) {
            usage();
            return;
        }

        if (infile == null) {
            usage();
            return;
        }

        final BufferedImage image = readBufferedImageFromFile(infile);
        // final CvRaster raster = CvRaster.create(image);
        // final short[] data = (short[]) raster.data;
        // final int width = raster.cols;
        // final short[] pixel = new short[1];
        // raster.apply((ShortPixelSetter) (r, c) -> {
        // final short pix = data[r * width + c];
        // final int pixu = Short.toUnsignedInt(pix);
        // pixel[0] = (short) (((pixu & 0xff00) >>> 8) | ((pixu & 0xff) << 8));
        // return pixel;
        // });

        for (final ImageDestinationDefinition dest : dests) {
            transcode(image, dest);

            if (dest.verify) {
                final RenderedImage im = readBufferedImageFromFile(dest.outfile);
                final int width2 = im.getWidth();
                final int height2 = im.getHeight();

                if (dest.maxw != width2 || dest.maxh != height2
                        || dest.maxe != ((width2 > height2) ? width2 : height2))
                    throw new IOException("Verification failed!");
            }
        }
    }

    static private List<ImageDestinationDefinition> commandLine(final String[] args) {
        final List<ImageDestinationDefinition> ret = new ArrayList<ImageDestinationDefinition>();
        ImageDestinationDefinition cur = null;

        for (int i = 0; i < args.length; i++) {
            final String optionArg = args[i];
            // see if we are asking for help
            if ("help".equalsIgnoreCase(optionArg) || "-help".equalsIgnoreCase(optionArg)) {
                usage();
                return null;
            }

            if ("-i".equalsIgnoreCase(optionArg)) {
                if (infile != null) {
                    System.err.println("One infile only");
                    usage();
                    return null;
                }
                infile = args[i + 1];
                i++;
            }

            else if ("-o".equalsIgnoreCase(args[i])) {
                cur = cur == null ? new ImageDestinationDefinition() : cur;
                if (cur.outfile != null)
                    cur = push(cur, ret);
                cur.outfile = args[i + 1];
                i++;
            } else if ("-verify".equalsIgnoreCase(args[i])) {
                cur = cur == null ? new ImageDestinationDefinition() : cur;
                if (cur.verify == false)
                    cur = push(cur, ret);
                cur.verify = true;
            } else if ("-maxw".equalsIgnoreCase(args[i])) {
                cur = cur == null ? new ImageDestinationDefinition() : cur;
                if (cur.maxw != -1)
                    cur = push(cur, ret);
                cur.maxw = Integer.parseInt(args[i + 1]);
                i++;
            } else if ("-maxh".equalsIgnoreCase(args[i])) {
                cur = cur == null ? new ImageDestinationDefinition() : cur;
                if (cur.maxh != -1)
                    cur = push(cur, ret);
                cur.maxh = Integer.parseInt(args[i + 1]);
                i++;
            } else if ("-maxe".equalsIgnoreCase(args[i])) {
                cur = cur == null ? new ImageDestinationDefinition() : cur;
                if (cur.maxe != -1)
                    cur = push(cur, ret);
                cur.maxe = Integer.parseInt(args[i + 1]);
                i++;
            } else {
                usage();
                return null;
            }
        }

        if (cur != null) {
            cur.set();
            ret.add(cur);
        }

        return ret;
    }

    static private ImageDestinationDefinition push(final ImageDestinationDefinition cur,
            final List<ImageDestinationDefinition> ret) {
        ret.add(cur);
        cur.set();
        return new ImageDestinationDefinition();
    }

    static private void usage() {
        System.out.println(
                "usage: java [javaargs] ImageFile -i infile -o outfile [-maxw width] [-maxh height] [-maxe maxEdge] [-verify]");
        System.out.println("       options -o through -verify can be repeated to convert an image file");
        System.out.println("       to a number of different formats and dimentions");
    }
}