/* * Copyright (c) 2010 Brasiliana Digital Library (http://brasiliana.usp.br). * Based on similar source code from Adore Djatoka. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ package gov.lanl.adore.djatoka.plugin; import javax.imageio.ImageIO; import gov.lanl.adore.djatoka.DjatokaDecodeParam; import gov.lanl.adore.djatoka.DjatokaException; import gov.lanl.adore.djatoka.IExtract; import gov.lanl.adore.djatoka.util.ImageProcessingUtils; import gov.lanl.adore.djatoka.util.ImageRecord; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.StringTokenizer; import java.util.logging.Level; import org.apache.log4j.Logger; /** * Uses JAI library to extract JPEG regions. * @author Fabio N. Kepler * */ public class ExtractJPG implements IExtract { private static Logger logger = Logger.getLogger(ExtractJPG.class); /** * Returns JPEG props in ImageRecord * @param r ImageRecord containing absolute file path of JPEG image file. * @return a populated ImageRecord object * @throws DjatokaException */ @Override public final ImageRecord getMetadata(ImageRecord r) throws DjatokaException { if ((r.getImageFile() == null || !new File(r.getImageFile()).exists()) && r.getObject() == null) throw new DjatokaException("Image Does Not Exist: " + r.toString()); try { /* JPEGImageDecoder decoder = null; if (r.getImageFile() != null && new File(r.getImageFile()).exists()) { decoder = JPEGCodec.createJPEGDecoder(new FileInputStream(r.getImageFile())); } else { decoder = JPEGCodec.createJPEGDecoder((InputStream) r.getObject()); } BufferedImage bi = decoder.decodeAsBufferedImage(); */ BufferedImage bi = null; if (r.getImageFile() != null && new File(r.getImageFile()).exists()) { bi = ImageIO.read(new File(r.getImageFile())); } else { bi = ImageIO.read((InputStream) r.getObject()); } if (bi == null) return null; r.setWidth(bi.getWidth()); r.setHeight(bi.getHeight()); int minLevels = 3; //FIXME int djatokaLevels = ImageProcessingUtils.getLevelCount(r.getWidth(), r.getHeight(), 128); r.setDWTLevels((djatokaLevels < minLevels) ? minLevels : djatokaLevels); r.setLevels((djatokaLevels < minLevels) ? minLevels : djatokaLevels); r.setBitDepth(bi.getColorModel().getPixelSize()); r.setNumChannels(bi.getColorModel().getNumColorComponents()); // r.setCompositingLayerCount(frames[0]); } catch (Exception e) { throw new DjatokaException(e); } return r; } public final ImageRecord getMetadata(BufferedImage bi) throws DjatokaException { if (bi == null) throw new DjatokaException("Image Does Not Exist"); try { ImageRecord r = new ImageRecord(); r.setWidth(bi.getWidth()); r.setHeight(bi.getHeight()); int minLevels = 5; r.setDWTLevels(minLevels); //FIXME int djatokaLevels = ImageProcessingUtils.getLevelCount(r.getWidth(), r.getHeight()); r.setLevels((djatokaLevels > minLevels) ? minLevels : djatokaLevels); r.setBitDepth(bi.getColorModel().getPixelSize()); r.setNumChannels(bi.getColorModel().getNumColorComponents()); // r.setCompositingLayerCount(frames[0]); return r; } catch (Exception e) { throw new DjatokaException(e); } } @Override // TODO // FIXME public final String[] getXMLBox(ImageRecord r) throws DjatokaException { String[] xml = null; try { if (r.getImageFile() == null && r.getObject() != null && r.getObject() instanceof InputStream) { // xml = new JP2ImageInfo((InputStream) r.getObject()).getXmlDocs(); } else { // xml = new JP2ImageInfo(new File(r.getImageFile())).getXmlDocs(); } } catch (Exception e) { logger.error(e, e); } return xml; } /** * Extracts region defined in DjatokaDecodeParam as BufferedImage * @param input absolute file path of JPEG image file. * @param params DjatokaDecodeParam instance containing region and transform settings. * @return extracted region as a BufferedImage * @throws DjatokaException */ @Override public BufferedImage process(String input, DjatokaDecodeParam params) throws DjatokaException { InputStream in = null; try { in = new FileInputStream(input); return process(in, params); } catch (IOException ex) { java.util.logging.Logger.getLogger(ExtractJPG.class.getName()).log(Level.SEVERE, null, ex); } finally { try { in.close(); } catch (IOException ex) { java.util.logging.Logger.getLogger(ExtractJPG.class.getName()).log(Level.SEVERE, null, ex); } } return null; } /** * Extracts region defined in DjatokaDecodeParam as BufferedImage * @param input InputStream containing a JPEG image bitstream. * @param params DjatokaDecodeParam instance containing region and transform settings. * @return extracted region as a BufferedImage * @throws DjatokaException */ @Override public BufferedImage process(InputStream input, DjatokaDecodeParam params) throws DjatokaException { try { BufferedImage bi = ImageIO.read(input); ImageRecord r = getMetadata(bi); setLevelReduction(r, params); if (params.getLevelReductionFactor() > 0) { int reduce = 1 << params.getLevelReductionFactor(); // => image.size() / 2^r: reduce 0 means image/1, reduce 1 means image/2, etc. bi = ImageProcessingUtils.scale(bi, 1.0 / reduce); } if (params.getRegion() != null) { ArrayList<Double> dims = null; dims = getRegionMetadata(r, params); // Region info: dims[0..3] = Y,X,H,W logger.debug("dims: "+dims.toString()); logger.debug("region: "+params.getRegion()); logger.debug("reduce: "+params.getLevelReductionFactor()); if (dims != null && dims.size() == 4) { double x = dims.get(1); double y = dims.get(0); double w = dims.get(3); double h = dims.get(2); bi = ImageProcessingUtils.clipRegion(bi, x, y, w, h); // dims[0..3] = Y,X,H,W } } if (params.getRotationDegree() > 0) { bi = ImageProcessingUtils.rotate(bi, params.getRotationDegree()); } return bi; } catch (IOException ex) { java.util.logging.Logger.getLogger(ExtractJPG.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalArgumentException ex) { java.util.logging.Logger.getLogger(ExtractJPG.class.getName()).log(Level.SEVERE, null, ex); } finally { try { input.close(); } catch (IOException ex) { java.util.logging.Logger.getLogger(ExtractJPG.class.getName()).log(Level.SEVERE, null, ex); } } return null; } /** * Extracts region defined in DjatokaDecodeParam as BufferedImage * @param input ImageRecord wrapper containing file reference, inputstream, etc. * @param params DjatokaDecodeParam instance containing region and transform settings. * @return extracted region as a BufferedImage * @throws DjatokaException */ @Override public BufferedImage process(ImageRecord input, DjatokaDecodeParam params) throws DjatokaException { if (input.getImageFile() != null) return process(input.getImageFile(), params); else if (input.getObject() != null && (input.getObject() instanceof InputStream)) return process((InputStream) input.getObject(), params); else throw new DjatokaException( "File not defined and Input Object Type " + input.getObject().getClass().getName() + " is not supported"); } private void setLevelReduction(ImageRecord r, DjatokaDecodeParam params) { if (params.getLevel() >= 0) { int levels = ImageProcessingUtils.getLevelCount(r.getWidth(), r.getHeight()); levels = (r.getDWTLevels() < levels) ? r.getDWTLevels() : levels; int reduce = levels - params.getLevel(); params.setLevelReductionFactor((reduce >= 0) ? reduce : 0); } else if (params.getLevel() == -1 && params.getRegion() == null && params.getScalingDimensions() != null) { int width = params.getScalingDimensions()[0]; int height = params.getScalingDimensions()[1]; int levels = ImageProcessingUtils.getLevelCount(r.getWidth(), r.getHeight()); int scale_level = ImageProcessingUtils.getScalingLevel(r.getWidth(), r.getHeight(), width, height); levels = (r.getDWTLevels() < levels) ? r.getDWTLevels() : levels; int reduce = levels - scale_level; System.out.println(reduce); params.setLevelReductionFactor((reduce >= 0) ? reduce : 0); } } private ArrayList<Double> getRegionMetadata(ImageRecord r, DjatokaDecodeParam params) throws DjatokaException { int reduce = 1 << params.getLevelReductionFactor(); // => image.size() / 2^r: reduce 0 means image/1, reduce 1 means image/2, etc. ArrayList<Double> dims = new ArrayList<Double>(); if (params.getRegion() != null) { StringTokenizer st = new StringTokenizer(params.getRegion(), "{},"); String token; logger.info("region params: " + params.getRegion()); // top if ((token = st.nextToken()).contains(".")) { dims.add(Double.parseDouble(token)); } else { int t = Integer.parseInt(token); if (r.getHeight() < t) { throw new DjatokaException("Region inset out of bounds: " + t + ">" + r.getHeight()); } dims.add(Double.parseDouble(token) / r.getHeight()); } // left if ((token = st.nextToken()).contains(".")) { dims.add(Double.parseDouble(token)); } else { int t = Integer.parseInt(token); if (r.getWidth() < t) { throw new DjatokaException("Region inset out of bounds: " + t + ">" + r.getWidth()); } dims.add(Double.parseDouble(token) / r.getWidth()); } // height if ((token = st.nextToken()).contains(".")) { dims.add(Double.parseDouble(token)); } else { dims.add(Double.parseDouble(token) / (Double.valueOf(r.getHeight()) / Double.valueOf(reduce))); // dims.add(Double.parseDouble(token) / Double.valueOf(r.getHeight())); } // width if ((token = st.nextToken()).contains(".")) { dims.add(Double.parseDouble(token)); } else { dims.add(Double.parseDouble(token) / (Double.valueOf(r.getWidth()) / Double.valueOf(reduce))); // dims.add(Double.parseDouble(token) / Double.valueOf(r.getWidth())); } } return dims; } }