All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openimaj.image.DisplayUtilities Maven / Gradle / Ivy

Go to download

Core definitions of images, pixels and connected components. Also contains interfaces for processors for these basic types. Includes loading, saving and displaying images.

The newest version!
/**
 * Copyright (c) 2011, The University of Southampton and the individual contributors.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *   * 	Redistributions of source code must retain the above copyright notice,
 * 	this list of conditions and the following disclaimer.
 *
 *   *	Redistributions in binary form must reproduce the above copyright notice,
 * 	this list of conditions and the following disclaimer in the documentation
 * 	and/or other materials provided with the distribution.
 *
 *   *	Neither the name of the University of Southampton nor the names of its
 * 	contributors may be used to endorse or promote products derived from this
 * 	software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.openimaj.image;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

import org.openimaj.image.DisplayUtilities.ImageComponent.ImageComponentListener;
import org.openimaj.image.pixel.ConnectedComponent;
import org.openimaj.image.processor.connectedcomponent.render.BlobRenderer;
import org.openimaj.math.geometry.point.Point2d;
import org.openimaj.math.geometry.point.Point2dImpl;
import org.openimaj.math.geometry.shape.Polygon;
import org.openimaj.math.geometry.shape.Rectangle;

/**
 * Static methods for displaying images using Swing.
 *
 * In addition to normal windows, the class also supports "named windows" which
 * can be referred to by name.
 *
 * @author Jonathon Hare ([email protected])
 */
public class DisplayUtilities {
	private static int windowCount = 0;

	private static int windowOpenCount = 0;

	private static Map namedWindows = new HashMap();

	/**
	 * Get the number of open windows
	 *
	 * @return number of open windows
	 */
	public static int openWindowCount() {
		return DisplayUtilities.windowOpenCount;
	}

	/**
	 * Display an image with the default name
	 *
	 * @param image
	 *            the image
	 * @return frame containing the image
	 */
	public static JFrame display(final Image image) {
		return DisplayUtilities.display(image, "Image: "
				+ DisplayUtilities.windowCount);
	}

	/**
	 * Display an image with the default name
	 *
	 * @param image
	 *            the image
	 * @return frame containing the image
	 */
	public static JFrame display(final BufferedImage image) {
		return DisplayUtilities.display(image, "Image: "
				+ DisplayUtilities.windowCount);
	}

	/**
	 * Display an image with the given title
	 *
	 * @param image
	 *            the image
	 * @param title
	 *            the title
	 * @return frame containing the image
	 */
	public static JFrame display(final Image image, final String title) {
		return DisplayUtilities.display(
				ImageUtilities.createBufferedImageForDisplay(image), title,
				image);
	}

	/**
	 * Display an image with the default name No additional functionality, such as
	 * zooming, is enabled.
	 *
	 * @param image
	 *            the image
	 * @return frame containing the image
	 */
	public static JFrame displaySimple(final Image image) {
		return DisplayUtilities.displaySimple(image, "Image: "
				+ DisplayUtilities.windowCount);
	}

	/**
	 * Display an image with the default name. No additional functionality, such as
	 * zooming, is enabled.
	 *
	 * @param image
	 *            the image
	 * @return frame containing the image
	 */
	public static JFrame displaySimple(final BufferedImage image) {
		return DisplayUtilities.displaySimple(image, "Image: "
				+ DisplayUtilities.windowCount);
	}

	/**
	 * Display an image with the given title. No additional functionality, such as
	 * zooming, is enabled.
	 *
	 * @param image
	 *            the image
	 * @param title
	 *            the title
	 * @return frame containing the image
	 */
	public static JFrame displaySimple(final Image image,
			final String title)
	{
		return DisplayUtilities.displaySimple(
				ImageUtilities.createBufferedImageForDisplay(image), title,
				image);
	}

	private static BufferedImage getImage(final JFrame frame) {
		if (frame == null)
			return null;

		if (frame.getContentPane().getComponentCount() > 0
				&& frame.getContentPane().getComponent(0) instanceof ImageComponent)
		{
			return ((ImageComponent) frame.getContentPane().getComponent(0)).image;
		}

		return null;
	}

	/**
	 * Display an image in the given frame
	 *
	 * @param image
	 *            the image
	 * @param frame
	 *            the frame
	 * @return the frame
	 */
	public static JFrame display(final Image image, final JFrame frame) {
		final BufferedImage bimg = DisplayUtilities.getImage(frame);
		return DisplayUtilities.display(
				ImageUtilities.createBufferedImageForDisplay(image, bimg),
				frame);
	}

	/**
	 * Set the position of a named window.
	 *
	 * @param name
	 *            The window name
	 * @param x
	 *            the x position
	 * @param y
	 *            the y position
	 */
	public static void positionNamed(final String name, final int x,
			final int y)
	{
		final JFrame w = DisplayUtilities.createNamedWindow(name);
		w.setBounds(x, y, w.getWidth(), w.getHeight());
	}

