package ie.dcu.segment; import ie.dcu.apps.ist.labelling.SpeciesTerm; import ie.dcu.apps.ist.views.SegmentationView; import ie.dcu.segment.annotate.*; import ie.dcu.segment.util.*; import ie.dcu.swt.SwtUtils; import ie.dcu.util.FileUtils; import java.io.*; import java.util.ArrayList; import java.util.logging.Logger; import java.util.zip.*; //import javax.xml.bind.DatatypeConverter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.*; import org.eclipse.swt.widgets.Display; import org.w3c.dom.Document; import org.w3c.dom.Element; //import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; /** * Instances of this class manage the context of an interactive segmentation * task. A segmentation context object is passed to an instance of * {@link Segmenter} when the segmentation is initialized or updated. The * segmentation context encapsulates the image being segmented, the current * segmentation mask, the and the annotations that have been applied to the * * image. * * * @author Kevin McGuinness */ public class SegmentationContext { private static final Logger log = Logger.getLogger(SegmentationContext.class.getName()); // Logger /** * The file extension given to a this object when it is saved on a disk */ public static final String EXTENSION = ".ctx"; /** * The original image data */ private final ImageData imageData; /** * The file that the context was loaded from or last saved to... this is * either the original image or a segmentation context file */ private File file; /** * For storing the mouse clicked point in Labelling mode */ public static Point mouseClickedPoint; /** * For storing independent Segmentation objects (i.e the masks which are formed into objects) */ private static ArrayList segmentationMaskObjects; // lazy create (use getters for these fields) private static AnnotationManager annotations; // SegmentationMask which has not yet been turned into an Object private static SegmentationMask currentSegmentMask; private Image image; private Rectangle bounds; /** * Internal constructor to create an instance of the segmentation context * from the given image data object and a file object. * * @param imageData * The image data object. * @param file * The file from which the image data was loaded. */ private SegmentationContext(ImageData imageData, File file) { this.imageData = imageData; this.file = file; segmentationMaskObjects = new ArrayList(); } /** * Returns true if the receiver is associated with an existing * segmentation context file. */ public boolean hasContextFile() { return isContextFile(file); } /** * Returns the file associated with the receiver. This is either the file * that the receiver was last stored to, or the image file that was used to * create the context. */ public File getFile() { return file; } /** * Get the folder containing the file associated with the receiver. * * @see #getFile() */ public File getFolder() { return file.getParentFile(); } /** * Return an appropriate filename that can be used for saving the receiver. * * @see #save(File) */ public String getDefaultFilename() { String name = file.getName(); if (hasContextFile()) { return name; } return FileUtils.replaceExtension(name, EXTENSION); } public final void nullifySegments() { currentSegmentMask = null; annotations.clear(); } /** * Returns the current annotations that have been applied to the image. * * These annotations can be converted to a raster using the * {@link AnnotationRasterizer} utility class. * * @return An instance of {@link AnnotationManager} */ public AnnotationManager getAnnotations() { if (annotations == null) { annotations = new AnnotationManager(); } return annotations; } /** * Returns the bounds of the current image and segmentation mask. * * @return An SWT Rectangle instance. */ public Rectangle getBounds() { if (bounds == null) { bounds = new Rectangle(0, 0, imageData.width, imageData.height); } return bounds; } public final static void formSegmentationObject() { // Makes needsHighlighting False for all the previous segments. disableAllSegments(); // Set needsHighlighting True for the current segment. currentSegmentMask.enabled = true; //currentSegmentMask.setAnnotations(getAnnotations()); segmentationMaskObjects.add(currentSegmentMask); // Making a new segmentationObject after storing the current SegmentationObject currentSegmentMask = null; annotations.clear(); } /** * @return A {@link SegmentationMask} instance. Returns the latest mask which hasn't yet turned into an object. */ public SegmentationMask getMask() { if (currentSegmentMask == null) { currentSegmentMask = new SegmentationMask(getBounds()); } return currentSegmentMask; } /** * All the masks excluding the current mask (i.e the mask which has not yet been made into a Segmentation object) * @return * A list of SegmentationMasks excluding the current mask which has not yet been made into a Segmentation object */ public ArrayList getSegmentationMasks() { return segmentationMaskObjects; } /** * @return * Returns boolean value based on whether there are segmentationMasks formed or not. */ public boolean hasSegmentationMasks() { return segmentationMaskObjects.size() != 0; } /** * For highlighting the clicked segmentObject * @param x, mouse clicked point x * @param y, mouse clicked point y */ public void enableClickedSegment(Point mouseClickedPoint) { for(SegmentationMask segmentMaskObject : segmentationMaskObjects) { if(segmentMaskObject.getPixel(mouseClickedPoint.x, mouseClickedPoint.y) == 1) { // Disable all previous segments disableAllSegments(); // Enable the currently clicked segment. segmentMaskObject.enabled = true; } } } public static void disableAllSegments() { if (!(segmentationMaskObjects.isEmpty())) { for(SegmentationMask currentSegmentMask : segmentationMaskObjects) { currentSegmentMask.enabled = false; } } } /** * @return A boolean value whether any mask is anabled or not. By default the last SegmentationMask created will have enabled status. */ public boolean isEnabled() { for(SegmentationMask SegmentMask : segmentationMaskObjects) { if(SegmentMask.enabled) { return true; } } return false; } /** * @return A {@link SegmentationMask} instance. Returns the mask which has enabled status. */ public SegmentationMask getEnabledMask() { for(SegmentationMask SegmentMask : segmentationMaskObjects) { if(SegmentMask.enabled == true) { return SegmentMask; } } return currentSegmentMask; } /** * Returns the current image being segmented. This method returns an an SWT * Image instance, constructing it if it is not already available. The * {@link #getImageData()} method returns an SWT ImageData representation of * the current image. * * @see #getImageData() */ public Image getImage() { if (image == null || image.isDisposed()) { image = new Image(Display.getCurrent(), imageData); } return image; } /** * Returns the image data. This is the preferred way of obtaining the image * pixels since it will never create a new SWT Image instance. The utility * classes {@link ImageArgbConverter} and {@link ImageByteConverter} can be * used to convert the image data to a pixel array. * * @return An SWT ImageData instance. */ public ImageData getImageData() { return imageData; } /** * Disposes of the SWT Image instance, if it exists. The Image instance will * be recreated the next time {@link #getImage()} is called. */ public void dispose() { // Dispose image if (image != null && !image.isDisposed()) { image.dispose(); image = null; } } /** * Returns true if the SWT Image managed by the receiver is * disposed. */ public boolean isDisposed() { if (image != null) { return image.isDisposed(); } return true; } /** * Create a new segmentation by loading the an image from the given file. * JPG, PNG, GIF, and BMP images are supported. * * @param image * An image file. * @return A new segmentation context object. * @throws IOException * If the segmentation context cannot be created. */ public static SegmentationContext create(File image) throws IOException { // Create a new segmentation context return new SegmentationContext(SwtUtils.loadImageData(image), image); } /** * Save the receiver to the given file. The context can be loaded again at a * later stage using the {@link #load(File)} method. * *

