package ie.dcu.segment.painters; import ie.dcu.segment.*; import ie.dcu.swt.ObservableImage; import org.eclipse.swt.graphics.*; import org.eclipse.swt.widgets.*; /** * An overlaid view of the image, segmentation mask, and markup. * * @author Kevin McGuinness */ public class SegmentPainter implements SegmentationPainter { public static final String NAME = "Segmented"; //here it applies private ImageData maskBorderData; private Image maskBorderImage; public String getName() { return NAME; } public String getDescription() { return "An overlaid view of the image, segmentation mask, and markup"; } public ImageData getMaskData(SegmentationContext ctx) { /*for(SegmentationMask segmentMask : ctx.getSegmentationMasks()) { if(segmentMask.enabled == true) { return segmentMask.getImageData(); } }*/ return maskBorderData; } public void paint(SegmentationContext ctx, ObservableImage im) { GC gc = im.beginPaint(); // Paint image gc.drawImage(ctx.getImage(), 0, 0); int numberOfMaskObjects = ctx.getSegmentationMasks().size(); // Paint all masks and its borders skipping the enabled mask. if(numberOfMaskObjects > 0) { // For drawing all the disabled masks. for(SegmentationMask segmentMask : ctx.getSegmentationMasks()) { if(segmentMask.enabled != true) { createVisibleMask(segmentMask,false); gc.drawImage(segmentMask.maskImage, 0, 0); createVisibleMaskBorder(segmentMask); gc.drawImage(maskBorderImage, 0, 0); } } // For drawing the enabled mask and its border.. for(SegmentationMask segmentMask : ctx.getSegmentationMasks()) { // Blending of background pixel values while pasting the last mask only if(segmentMask.enabled == true) { createVisibleMask(segmentMask,true); gc.drawImage(segmentMask.maskImage, 0, 0); createVisibleMaskBorder(segmentMask); gc.drawImage(maskBorderImage, 0, 0); } } // Current mask which has not yet been made into an object createVisibleMask(ctx.getMask(),false); gc.drawImage(ctx.getMask().getImage(), 0, 0); createVisibleMaskBorder(ctx.getMask()); gc.drawImage(maskBorderImage, 0, 0); } // Very initial mask, which has not yet been turned into an object. else { createVisibleMask(ctx.getMask(),true); gc.drawImage(ctx.getMask().getImage(), 0, 0); createVisibleMaskBorder(ctx.getMask()); gc.drawImage(maskBorderImage, 0, 0); } // Paint all annotations ctx.getAnnotations().paint(im); // Commit changes im.endPaint(); } /** * * @param mask * Mask object to be drawn. * @param isFirstObject * If set to True will blend the background and fore ground pixels. */ private void createVisibleMask(SegmentationMask mask,boolean enabled) { dispose(); if (isNewMaskDataRequired(mask)) { mask.maskImageData = createMaskData(mask.getBounds()); } //byte main_index; // Blit in pixels for (int y = 0, i = 0; y < mask.height; y++) { int rowOffset = y * mask.maskImageData.bytesPerLine; for (int x = 0; x < mask.width; x++) { byte alpha, index; switch (mask.values[i]) { case SegmentationMask.BACKGROUND: alpha = enabled ? (byte) 128 : (byte) 0; index = 0; break; case SegmentationMask.FOREGROUND: alpha = (byte) 128; index = (byte) (mask.layerNumber%3+1); break; default: alpha = 0; index = 0; break; } // The SWT ImageData buffer doesn't usually have it's rows aligned // contiguously in memory (i.e. there are > width bytes per scan-line in // the buffer), so we can't directly copy in at the same index as the // mask. mask.maskImageData.data[x + rowOffset] = index; // However, the alpha data is always aligned correctly mask.maskImageData.alphaData[i] = alpha; // Next location in the mask i++; //main_index = index; } } // Create new mask mask.maskImage = new Image(Display.getCurrent(), mask.maskImageData); } private void createVisibleMaskBorder(SegmentationMask mask) { dispose(); if (isNewMaskBorderDataRequired(mask.getBounds())) { maskBorderData = createMaskBorderData(mask.getBounds()); } // Set pixels byte[] buff = new byte[maskBorderData.width]; for (int y = 0, i = 0; y < mask.height-1; y++) { for (int x = 0; x < mask.width-1; x++) { // Make transparent buff[x] = 0; // Current pixel byte pix1 = mask.values[i]; // Neighbor to right & neighbor below byte pix2 = mask.values[i+1]; byte pix3 = mask.values[i+mask.width]; // Set pixel if either neighbor is different if (pix1 != pix2 || pix1 != pix3) { buff[x] = 1; } // Next pixel i++; } if (mask.width != 0) { // Last pixel in row byte pix1 = mask.values[i]; byte pix2 = mask.values[i+mask.width]; buff[mask.width-1] = (pix1 != pix2) ? (byte) 1 : 0; // Next row i++; } // Blit pixels maskBorderData.setPixels(0, y, buff.length, buff, 0); } // Last row if (mask.height != 0) { int yoff = (mask.height - 1) * mask.width; for (int x = 0; x < mask.width-1; x++) { byte pix1 = mask.values[x+yoff]; byte pix2 = mask.values[x+yoff+1]; buff[x] = (pix1 != pix2) ? (byte) 1 : 0; } // Blit maskBorderData.setPixels(0, mask.height-1, buff.length, buff, 0); } // Create new maskBorder maskBorderImage = new Image(Display.getCurrent(), maskBorderData); } private boolean isNewMaskDataRequired(SegmentationMask mask) { if (mask.getImageData() == null) { return true; } else { return mask.getImageData().width != mask.getBounds().width || mask.getImageData().height != mask.getBounds().height; } } private boolean isNewMaskBorderDataRequired(Rectangle bounds) { if (maskBorderData == null) { return true; } else { return maskBorderData.width != bounds.width || maskBorderData.height != bounds.height; } } private static ImageData createMaskData(Rectangle bounds) { RGB[] colors = new RGB[] { new RGB(0, 0, 0), new RGB(255,180,180), new RGB(180,255,180), new RGB(180,180,255) }; // Create 3 color indexed palette PaletteData palette = new PaletteData(colors); // Create 8 bit indexed image ImageData data = new ImageData(bounds.width, bounds.height, 8, palette); // Create alpha mask data.alphaData = new byte[bounds.width*bounds.height]; // Create and return the image return data; } private static ImageData createMaskBorderData(Rectangle bounds) { RGB[] colors = new RGB[] { new RGB(255,255,255), new RGB(255,140,0) }; // Create binary indexed palette PaletteData palette = new PaletteData(colors); // Create 1 bit indexed image ImageData data = new ImageData( bounds.width, bounds.height, 1, palette); // Set transparent pixel data.transparentPixel = 0; // Create and return the image return data; } public void dispose() { /* // Dispose mask if (maskImage != null) { if (!maskImage.isDisposed()) { maskImage.dispose(); } maskImage = null; } if (maskBorderImage != null) { if (!maskBorderImage.isDisposed()) { maskBorderImage.dispose(); } maskBorderImage = null; }*/ } }