	/**
	 * Update the image that is being displayed in the given named window.
	 *
	 * @param name
	 *            The named window
	 * @param newImage
	 *            The new image to display
	 * @param title
	 *            The window title
	 */
	public static void updateNamed(final String name,
			final Image newImage, final String title)
	{
		final JFrame w = DisplayUtilities.createNamedWindow(name, title, true);
		final BufferedImage bimg = DisplayUtilities.getImage(w);

		((ImageComponent) w.getContentPane().getComponent(0))
				.setImage(ImageUtilities.createBufferedImageForDisplay(
						newImage, bimg));
	}

	/**
	 * Create a named window with a title that is also the name
	 *
	 * @param name
	 * @return the window
	 */
	public static JFrame createNamedWindow(final String name) {
		return DisplayUtilities.createNamedWindow(name, name, false);
	}

	/**
	 * Create a named window with a title
	 *
	 * @param name
	 * @param title
	 * @return the window
	 */
	public static JFrame createNamedWindow(final String name,
			final String title)
	{
		return DisplayUtilities.createNamedWindow(name, title, false);
	}

	/**
	 * Create a named window that auto resizes
	 *
	 * @param name
	 * @param title
	 * @param autoResize
	 * @return the window
	 */
	public static JFrame createNamedWindow(final String name,
			final String title, final boolean autoResize)
	{
		if (DisplayUtilities.namedWindows.containsKey(name))
			return DisplayUtilities.namedWindows.get(name);
		final JFrame frame = DisplayUtilities.makeDisplayFrame(title, 0, 0,
				null);
		((ImageComponent) frame.getContentPane().getComponent(0)).autoResize = autoResize;
		((ImageComponent) frame.getContentPane().getComponent(0)).autoPack = autoResize;
		DisplayUtilities.namedWindows.put(name, frame);
		return frame;
	}

	/**
	 * Display an image in the given frame by name (will be created if not already
	 * done so using {@link #createNamedWindow(String)}
	 *
	 * @param image
	 *            the image
	 * @param name
	 *            the name of the frame
	 * @return the frame
	 */
	public static JFrame displayName(final Image image, final String name) {
		if (GraphicsEnvironment.isHeadless())
			return null;

		final JFrame frame = DisplayUtilities.createNamedWindow(name);
		final BufferedImage bimg = DisplayUtilities.getImage(frame);
		return DisplayUtilities.display(
				ImageUtilities.createBufferedImageForDisplay(image, bimg),
				frame, image);
	}

	/**
	 * Display an image in the given frame by name (will be created if not already
	 * done so using {@link #createNamedWindow(String)}
	 *
	 * @param image
	 *            the image
	 * @param name
	 *            the name of the frame
	 * @param autoResize
	 *            should the frame resize to fit its contents
	 * @return the frame
	 */
	public static JFrame displayName(final Image image,
			final String name, final boolean autoResize)
	{
		if (GraphicsEnvironment.isHeadless())
			return null;

		final JFrame frame = DisplayUtilities.createNamedWindow(name, name,
				autoResize);
		final BufferedImage bimg = DisplayUtilities.getImage(frame);
		return DisplayUtilities.display(
				ImageUtilities.createBufferedImageForDisplay(image, bimg),
				frame, image);
	}

