package cz.nkp.differ.compare.io; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import cz.nkp.differ.compare.metadata.ImageMetadata; import com.vaadin.data.util.BeanItemContainer; import com.vaadin.terminal.FileResource; import com.vaadin.terminal.Resource; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Component; import com.vaadin.ui.Embedded; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import java.io.File; import com.vaadin.ui.Layout; import com.vaadin.ui.Table; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window.Notification; import com.vaadin.ui.themes.BaseTheme; import cz.nkp.differ.DifferApplication; import cz.nkp.differ.gui.windows.FullSizeImageWindow; import cz.nkp.differ.gui.windows.HistogramSettingsWindow; import cz.nkp.differ.gui.windows.RawDataWindow; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import java.util.List; import javax.imageio.ImageIO; import org.apache.commons.io.FileUtils; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.vaadin.addon.JFreeChartWrapper; public class ImageFileAnalysisContainer { private ImageProcessorResult result; private CompareComponent parent; private XYPlot plot; private final int scaleFactor = 400; private JFreeChartWrapper chartComponent = null; private List<String> nonConflictMetadata = Arrays.asList("exit-code"); public ImageFileAnalysisContainer(ImageProcessorResult result, CompareComponent parent) { this.result = result; this.parent = parent; } public Image getImage() { return result.getPreview(); } public Layout getComponent() { final VerticalLayout layout = new VerticalLayout(); generateComponent(layout); return layout; } private void generateComponent(final Layout layout) { // Image preview final Resource imageFullResource = imageToResource(result.getFullImage()); final Resource imageScaledResource = imageToResource(result.getPreview()); Button imageButton = new Button(); imageButton.setStyleName(BaseTheme.BUTTON_LINK); imageButton.setIcon(imageScaledResource); if (imageFullResource != null) { imageButton.addListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { Embedded img = new Embedded(null, imageFullResource); img.setType(Embedded.TYPE_IMAGE); DifferApplication.getCurrentApplication().getMainWindow().addWindow(new FullSizeImageWindow(img)); } }); } layout.addComponent(imageButton); // Image checksum Label hashLabel = new Label(); hashLabel.setCaption(String.format("Hash: %s", result.getMD5Checksum())); layout.addComponent(hashLabel); // Histogram if (result.getType() == ImageProcessorResult.Type.IMAGE) { generateHistogramComponent(layout, false); } else { generateHistogramComponent(layout, true); } // Metadata table BeanItemContainer metadataContainer = new BeanItemContainer<ImageMetadata>(ImageMetadata.class, result.getMetadata()); metadataContainer.sort(new String[]{"key"}, new boolean[]{true}); final Table metadataTable = new Table("Metadata", metadataContainer); metadataTable.setSelectable(true); metadataTable.setMultiSelect(false); metadataTable.setImmediate(true); metadataTable.setVisibleColumns(new Object[]{"key", "source", "value", "unit"}); metadataTable.setCellStyleGenerator(new Table.CellStyleGenerator() { @Override public String getStyle(Object itemId, Object propertyId) { ImageMetadata metadata = (ImageMetadata) itemId; if (result.getType() == ImageProcessorResult.Type.COMPARISON) { String key = metadata.getKey(); if (Arrays.asList("red", "blue", "green").contains(key)) { return key; } } else { if (!nonConflictMetadata.contains(metadata.getKey())) { if (metadata.isConflict()) { return "red"; } else { return "green"; } } } return ""; } }); layout.addComponent(metadataTable); final Button rawData = new Button(); rawData.setCaption("Raw data"); rawData.addListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { try { ImageMetadata metadata = (ImageMetadata) metadataTable.getValue(); DifferApplication.getCurrentApplication().getMainWindow().addWindow(new RawDataWindow(parent, metadata.getSource())); } catch (Exception ex) { ex.printStackTrace(); } } }); rawData.setImmediate(true); rawData.setEnabled(false); //FIXME layout.addComponent(rawData); metadataTable.addListener(new ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { rawData.setEnabled(true); } }); } private void generateHistogramComponent(final Layout mainLayout, boolean logarithmic) { HorizontalLayout histogramLayout = new HorizontalLayout(); histogramLayout.setMargin(true); histogramLayout.setWidth("100%"); histogramLayout.setSpacing(true); this.chartComponent = createHistogramComponent(result.getHistogram(), logarithmic); mainLayout.addComponent(chartComponent); Button downloadButton = new Button(); downloadButton.setCaption("Download as CSV"); downloadButton.addListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { try { StringBuilder sb = new StringBuilder(); int[][] bins = result.getHistogram(); for (int i = 0; i < 256; i++) { sb.append(i).append(',').append(bins[0][i]).append(',').append(bins[1][i]).append(',').append(bins[2][i]).append("\n"); } File tmpFile = File.createTempFile("histogram", ".csv"); tmpFile.deleteOnExit(); FileUtils.writeByteArrayToFile(tmpFile, sb.toString().getBytes()); FileResource resource = new FileResource(tmpFile, parent.getApplication()); parent.getApplication().getMainWindow().open(resource); } catch (Exception ex) { parent.getApplication().getMainWindow().showNotification("Error when creating CSV file for upload", "", Notification.TYPE_ERROR_MESSAGE); } } }); histogramLayout.addComponent(downloadButton); if (result.getType() == ImageProcessorResult.Type.COMPARISON) { Button zoomButton = new Button(); zoomButton.setImmediate(true); zoomButton.setCaption("Setting"); zoomButton.addListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { double startX = plot.getDomainAxis().getLowerBound(); double endX = plot.getDomainAxis().getUpperBound(); double startY = plot.getRangeAxis().getLowerBound(); double endY = plot.getRangeAxis().getUpperBound(); final HistogramSettingsWindow zoomSettings = new HistogramSettingsWindow(new double[]{startX, endX, startY, endY}, true); ClickListener onSubmit = new ClickListener() { @Override public void buttonClick(ClickEvent event) { try { double[] xRange = zoomSettings.getXRange(); double[] yRange = zoomSettings.getYRange(); boolean logarithmic = zoomSettings.isLogarithmic(); JFreeChartWrapper newChartComponent = createHistogramComponent(result.getHistogram(), logarithmic, xRange[0], xRange[1], yRange[0], yRange[1]); mainLayout.replaceComponent(chartComponent, newChartComponent); chartComponent = newChartComponent; } catch (NumberFormatException nfe) { parent.getApplication().getMainWindow().showNotification("Invalid value", "", Notification.TYPE_ERROR_MESSAGE); } } }; zoomSettings.setOnSubmit(onSubmit); parent.getApplication().getMainWindow().addWindow(zoomSettings); } }); histogramLayout.addComponent(zoomButton); } mainLayout.addComponent(histogramLayout); } private JFreeChartWrapper createHistogramComponent(int[][] bins, boolean logarithmic) { return createHistogramComponent(bins, logarithmic, -1.0, -1.0, -1.0, -1.0); } private JFreeChartWrapper createHistogramComponent(int[][] bins, boolean logarithmic, double xStart, double xEnd, double yStart, double yEnd) { XYSeries redChannel = new XYSeries("Red"); XYSeries greenChannel = new XYSeries("Green"); XYSeries blueChannel = new XYSeries("Blue"); for (int i = 0; i < 256; i++) { if (logarithmic) { redChannel.add((double) i, (bins[0][i] > 0) ? Math.log10(bins[0][i]) : 0); greenChannel.add((double) i, (bins[1][i] > 0) ? Math.log10(bins[1][i]) : 0); blueChannel.add((double) i, (bins[2][i] > 0) ? Math.log10(bins[2][i]) : 0); } else { redChannel.add(i, bins[0][i]); greenChannel.add(i, bins[1][i]); blueChannel.add(i, bins[2][i]); } } XYSeriesCollection rgb = new XYSeriesCollection(); rgb.addSeries(redChannel); rgb.addSeries(greenChannel); rgb.addSeries(blueChannel); JFreeChart histogram = ChartFactory.createXYLineChart( "", "", "", rgb, //imageProcessor.getHistogramDataset(), PlotOrientation.VERTICAL, false, false, false); histogram.setBackgroundPaint(Color.WHITE); // get a reference to the plot for further customization... plot = histogram.getXYPlot(); if (xStart >= 0 && xEnd >= 0 && yStart >= 0 && yEnd >= 0) { plot.getDomainAxis(0).setLowerBound(xStart); plot.getDomainAxis(0).setUpperBound(xEnd); plot.getRangeAxis().setLowerBound(yStart); plot.getRangeAxis().setUpperBound(yEnd); } plot.setBackgroundPaint(Color.WHITE); plot.setDomainGridlinesVisible(true); plot.setRangeGridlinesVisible(true); plot.setRangeGridlinePaint(Color.GRAY); plot.setDomainGridlinePaint(Color.GRAY); JFreeChartWrapper chart = new JFreeChartWrapper(histogram, JFreeChartWrapper.RenderingMode.PNG); chart.setGraphHeight(scaleFactor); chart.setGraphWidth(scaleFactor); chart.setWidth(scaleFactor, Component.UNITS_PIXELS); chart.setHeight(scaleFactor, Component.UNITS_PIXELS); return chart; } public Resource imageToResource(Image img) { if (img == null) { return null; } if (img instanceof SerializableImage) { img = ((SerializableImage) img).getBufferedImage(); } try { String FILE_EXT = "png"; File temp = File.createTempFile("image", "." + FILE_EXT); OutputStream stream = new BufferedOutputStream(new FileOutputStream(temp)); /* Write the image to a buffer. */ BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics g = bimage.getGraphics(); g.drawImage(img, 0, 0, null); g.dispose(); ImageIO.setUseCache(false); ImageIO.write(bimage, FILE_EXT, stream); bimage = null; stream.flush(); stream.close(); return new FileResource(temp, parent.getApplication()); } catch (IOException e) { return null; } } }