org.uva.itast.blended.omr.pages.PageImage.java Source code

Java tutorial

Introduction

Here is the source code for org.uva.itast.blended.omr.pages.PageImage.java

Source

/**
* Note: Original work copyright to respective authors
*
* This file is part of Blended (c) 2009-2010 University of Valladolid..
*
* Blended 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.
*
* Blended 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.
*
*
* Module developed at the University of Valladolid http://www.eduvalab.uva.es
*
* http://www.itnt.uva.es , http://www.eduvalab.uva.es
*
* Designed and directed by Juan Pablo de Castro with 
* the effort of many other students of telecommunication 
* engineering.
* This module is provides as-is without any 
* guarantee. Use it as your own risk.
*
* @author Juan Pablo de Castro
* @author Jesus Rodilana
* @author Mara Jess Verd 
* @author Luisa Regueras 
* @author Elena Verd
* 
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package blended
 ***********************************************************************/

package org.uva.itast.blended.omr.pages;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;

import javax.imageio.ImageIO;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.uva.itast.blended.omr.BufferedImageUtil;
import org.uva.itast.blended.omr.scanners.Size;

public abstract class PageImage {
    /**
     * Logger for this class
     */
    static final Log logger = LogFactory.getLog(PageImage.class);
    private static final int REPORTING_WIDTH = 1500;
    public static double a4width = 210; // mm
    public static double a4height = 297; // mm
    private BufferedImage image;
    private BufferedImage reportImage;
    /**
     * Sets the transformation data needed for aligning the Page
     * contains information about the traslation and rotation of the page in pixels units.
     * Need to consider image scale to convert from milimeters
     *
     **/
    private AffineTransform alignmentTransform;

    /**
     * Calculated resolution
     */
    double vertResolution = Double.NaN;
    double horizResolution = Double.NaN;

    /**
     * @return the scanned image
     */
    public BufferedImage getImage() {
        if (image == null) {
            setImage(createImage());
        }
        return image;
    }

    /**
     * @return
     */
    abstract BufferedImage createImage();

    /**
     * stores the {@link BufferedImage} of the page. It can be in portrait or in landscape proportions (90 degrees clockwise rotation).
     * @param imagen the imagen to set
     * The image is handled in portrait format.
     * If the Image has landscape proportions it is assumed to be clockwise rotated and this method rotates it.
     * 
     */
    protected void setImage(BufferedImage imagen) {
        //
        if (imagen != null && imagen.getHeight() < imagen.getWidth()) {
            logger.debug("Rotating image. Original size was:" + imagen.getWidth() + "x" + imagen.getWidth()
                    + " pixeles.");
            this.image = BufferedImageUtil.rotateImage(imagen, -90);
        } else {
            this.image = imagen;
        }
        // configure alignment information
        resetAlignmentInfo();
    }

    /**
     * @param data.getImage()
     */
    public void resetAlignmentInfo() {
        AffineTransform tr = new AffineTransform();

        double horizRatio = getHorizontalResolution();
        double vertRatio = getVerticalResolution();

        // Do not assume square pixels
        tr.setToScale(horizRatio, vertRatio);
        setAlignmentInfo(tr);
    }

    /**
     * Sets the transformation data needed for aligning the Page
     * contains information about the traslation and rotation of the page in pixels units.
     * Need to consider image scale to convert from milimeters
     * @param transform
     * @see AffineTransform
     * @see getVerticalRatio
     * @see getHorizontalRatio
     */
    public void setAlignmentInfo(AffineTransform transform) {
        this.alignmentTransform = transform;
    }

    public AffineTransform getAllignmentInfo() {
        if (this.alignmentTransform == null)
            resetAlignmentInfo();
        return this.alignmentTransform;
    }

    /**
     * Try to detect alignment marks and create the spatial transformation info
     * @see java.awt.geom.AffineTransform 
     */
    public void alignImage() {
        long funcStart = System.currentTimeMillis();

        AffineTransform transform = getAllignmentInfo();

        // obtain rotation and traslation
        transform.translate(0, 0); //pixels

        setAlignmentInfo(transform);

        logger.debug("Page aligned in (ms)" + (System.currentTimeMillis() - funcStart)); //$NON-NLS-1$

    }

    /**
     * Mark the results in a thumbnail of the page intended for reporting
     */
    public void labelPageAsProcessed() {

        BufferedImage imagen = getReportingImage();
        Graphics2D g = imagen.createGraphics();
        g.setColor(Color.RED);
        g.drawString("Page Processed at:" + new Date() + " W:" + imagen.getWidth() + " H:" + imagen.getHeight(), 10,
                10);

    }