	/**
	 * An image viewer that displays and image and allows zooming and panning of
	 * images.
	 * 

* When allowZooming is TRUE, clicking in the image will zoom in. CTRL-click in * the image to zoom out. * * @author Jonathon Hare ([email protected]) * @author David Dupplaw ([email protected]) */ public static class ImageComponent extends JComponent implements MouseListener, MouseMotionListener { /** * Listener for zoom and pan events * * @author David Dupplaw ([email protected]) * @created 25 Jul 2012 * @version $Author$, $Revision$, $Date$ */ public static interface ImageComponentListener { /** * Called when the image has been zoomed to the new zoom factor. * * @param newScaleFactor * The new zoom factor */ public void imageZoomed(double newScaleFactor); /** * Called when the image has been panned to a new position. * * @param newX * The new X position * @param newY * The new Y position */ public void imagePanned(double newX, double newY); } /** */ private static final long serialVersionUID = 1L; /** The image being displayed */ protected BufferedImage image; /** The original image being displayed. Used for pixel interrogation */ protected Image originalImage; /** Whether to auto resize the component to the content size */ private boolean autoResize = false; /** Whether to pack the component on resize */ private boolean autoPack = false; /** Whether to size the image to fit within the component's given size */ private boolean autoFit = false; /** When using autoFit, whether to keep the aspect ratio constant */ private boolean keepAspect = true; /** Draw a grid where there is no image */ private boolean drawTransparencyGrid = false; /** Whether to draw the mouse over pixel colour on the next paint */ private boolean drawPixelColour = false; /** Whether to show pixel colours on mouse over */ private boolean showPixelColours = true; /** Whether to show the XY coordinate of the mouse */ private boolean showXY = true; /** Whether to allow zooming */ private boolean allowZooming = true; /** Whether to allow dragging */ private boolean allowDragging = true; /** Gives the image-coord point in the centre of the image */ private double drawX = 0; /** Gives the image-coord point in the centre of the image */ private double drawY = 0; /** Gives the image scale */ private double scaleFactorX = 1; /** Gives the image scale */ private double scaleFactorY = 1; /** The last location of the drag - x-coordinate */ private int dragStartX = 0; /** The last location of the drag - y-coordinate */ private int dragStartY = 0; /** The x-coordinate of the pixel being displayed */ private int pixelX = 0; /** The y-coordinate of the pixel being displayed */ private int pixelY = 0; /** The current mouse coordinate */ private int mouseX = 0; /** The current mouse coordinate */ private int mouseY = 0; /** The current pixel colour */ private Float[] currentPixelColour = null; /** List of listeners */ private final ArrayList listeners = new ArrayList(); /** The last displayed image */ private BufferedImage displayedImage = null; /** * Default constructor */ public ImageComponent() { this(false, false); } /** * Default constructor. Allows setting of the autoResize parameter which if true * changes the size of the component to fit the contents. * * @param autoResize * automatically resize the component to the content size */ public ImageComponent(final boolean autoResize) { this(autoResize, true); } /** * Construct with given image * * @param image * the image */ public ImageComponent(final BufferedImage image) { this(true, true); this.setImage(image); } /** * Default constructor. Allows setting of the autoResize parameter which if true * changes the size of the component to fit the contents, and the autoPack * parameter which automatically packs the containers root (if its a JFrame) * whenever it is resized. * * @param autoResize * automatically resize the component to the content size * @param autoPack * automatically pack the root component on resize */ public ImageComponent(final boolean autoResize, final boolean autoPack) { this(1f, autoResize, autoPack); } /** * Default constructor. Allows setting of the autoResize parameter which if true * changes the size of the component to fit the contents, and the autoPack * parameter which automatically packs the containers root (if its a JFrame) * whenever it is resized. * * @param initialScale * initial scale of the image * @param autoResize * automatically resize the component to the content size * @param autoPack * automatically pack the root component on resize */ public ImageComponent(final float initialScale, final boolean autoResize, final boolean autoPack) { this.autoPack = autoPack; this.autoResize = autoResize; this.scaleFactorX = initialScale; this.scaleFactorY = initialScale; this.addMouseListener(this); this.addMouseMotionListener(this); // Add a component listener so that we can detect when the // component has been resized so that we can update this.addComponentListener(new ComponentAdapter() { @Override public void componentResized(final ComponentEvent e) { ImageComponent.this.calculateScaleFactorsToFit( ImageComponent.this.image, ImageComponent.this.getBounds()); }; }); } /** * Add the given listener to this image component. * * @param l * The listener to add */ public void addImageComponentListener(final ImageComponentListener l) { this.listeners.add(l); } /** * Remove the given listener from this image component. * * @param l * The listener to remove. */ public void removeImageComponentListener(final ImageComponentListener l) { this.listeners.remove(l); } /** * Set whether to allow zooming. * * @param allowZoom * TRUE to allow zooming */ public void setAllowZoom(final boolean allowZoom) { this.allowZooming = allowZoom; if (allowZoom) this.autoFit = false; } /** * Set whether to allow panning. * * @param allowPan * TRUE to allow panning */ public void setAllowPanning(final boolean allowPan) { this.allowDragging = allowPan; if (allowPan) this.autoFit = false; } /** * Set whether to allow drawing of the transparency grid. * * @param drawGrid * TRUE draws the grid */ public void setTransparencyGrid(final boolean drawGrid) { this.drawTransparencyGrid = drawGrid; this.repaint(); } /** * Set whether to show pixel colours or not. * * @param showPixelColours * TRUE to show pixel colours */ public void setShowPixelColours(final boolean showPixelColours) { this.showPixelColours = showPixelColours; this.repaint(); } /** * Set whether to show the XY position of the mouse curson or not * * @param showXYPosition * TRUE to show XY position */ public void setShowXYPosition(final boolean showXYPosition) { this.showXY = showXYPosition; this.repaint(); } /** * Set the image to draw * * @param image * the image */ public void setImage(final BufferedImage image) { this.image = image; if (this.autoFit) { this.calculateScaleFactorsToFit(image, this.getBounds()); } else if (this.autoResize) { // If the component isn't the right shape, we'll resize the // component. if (image.getWidth() != this.getWidth() || image.getHeight() != this.getHeight()) { this.setPreferredSize(new Dimension( (int) (image.getWidth() * this.scaleFactorX), (int) (image.getHeight() * this.scaleFactorY))); this.setSize(new Dimension( (int) (image.getWidth() * this.scaleFactorX), (int) (image.getHeight() * this.scaleFactorY))); } final Component c = SwingUtilities.getRoot(this); if (c == null) return; c.validate(); if (c instanceof JFrame && this.autoPack) { final JFrame f = (JFrame) c; f.pack(); } } if (this.showPixelColours) // This forces a repaint if showPixelColours is true this.updatePixelColours(); else this.repaint(); } /** * Given an image, will calculate two scale factors for the X and Y dimensions * of the image, such that the image will fit within the bounds. * * @param image * The image to fit * @param bounds * The bounds to fit within */ private void calculateScaleFactorsToFit(final BufferedImage image, final java.awt.Rectangle bounds) { if (image == null || bounds == null) return; if (this.autoFit) { // If we can stretch the image it's pretty simple. if (!this.keepAspect) { this.scaleFactorX = bounds.width / (double) image.getWidth(); this.scaleFactorY = bounds.height / (double) image.getHeight(); } // Otherwise we need to find the ratios to fit while keeping // aspect else { this.scaleFactorX = this.scaleFactorY = Math.min( bounds.width / (double) image.getWidth(), bounds.height / (double) image.getHeight()); } } } /** * Move the image to the given position (image coordinates) * * @param x * The x image coordinate * @param y * The y image coordinate */ public void moveTo(final double x, final double y) { if (this.drawX != x || this.drawY != y) { this.drawX = x; this.drawY = y; this.repaint(); for (final ImageComponentListener l : this.listeners) l.imagePanned(x, y); } } /** * Set the scale factor to zoom to * * @param sf * The scale factor */ public void zoom(final double sf) { this.scaleFactorX = this.scaleFactorY = sf; this.repaint(); for (final ImageComponentListener l : this.listeners) l.imageZoomed(sf); } /** * Set the scale factor to draw the image in the x-direction. Allows the image * to be stretched or shrunk horizontally. * * @param sf * The new scale factor */ public void setScaleFactorX(final double sf) { this.scaleFactorX = sf; } /** * Set the scale factor to draw the image in the y-direction. Allows the image * to be stretched or shrunk vertically. * * @param sf * The new scale factor */ public void setScaleFactorY(final double sf) { this.scaleFactorY = sf; } /** * Set the scale factor to draw the image. Allows the image to be stretched or * shrunk both horizontall or vertically. * * @param sfx * The new x scale factor * @param sfy * The new y scale factor */ public void setScaleFactor(final double sfx, final double sfy) { this.setScaleFactorX(sfx); this.setScaleFactorY(sfy); } /** * If you want to be able to inspect the original image's pixel values (rather * than the generated BufferedImage) set the original image here. Use null to * enforce showing the BufferedImage pixel values. This does not set the * BufferedImage that is being used for the display. * * @param image * The original image. */ public void setOriginalImage(final Image image) { this.originalImage = image; } /** * Make sure the x and y position we're drawing the image in is not going mad. */ private void sanitiseVars() { // Make sure we're not going out of the space // this.moveTo( // Math.max( // this.image.getWidth() / this.scaleFactorX / 2, // Math.min( // this.drawX, // this.image.getWidth() // - (this.getWidth() / 2 / this.scaleFactorX) ) ), // Math.max( this.image.getHeight() / this.scaleFactorY / 2, // Math.min( // this.drawY, // this.image.getHeight() // - (this.getHeight() / 2 / this.scaleFactorY) ) ) ); } /** * {@inheritDoc} * * @see javax.swing.JComponent#paint(java.awt.Graphics) */ @Override public void paint(final Graphics gfx) { // Create a double buffer into which we'll draw first. final BufferedImage img = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_3BYTE_BGR); final Graphics2D g = (Graphics2D) img.getGraphics(); if (this.drawTransparencyGrid) { final BufferedImage transparencyGrid = new BufferedImage( this.getWidth(), this.getHeight(), BufferedImage.TYPE_3BYTE_BGR); final Graphics tg = transparencyGrid.getGraphics(); final int gridSizeX = (int) (20 * this.scaleFactorX); final int gridSizeY = (int) (20 * this.scaleFactorY); for (int y = 0; y < this.getHeight(); y += gridSizeY) { for (int x = 0; x < this.getWidth(); x += gridSizeX) { final int c = (x / gridSizeX + y / gridSizeY) % 2; if (c == 0) tg.setColor(new Color(220, 220, 220)); else tg.setColor(Color.white); tg.fillRect(x, y, gridSizeX, gridSizeY); } } g.drawImage(transparencyGrid, 0, 0, null); } // Draw the image if (this.image != null) { // Scale and translate to the image drawing coordinates g.scale(this.scaleFactorX, this.scaleFactorY); g.translate(-this.drawX, -this.drawY); // Blat the image to the screen g.drawImage(this.image, 0, 0, this.image.getWidth(), this.image.getHeight(), null); // Reset the graphics back to the original pixel-based coords g.translate(this.drawX, this.drawY); g.scale(1 / this.scaleFactorX, 1 / this.scaleFactorY); // If we're to show pixel colours and we're supposed to do it // on this time around... if ((this.showPixelColours || this.showXY) && this.drawPixelColour) { final StringBuffer pixelColourStrB = new StringBuffer(); if (this.showXY) pixelColourStrB.append("[" + this.pixelX + "," + this.pixelY + "] "); if (this.showPixelColours) pixelColourStrB.append(Arrays .toString(this.currentPixelColour)); // Calculate the size to draw final FontMetrics fm = g.getFontMetrics(); final int fw = fm.stringWidth(pixelColourStrB.toString()); final int fh = fm.getHeight() + fm.getDescent(); final int p = 4; // padding final int dx = 0; int dy = this.getHeight() - (fh + p); // If the mouse is over where we want to put the box, // we'll move the box to another corner if (this.mouseX <= dx + fw + p && this.mouseX >= dx && this.mouseY >= dy && this.mouseY <= dy + fh + p) dy = 0; // Draw a box g.setColor(new Color(0, 0, 0, 0.5f)); g.fillRect(dx, dy, fw + p, fh + p); // Draw the text g.setColor(Color.white); g.drawString(pixelColourStrB.toString(), dx + p / 2, dy + fm.getHeight() + p / 2); } } // Blat our offscreen image to the screen gfx.drawImage(img, 0, 0, null); // Store this displayed image this.displayedImage = img; } /** * {@inheritDoc} * * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent) */ @Override public void mouseClicked(final MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1 && this.allowZooming) { if (e.isControlDown()) { // Scale the scalars down this.scaleFactorX /= 2; this.scaleFactorY /= 2; final double moveX = this.drawX - e.getX() / this.scaleFactorX / 2; final double moveY = this.drawY - e.getY() / this.scaleFactorY / 2; if (this.allowDragging) this.moveTo(moveX, moveY); else this.moveTo(0, 0); } else { // Scale the scalars up this.scaleFactorX *= 2; this.scaleFactorY *= 2; // Make sure we zoom in on the bit the user clicked on if (this.allowDragging) this.moveTo( this.drawX + e.getX() / this.scaleFactorX, this.drawY + e.getY() / this.scaleFactorY); else this.moveTo(0, 0); } // Make sure we're not going to draw out of bounds. this.sanitiseVars(); this.repaint(); } } @Override public void mousePressed(final MouseEvent e) { if (this.allowDragging) { this.dragStartX = e.getX(); this.dragStartY = e.getY(); } } @Override public void mouseReleased(final MouseEvent e) { } @Override public void mouseEntered(final MouseEvent e) { } @Override public void mouseExited(final MouseEvent e) { this.drawPixelColour = false; this.repaint(); } @Override public void mouseDragged(final MouseEvent e) { if (!this.allowDragging) return; final int diffx = e.getX() - this.dragStartX; final int diffy = e.getY() - this.dragStartY; if (diffx == 0 && diffy == 0) return; // Update the draw position this.moveTo(this.drawX - diffx / this.scaleFactorX, this.drawY - diffy / this.scaleFactorY); // Reset the draggers this.dragStartX = e.getX(); this.dragStartY = e.getY(); // Make sure the drag stays within the bounds this.sanitiseVars(); // Redraw the component this.repaint(); } @Override public void mouseMoved(final MouseEvent e) { if (this.image == null) return; // Convert the screen coords into image coords final double x = e.getX() / this.scaleFactorX + this.drawX; final double y = e.getY() / this.scaleFactorY + this.drawY; // If we're outside the image we don't print anything if (x >= this.image.getWidth() || y >= this.image.getHeight() || x < 0 || y < 0) { this.drawPixelColour = false; this.repaint(); return; } // Pixel coordinates in the image this.pixelX = (int) x; this.pixelY = (int) y; this.mouseX = e.getX(); this.mouseY = e.getY(); this.updatePixelColours(); } /** * Update the display of pixel colours */ protected void updatePixelColours() { if (this.showPixelColours && this.image != null) { // If we don't have the original image, we'll just use the // colours from the BufferedImage if (this.originalImage == null) { final int colour = this.image.getRGB(this.pixelX, this.pixelY); this.currentPixelColour = new Float[3]; this.currentPixelColour[0] = (float) ((colour & 0x00ff0000) >> 16); this.currentPixelColour[1] = (float) ((colour & 0x0000ff00) >> 8); this.currentPixelColour[2] = (float) ((colour & 0x000000ff)); } else { // If we're outside of the original image's coordinates, // we don't need to do anything else.. if (this.pixelX >= this.originalImage.getWidth() || this.pixelX < 0 || this.pixelY >= this.originalImage.getHeight() || this.pixelY < 0) return; // If we have the original image we get each of the bands // from it and update the current pixel colour member if (this.originalImage instanceof FImage) { final Object o = this.originalImage.getPixel(this.pixelX, this.pixelY); this.currentPixelColour = new Float[1]; this.currentPixelColour[0] = (Float) o; } else if (this.originalImage instanceof MBFImage) { final MBFImage i = (MBFImage) this.originalImage; this.currentPixelColour = new Float[i.numBands()]; for (int b = 0; b < i.numBands(); b++) this.currentPixelColour[b] = i.getBand(b) .getPixel(this.pixelX, this.pixelY); } } this.drawPixelColour = true; this.repaint(); } if (this.showXY) { this.drawPixelColour = true; this.repaint(); } } /** * Sets whether to automatically size the image to fit within the bounds of the * image component which is being sized externally. This shouldn't be used in * combination with autoResize. When this method is called with TRUE, zooming * and dragging are disabled. * * @param tf * TRUE to auto fit the image. */ public void setAutoFit(final boolean tf) { this.autoFit = tf; if (this.autoFit) { this.allowZooming = false; this.allowDragging = false; } } /** * Sets whether to keep the aspect ratio of the image constant when the image is * being autoFit into the component. * * @param tf * TRUE to keep the aspect ratio constant */ public void setKeepAspect(final boolean tf) { this.keepAspect = tf; } /** * Sets whether to automatically resize the component to fit image (at it's * given scale factor) within it. Note that in certain circumstances, where the * image component is being sized by external forces (such as a layout manager), * setting this to true can cause weird results where the image is pulled out * and in constantly. This shouldn't be used in combination with autoFit. * * @param tf * TRUE to resize the component. */ public void setAutoResize(final boolean tf) { this.autoResize = tf; } /** * Sets whether the component is to attempt to pack a frame into which it is * added. If it is not in a frame this will have no effect. This allows the * frame to resize with the component. * * @param tf * TRUE to auto pack the parent frame. */ public void setAutoPack(final boolean tf) { this.autoPack = tf; } /** * Returns the current mouse position in pixels within the viewport. Will return * the last known position if the mouse is no longer within the viewport. * * @return The position in pixels */ public Point2d getCurrentMousePosition() { return new Point2dImpl(this.mouseX, this.mouseY); } /** * Returns the current mouse position in the coordinates of the image and is * determined by the scaling factors and the position of the image within the * viewport. If the mouse is no longer in the viewport, the last known mouse * position will be returned. * * @return The position in image coordinates. */ public Point2d getCurrentMouseImagePosition() { return new Point2dImpl(this.pixelX, this.pixelY); } /** * Returns the current pixel colour at the point of the mouse. The number of * elements in the array will equal be 3, if no original has been supplied to * the image component. The values will be between 0 and 255 and ordered red, * green and blue. If the original has been supplied, then the number of * elements will be equal to the number of bands in the original image and the * values will be the original pixel values in the original image. * * @return The current pixel colour. */ public Float[] getCurrentPixelColour() { return this.currentPixelColour; } /** * Returns the current displayed pixel colour (as an RGB encoded int) from the * currently displayed image. * * @return The current displayed pixel colour. */ public int getCurrentDisplayedPixelColour() { return this.displayedImage.getRGB(this.mouseX, this.mouseY); } /** * Returns the currently displaying image. * * @return The displayed image. */ public BufferedImage getDisplayedImage() { return this.displayedImage; } } /** * An extension of {@link ImageComponent} that scales the displayed image. * * @author Jonathon Hare ([email protected]) * */ public static class ScalingImageComponent extends ImageComponent { /** * */ private static final long serialVersionUID = 1L; private boolean hq = false; /** * Construct the ScalingImageComponent with fast scaling enabled */ public ScalingImageComponent() { } /** * Construct the ScalingImageComponent, choosing between fast scaling or high * quality scaling * * @param hq * true if high quality scaling is required. */ public ScalingImageComponent(boolean hq) { this.hq = hq; } @Override public void paint(final Graphics g) { if (hq) ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); final Component f = SwingUtilities.getRoot(this); if (this.image != null) g.drawImage(this.image, 0, 0, this.getWidth(), this.getHeight(), f); } } /** * Display an image in the given frame * * @param image * the image * @param frame * the frame * @return the frame */ public static JFrame display(final BufferedImage image, final JFrame frame) { return DisplayUtilities.display(image, frame, null); } /** * Displays an image in the given named window * * @param image * The image * @param name * The name of the window * @return The frame that was created. */ public static JFrame displayName(final BufferedImage image, final String name) { if (GraphicsEnvironment.isHeadless()) return null; final JFrame f = DisplayUtilities.createNamedWindow(name); return DisplayUtilities.display(image, f); } /** * Display an image in the given frame * * @param image * the image * @param frame * the frame * @param originalImage * the original image * @return the frame */ public static JFrame display(final BufferedImage image, final JFrame frame, final Image originalImage) { if (frame == null) return DisplayUtilities.makeDisplayFrame("Image: " + DisplayUtilities.windowCount, image.getWidth(), image.getHeight(), image); if (frame.getContentPane().getComponentCount() > 0 && frame.getContentPane().getComponent(0) instanceof ImageComponent) { final ImageComponent cmp = ((ImageComponent) frame.getContentPane() .getComponent(0)); if (!frame.isVisible()) { final boolean ar = cmp.autoResize; final boolean ap = cmp.autoPack; cmp.autoResize = true; cmp.autoPack = true; cmp.setImage(image); cmp.setOriginalImage(originalImage); cmp.autoResize = ar; cmp.autoPack = ap; frame.setVisible(true); } else { cmp.setImage(image); cmp.setOriginalImage(originalImage); } } else { frame.getContentPane().removeAll(); final ImageComponent c = new ImageComponent(image); c.setOriginalImage(originalImage); frame.add(c); frame.pack(); frame.setVisible(true); } return frame; } /** * Make a frame with the given title. * * @param title * the title * @return the frame */ public static JFrame makeFrame(final String title) { final JFrame f = new JFrame(title); f.setResizable(false); f.setUndecorated(false); f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(final WindowEvent evt) { DisplayUtilities.windowOpenCount = DisplayUtilities.windowCount - 1; f.dispose(); } }); return f; } /** * Display an image with the given title. No additional functionality, such as * zooming, is enabled. * * @param image * the image * @param title * the title * @return frame containing the image */ public static JFrame displaySimple(final BufferedImage image, final String title) { return DisplayUtilities.displaySimple(image, title, null); } /** * Display an image with the given title * * @param image * the image * @param title * the title * @return frame containing the image */ public static JFrame display(final BufferedImage image, final String title) { return DisplayUtilities.display(image, title, null); } /** * Display an image with the given title. No additional functionality, such as * zooming, is enabled. * * @param image * the image * @param title * the title * @param originalImage * original image * @return frame containing the image */ public static JFrame displaySimple(final BufferedImage image, final String title, final Image originalImage) { if (GraphicsEnvironment.isHeadless()) return null; return DisplayUtilities.makeDisplayFrameSimple(title, image.getWidth(), image.getHeight(), image, originalImage); } /** * Get a frame that will display an image. No additional functionality, such as * zooming, is enabled. * * @param title * the frame title * @param width * the frame width * @param height * the frame height * @param img * the image to display * @param originalImage * the original image * @return A {@link JFrame} that allows images to be displayed. */ public static JFrame makeDisplayFrameSimple(final String title, final int width, final int height, final BufferedImage img, final Image originalImage) { final JFrame f = DisplayUtilities.makeFrame(title); final ImageComponent c = new ImageComponent(); if (img != null) c.setImage(img); c.setOriginalImage(originalImage); c.setSize(width, height); c.setPreferredSize(new Dimension(c.getWidth(), c.getHeight())); c.removeMouseListener(c); c.removeMouseMotionListener(c); c.setShowPixelColours(false); c.setShowXYPosition(false); c.setAllowZoom(false); c.setAutoscrolls(false); c.setAllowPanning(false); f.add(c); f.pack(); f.setVisible(img != null); DisplayUtilities.windowCount++; return f; } /** * Display an image with the given title * * @param image * the image * @param title * the title * @param originalImage * original image * @return frame containing the image */ public static JFrame display(final BufferedImage image, final String title, final Image originalImage) { if (GraphicsEnvironment.isHeadless()) return null; return DisplayUtilities.makeDisplayFrame(title, image.getWidth(), image.getHeight(), image, originalImage); } /** * Get a frame that will display an image. * * @param title * the frame title * @param width * the frame width * @param height * the frame height * @return A {@link JFrame} that allows images to be displayed. */ public static JFrame makeDisplayFrame(final String title, final int width, final int height) { return DisplayUtilities.makeDisplayFrame(title, width, height, null); } /** * Get a frame that will display an image. * * @param title * the frame title * @param width * the frame width * @param height * the frame height * @param img * the image to display * @return A {@link JFrame} that allows images to be displayed. */ public static JFrame makeDisplayFrame(final String title, final int width, final int height, final BufferedImage img) { return DisplayUtilities.makeDisplayFrame(title, width, height, img, null); } /** * Get a frame that will display an image. * * @param title * the frame title * @param width * the frame width * @param height * the frame height * @param img * the image to display * @param originalImage * the original image * @return A {@link JFrame} that allows images to be displayed. */ public static JFrame makeDisplayFrame(final String title, final int width, final int height, final BufferedImage img, final Image originalImage) { final JFrame f = DisplayUtilities.makeFrame(title); final ImageComponent c = new ImageComponent(); if (img != null) c.setImage(img); c.setOriginalImage(originalImage); c.setSize(width, height); c.setPreferredSize(new Dimension(c.getWidth(), c.getHeight())); f.add(c); f.pack(); f.setVisible(img != null); DisplayUtilities.windowCount++; return f; } /** * Render a connected component and display it * * @param input * the connected component * @return frame containing the rendered image */ public static JFrame display(final ConnectedComponent input) { return DisplayUtilities.display(input, 1.0f); } /** * Render a connected component with a given grey level and display it * * @param input * the connected component * @param col * the grey level * @return frame containing the rendered image */ public static JFrame display(final ConnectedComponent input, final float col) { final ConnectedComponent cc = input.clone(); final Rectangle bb = cc.calculateRegularBoundingBox(); // Render the mask, leaving a 10 px border cc.translate(10 - (int) bb.x, 10 - (int) bb.y); final FImage mask = new FImage((int) Math.max(bb.width + 20, 100), (int) Math.max(bb.height + 20, 100)); final BlobRenderer br = new BlobRenderer(mask, 1.0F); cc.process(br); return DisplayUtilities.display(mask); } /** * Render a polygon to an image and display it. * * @param input * the polygon * @return the frame */ public static JFrame display(final Polygon input) { return DisplayUtilities.display(input, 1.0f); } /** * Render a polygon with a given grey level and display it * * @param input * the polygon * @param col * the grey level * @return frame containing the rendered image */ public static JFrame display(final Polygon input, final float col) { final Polygon p = input.clone(); final Rectangle bb = p.calculateRegularBoundingBox(); // Render the mask, leaving a 1 px border p.translate(10 - bb.x, 10 - bb.y); final FImage mask = new FImage((int) (bb.width + 20), (int) (bb.height + 20)); mask.createRenderer().drawPolygon(p, col); return DisplayUtilities.display(mask); } /** * Display multiple images in an array * * @param title * the frame title * @param images * the images * @return the frame */ public static JFrame display(final String title, final Image... images) { final BufferedImage[] bimages = new BufferedImage[images.length]; for (int i = 0; i < images.length; i++) bimages[i] = ImageUtilities .createBufferedImageForDisplay(images[i]); return DisplayUtilities.display(title, bimages); } /** * Display multiple images in a collection * * @param title * the frame title * @param images * the images * @return the frame */ public static JFrame display(final String title, final Collection> images) { final BufferedImage[] bimages = new BufferedImage[images.size()]; int i = 0; for (final Image img : images) bimages[i++] = ImageUtilities .createBufferedImageForDisplay(img); return DisplayUtilities.display(title, bimages); } /** * Display multiple images in an array * * @param title * the frame title * @param cols * number of columns * @param images * the images * @return the frame */ public static JFrame display(final String title, final int cols, final Image... images) { final JFrame f = new JFrame(title); f.getContentPane().setLayout(new GridLayout(0, cols)); for (final Image image : images) { if (image != null) { final ImageComponent ic = new ImageComponent( ImageUtilities.createBufferedImageForDisplay(image)); ic.setOriginalImage(image); f.getContentPane().add(ic); } } f.pack(); f.setVisible(true); return f; } /** * Display multiple images in an array * * @param title * the frame title * @param cols * number of columns * @param images * the images * @return the frame */ public static JFrame displayLinked(final String title, final int cols, final Image... images) { final JFrame f = new JFrame(title); f.getContentPane().setLayout(new GridLayout(0, cols)); ImageComponent ic = null; for (final Image image : images) { if (image != null) { final ImageComponent ic2 = new ImageComponent( ImageUtilities.createBufferedImageForDisplay(image)); if (ic != null) { ic.addImageComponentListener(new ImageComponentListener() { @Override public void imageZoomed(final double newScaleFactor) { ic2.zoom(newScaleFactor); } @Override public void imagePanned(final double newX, final double newY) { ic2.moveTo(newX, newY); } }); } ic2.setOriginalImage(image); f.getContentPane().add(ic2); ic = ic2; } } f.pack(); f.setVisible(true); return f; } /** * Display multiple images in an array of frames * * @param title * the frame title * @param images * the images * @return the frame */ public static JFrame display(final String title, final BufferedImage... images) { if (GraphicsEnvironment.isHeadless()) return null; final JFrame f = new JFrame(title); final int box_size = 200; final int n_images = images.length; final int n_boxes_x = 4; final int width = n_boxes_x * box_size; final int height = box_size * n_images / n_boxes_x; f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(final WindowEvent evt) { DisplayUtilities.windowOpenCount = DisplayUtilities.windowCount - 1; f.dispose(); } }); final Container scrollContainer = new Container(); scrollContainer.setLayout(new FlowLayout()); final Container container = new Container(); container.setSize(new Dimension(width, height)); container.setPreferredSize(new Dimension(width, height)); container.setLayout(new GridLayout(0, n_boxes_x)); scrollContainer.add(container); for (final BufferedImage img : images) { final JComponent c = new JComponent() { private static final long serialVersionUID = 1L; @Override public void paint(final Graphics g) { final int cw = this.getWidth(); final int ch = this.getHeight(); if (img.getWidth() < cw && img.getHeight() < ch) { final int x = (cw - img.getWidth()) / 2; final int y = (ch - img.getHeight()) / 2; g.drawImage(img, x, y, img.getWidth(), img.getHeight(), f); } else if (img.getWidth() > img.getHeight()) { final float sf = (float) cw / (float) img.getWidth(); final int h = Math.round(sf * img.getHeight()); g.drawImage(img, 0, (ch - h) / 2, cw, h, f); } else { final float sf = (float) ch / (float) img.getHeight(); final int w = Math.round(sf * img.getWidth()); g.drawImage(img, (cw - w) / 2, 0, w, ch, f); } // TODO: scale image proportionally and draw centered } }; c.setSize(200, 200); c.setPreferredSize(new Dimension(c.getWidth(), c.getHeight())); container.add(c); } f.setSize(new Dimension(840, 600)); f.setPreferredSize(new Dimension(840, 600)); f.getContentPane().add(new JScrollPane(scrollContainer)); f.pack(); f.setVisible(true); DisplayUtilities.windowCount++; return f; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy