use of org.diirt.util.array.IteratorNumber in project yamcs-studio by yamcs.
the class StatisticsUtil method statisticsOf.
/**
* Calculates data statistics, excluding NaN values.
*
* @param data the data
* @return the calculated statistics
*/
public static Statistics statisticsOf(CollectionNumber data) {
IteratorNumber iterator = data.iterator();
if (!iterator.hasNext()) {
return null;
}
int count = 0;
double min = iterator.nextDouble();
while (Double.isNaN(min)) {
if (!iterator.hasNext()) {
return null;
} else {
min = iterator.nextDouble();
}
}
double max = min;
double total = min;
double totalSquare = min * min;
count++;
while (iterator.hasNext()) {
double value = iterator.nextDouble();
if (!Double.isNaN(value)) {
if (value > max)
max = value;
if (value < min)
min = value;
total += value;
totalSquare += value * value;
count++;
}
}
double average = total / count;
double stdDev = Math.sqrt(totalSquare / count - average * average);
return new StatisticsImpl(Ranges.range(min, max), count, average, stdDev);
}
use of org.diirt.util.array.IteratorNumber in project yamcs-studio by yamcs.
the class HistogramOfFormulaFunction method calculate.
@Override
public Object calculate(List<Object> args) {
VNumberArray numberArray = (VNumberArray) args.get(0);
if (numberArray == null) {
return null;
}
// If no change, return previous
if (previousValue == numberArray) {
return previousResult;
}
Statistics stats = StatisticsUtil.statisticsOf(numberArray.getData());
int nBins = 100;
Range aggregatedRange = Ranges.aggregateRange(stats.getRange(), previousXRange);
Range xRange;
if (Ranges.overlap(aggregatedRange, stats.getRange()) >= 0.75) {
xRange = aggregatedRange;
} else {
xRange = stats.getRange();
}
IteratorNumber newValues = numberArray.getData().iterator();
double minValueRange = xRange.getMinimum();
double maxValueRange = xRange.getMaximum();
ListNumber xBoundaries = ListNumbers.linearListFromRange(minValueRange, maxValueRange, nBins + 1);
String unit = numberArray.getUnits();
int[] binData = new int[nBins];
double maxCount = 0;
while (newValues.hasNext()) {
double value = newValues.nextDouble();
// Check value in range
if (xRange.contains(value)) {
int bin = (int) Math.floor(xRange.normalize(value) * nBins);
if (bin == nBins) {
bin--;
}
binData[bin]++;
if (binData[bin] > maxCount) {
maxCount = binData[bin];
}
}
}
if (previousMaxCount > maxCount && previousMaxCount < maxCount * 2.0) {
maxCount = previousMaxCount;
}
previousMaxCount = maxCount;
previousXRange = xRange;
previousValue = numberArray;
previousResult = newVNumberArray(new ArrayInt(binData), new ArrayInt(nBins), Arrays.asList(newDisplay(xBoundaries, unit)), numberArray, numberArray, newDisplay(0.0, 0.0, 0.0, "count", NumberFormats.format(0), maxCount, maxCount, maxCount, Double.NaN, Double.NaN));
return previousResult;
}
use of org.diirt.util.array.IteratorNumber in project org.csstudio.display.builder by kasemir.
the class ImagePlot method drawDataRGB.
/**
* @param data_width
* @param data_height
* @param numbers
* @param next_rgbs
* @param type RGB type (RGB1, RGB2, or RGB3)
* @return {@link BufferedImage}, sized to match data
*/
private Object drawDataRGB(final int data_width, final int data_height, final ListNumber numbers, final ToIntFunction<IteratorNumber>[] next_rgbs, final VImageType type) {
if (data_width <= 0 || data_height <= 0) {
// With invalid size, cannot create a BufferedImage, not even for the error message
return "Cannot draw image sized " + data_width + " x " + data_height;
}
final BufferUtil buffer = data_buffers.getBufferedImage(data_width, data_height);
if (buffer == null)
return "Cannot get buffer";
final BufferedImage image = buffer.getImage();
if (numbers.size() < data_width * data_height * 3)
return "RGB image sized " + data_width + " x " + data_height + " received only " + numbers.size() + " data samples";
// Using direct access to 'int' pixels in data buffer for speed. See other drawData() for details.
final int[] data = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
final IteratorNumber iter = numbers.iterator();
switch(type) {
case TYPE_RGB2:
for (int y_times_width = 0; y_times_width < data_height * data_width; y_times_width += data_width) {
// red
for (int x = 0; x < data_width; ++x) data[y_times_width + x] = next_rgbs[0].applyAsInt(iter);
;
// green
for (int x = 0; x < data_width; ++x) data[y_times_width + x] |= next_rgbs[1].applyAsInt(iter);
// blue
for (int x = 0; x < data_width; ++x) data[y_times_width + x] |= next_rgbs[2].applyAsInt(iter);
}
break;
case TYPE_RGB3:
// red
for (int i = 0; i < data_height * data_width; ++i) data[i] = next_rgbs[0].applyAsInt(iter);
// green
for (int i = 0; i < data_height * data_width; ++i) data[i] |= next_rgbs[1].applyAsInt(iter);
// blue
for (int i = 0; i < data_height * data_width; ++i) data[i] |= next_rgbs[2].applyAsInt(iter);
break;
default:
throw new IllegalArgumentException("Image type must be an RGB type");
// no "break;"
case TYPE_RGB1:
for (int i = 0; i < data_height * data_width; ++i) data[i] = next_rgbs[0].applyAsInt(iter) | next_rgbs[1].applyAsInt(iter) | next_rgbs[2].applyAsInt(iter);
}
return image;
}
use of org.diirt.util.array.IteratorNumber in project org.csstudio.display.builder by kasemir.
the class ImagePlot method drawData.
/**
* @param data_width
* @param data_height
* @param numbers
* @param next_sample_func
* @param min
* @param max
* @param color_mapping
* @return {@link BufferedImage}, sized to match data or String with error message
*/
private Object drawData(final int data_width, final int data_height, final ListNumber numbers, final ToDoubleFunction<IteratorNumber> next_sample_func, double min, double max, final ColorMappingFunction color_mapping) {
if (data_width <= 0 || data_height <= 0) {
// With invalid size, cannot create a BufferedImage, not even for the error message
return "Cannot draw image sized " + data_width + " x " + data_height;
}
final BufferUtil buffer = data_buffers.getBufferedImage(data_width, data_height);
if (buffer == null)
return "Cannot get buffer";
final BufferedImage image = buffer.getImage();
if (numbers.size() < data_width * data_height)
return "Image sized " + data_width + " x " + data_height + " received only " + numbers.size() + " data samples";
if (// Implies min and max being finite, not-NaN
!(min < max)) {
logger.log(Level.WARNING, "Invalid value range {0} .. {1}", new Object[] { min, max });
min = 0.0;
max = 1.0;
}
// Direct access to 'int' pixels in data buffer is about twice as fast as access
// via image.setRGB(x, y, color.getRGB()),
// which in turn is about 3x faster than drawLine or fillRect.
// Creating a byte[] with one byte per pixel and ColorModel based on color map is fastest,
// but only 8 bits per pixel instead of 8 bits each for R, G and B isn't enough resolution.
// Rounding of values into 8 bits creates artifacts.
final int[] data = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
final IteratorNumber iter = numbers.iterator();
int idx = 0;
if (colorbar_axis.isLogarithmic()) {
final double lmin = Log10.log10(min), lmax = Log10.log10(max), span = lmax - lmin;
for (int y = 0; y < data_height; ++y) for (int x = 0; x < data_width; ++x) {
final double sample = Log10.log10(next_sample_func.applyAsDouble(iter));
double scaled = (sample - lmin) / span;
if (scaled < 0.0)
scaled = 0;
else if (scaled > 1.0)
scaled = 1.0;
data[idx++] = color_mapping.getRGB(scaled);
}
} else {
final double span = max - min;
for (int y = 0; y < data_height; ++y) for (int x = 0; x < data_width; ++x) {
final double sample = next_sample_func.applyAsDouble(iter);
double scaled = (sample - min) / span;
if (scaled < 0.0)
scaled = 0;
else if (scaled > 1.0)
scaled = 1.0;
data[idx++] = color_mapping.getRGB(scaled);
}
}
return image;
}
use of org.diirt.util.array.IteratorNumber in project org.csstudio.display.builder by kasemir.
the class ImagePlot method updateImageBuffer.
/**
* Draw all components into image buffer
*/
@Override
protected BufferedImage updateImageBuffer() {
// Would like to use JFX WritableImage,
// but rendering problem on Linux (sandbox.ImageScaling),
// and no way to disable the color interpolation that 'smears'
// the scaled image.
// (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8091877).
// So image is prepared in AWT and then converted to JFX
logger.log(Level.FINE, "updateImageBuffer");
final Rectangle area_copy = area;
if (area_copy.width <= 0 || area_copy.height <= 0)
return null;
final BufferUtil buffer = buffers.getBufferedImage(area_copy.width, area_copy.height);
if (buffer == null)
return null;
final BufferedImage image = buffer.getImage();
final Graphics2D gc = buffer.getGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gc.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
// Get safe copy of the data
// (not synchronized, i.e. width vs. data may be inconsistent,
// but at least data won't change within this method)
final int data_width = this.data_width, data_height = this.data_height;
final ListNumber numbers = this.image_data;
final boolean unsigned = this.unsigned_data;
double min = this.min, max = this.max;
final VImageType type = this.vimage_type;
final ColorMappingFunction color_mapping = this.color_mapping;
ToDoubleFunction<IteratorNumber> next_sample_func = IteratorNumber::nextDouble;
boolean isRGB = type == VImageType.TYPE_RGB1 || type == VImageType.TYPE_RGB2 || type == VImageType.TYPE_RGB3;
@SuppressWarnings("unchecked") final ToIntFunction<IteratorNumber>[] next_rgb = new ToIntFunction[3];
if (numbers != null) {
if (isRGB) {
if (numbers instanceof ArrayShort) {
if (unsigned) {
next_rgb[0] = (iter) -> getUShortForRGB(iter) << 8 & 0xFF0000;
next_rgb[1] = (iter) -> getUShortForRGB(iter) & 0xFF00;
next_rgb[2] = (iter) -> getUShortForRGB(iter) >>> 8;
} else {
next_rgb[0] = (iter) -> getShortForRGB(iter) << 8 & 0xFF0000;
next_rgb[1] = (iter) -> getShortForRGB(iter) & 0xFF00;
next_rgb[2] = (iter) -> getShortForRGB(iter) >>> 8;
}
}
if (numbers instanceof ArrayInt) {
if (unsigned) {
next_rgb[0] = (iter) -> getUIntForRGB(iter) >>> 8 & 0xFF0000;
next_rgb[1] = (iter) -> getUIntForRGB(iter) >>> 16 & 0xFF00;
next_rgb[2] = (iter) -> getUIntForRGB(iter) >>> 24;
} else {
next_rgb[0] = (iter) -> getIntForRGB(iter) >>> 8 & 0xFF0000;
next_rgb[1] = (iter) -> getIntForRGB(iter) >>> 16 & 0xFF00;
next_rgb[2] = (iter) -> getIntForRGB(iter) >>> 24;
}
} else {
if (!(numbers instanceof ArrayByte))
logger.log(Level.WARNING, "Cannot handle rgb1 image data of type " + numbers.getClass().getName());
if (unsigned) {
next_rgb[0] = (iter) -> getUByteForRGB(iter) << 16;
next_rgb[1] = (iter) -> getUByteForRGB(iter) << 8;
next_rgb[2] = (iter) -> getUByteForRGB(iter);
} else {
next_rgb[0] = (iter) -> getByteForRGB(iter) << 16;
next_rgb[1] = (iter) -> getByteForRGB(iter) << 8;
next_rgb[2] = (iter) -> getByteForRGB(iter);
}
}
} else // is not RGB
{
if (unsigned) {
if (numbers instanceof ArrayShort)
next_sample_func = ImagePlot::getUnsignedShort;
else if (numbers instanceof ArrayByte)
next_sample_func = ImagePlot::getUnsignedByte;
else if (numbers instanceof ArrayInt)
next_sample_func = ImagePlot::getUnsignedInt;
else
logger.log(Level.WARNING, "Cannot handle unsigned data of type " + numbers.getClass().getName());
}
if (autoscale) {
// Compute min..max before layout of color bar
final IteratorNumber iter = numbers.iterator();
min = Double.MAX_VALUE;
max = Double.NEGATIVE_INFINITY;
while (iter.hasNext()) {
final double sample = next_sample_func.applyAsDouble(iter);
if (sample > max)
max = sample;
if (sample < min)
min = sample;
}
logger.log(Level.FINE, "Autoscale range {0} .. {1}", new Object[] { min, max });
}
}
}
// If log, min needs to be 1
if (colorbar_axis.isLogarithmic() && min < 1.0)
min = 1;
colorbar_axis.setValueRange(min, max);
if (need_layout.getAndSet(false))
computeLayout(gc, area_copy, min, max);
// Fill with a 'background' color
gc.setColor(background);
gc.fillRect(0, 0, area_copy.width, area_copy.height);
if (numbers != null) {
// Paint the image
gc.setClip(image_area.x, image_area.y, image_area.width, image_area.height);
final Object image_or_error = !isRGB ? drawData(data_width, data_height, numbers, next_sample_func, min, max, color_mapping) : drawDataRGB(data_width, data_height, numbers, next_rgb, type);
if (image_or_error instanceof BufferedImage) {
final BufferedImage unscaled = (BufferedImage) image_or_error;
// Transform from full axis range into data range,
// using the current 'zoom' state of each axis
final LinearScreenTransform t = new LinearScreenTransform();
AxisRange<Double> zoomed = x_axis.getValueRange();
t.config(min_x, max_x, 0, data_width);
// Round down .. up to always cover the image_area
final int src_x1 = Math.max(0, (int) t.transform(zoomed.getLow()));
final int src_x2 = Math.min(data_width, (int) (t.transform(zoomed.getHigh()) + 1));
// Pixels of the image need to be aligned to their axis location,
// especially when zoomed way in and the pixels are huge.
// Turn pixel back into axis value, and then determine its destination on screen.
final int dst_x1 = x_axis.getScreenCoord(t.inverse(src_x1));
final int dst_x2 = x_axis.getScreenCoord(t.inverse(src_x2));
// For Y axis, min_y == bottom == data_height
zoomed = y_axis.getValueRange();
t.config(min_y, max_y, data_height, 0);
final int src_y1 = Math.max(0, (int) t.transform(zoomed.getHigh()));
final int src_y2 = Math.min(data_height, (int) (t.transform(zoomed.getLow()) + 1));
final int dst_y1 = y_axis.getScreenCoord(t.inverse(src_y1));
final int dst_y2 = y_axis.getScreenCoord(t.inverse(src_y2));
switch(interpolation) {
case NONE:
gc.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
break;
case INTERPOLATE:
gc.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
break;
default:
// If image is smaller than screen area, show the actual pixels
if ((src_x2 - src_x1) < image_area.width && (src_y2 - src_y1) < image_area.height)
gc.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
else
// If image is larger than screen area, use best possible interpolation
// to avoid artifacts from statistically picking some specific nearest neighbor
gc.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
}
gc.drawImage(unscaled, dst_x1, dst_y1, dst_x2, dst_y2, src_x1, src_y1, src_x2, src_y2, /* ImageObserver */
null);
} else {
gc.setColor(Color.RED);
gc.setFont(x_axis.label_font);
gc.drawString(Objects.toString(image_or_error), image_area.x + 10, image_area.y + 20);
}
gc.setClip(0, 0, area_copy.width, area_copy.height);
}
// Axes
y_axis.paint(gc, image_area);
x_axis.paint(gc, image_area);
// Color bar
if (colorbar_area != null) {
final BufferedImage bar = drawColorBar(min, max, color_mapping);
gc.drawImage(bar, colorbar_area.x, colorbar_area.y, colorbar_area.width, colorbar_area.height, null);
colorbar_axis.paint(gc, colorbar_area);
}
// ROI uses X axis font
gc.setFont(x_axis.label_font);
for (RegionOfInterest roi : rois) drawROI(gc, roi);
return image;
}
Aggregations