    /**
     * Creates an small-resolution image for reporting purposes
     * @return
     */
    public BufferedImage getReportingImage() {
        if (this.reportImage == null) {
            // Create a fixed (smaller) resolution image
            int w = Math.min(REPORTING_WIDTH, getImage().getWidth());
            //int w=getImage().getWidth(); // scale 1
            int h = getImage().getHeight() * w / getImage().getWidth();

            this.reportImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            Image scaledImage = getImage().getScaledInstance(w, h, Image.SCALE_DEFAULT);
            this.reportImage.createGraphics().drawImage(scaledImage, 0, 0, null);
        }

        return this.reportImage;
    }

    /**
     * @return
     */
    public abstract String getType();

    /**
     * @return
     */
    public abstract String getName();

    /**
     * Free up memory resources
     */
    public void freeMemory() {
        long tstart = System.currentTimeMillis();
        long freeMem = Runtime.getRuntime().freeMemory();
        long availMem = Runtime.getRuntime().totalMemory();
        this.reportImage.flush();
        this.reportImage = null;

        this.image.flush();
        setImage(null);
        System.gc();

        if (logger.isDebugEnabled()) {
            logger.debug("endUse()-Free Memory in " + (System.currentTimeMillis() - tstart) + " ms TotalMem:" //$NON-NLS-1$//$NON-NLS-2$
                    + availMem / 1024 / 1024 + " MB freeMem Before:" + freeMem / 1024 / 1024 + ", freeMem After:"
                    + Runtime.getRuntime().freeMemory() / 1024 / 1024);
        }
    }

    /**
     * @param outputdir
     * @throws IOException 
     */
    public File outputMarkedPage(String outputdir) throws IOException {
        File debugImagePath;

        debugImagePath = File.createTempFile("OMR_original_marked", ".jpg", new File(outputdir));
        ImageIO.write(getReportingImage(), "JPG", debugImagePath);
        return debugImagePath;
    }

    /**
     * @param outputdir
     * @throws IOException 
     */
    public void outputWorkingPage(String outputdir) throws IOException {
        File debugImagePath;

        debugImagePath = File.createTempFile("OMR_working_debug", ".jpg", new File(outputdir));
        ImageIO.write(getImage(), "JPG", debugImagePath);

    }

    /**
     * Returns the ratio between pixels and milimeters
     * default implementation getImagen().getWidth()/PageImage.a4width 
     * @see #getPreferredHorizontalResolution()
     * @return resolution in pixels/mm
     */
    public double getHorizontalResolution() {
        if (!Double.isNaN(horizResolution))
            return horizResolution;
        else
            return getPreferredHorizontalResolution();
    }

    protected abstract double getPreferredHorizontalResolution();

    /**
     * Returns the ratio between pixels and milimeters
     * default implementation getImagen().getHeight()/PageImage.a4height
     * @see #getPreferredVerticalResolution()
     * @return resolution in pixels/mm
     */
    public double getVerticalResolution() {
        if (!Double.isNaN(vertResolution))
            return vertResolution;
        else
            return getPreferredVerticalResolution();
    }

    protected abstract double getPreferredVerticalResolution();

    /**
     * Sets a new vertical resolution if calculated
     * @param res resolution in pixels/mm
     */
    public void setVerticalResolution(double res) {
        this.vertResolution = res;
        resetAlignmentInfo();
    };

    /**
     * Sets a new vertical resolution if calculated
     * @param res resolution in pixels/mm
     */
    public void setHorizontalResolution(double res) {
        this.horizResolution = res;
        resetAlignmentInfo();
    };

    /**
     * Use alignment information to transform from milimeters to pixel coordinates at the preferred resolution for this page
     * @param x
     * @return
     */
    public Point toPixels(double x, double y) {
        AffineTransform alignment = getAllignmentInfo();
        Point2D coord = new Point2D.Double(x, y);
        Point coordTransformed = new Point();
        alignment.transform(coord, coordTransformed);

        return coordTransformed;
    }

    public Point toPixelsPoint(double x, double y) {
        AffineTransform alignment = getAllignmentInfo();
        alignment.setToScale(getPreferredHorizontalResolution(), getPreferredVerticalResolution());

        Point2D coord = new Point2D.Double(x, y);
        Point coordTransformed = new Point();
        alignment.transform(coord, coordTransformed);

        return coordTransformed;
    }

    /**
     * Obtain a subimage from the pageimage (in milimeters and related to actual paper)
     * Place to make optimizations when rendering high resolution files.
     * It takes into account the traslation and rotation of the physical page.
     * 
     * Default implementation uses getImage() which should decode entire image.
     * @param x mm
     * @param y mm
     * @param w mm
     * @param h mm
     * @param imageType
     * @return SubImage
     * @see SubImage
     */
    public SubImage getSubimage(double x, double y, double w, double h, int imageType) {
        Rectangle2D rectMM = new Rectangle2D.Double();
        rectMM.setFrame(x, y, w, h);
        return getSubimage(rectMM, imageType);
    }

