org.openimaj.image.DisplayUtilities Maven / Gradle / Ivy
Show all versions of core-image Show documentation
/**
* 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 extends Image, ?>> 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;
}
}