From 9da3a7c6ab4854fb9647f8410b06b0758a67f61b Mon Sep 17 00:00:00 2001 From: j2blake Date: Tue, 12 Apr 2011 17:11:38 +0000 Subject: [PATCH] NIHVIVO-2477 Create another test app to help view thumbnail quality. --- .../ImageUploaderThumbnailerTester_2.java | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploaderThumbnailerTester_2.java diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploaderThumbnailerTester_2.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploaderThumbnailerTester_2.java new file mode 100644 index 000000000..a1f0a18c1 --- /dev/null +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploaderThumbnailerTester_2.java @@ -0,0 +1,299 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.freemarker; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.Raster; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.media.jai.JAI; +import javax.media.jai.RenderedOp; +import javax.media.jai.operator.StreamDescriptor; +import javax.media.jai.widget.ImageCanvas; +import javax.swing.BorderFactory; +import javax.swing.JPanel; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.log4j.Appender; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.PatternLayout; + +import com.sun.media.jai.codec.MemoryCacheSeekableStream; + +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.CropRectangle; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadHelper.NonNoisyImagingListener; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploaderThumbnailerTester_2.CropDataSet.CropData; + +/** + * This is not a unit test, so it is not named BlahBlahTest. + * + * Instead, it's a test harness that creates thumbnails and displays them in a + * window on the screen. It takes human intervention to evaluate. + * + * The goal here is to see whether differences in crop dimensions might cause + * one or more black edges on the thumbnails. + */ +public class ImageUploaderThumbnailerTester_2 extends Frame { + private static final Log log = LogFactory + .getLog(ImageUploaderThumbnailerTester_2.class); + + private static final int ROWS = 6; + private static final int COLUMNS = 9; + + private static final int EDGE_THRESHOLD = 6000; + + /** Keep things quiet. */ + static { + JAI.getDefaultInstance().setImagingListener( + new NonNoisyImagingListener()); + } + + private final String imagePath; + private final ImageUploadThumbnailer thumbnailer; + + public ImageUploaderThumbnailerTester_2(String imagePath, + CropDataSet cropDataSet) { + this.imagePath = imagePath; + this.thumbnailer = new ImageUploadThumbnailer(200, 200); + + setTitle("Cropping edging test"); + addWindowListener(new CloseWindowListener()); + setLayout(createLayout()); + + for (CropData cropData : cropDataSet.crops()) { + add(createImagePanel(cropData)); + } + + pack(); + setVisible(true); + } + + private Component createImagePanel(CropData cropData) { + RenderedOp image = createCroppedImage(cropData); + +// Set blackSides = checkBlackPixels(image); +// if (!blackSides.isEmpty()) { +// log.warn("pixels at " + cropData + ", " + blackSides); +// } + + Set blackSides = checkBlackEdges(image); + if (!blackSides.isEmpty()) { + log.warn("edges at " + cropData + ", " + blackSides); + } + + String legend = "left=" + cropData.left + ", top=" + cropData.top + + ", size=" + cropData.size; + Label l = new Label(); + l.setAlignment(Label.CENTER); + if (!blackSides.isEmpty()) { + l.setBackground(new Color(0xFFDDDD)); + legend += " " + blackSides; + } + l.setText(legend); + + JPanel p = new JPanel(); + p.setLayout(new BorderLayout()); + p.add("South", l); + p.add("Center", new ImageCanvas(image)); + p.setBackground(new Color(0xFFFFFF)); + p.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + return p; + } + + private RenderedOp createCroppedImage(CropData cropData) { + try { + InputStream mainStream = new FileInputStream(imagePath); + CropRectangle rectangle = new CropRectangle(cropData.left, + cropData.top, cropData.size, cropData.size); + InputStream thumbnailStream = thumbnailer.cropAndScale(mainStream, + rectangle); + + return StreamDescriptor.create(new MemoryCacheSeekableStream( + thumbnailStream), null, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private Set checkBlackEdges(RenderedOp image) { + Raster imageData = image.getData(); + + int minX = imageData.getMinX(); + int minY = imageData.getMinY(); + int maxX = minX + imageData.getWidth() - 1; + int maxY = minY + imageData.getHeight() - 1; + + Set blackSides = new HashSet(); + if (isBlackEdge(minX, minX, minY, maxY, imageData)) { + blackSides.add("left"); + } + if (isBlackEdge(minX, maxX, minY, minY, imageData)) { + blackSides.add("top"); + } + if (isBlackEdge(maxX, maxX, minY, maxY, imageData)) { + blackSides.add("right"); + } + if (isBlackEdge(minX, maxX, maxY, maxY, imageData)) { + blackSides.add("bottom"); + } + return blackSides; + } + + private boolean isBlackEdge(int fromX, int toX, int fromY, int toY, + Raster imageData) { + int edgeTotal = 0; + for (int col = fromX; col <= toX; col++) { + for (int row = fromY; row <= toY; row++) { + edgeTotal += sumPixel(imageData, row, col); + } + } + + log.debug("edge total = " + edgeTotal); + return edgeTotal < EDGE_THRESHOLD; + } + + private int sumPixel(Raster imageData, int row, int col) { + int[] pixel = imageData.getPixel(row, col, new int[3]); + int pixelSum = 0; + for (int value : pixel) { + pixelSum += value; + } + return pixelSum; + } + +// private Set checkBlackPixels(RenderedOp image) { +// Raster imageData = image.getData(); +// +// int minX = imageData.getMinX(); +// int minY = imageData.getMinY(); +// int width = imageData.getWidth(); +// int height = imageData.getHeight(); +// int centerX = (minX + width) / 2; +// int centerY = (minY + height) / 2; +// +// int[] leftBorderPixel = imageData.getPixel(minX, centerY, new int[3]); +// int[] topBorderPixel = imageData.getPixel(centerX, minY, new int[3]); +// int[] rightBorderPixel = imageData.getPixel(minX + width - 1, centerY, +// new int[3]); +// int[] bottomBorderPixel = imageData.getPixel(centerX, +// minY + height - 1, new int[3]); +// +// Set blackSides = new HashSet(); +// checkPixel(blackSides, leftBorderPixel, "left"); +// checkPixel(blackSides, topBorderPixel, "top"); +// checkPixel(blackSides, rightBorderPixel, "right"); +// checkPixel(blackSides, bottomBorderPixel, "bottom"); +// +// return blackSides; +// } + +// private void checkPixel(Set blackSides, int[] pixel, String string) { +// int pixelSum = 0; +// for (int i = 0; i < pixel.length; i++) { +// pixelSum += pixel[i]; +// } +// if (pixelSum <= 20) { +// blackSides.add(string); +// } +// } + + private GridLayout createLayout() { + GridLayout layout = new GridLayout(ROWS, COLUMNS); + return layout; + } + + /** + *
+	 * The plan:
+	 * 
+	 * Provide the path to an image file.
+	 * Figure how many images can fit on the screen.
+	 * Crop in increments, starting at 0,0 and varying the size of the crop.
+	 * Crop in increments, incrementing from 0,0 downward, and varying the size of the crop.
+	 * 
+	 * Start by creating 4 x 4 images in a window, and incrementing from 201 to 216.
+	 * 
+ */ + + public static void main(String[] args) { + Logger rootLogger = Logger.getRootLogger(); + Appender appender = (Appender) rootLogger.getAllAppenders() + .nextElement(); + appender.setLayout(new PatternLayout("%-5p [%c{1}] %m%n")); + + // Logger.getLogger(ImageUploadThumbnailer.class).setLevel(Level.DEBUG); + Logger.getLogger(ImageUploaderThumbnailerTester_2.class).setLevel( + Level.INFO); + + CropDataSet cropDataSet = new CropDataSet(); + for (int i = 0; i < ROWS * COLUMNS; i++) { + cropDataSet.add(0 + i, 0 + i, 201 + i); + } + + new ImageUploaderThumbnailerTester_2( + "C:/Development/JIRA issues/NIHVIVO-2477 Black borders on thumbnails/" + + "images from Alex/uploads/file_storage_root/a~n/411/9/" + + "De^20Bartolome^2c^20Charles^20A^20M_100037581.jpg", + cropDataSet); + } + + // ---------------------------------------------------------------------- + // helper classes + // ---------------------------------------------------------------------- + + private class CloseWindowListener extends WindowAdapter { + @Override + public void windowClosing(WindowEvent e) { + setVisible(false); + dispose(); + System.exit(0); + } + } + + public static class CropDataSet { + private final List crops = new ArrayList(); + + CropDataSet add(int left, int top, int size) { + crops.add(new CropData(left, top, size)); + return this; + } + + Collection crops() { + return Collections.unmodifiableCollection(crops); + } + + public static class CropData { + final int left; + final int top; + final int size; + + CropData(int left, int top, int size) { + this.left = left; + this.top = top; + this.size = size; + } + + @Override + public String toString() { + return "CropData[" + left + ", " + top + ", " + size + "]"; + } + } + } +}