    /**
     * Convert from  pixel-space to paper-space.
     * Paper-space refers to logical area of the paper in the image.
     * Pixel-space refers to entire area of the image that contains the image of the paper. (Maybe with offset and rotation)
     * @param i
     * @param j
     * @return
     * @throws NoninvertibleTransformException 
     */
    public Point2D toMilimeters(int i, int j) {

        try {
            AffineTransform tr = getAllignmentInfo();
            AffineTransform inv;
            inv = tr.createInverse();
            Point2D pixeles = new Point(i, j);
            Point2D dest = new Point2D.Double();
            return inv.transform(pixeles, dest);
        } catch (NoninvertibleTransformException e) {
            throw new RuntimeException("error page definition.", e);
        }

    }

    /**
     * Convert box from Milimeters to Pixels relative to the PageImage in the preferred resolution
     * in case of alignment rotation the returned Rectangle is the minimum bounding box that contains
     * the original corners transformed.
     * @param box in milimeters related to actual page
     * @return minimum bounding box in pixels related to image representation
     */
    public Rectangle toPixels(Rectangle2D box) {

        Point p1 = toPixels(box.getX(), box.getY());
        Point p2 = toPixels(box.getMaxX(), box.getMaxY());
        Rectangle bboxPx = new Rectangle(p1);
        bboxPx.add(p2);
        return bboxPx;

    }

    /**
     * Scaled graphics for drawing on a small version of the page.
     * Transform is set for drawing in pixels refered to the original scanned image
     * @return
     */
    public Graphics2D getReportingGraphics() {
        // TODO: Crear una transformacion equivalente a la de la pgina para la imagen reducida
        BufferedImage reportingImage = this.getReportingImage();
        Graphics2D g = reportingImage.createGraphics();

        //      AffineTransform origTransf=getAllignmentInfo();
        //
        //      AffineTransform trans=(AffineTransform) origTransf.clone();
        //      trans.scale(reportingImage.getWidth()/(getImage().getWidth()),
        //         reportingImage.getHeight()/(getImage().getHeight()));
        //      AffineTransform trans=g.getTransform();
        //      trans.scale(reportingImage.getWidth()/(getPreferredHorizontalResolution()*PageImage.a4width),
        //            reportingImage.getHeight()/(getPreferredVerticalResolution()*PageImage.a4height));
        double scaleX = ((double) reportingImage.getWidth()) / (getImage().getWidth());
        double scaleY = ((double) reportingImage.getHeight()) / (getImage().getHeight());
        AffineTransform trans = g.getTransform();
        trans.scale(scaleX, scaleY);
        g.setTransform(trans);
        return g;
    }

    /**
     * @param rectMM bounding box in milimeters
     * @see #getSubimage(double, double, double, double, int)
     * @return
     */
    public SubImage getSubimage(Rectangle2D rectMM, int imageType) {

        Rectangle rect = this.toPixels(rectMM);
        if (logger.isDebugEnabled()) {
            logger.debug("Subimage " + rectMM + " mm " + rect + " px.");
        }
        PagePoint upperLeft = new PagePoint(this, rectMM.getX(), rectMM.getY());

        Point reference = upperLeft.getPixelsPoint();

        BufferedImage originalImage = getImage();
        Rectangle originalRect = new Rectangle(originalImage.getWidth(), originalImage.getHeight());
        // copy what is available from image
        Rectangle available = rect.intersection(originalRect);

        BufferedImage originalSubImage = originalImage.getSubimage(available.x, available.y, available.width,
                available.height);

        //rotate image
        SubImage subimage = new SubImage(rect.width, rect.height, imageType);
        subimage.setReference(reference);
        subimage.setBoundingBox(rect);
        subimage.setCapturedBoundingBox(available);

        Graphics2D g = subimage.createGraphics();
        g.drawImage(originalSubImage, available.x - rect.x, available.y - rect.y, null);

        return subimage;
    }

    /**
     * @param pointMM
     * @return
     */
    public Point toPixels(Point2D pointMM) {
        return toPixels(pointMM.getX(), pointMM.getY());
    }

    /**
     * @param templateRectPx
     * @return
     */
    public Rectangle2D toMilimeters(Rectangle boxPx) {
        Point2D p1 = toMilimeters(boxPx.x, boxPx.y);
        Point2D p2 = toMilimeters((int) boxPx.getMaxX(), (int) boxPx.getMaxY());
        Rectangle2D bbox = new Rectangle();
        bbox.setFrameFromDiagonal(p1, p2);

        return bbox;
    }

    public Dimension2D sizeInPixels(Size size) {
        Dimension2D dim = new Dimension();
        dim.setSize(getAllignmentInfo().getScaleX() * size.getWidth(),
                getAllignmentInfo().getScaleY() * size.getHeight());
        return dim;
    }

    public Dimension2D sizeToMilimeters(Size size) {
        Dimension2D dim = new Dimension();
        dim.setSize(size.getWidth() / getAllignmentInfo().getScaleX(),
                size.getHeight() / getAllignmentInfo().getScaleY());
        return dim;
    }

}