package ie.dcu.apps.ist.export.imagemap; import ie.dcu.apps.ist.Application; import ie.dcu.apps.ist.views.SegmentationView; import ie.dcu.image.ContourTracer; import ie.dcu.segment.SegmentationContext; import ie.dcu.segment.SegmentationMask; import java.awt.Polygon; import java.awt.image.*; import java.io.*; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.imageio.ImageIO; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Exports HTML image maps from a segmentation mask and an image. * * @author Kevin McGuinness */ public class Exporter { private final BufferedImage image; private File file; private RolloverEffect effect; private String htmlFile = "imagemap.html"; private String zipFile = ""; private String imageFile = "image.png"; private String imageName = "image"; private String origImageFileName = ""; private String objectDescription = ""; private String objectLink = ""; private AreaShape exportShape = AreaShape.Polygon; public Exporter(BufferedImage image, SegmentationContext ctx) { this.image = image; this.origImageFileName = ctx.getFile().getName(); } public RolloverEffect getEffect() { return effect; } public void setEffect(RolloverEffect effect) { this.effect = effect; } public String getHtmlFile() { return htmlFile; } public void setHtmlFile(String htmlFile) { this.htmlFile = htmlFile; } public String getZipFile() { return zipFile; } public void setZipFile(String zipFile) { this.zipFile = zipFile; } public String getImageFile() { return imageFile; } public void setImageFile(String imageFile) { this.imageFile = imageFile; } public String getImageName() { return imageName; } public void setImageName(String imageName) { this.imageName = imageName; } public String getObjectDescription() { return objectDescription; } public void setObjectDescription(String description) { this.objectDescription = description; } public String getObjectLink() { return objectLink; } public void setObjectLink(String link) { this.objectLink = link; } public AreaShape getExportShape() { return exportShape; } public void setExportShape(AreaShape shape) { this.exportShape = shape; } public void export(File folder, boolean exportHTML, File exportFolder, List masks) throws IOException, ExportException { // Create a zip file for saving imageMaps file = new File(folder,zipFile); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file)); // Create image map ImageMap map = new ImageMap(); map.setImageHref(imageFile); map.setImageName(imageName); map.setCuratorName(SegmentationView.curatorCombo.getText()); map.setSpeciesName(SegmentationView.speciesCombo.getText()); map.setCollectionId(SegmentationView.collectionId.getText()); map.setComments(SegmentationView.comment.getText()); List preloads = new ArrayList(); map.addPreload(imageFile); try { // Low (fast) compression.. most of data is PNG compressed anyway out.setLevel(9); out.flush(); // Create an entry for the image ZipEntry entryImage = new ZipEntry(imageFile); // Start the entry out.putNextEntry(entryImage); ImageIO.write(image, "png", out); // Saving the annotation specific metadata ZipEntry metadata = new ZipEntry("metadata.xml"); out.putNextEntry(metadata); saveMetadata(out,masks); for(SegmentationMask mask : masks) { ContourTracer tracer = new ContourTracer(mask, SegmentationMask.FOREGROUND); List trace = tracer.trace(); if (trace.size() == 0) { throw new ExportException("No objects found"); } preloads.clear(); preloads.add(getPreloads(trace, mask.layerNumber).get(0)); // Write image if (exportHTML) { ImageIO.write(image, "png", new File(exportFolder, imageFile)); } if (effect != null) { int i = 0; // Generate effect images for (Polygon object : trace) { RenderedImage im = effect.createEffect(image, object); // '0' for Rollover effect File exportOut = null; if (exportHTML) { exportOut = new File(exportFolder, preloads.get(i)); } // Low (fast) compression.. most of data is PNG compressed anyway //out.setLevel(0); // Create an entry for the image ZipEntry entryMaskImage = new ZipEntry(preloads.get(i).toString()); // Start the entry out.putNextEntry(entryMaskImage); // Writing to the Zip file ImageIO.write(im, "png", out); // writing to the folder if (exportHTML) { ImageIO.write(im, "png", exportOut); } // Saving the marked up annotations ZipEntry markupData = new ZipEntry("markup-"+mask.layerNumber+".dat"); out.putNextEntry(markupData); saveMaskByteValues(out,mask); } } // Add javascript preloads for (String str : preloads) { map.addPreload(str); } // Add areas for (Polygon polygon : trace) { MapArea area = new MapArea(); switch (exportShape) { case Polygon: area.setPolygon(polygon); break; case Rectangle: area.setRect(polygon.getBounds()); break; case Circle: // TODO: Implement circle! default: area.setPolygon(polygon); break; } //area.setAlt(objectDescription); //area.setHref(objectLink); if (effect != null) { area.addAttr("onmouseover", String.format("rollover(document.%s, image%s)", imageName, mask.layerNumber)); area.addAttr("onmouseout", String.format("rollover(document.%s, image0)", imageName)); area.addAttr("title",mask.ontologyTerm.getName() + " {" + mask.ontologyTerm.getAccessionId() + "}"); } map.addArea(area); } } // Exporting to Folder if (exportHTML) { map.exportToFile(new File(exportFolder, htmlFile)); } } finally { out.flush(); out.close(); // close the output zip file stream (makes the zip file) } } private void saveMaskByteValues(OutputStream o, SegmentationMask mask) throws IOException { DataOutputStream out = new DataOutputStream(new BufferedOutputStream(o)); byte[] intensityValues = mask.values; out.write(intensityValues); } private void saveMetadata(OutputStream o, List masks) throws IOException { DataOutputStream out = new DataOutputStream(new BufferedOutputStream(o)); try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.newDocument(); Element rootElement = document.createElement("image_data"); document.appendChild(rootElement); Element versionElement = document.createElement("app_version"); versionElement.setTextContent(Application.APP_VERSION); rootElement.appendChild(versionElement); Element imageElement = document.createElement("orig_image_filename"); imageElement.setTextContent(this.origImageFileName); rootElement.appendChild(imageElement); Element speciesElement = document.createElement("species"); speciesElement.setAttribute("name", SegmentationView.speciesCombo.getText()); if (SegmentationView.speciesCombo.getData(SegmentationView.speciesCombo.getText()) != null) { speciesElement.setAttribute("ubio_id", (SegmentationView.speciesCombo.getData(SegmentationView.speciesCombo.getText())).toString()); } else { speciesElement.setAttribute("ubio_id",""); } rootElement.appendChild(speciesElement); Element curatorElement = document.createElement("curator_name"); curatorElement.setTextContent(SegmentationView.curatorCombo.getText()); rootElement.appendChild(curatorElement); Element commentElement = document.createElement("comment"); commentElement.setTextContent(SegmentationView.comment.getText()); rootElement.appendChild(commentElement); Element collectionIdElement = document.createElement("collection_id"); collectionIdElement.setTextContent(SegmentationView.collectionId.getText()); rootElement.appendChild(collectionIdElement); Element segmentsElement = document.createElement("segments"); rootElement.appendChild(segmentsElement); for (SegmentationMask mask : masks) { Element segmentElement = document.createElement("segment"); segmentsElement.appendChild(segmentElement); String url = ""; Element urlElem = document.createElement("url"); urlElem.appendChild(document.createTextNode(url)); segmentElement.appendChild(urlElem); String layer = ""+mask.layerNumber; Element layerElem = document.createElement("layer"); layerElem.appendChild(document.createTextNode(layer)); segmentElement.appendChild(layerElem); String annotationTerm = mask.ontologyTerm.getName(); Element annotationTermElem = document.createElement("annotation_term"); annotationTermElem.appendChild(document.createTextNode(annotationTerm)); segmentElement.appendChild(annotationTermElem); String annotationId = mask.ontologyTerm.getAccessionId(); Element annotationIdElem = document.createElement("annotation_id"); annotationIdElem.appendChild(document.createTextNode(annotationId)); segmentElement.appendChild(annotationIdElem); // Functionality for saving polygon coordinates ContourTracer tracer = new ContourTracer(mask, SegmentationMask.FOREGROUND); List trace = tracer.trace(); String polygonCoords = ""; for (Polygon polygon : trace) { for (int i = 0; i < polygon.npoints; i++) { polygonCoords = polygonCoords+polygon.xpoints[i]+","+polygon.ypoints[i]+","; } } Element polygonCoordsElem = document.createElement("polygon_coords"); polygonCoordsElem.appendChild(document.createTextNode(polygonCoords)); segmentElement.appendChild(polygonCoordsElem); } TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(document); StreamResult result = new StreamResult(out); transformer.transform(source, result); }catch (ParserConfigurationException pce) { pce.printStackTrace(); }catch (TransformerException tfe) { tfe.printStackTrace(); } } private List getPreloads(List trace, int layerNumber) { List preloads = new ArrayList(); //if (effect != null) { //String basename = FileUtils.removeExtension(imageFile); String basename = "segment"; for (int i = 0; i < trace.size(); i++) { String filename = String.format("%s-%d.png", basename, layerNumber); preloads.add(filename); } //} return preloads; } }