package it.geosolutions.imageio.plugins.jp2k;
/*
* $RCSfile: HistogramFrame.java,v $
*
*
* Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution 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 Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for
* use in the design, construction, operation or maintenance of any
* nuclear facility.
*
* $Revision: 1.1 $
* $Date: 2005/02/11 04:40:06 $
* $State: Exp $
*/
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import javax.media.jai.Histogram;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import com.sun.media.jai.widget.DisplayJAI;
/**
* This class is defined to display the statistics or the histogram in a
* <code>JFrame</code>.
*
* When the histogram is displayed, the color of the bar for each bin is
* corresponding to the color of the pixels whose values fall into this bin.
*
* When the statistic parameters are displayed, the following parameters of a
* rectangular ROI are listed in a table: the area, the width, the height, the
* maximum, the minimum, the mean, the standard deviation and the entropy of the
* values of the pixels located in this ROI.
*
*/
@SuppressWarnings("serial")
class HistogramFrame extends JFrame {
/**
* The histogram to be displayed or to be used to calculate the statistics.
*/
private Histogram histogram;
/**
* Constructor.
*
* It accepts four parameters as defined below:
*
* @param h
* The histogram object to display or to calculate the
* statistics.
* @param displayHistogram
* Indicates to display histogram or statistics.
* @param lut
* The lut for window/level operation. Used to define the color
* of each bin of the histogram when display.
* @param roi
* The ROI in the original image coordinate system.
*/
public HistogramFrame(Histogram h, boolean displayHistogram) {
this.histogram = h;
if (displayHistogram) {
// display histogram
this.setTitle("Histogram");
DisplayJAI pane = new DisplayJAI(displayHistogram(h));
this.getContentPane().add(pane);
} else {
// display statistics
this.setTitle("Statistics");
JTable table = getStatistics(h);
JScrollPane scrollpane = new JScrollPane(table);
this.getContentPane().add(scrollpane);
}
}
/**
* Create the table of the statistic parameters. Currently, the following
* parameters are calculated and displayed: the area, the width, the height,
* the maximum, the minimum, the mean, the standard deviation and the
* entropy of the values of the pixels located in the ROI.
*/
private JTable getStatistics(Histogram h) {
double[] mean = h.getMean();
mean[0] = ((int) (mean[0] * 10)) / 10.0;
int[] minValue = getMinValue();
int[] maxValue = getMaxValue();
double[] stdev = h.getStandardDeviation();
stdev[0] = ((int) (stdev[0] * 10)) / 10.0;
double[] entropy = h.getEntropy();
entropy[0] = ((int) (entropy[0] * 10)) / 10.0;
String[] heading = new String[] { "Parameter", "Value" };
String[][] content = new String[][] { { "Max", "" + maxValue[0] },
{ "Min", "" + minValue[0] }, { "Mean", "" + mean[0] },
{ "StDev", "" + stdev[0] }, { "Entropy", "" + entropy[0] } };
return new JTable(content, heading);
}
/**
* Calculate the area of the ROI, in <code>mm<sup>2</sup></code>, based
* on the path iterator.
*/
private double getArea(Area roi) {
PathIterator path = roi.getPathIterator(new AffineTransform(), 0.1);
double[] coordinates = new double[6];
double x1 = 0.0, y1 = 0.0;
double area = 0.0;
while (path.isDone() == false) {
int type = path.currentSegment(coordinates);
switch (type) {
case PathIterator.SEG_MOVETO:
x1 = coordinates[0];
y1 = coordinates[1];
break;
case PathIterator.SEG_LINETO:
area += (coordinates[1] - y1) * (x1 + coordinates[0]) / 2.0;
x1 = coordinates[0];
y1 = coordinates[1];
break;
}
path.next();
}
area = Math.abs(((int) (area * 10)) / 10.0);
return area;
}
/** Compute the X-direction extension of the ROI, in <code>mm</code>. */
private double getWidth(Area roi) {
PathIterator path = roi.getPathIterator(new AffineTransform(), 0.1);
double[] coordinates = new double[6];
double minX = Double.MAX_VALUE, maxX = Double.MIN_VALUE;
while (path.isDone() == false) {
int type = path.currentSegment(coordinates);
if (coordinates[0] > maxX)
maxX = coordinates[0];
if (coordinates[0] < minX)
minX = coordinates[0];
path.next();
}
return ((int) Math.abs(maxX - minX) * 10) / 10.0;
}
/** Compute the Y-direction extension of the ROI, in <code>mm</code>. */
private double getHeight(Area roi) {
PathIterator path = roi.getPathIterator(new AffineTransform(), 0.1);
double[] coordinates = new double[6];
double minY = Double.MAX_VALUE, maxY = Double.MIN_VALUE;
while (path.isDone() == false) {
int type = path.currentSegment(coordinates);
if (coordinates[1] > maxY)
maxY = coordinates[1];
if (coordinates[1] < minY)
minY = coordinates[1];
path.next();
}
return ((int) Math.abs(maxY - minY) * 10) / 10.0;
}
/** Return the minimum pixel value in the provided ROI. */
private int[] getMinValue() {
int numBands = histogram.getNumBands();
int numBins = histogram.getNumBins(0);
int[] minValue = new int[numBands];
double[] lowValue = histogram.getLowValue();
double[] highValue = histogram.getHighValue();
for (int b = 0; b < numBands; b++) {
int[] bins = histogram.getBins(b);
for (int i = 0; i < numBins; i++) {
if (bins[i] > 0) {
minValue[b] = (int) (i * (highValue[b] - lowValue[b])
/ numBins + lowValue[b]);
break;
}
}
}
return minValue;
}
/** Return the maximum pixel value of the provided ROI. */
private int[] getMaxValue() {
int numBands = histogram.getNumBands();
int numBins = histogram.getNumBins(0);
int[] maxValue = new int[numBands];
double[] lowValue = histogram.getLowValue();
double[] highValue = histogram.getHighValue();
for (int b = 0; b < numBands; b++) {
int[] bins = histogram.getBins(b);
for (int i = numBins - 1; i >= 0; i--) {
if (bins[i] > 0) {
maxValue[b] = (int) (i * (highValue[b] - lowValue[b])
/ numBins + lowValue[b]);
break;
}
}
}
return maxValue;
}
/**
* Draw the histogram in a RenderedImage for display. The color of the bar
* for each bin is defined by the provided LUT.
*
* @throws RuntimeException
* When the bands have different number of bins.
*/
private RenderedImage displayHistogram(Histogram h) {
int numBands = h.getNumBands();
int numBins = h.getNumBins(0);
for (int b = 1; b < numBands; b++) {
if (h.getNumBins(b) != numBins) {
throw new RuntimeException("All bands must have same numBins.");
}
}
// compute the maximum count
double maxCount = 0;
for (int b = 0; b < numBands; b++) {
int[] bins = h.getBins(b);
for (int i = 0; i < numBins; i++) {
if (bins[i] > maxCount) {
maxCount = bins[i];
}
}
}
// define the size of the image and create the image
int width = 2 * numBins + 70;
int height = numBins + 30;
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_3BYTE_BGR);
// set background color
Graphics2D g = bi.createGraphics();
int maxVal = numBins - 1;
g.setColor(new Color(10, 128, 128));
g.fillRect(0, 0, width, height);
// set the area for the rulers to the background color
g.setColor(new Color(10, 128, 128));
g.fillRect(0, maxVal + 1, width, height);
// draw the coordinate axes
g.setColor(Color.green);
g.drawLine(40, 0, 40, maxVal);
g.drawLine(35, 0, 40, 0);
g.drawLine(35, maxVal, 40, maxVal);
g.drawString("0", 10, maxVal);
g.drawString("" + ((int) maxCount), 0, 10);
// draw the ticks and the labels
g.drawLine(40, maxVal, width - 30, maxVal);
for (int i = 0; i <= 8; i++) {
int x = 40 + i * numBins / 4;
g.drawLine(x, maxVal, x, maxVal + 5);
String label = "" + (i * ((int) h.getHighValue()[0]) / 8);
g.drawString(label, x - label.length() * 3, maxVal + 18);
}
g.dispose();
return bi;
}
}