* The saved context is essentially a ZIP file that contains a PNG * representation of the current image, a PNG representation of the current * mask file, and a binary representation of the current annotations. *

* * @param file * A file. * @throws IOException * If there is error saving the receiver to the given file. */ public void save(File file) throws IOException { // Based on zip file format ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file)); try { // Low (fast) compression.. most of data is PNG compressed anyway out.setLevel(0); // Create an entry for the image ZipEntry entry = new ZipEntry("image.png"); // Start the entry out.putNextEntry(entry); // Store the image SwtUtils.saveImage(getImageData(), out, SWT.IMAGE_PNG); // End the entry out.closeEntry(); // Create an entry for the mask entry = new ZipEntry("mask.png"); // Start the entry out.putNextEntry(entry); // Save the mask //getMask().save(out); // End the entry out.closeEntry(); // Create an entry for the annotations entry = new ZipEntry("markup.dat"); // Put next entry out.putNextEntry(entry); // Store the markup getAnnotations().save(out); // close entry out.closeEntry(); } finally { // Close out.close(); // Set new filename this.file = file; } } /** * Load a segmentation context object from the disk. * * @param file * A file containing the saved context. * @return A new segmentation context object. * @throws IOException * If there is an error loading the segmentation context file. */ public static SegmentationContext load(File file) throws IOException { ZipInputStream in = new ZipInputStream(new BufferedInputStream( new FileInputStream(file))); ImageData imageData = null; SegmentationMask segmentMask = null; AnnotationManager annotations = null; try { ZipEntry entry; while ((entry = in.getNextEntry()) != null) { String name = entry.getName(); if (name.equals("image.png")) { imageData = SwtUtils.loadImageData(in); } else if (name.equals("mask.png")) { segmentMask = (SegmentationMask) SegmentationMask.read(in); } else if (name.equals("markup.dat")) { annotations = new AnnotationManager(); annotations.load(in); } in.closeEntry(); } } finally { in.close(); } if (imageData == null) { throw new IOException("No image found in context file"); } if (segmentMask == null) { throw new IOException("No mask found in context file"); } if (annotations == null) { throw new IOException("No annotations found in context file"); } SegmentationContext ctx = new SegmentationContext(imageData, file); ctx.currentSegmentMask = segmentMask; ctx.annotations = annotations; return ctx; } /** * Load a ImageMap object from the disk. * * @param file * A file containing the Imagemap. * @return A new segmentation context object. * @throws IOException * If there is an error loading the segmentation context file. */ public static SegmentationContext loadImageMap(File file) throws IOException { ZipInputStream in = new ZipInputStream(new BufferedInputStream( new FileInputStream(file))); ZipFile zip = new ZipFile(file); ImageData imageData = null; SegmentationMask segmentMask = null; String speciesTermName = "", collectionId = "", comment = "", curatorName = "",speciesTermId = ""; int width = 0,height = 0; int noOfSegments = 0; int noOfChildern = 0; String [][] tempStorage = null; SegmentationContext ctx = null; try { ZipEntry entry; while ((entry = in.getNextEntry()) != null) { String name = entry.getName(); if (name.equals("image.png")) { imageData = SwtUtils.loadImageData(in); ctx = new SegmentationContext(imageData, file); width = imageData.width; height = imageData.height; } else if (name.startsWith("markup")) { SegmentationMask mask = new SegmentationMask(width,height); InputStream input = zip.getInputStream(entry); segmentationMaskObjects.add(setMaskPixelValues(input,mask,width,height)); } else if (name.startsWith("metadata")) { try { InputStream is = zip.getInputStream(entry); Document doc = loadXMLFromZip(is); Element docEle = doc.getDocumentElement(); // getting species attributes NodeList speciesNode = docEle.getElementsByTagName("species"); Element speciesEle = (Element)speciesNode.item(0); speciesTermName = speciesEle.getAttribute("name"); speciesTermId = speciesEle.getAttribute("ubio_id"); // getting curator names NodeList curatorNode = docEle.getElementsByTagName("curator_name"); Element curatorEle = (Element)curatorNode.item(0); curatorName = curatorEle.getTextContent(); // getting comment NodeList commentNode = docEle.getElementsByTagName("comment"); Element commentEle = (Element)commentNode.item(0); comment = commentEle.getTextContent(); // getting collection ids NodeList collectionNode = docEle.getElementsByTagName("collection_id"); Element collectionEle = (Element)collectionNode.item(0); collectionId = collectionEle.getTextContent(); // getting collection ids NodeList nlist = docEle.getElementsByTagName("segment"); noOfSegments = nlist.getLength(); noOfChildern = nlist.item(0).getChildNodes().getLength(); tempStorage = new String[noOfSegments][noOfChildern]; if(nlist != null && nlist.getLength() > 0) { for(int i = 0 ; i < nlist.getLength();i++) { Element segEle = (Element)nlist.item(i); NodeList segDetails = segEle.getChildNodes(); if(segDetails != null && segDetails.getLength() > 0) { for(int x = 0 ; x < segDetails.getLength();x++) { Element segDet = (Element)segDetails.item(x); tempStorage[i][x] = segDet.getTextContent(); } } } } SegmentationView.speciesCombo.setText(speciesTermName); SegmentationView.speciesCombo.setData(speciesTermName,speciesTermId); /*log.info(SegmentationView.speciesCombo.getText() + ":" + (SegmentationView.speciesCombo.getData(speciesTermName)).toString());*/ // TEST SegmentationView.curatorCombo.setText(curatorName); SegmentationView.collectionId.setText(collectionId); SegmentationView.comment.setText(comment); } catch(Exception e) { System.out.println("XML exception"+e.toString()); } } } } catch(Exception e) { System.out.println("Exception is "+e.toString()); } finally { for(int i = 0 ; i < noOfSegments;i++) { for(int x = 0 ; x < noOfChildern;x++) { if (x == 1) { segmentationMaskObjects.get(i).layerNumber = Integer.parseInt(tempStorage[i][x]); } if (x == 2) { segmentationMaskObjects.get(i).ontologyTerm.setName(tempStorage[i][x]); } if (x == 3) { segmentationMaskObjects.get(i).ontologyTerm.setAccessionId(tempStorage[i][x]); } } } in.close(); } SegmentationView.comboLabel.setText(tempStorage[noOfSegments-1][2]); SegmentationView.termDetailLookup(ctx.getEnabledMask().ontologyTerm.getAccessionId()); return ctx; } private static SegmentationMask setMaskPixelValues(InputStream in, SegmentationMask mask, int width, int height) throws IOException { try { DataInputStream input = new DataInputStream(new BufferedInputStream(in)); int length = width * height; //boolean[] intensity = new boolean[length]; byte[] intensity = new byte[length]; int index = 0; boolean EOF = false; while(!EOF) { try { intensity[index] = (byte)input.read(); } catch(Exception e) { EOF = true; } index = index+1; } int i = 0; for (int y=0;ytrue if the file appears to be a stored segmentation * context file. */ public static boolean isContextFile(File file) { if (file != null) { String name = file.getName().toLowerCase(); return name.endsWith(EXTENSION); } return false; } /** * Add an annotation listener. This method simply forwards the call to the * {@link AnnotationManager} class. * * @param listener * An {@link AnnotationListener} instance. */ public void addAnnotationListener(AnnotationListener listener) { getAnnotations().addAnnotationListener(listener); } /** * Remove an annotation listener. This method simply forwards the call to * the {@link AnnotationManager} class. * * @param listener * An {@link AnnotationListener} instance. */ public void removeAnnotationListener(AnnotationListener listener) { getAnnotations().removeAnnotationListener(listener); } }