Java tutorial
package org.uva.itast.blended.omr; /* * ==================================================================== * * License: GNU General Public License * * 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 ***********************************************************************/ import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.Collection; import java.util.Hashtable; import java.util.Map; import javax.imageio.ImageIO; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.uva.itast.blended.omr.align.AbstractAlignMarkDetector; import org.uva.itast.blended.omr.align.AlignMarkDetector; import org.uva.itast.blended.omr.align.AlignmentResult; import org.uva.itast.blended.omr.pages.PageImage; import org.uva.itast.blended.omr.pages.PagePoint; import org.uva.itast.blended.omr.pages.SubImage; import org.uva.itast.blended.omr.scanners.BarcodeScanner; import org.uva.itast.blended.omr.scanners.MarkDetectionStatus; import org.uva.itast.blended.omr.scanners.MarkScanner; import org.uva.itast.blended.omr.scanners.MarkScannerException; import org.uva.itast.blended.omr.scanners.ScanResult; import org.uva.itast.blended.omr.scanners.SolidCircleMarkScanner; import org.uva.itast.blended.omr.scanners.SolidSquareMarkScanner; import com.google.zxing.BinaryBitmap; import com.google.zxing.NotFoundException; import com.google.zxing.common.BitArray; import com.google.zxing.common.BitMatrix; import com.sun.pdfview.PDFFile; public class OMRUtils { /** * Logger for this class */ private static final Log logger = LogFactory.getLog(OMRUtils.class); public static final String USERID_FIELDNAME = "USERID"; public static final String TEMPLATEID_FIELDNAME = "TEMPLATEFIELD"; public static final String IMAGE_TYPE = "png"; /** * Mtodo que lee una imagen y la transforma en un objeto de tipo * BufferedImage reescalado * * @param filename * @param outputdir * @return imagenSalida * @throws IOException */ /** * Mtodo que salva un objeto tipo imagen en un archivo fsico de * extensin png * * @param imagen * @param filename * @param imageFormat * @throws IOException */ public static void saveImageToFile(Image imagen, String filename, String imageFormat) throws IOException { File rasterImageFile = new File(filename); rasterImageFile.mkdirs(); ImageIO.write((RenderedImage) imagen, imageFormat, rasterImageFile); } /** * Mtodo que a partir de un fichero pdf de entrada devuelve el * nmero su pginas * * @param inputdir * @return pdffile.getNumpages(); * @throws IOException */ public static int getNumpagesPDF(String inputdir) throws IOException { RandomAccessFile raf = new RandomAccessFile(inputdir, "r"); // se carga // la imagen // pdf para // leerla FileChannel channel = raf.getChannel(); ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); PDFFile pdffile = new PDFFile(buf); // se crea un objeto de tipo PDFFile // para almacenar las pginas return pdffile.getNumPages(); // se obtiene el nmero de paginas } /** * Mtodo que procesa una imgen dada por el inputpath y que llama * a los mtodos que harn posible el procesado de los datos que * contenga * * @param inputpath * @param align * @param medianfilter * @param outputdir * @param plantilla * @throws IOException */ public static void processsPageAnSaveResultsWithLogging(OMRProcessor omr, PageImage page, boolean align, boolean medianfilter, String outputdir, Map<String, OMRTemplate> plantillas, String acticode, String userid) throws IOException { long taskStart = System.currentTimeMillis(); processPageAndSaveResults(omr, align, medianfilter, outputdir, plantillas, page, acticode, userid); logger.debug("Page (" + page.getName() + ") processed in (ms)" + (System.currentTimeMillis() - taskStart)); //$NON-NLS-1$ } /** * @param inputpath * @param align * @param medianfilter * @param outputdir * @param plantilla * @param pageImage * @throws FileNotFoundException */ public static void processPageAndSaveResults(OMRProcessor omr, boolean align, boolean medianfilter, String outputdir, Map<String, OMRTemplate> plantillas, PageImage pageImage, String acticode, String userid) throws FileNotFoundException { OMRTemplate plantilla = findBestSuitedTemplate(omr, pageImage, plantillas, medianfilter); processPage(omr, pageImage, align, medianfilter, outputdir, plantilla); // se // procesa // la // pgina saveOMRResults(pageImage.getName(), outputdir, plantilla, acticode, userid);// se // salvan // los // resultados // en // archivo } /** * Mtodo que procesa una pgina a partir de un BufferedImage * invocando a los mtodos que buscarn las marcas. All templates * passed as argument must share the location of the TemplateIdentification * in the screen TODO refactor this. Not to be static. * * @param pageImage * @param align * @param outputdir * @param plantillas * usado como in/out devuelve los valores reconocidos * @throws FileNotFoundException */ public static void processPage(OMRProcessor omr, PageImage pageImage, boolean align, boolean medianfilter, String outputdir, OMRTemplate plantilla) throws FileNotFoundException { if (align) { // pageImage.align(); //encapsula procesamiento y // representacin AlignMarkDetector borderDetect = omr.getAlignMarkDetector(); AlignmentResult alignmentInfo = borderDetect.align(pageImage); AbstractAlignMarkDetector.logTransformedFrame(pageImage, alignmentInfo); } long taskStart = System.currentTimeMillis(); searchMarks(omr, outputdir, plantilla, pageImage, medianfilter); // se // buscan // las // marcas logger.info("\tMarks scanned in (ms)" + (System.currentTimeMillis() - taskStart)); //$NON-NLS-1$ } public static void processPage(OMRProcessor omr, PageImage pageImage, boolean align, boolean medianfilter, String outputdir, Map<String, OMRTemplate> plantillas) throws FileNotFoundException { OMRTemplate plantilla = findBestSuitedTemplate(omr, pageImage, plantillas, medianfilter); processPage(omr, pageImage, align, medianfilter, outputdir, plantilla); } /** * Scan the page searching the {@value #TEMPLATEID_FIELDNAME} tag and select * the proper template in the map. If no valid TemplateId code is found the * first item in the map is selected. * * @param pageImage * @param templates * @param medianfilter * @return */ public static OMRTemplate findBestSuitedTemplate(OMRProcessor omr, PageImage pageImage, Map<String, OMRTemplate> templates, boolean medianfilter) { /** * Get any (first) template with information for recognizing the * TemplateID. */ // TODO do not iterate every template assume every template has template in the same place //for (OMRTemplate aTemplate : templates.values()) OMRTemplate aTemplate = templates.values().iterator().next(); { PageTemplate firstPage = aTemplate.getPage(1); aTemplate.setSelectedPage(1); Field field = firstPage.getFields().get(TEMPLATEID_FIELDNAME); if (field != null) { scanField(omr, pageImage, field, medianfilter); String templateId = field.getValue(); int pageNumber; // extract the page number if (templateId != null) { pageNumber = Integer.parseInt(templateId.substring(templateId.length() - 1)); templateId = templateId.substring(0, templateId.length() - 1); /** * get the actual template */ logger.info("Loading Template =" + templateId); OMRTemplate plantilla = templates.get(templateId); if (plantilla != null) { plantilla.setSelectedPage(pageNumber); return plantilla; } else // return current template instead { logger.warn("findBestSuitedTemplate: Using a default template for id =" + templateId //$NON-NLS-1$ + "! May render unexpected results if documents have different structure!!"); return aTemplate; } } else { logger.debug("findBestSuitedTemplate- Template " + TEMPLATEID_FIELDNAME + " has no value."); //$NON-NLS-1$ } } logger.debug("findBestSuitedTemplate- Template do not have a " + TEMPLATEID_FIELDNAME //$NON-NLS-1$ + " field. Try the next."); } throw new RuntimeException("No " + TEMPLATEID_FIELDNAME + " field found! in the templates in use."); } /** * Mtodo para buscar las marcas dentro de un objeto tipo Gray8Image * * @param outputdir * @param plantilla * @param pageImage * @param imagen * @param medianfilter * @return plantilla * @throws FileNotFoundException */ public static OMRTemplate searchMarks(OMRProcessor omr, String outputdir, OMRTemplate plantilla, PageImage pageImage, boolean medianfilter) throws FileNotFoundException { // se recorren todas las marcas de una pgina determinada // marcas Collection<Field> fields = plantilla.getPage(plantilla.getSelectedPageNumber()).getFields().values(); for (Field field : fields) { // vamos a buscar en los campos ledos, en marcas[] estn // almacenadas las keys if (field.getName().equals(TEMPLATEID_FIELDNAME)) // this field is known { field.setValue(plantilla.getTemplateID() + plantilla.getSelectedPageNumber()); field.setValid(true); continue; } scanField(omr, pageImage, field, medianfilter); } pageImage.labelPageAsProcessed(); return plantilla; } /** * @param pageImage * @param campo * @param i * @param medianfilter */ private static void scanField(OMRProcessor omr, PageImage pageImage, Field campo, boolean medianfilter) { int tipo = campo.getTipo(); // se almacena el tipo para separar // entre si es un barcode o un // circle if (tipo == Field.CIRCLE) { searchMarkCircle(omr, pageImage, campo, medianfilter); } else if (tipo == Field.SQUARE) { searchMarkSquare(omr, pageImage, campo, medianfilter); } else if (tipo == Field.CODEBAR) { searchBarcodeMark(omr, pageImage, campo, medianfilter); } } private static void searchMarkSquare(OMRProcessor omr, PageImage pageImage, Field field, boolean medianfilter) { Rectangle2D bbox = field.getBBox();// milimeters // leemos la anchura de las marcas en milmetros double markWidth = Math.max(1, bbox.getWidth()); double markHeight = Math.max(1, bbox.getHeight()); SolidSquareMarkScanner markScanner = new SolidSquareMarkScanner(omr, pageImage, markWidth, markHeight, medianfilter); if (logger.isDebugEnabled()) { logger.debug("searchMarkCircle - field name=" + field.getName() + " at position:" + field.getBBox()); //$NON-NLS-1$ } searchMark(pageImage, field, markScanner); } /** * Mtodo que busca marcas de tipo codebar en un objeto tipo * BufferedImage * * @param pageImage * @param field * @param medianfilter */ private static void searchBarcodeMark(OMRProcessor omr, PageImage pageImage, Field field, boolean medianFilter) { BarcodeScanner barcodeScanner = new BarcodeScanner(omr, pageImage, medianFilter); try { field.setValue(barcodeScanner.getParsedCode(field)); } catch (MarkScannerException e) { field.setValue(null); field.setValid(false); logger.debug("Field " + field + " can't be readed from image."); // if (logger.isErrorEnabled()) // barcodeScanner.markBarcode(field); } // barcodeManipulator.markBarcode(campo); } /** * Mtodo que busca marcas de tipo circle en un objeto tipo Gray8Image * * @param i * * @param field * @param markedImage * @param mark * @param markedImage * @param field * @param medianfilter */ private static void searchMarkCircle(OMRProcessor omr, PageImage pageImage, Field field, boolean medianfilter) { Rectangle2D bbox = field.getBBox();// milimeters // leemos la anchura de las marcas en milmetros double markWidth = Math.max(1, bbox.getWidth()); double markHeight = Math.max(1, bbox.getHeight()); SolidCircleMarkScanner markScanner = new SolidCircleMarkScanner(omr, pageImage, markWidth, markHeight, medianfilter); if (logger.isDebugEnabled()) { logger.debug( "START searchMarkCircle - field name=" + field.getName() + " at position:" + field.getBBox()); //$NON-NLS-1$ } searchMark(pageImage, field, markScanner); } /** * @param pageImage * @param field * @param markScanner */ private static void searchMark(PageImage pageImage, Field field, MarkScanner markScanner) { try { ScanResult res = markScanner.scanField(field); MarkDetectionStatus result = (MarkDetectionStatus) res.getResult(); switch (result) { case MARK: if (logger.isDebugEnabled()) { logger.debug("RESULT: searchMark - " + field.getName() + " >>>>>>>Found mark at " //$NON-NLS-1$//$NON-NLS-2$ + field.getBBox() + " (mm) :" + field); //$NON-NLS-1$ //$NON-NLS-4$ } field.setValue("true"); field.setValid(true); // si se ha encontrado la marca markScanner.putEmphasisMarkOnImage(pageImage, Color.RED); break; case NO_MARK: field.setValue("false"); field.setValid(true); break; case DOUBT_MARK: default: field.setValue("?"); field.setValid(false); markScanner.putEmphasisMarkOnImage(pageImage, Color.GREEN); }//switch } catch (MarkScannerException e) { field.setValid(false); } } /** * Mtodo para guardar los resultados del proceso de reconocimiento de * marcas * * @param outputdir * @param inputpath * @param template * @throws FileNotFoundException */ public static File saveOMRResults(String inputpath, String outputdir, OMRTemplate template, String templateIdName, String userIdName) throws FileNotFoundException, NumberFormatException { Hashtable<String, Field> fields = template.getSelectedPage().getFields(); Field templateIdField = fields.get(templateIdName); Field useridField = fields.get(userIdName); try { if (useridField == null || templateIdField == null) // simulate a // NumberFormat. throw new NumberFormatException( "There is no " + TEMPLATEID_FIELDNAME + " field defined in the template!!"); /** * Force to cast to integer to avoid the injection of paths in the * Ids. */ int useridInt = useridField.getValue() == null ? -1 : Integer.parseInt(useridField.getValue()); int templateIdInt = templateIdField.getValue() == null ? -1 : Integer.parseInt(templateIdField.getValue()); File dir = new File(outputdir); // que venga de parametro dir.mkdirs(); // ensure dir exists // File outputFile = new File(dir, "omr_result["+ // template.getTemplateID() + "].txt"); File outputFile = new File(dir, "omr_result[" + (int) (templateIdInt / 10) + "].txt"); PrintWriter out = new PrintWriter(new FileOutputStream(outputFile, true)); // TODO: solo volcar la pgina seleccionada en esta fase. Luego se // volcarn las dos pginas en el otro proceso // de volcado de resultados finales... PageTemplate page = template.getSelectedPage(); fields = page.getFields(); out.println("Filename=" + inputpath); out.println("[Page" + page.getPageNumber() + "]"); for (int k = 0; k < page.getMarks().size(); k++) { Field field = fields.get(page.getMarks().elementAt(k)); out.println(field.getName() + "=" + field.getValue()); } out.close(); return outputFile; } catch (NumberFormatException e) { logger.error("saveOMRResults: Report can't be written. Both ids are not available: " + templateIdName //$NON-NLS-1$ + "=" + templateIdField + " and " + userIdName + "=" + useridField + ".", e); } return null; } /** * @param subImage */ public static void logSubImage(OMRProcessor omr, String textId, BufferedImage subImage) { long start = System.currentTimeMillis(); try { File testPath = getDebugOutputPath(omr); File imgFile = new File(testPath, "debug_" + textId + System.currentTimeMillis() + ".png"); OMRUtils.saveImageToFile(subImage, imgFile.getAbsolutePath(), "PNG"); logger.debug( "Dumped " + textId + " in (ms) (path=" + imgFile + "):" + (System.currentTimeMillis() - start)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @param omr * @return */ public static File getDebugOutputPath(OMRProcessor omr) { File testPath = new File(new File(omr.getOutputdir()), "output"); return testPath; } public static void logSubImage(OMRProcessor omr, SubImage subImage) { logSubImage(omr, "subimage", subImage); } /** * @param prefix * @param medianed */ public static void logSubImage(OMRProcessor omr, String prefix, SubImage subImage) { Rectangle2D markArea = subImage.getBoundingBox(); logger.debug("Dumped subimage " + markArea); logSubImage(omr, prefix, (BufferedImage) subImage); } /** * draws a rectangle expressed in milimeters * * @param pageImage * @param markArea */ public static void logFrame(PageImage pageImage, Rectangle2D markArea, Color color, String label) { OMRUtils.logFrame(pageImage, new PagePoint(pageImage, markArea.getMinX(), markArea.getMinY()), new PagePoint(pageImage, markArea.getMaxX(), markArea.getMinY()), new PagePoint(pageImage, markArea.getMinX(), markArea.getMaxY()), new PagePoint(pageImage, markArea.getMaxX(), markArea.getMaxY()), color, label); } public static void logFrame(PageImage pageImage, PagePoint topleft, PagePoint topright, PagePoint bottomleft, PagePoint bottomright, Color color, String label) { if (topleft == null || topright == null || bottomleft == null || bottomright == null) return; Graphics2D g = pageImage.getReportingGraphics(); AffineTransform t = g.getTransform(); g.setColor(color); g.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 1, new float[] { (float) (3 / t.getScaleX()), (float) (6 / t.getScaleY()) }, 0)); // Point framePxUL=pageImage.toPixels(topleft.getX(), topleft.getY()); // Point framePxUR=pageImage.toPixels(topright.getX(), topright.getY()); // Point framePxBL=pageImage.toPixels(bottomleft.getX(), // bottomleft.getY()); // Point framePxBR=pageImage.toPixels(bottomright.getX(), // bottomright.getY()); g.drawLine(topleft.getXpx(), topleft.getYpx(), topright.getXpx(), topright.getYpx()); g.drawLine(topleft.getXpx(), topleft.getYpx(), bottomleft.getXpx(), bottomleft.getYpx()); g.drawLine(topright.getXpx(), topright.getYpx(), bottomright.getXpx(), bottomright.getYpx()); g.drawLine(bottomleft.getXpx(), bottomleft.getYpx(), bottomright.getXpx(), bottomright.getYpx()); if (label != null) { g.drawString(label, topleft.getXpx(), topleft.getYpx()); } } /** * @param x * in pixels * @param y * in pixels */ public static void markPointInImage(PageImage pageImage, int x, int y) { Graphics2D g = pageImage.getReportingGraphics(); // undo the transformation to pixeles g.setColor(Color.WHITE); g.fillOval((int) (x - 1), (int) (y - 1), (int) (2), (int) (2)); // g.drawRect(i-w/2-1, j-h/2-1, w, h); g.setColor(Color.BLACK); g.drawOval((int) (x - 1), (int) (y - 1), (int) (2), (int) (2)); // g.drawRect(i-w/2, j-h/2, w, h); } /** * ZXing dump image */ /** * Writes out a single PNG which is three times the width of the input image, containing from left * to right: the original image, the row sampling monochrome version, and the 2D sampling * monochrome version. */ public static void dumpBlackPoint(URI uri, BufferedImage image, BinaryBitmap bitmap) { // TODO: Update to compare different Binarizer implementations. String inputName = uri.getPath(); if (inputName.contains(".mono.png")) { return; } int width = bitmap.getWidth(); int height = bitmap.getHeight(); int stride = width * 3; int[] pixels = new int[stride * height]; // The original image int[] argb = new int[width]; for (int y = 0; y < height; y++) { image.getRGB(0, y, width, 1, argb, 0, width); System.arraycopy(argb, 0, pixels, y * stride, width); } // Row sampling BitArray row = new BitArray(width); for (int y = 0; y < height; y++) { try { row = bitmap.getBlackRow(y, row); } catch (NotFoundException nfe) { // If fetching the row failed, draw a red line and keep going. int offset = y * stride + width; for (int x = 0; x < width; x++) { pixels[offset + x] = 0xffff0000; } continue; } int offset = y * stride + width; for (int x = 0; x < width; x++) { if (row.get(x)) { pixels[offset + x] = 0xff000000; } else { pixels[offset + x] = 0xffffffff; } } } // 2D sampling try { for (int y = 0; y < height; y++) { BitMatrix matrix = bitmap.getBlackMatrix(); int offset = y * stride + width * 2; for (int x = 0; x < width; x++) { if (matrix.get(x, y)) { pixels[offset + x] = 0xff000000; } else { pixels[offset + x] = 0xffffffff; } } } } catch (NotFoundException nfe) { } writeResultImage(stride, height, pixels, uri, inputName, ".mono.png"); } private static void writeResultImage(int stride, int height, int[] pixels, URI uri, String inputName, String suffix) { // Write the result BufferedImage result = new BufferedImage(stride, height, BufferedImage.TYPE_INT_ARGB); result.setRGB(0, 0, stride, height, pixels, 0, stride); // Use the current working directory for URLs String resultName = inputName; if ("http".equals(uri.getScheme())) { int pos = resultName.lastIndexOf('/'); if (pos > 0) { resultName = '.' + resultName.substring(pos); } } int pos = resultName.lastIndexOf('.'); if (pos > 0) { resultName = resultName.substring(0, pos); } resultName += suffix; OutputStream outStream = null; try { outStream = new FileOutputStream(resultName); ImageIO.write(result, "png", outStream); } catch (FileNotFoundException e) { System.err.println("Could not create " + resultName); } catch (IOException e) { System.err.println("Could not write to " + resultName); } finally { try { if (outStream != null) { outStream.close(); } } catch (IOException ioe) { // continue } } } /** * ZXING dump */ }