Initial version of stroke core segmentation plugin.
authormccreedy@NIH.GOV <mccreedy@NIH.GOV@ba61647d-9d00-f842-95cd-605cb4296b96>
Mon, 5 Feb 2018 16:39:50 +0000 (16:39 +0000)
committermccreedy@NIH.GOV <mccreedy@NIH.GOV@ba61647d-9d00-f842-95cd-605cb4296b96>
Mon, 5 Feb 2018 16:39:50 +0000 (16:39 +0000)
git-svn-id: https://citdcbmipav.cit.nih.gov/repos-pub/mipav/trunk@15360 ba61647d-9d00-f842-95cd-605cb4296b96

mipav/src/plugins/PlugInAlgorithmStrokeSegmentation.java [new file with mode: 0644]
mipav/src/plugins/PlugInDialogStrokeSegmentation.java [new file with mode: 0644]
mipav/src/plugins/PlugInStrokeSegmentation.java [new file with mode: 0644]

diff --git a/mipav/src/plugins/PlugInAlgorithmStrokeSegmentation.java b/mipav/src/plugins/PlugInAlgorithmStrokeSegmentation.java
new file mode 100644 (file)
index 0000000..99c74c2
--- /dev/null
@@ -0,0 +1,538 @@
+import gov.nih.mipav.model.algorithms.*;\r
+import gov.nih.mipav.model.file.*;\r
+\r
+import gov.nih.mipav.model.structures.*;\r
+\r
+import gov.nih.mipav.view.*;\r
+import gov.nih.mipav.view.dialogs.JDialogFuzzyCMeans;\r
+\r
+import java.io.*;\r
+import java.nio.file.*;\r
+import java.util.Stack;\r
+import java.util.Vector;\r
+\r
+import org.apache.commons.csv.*;\r
+\r
+\r
+public class PlugInAlgorithmStrokeSegmentation extends AlgorithmBase {\r
+    private ModelImage dwiImage;\r
+    \r
+    private ModelImage dwiSeg;\r
+    \r
+    private ModelImage adcImage;\r
+    \r
+    private int adcThreshold;\r
+    \r
+    private VOI coreVOI;\r
+    \r
+    private MaskObject largestObject;\r
+    \r
+    private FileIO fileIO;\r
+    \r
+    private int minAdcObjectSize = 10;\r
+    private int maxAdcObjectSize = 100000;\r
+    \r
+    private static final String outputBasename = "CoreSeg";\r
+    \r
+    private static final String voiExtension = ".xml";\r
+    \r
+    private String coreOutputDir;\r
+    \r
+    /**\r
+     * Constructor.\r
+     *\r
+     * @param  dwi  DWI image\r
+     * @param  adc  ADC image\r
+     */\r
+    public PlugInAlgorithmStrokeSegmentation(ModelImage dwi, ModelImage adc, int threshold, String outputDir) {\r
+        super();\r
+        \r
+        dwiImage = dwi;\r
+        adcImage = adc;\r
+        adcThreshold = threshold;\r
+        coreOutputDir = outputDir;\r
+    }\r
+    \r
+    /**\r
+     * Starts the algorithm.\r
+     */\r
+    public void runAlgorithm() {\r
+       // DWI -> fuzzy c means 4 class\r
+        fireProgressStateChanged("Segmenting DWI image ...");\r
+        fireProgressStateChanged(5);\r
+\r
+        final int nClasses = 4;\r
+        final int nPyramid = 4;\r
+        final int oneJacobiIter = 1;\r
+        final int twoJacobiIter = 2;\r
+        final float q = 2.0f;\r
+        final float oneSmooth = 2e4f;\r
+        final float twoSmooth = 2e5f;\r
+        final boolean outputGainField = false;\r
+        final int segmentation = AlgorithmFuzzyCMeans.HARD_ONLY;\r
+        final boolean cropBackground = false;\r
+        final float threshold = 0.0f;\r
+        final int maxIter = 200;\r
+        final float endTolerance = 0.01f;\r
+        final boolean wholeImage = true;\r
+        \r
+        ModelImage[] dwiSegArray = new ModelImage[1];\r
+        FileInfoBase fileInfo1;\r
+        dwiSegArray[0] = new ModelImage(ModelStorageBase.UBYTE, dwiImage.getExtents(), "dwi_hardSeg");\r
+        fileInfo1 = dwiSegArray[0].getFileInfo()[0];\r
+        fileInfo1.setResolutions(dwiImage.getResolutions(0));\r
+        fileInfo1.setUnitsOfMeasure(dwiImage.getUnitsOfMeasure());\r
+        dwiSegArray[0].setFileInfo(fileInfo1, 0);\r
+\r
+        AlgorithmFuzzyCMeans fcmAlgo = new AlgorithmFuzzyCMeans(dwiSegArray, dwiImage, nClasses, nPyramid, oneJacobiIter, twoJacobiIter, q, oneSmooth,\r
+                                           twoSmooth, outputGainField, segmentation, cropBackground, threshold, maxIter,\r
+                                           endTolerance, wholeImage);\r
+        \r
+        //final float[] centroids = getCentroid(dwiImage, nClasses);\r
+        final float[] centroids = JDialogFuzzyCMeans.getDefaultCentroids(dwiImage, nClasses, wholeImage, null, cropBackground, threshold);\r
+        fcmAlgo.setCentroids(centroids);\r
+        fcmAlgo.run();\r
+        fcmAlgo.finalize();\r
+        fcmAlgo = null;\r
+        \r
+        dwiSeg = dwiSegArray[0];\r
+        \r
+        fireProgressStateChanged(40);\r
+        \r
+        // extract class 4 as mask\r
+        fireProgressStateChanged("Extracting DWI mask ...");\r
+        fireProgressStateChanged(45);\r
+        \r
+        final int volLength = dwiImage.getExtents()[0] * dwiImage.getExtents()[1] * dwiImage.getExtents()[2];\r
+        short[] dwiSegBuffer = new short[volLength];\r
+        try {\r
+            dwiSeg.exportData(0, volLength, dwiSegBuffer);\r
+        } catch (IOException error) {\r
+            dwiSegBuffer = null;\r
+            displayError("Error on dwi segmentation export: " + dwiImage.getImageName());\r
+            setCompleted(false);\r
+            return;\r
+        }\r
+        \r
+        // clear all values not in class 4 (lesion/gray)\r
+        for (int i = 0; i < volLength; i++) {\r
+            if (dwiSegBuffer[i] != 4) {\r
+                dwiSegBuffer[i] = 0;\r
+            }\r
+        }\r
+        \r
+        try {\r
+            dwiSeg.importData(0, dwiSegBuffer, true);\r
+        } catch (IOException error) {\r
+            dwiSegBuffer = null;\r
+            displayError("Error on dwi segementation importData: " + dwiImage.getImageName());\r
+            setCompleted(false);\r
+            return;\r
+        }\r
+        \r
+        // output mask to disk\r
+        fireProgressStateChanged("Saving DWI mask ...");\r
+        fireProgressStateChanged(50);\r
+\r
+        saveImageFile(dwiSeg, coreOutputDir, outputBasename + "_DWI_seg");\r
+        \r
+        // get pixels from ADC within mask with intensity < 620\r
+        fireProgressStateChanged("Thresholding ADC ...");\r
+        fireProgressStateChanged(60);\r
+        \r
+        for (int i = 0; i < volLength; i++) {\r
+            if (dwiSegBuffer[i] != 0) {\r
+                if (adcImage.getInt(i) < adcThreshold) {\r
+                    dwiSegBuffer[i] = 1;\r
+                } else {\r
+                    dwiSegBuffer[i] = 0;\r
+                }\r
+            }\r
+        }\r
+        \r
+        try {\r
+            dwiSeg.importData(0, dwiSegBuffer, true);\r
+        } catch (IOException error) {\r
+            dwiSegBuffer = null;\r
+            displayError("Error on adc threshold importData: " + dwiImage.getImageName());\r
+            setCompleted(false);\r
+            return;\r
+        }\r
+        \r
+        saveImageFile(dwiSeg, coreOutputDir, outputBasename + "_ADC_thresh");\r
+        \r
+        // select largest object\r
+        // TODO: non-symmetric across midline?\r
+        fireProgressStateChanged("Finding core lesion ...");\r
+        fireProgressStateChanged(70);\r
+        \r
+        short[] objectBuffer = keepOnlyLargestObject(dwiSeg, dwiSegBuffer);\r
+        \r
+        try {\r
+            dwiSeg.importData(0, objectBuffer, true);\r
+        } catch (IOException error) {\r
+            dwiSegBuffer = null;\r
+            objectBuffer = null;\r
+            displayError("Error on adc threshold importData: " + dwiImage.getImageName());\r
+            setCompleted(false);\r
+            return;\r
+        }\r
+        \r
+        saveImageFile(dwiSeg, coreOutputDir, outputBasename + "_ADC_thresh_only_largest");\r
+        \r
+        // output core object to VOI on disk\r
+        fireProgressStateChanged("Saving core VOI ...");\r
+        fireProgressStateChanged(80);\r
+        \r
+        coreVOI = maskToVOI(dwiSeg);\r
+        if (!saveVOI(dwiSeg, coreVOI, coreOutputDir, outputBasename + "_VOI")) {\r
+            // problem saving voi\r
+            displayError("Error saving core VOI");\r
+            setCompleted(false);\r
+            return;\r
+        }\r
+        \r
+        // save core stats to tab-delmited file\r
+        if (!saveCoreStats(coreOutputDir, dwiImage.getImageFileName(), adcImage.getImageFileName(), outputBasename + "_VOI" + voiExtension, largestObject.size, adcImage.getResolutions(0))) {\r
+            setCompleted(false);\r
+            return;\r
+        }\r
+        \r
+        setCompleted(true);\r
+    }\r
+    \r
+    /**\r
+     * Prepares this class for destruction.\r
+     */\r
+    public void finalize() {\r
+        dwiImage = null;\r
+        adcImage = null;\r
+        if (dwiSeg != null) {\r
+            dwiSeg.disposeLocal();\r
+            dwiSeg = null;\r
+        }\r
+        super.finalize();\r
+    }\r
+    \r
+    public VOI getCoreVOI() {\r
+        return coreVOI;\r
+    }\r
+    \r
+    private void saveImageFile(final ModelImage img, final String dir, final String fileBasename) {\r
+        if (fileIO == null) {\r
+            fileIO = new FileIO();\r
+            fileIO.setQuiet(true);\r
+        }\r
+        \r
+        FileWriteOptions opts = new FileWriteOptions(true);\r
+        opts.setFileDirectory(dir);\r
+\r
+        if (img.getNDims() == 3) {\r
+            opts.setBeginSlice(0);\r
+            opts.setEndSlice(img.getExtents()[2] - 1);\r
+        } else if (img.getNDims() == 4) {\r
+            opts.setBeginSlice(0);\r
+            opts.setEndSlice(img.getExtents()[2] - 1);\r
+            opts.setBeginTime(0);\r
+            opts.setEndTime(img.getExtents()[3] - 1);\r
+        }\r
+\r
+        opts.setFileType(FileUtility.XML);\r
+        opts.setFileName(fileBasename + ".xml");\r
+\r
+        opts.setOptionsSet(true);\r
+        opts.setMultiFile(false);\r
+\r
+        fileIO.writeImage(img, opts, false, false);\r
+    }\r
+    \r
+    public short[] keepOnlyLargestObject(final ModelImage img, final short[] imgBuffer) {\r
+        final int imageLength = imgBuffer.length;\r
+        short[] processBuffer = new short[imgBuffer.length];\r
+        \r
+        Vector<MaskObject> objects = new Vector<MaskObject>();\r
+        \r
+        final int xDim = img.getExtents()[0];\r
+        final int yDim = img.getExtents()[1];\r
+        final int zDim = img.getExtents()[2];\r
+        short floodValue = 1;\r
+        int count = 0;\r
+        \r
+        deleteObjects(img, objects, imgBuffer, processBuffer, minAdcObjectSize, maxAdcObjectSize, true);\r
+\r
+        objects.removeAllElements();\r
+        \r
+        for (int i = 0; (i < imageLength) && !threadStopped; i++) {\r
+            if (imgBuffer[i] > 0) {\r
+                count = floodFill(imgBuffer, img.getExtents(), processBuffer, i, floodValue, imgBuffer[i]);\r
+                objects.addElement(new MaskObject(i, floodValue, count));\r
+                floodValue++;\r
+            }\r
+        }\r
+\r
+        String mStr;\r
+        float vol;\r
+\r
+        mStr = img.getFileInfo(0).getVolumeUnitsOfMeasureStr();\r
+        ViewUserInterface.getReference().getMessageFrame().getData().append(\r
+                " Object \t# of pixels\tVolume(" + mStr + ")\n");\r
+        \r
+        // default to no size\r
+        largestObject = new MaskObject(0, (short) 0, 0);\r
+\r
+        for (int i = 0; i < objects.size(); i++) {\r
+            final MaskObject curObj = objects.elementAt(i);\r
+            \r
+            if (curObj.size > largestObject.size) {\r
+                largestObject = curObj;\r
+            }\r
+            \r
+            vol = (curObj.size * img.getFileInfo(0).getResolutions()[0]\r
+                    * img.getFileInfo(0).getResolutions()[1] * img.getFileInfo(0).getResolutions()[2]);\r
+\r
+            // UI.setDataText(\r
+            ViewUserInterface.getReference().getMessageFrame().getData().append(\r
+                    "    " + (i + 1) + "\t" + + curObj.size + "\t" + vol + "\n");\r
+        }\r
+        \r
+        // set the mask value for any object that isn't the largest to 0\r
+        for (int i = 0; i < processBuffer.length; i++) {\r
+            if (processBuffer[i] != largestObject.id) {\r
+                processBuffer[i] = 0;\r
+            }\r
+        }\r
+        \r
+        return processBuffer;\r
+    }\r
+    \r
+    private void deleteObjects(final ModelImage img, final Vector<MaskObject> objects, short[] imgBuffer, short[] processBuffer, final int min, final int max, final boolean returnFlag) {\r
+        int i, pix;\r
+        int count;\r
+        final int xDim = img.getExtents()[0];\r
+        final int yDim = img.getExtents()[1];\r
+        final int zDim = img.getExtents()[2];\r
+        final int volumeLength = xDim * yDim * zDim;\r
+        short floodValue = 1;\r
+        short[] tmpBuffer;\r
+\r
+        objects.removeAllElements();\r
+\r
+        for (pix = 0; (pix < volumeLength) && !threadStopped; pix++) {\r
+            if (imgBuffer[pix] > 0) {\r
+                count = floodFill(imgBuffer, img.getExtents(), processBuffer, pix, floodValue, imgBuffer[pix]);\r
+                objects.addElement(new MaskObject(pix, floodValue, count));\r
+                floodValue++;\r
+            }\r
+        }\r
+\r
+        tmpBuffer = imgBuffer;\r
+        imgBuffer = processBuffer;\r
+        processBuffer = tmpBuffer;\r
+\r
+        for (i = 0; i < objects.size(); i++) {\r
+            if ( (objects.elementAt(i).size < min) || (objects.elementAt(i).size > max)) {\r
+                floodFill(imgBuffer, img.getExtents(), processBuffer, objects.elementAt(i).index, (short) 0,\r
+                        objects.elementAt(i).id);\r
+                objects.removeElementAt(i);\r
+                i--;\r
+            }\r
+        }\r
+\r
+        // relabel objects in order\r
+        for (i = 0; i < objects.size(); i++) {\r
+                floodFill(imgBuffer, img.getExtents(), processBuffer, objects.elementAt(i).index, (short) (i + 1),\r
+                        objects.elementAt(i).id);\r
+        }\r
+    }\r
+    \r
+    /**\r
+     * 3D flood fill that forms a short mask.\r
+     * \r
+     * @param imgBuffer buffer of image data being processed\r
+     * @param extents extents of image data being processed (3D)\r
+     * @param idBuffer buffer to store flooding results\r
+     * @param stIndex starting index indicating the starting location of the flood fill\r
+     * @param floodValue the value to flood the area with\r
+     * @param objValue DOCUMENT ME!\r
+     * \r
+     * @return DOCUMENT ME!\r
+     */\r
+    private int floodFill(final short[] imgBuffer, final int[] extents, final short[] idBuffer, final int stIndex, final short floodValue, final short objValue) {\r
+        final int xDim = extents[0];\r
+        final int yDim = extents[1];\r
+        final int zDim = extents[2];\r
+        final int sliceSize = xDim * yDim;\r
+        int x, y, z;\r
+        int indexZ, indexY;\r
+        int pixCount = 0;\r
+\r
+        Point3D pt;\r
+        Point3D tempPt;\r
+        final Point3D seed3DPt = new Point3D( (stIndex % sliceSize) % xDim, (stIndex % sliceSize) / xDim,\r
+                (stIndex / sliceSize));\r
+        final Stack<Point3D> stack = new Stack<Point3D>();\r
+\r
+        if (imgBuffer[ (seed3DPt.z * sliceSize) + (seed3DPt.y * xDim) + seed3DPt.x] > 0) {\r
+            stack.push(seed3DPt);\r
+            imgBuffer[ (seed3DPt.z * sliceSize) + (seed3DPt.y * xDim) + seed3DPt.x] = 0;\r
+\r
+            while ( !stack.empty()) {\r
+                pt = (Point3D) stack.pop();\r
+                x = pt.x;\r
+                y = pt.y;\r
+                z = pt.z;\r
+\r
+                indexZ = z * sliceSize;\r
+                indexY = y * xDim;\r
+                idBuffer[indexZ + indexY + x] = floodValue;\r
+                pixCount++;\r
+\r
+                if ( (x + 1) < xDim) {\r
+\r
+                    if (imgBuffer[indexZ + indexY + x + 1] == objValue) {\r
+                        tempPt = new Point3D(x + 1, y, z);\r
+                        stack.push(tempPt);\r
+                        imgBuffer[indexZ + indexY + tempPt.x] = 0;\r
+                    }\r
+                }\r
+\r
+                if ( (x - 1) >= 0) {\r
+\r
+                    if (imgBuffer[indexZ + indexY + x - 1] == objValue) {\r
+                        tempPt = new Point3D(x - 1, y, z);\r
+                        stack.push(tempPt);\r
+                        imgBuffer[indexZ + indexY + tempPt.x] = 0;\r
+                    }\r
+                }\r
+\r
+                if ( (y + 1) < yDim) {\r
+\r
+                    if (imgBuffer[indexZ + ( (y + 1) * xDim) + x] == objValue) {\r
+                        tempPt = new Point3D(x, y + 1, z);\r
+                        stack.push(tempPt);\r
+                        imgBuffer[indexZ + (tempPt.y * xDim) + x] = 0;\r
+                    }\r
+                }\r
+\r
+                if ( (y - 1) >= 0) {\r
+\r
+                    if (imgBuffer[indexZ + ( (y - 1) * xDim) + x] == objValue) {\r
+                        tempPt = new Point3D(x, y - 1, z);\r
+                        stack.push(tempPt);\r
+                        imgBuffer[indexZ + (tempPt.y * xDim) + x] = 0;\r
+                    }\r
+                }\r
+\r
+                if ( (z + 1) < zDim) {\r
+\r
+                    if (imgBuffer[ ( (z + 1) * sliceSize) + indexY + x] == objValue) {\r
+                        tempPt = new Point3D(x, y, z + 1);\r
+                        stack.push(tempPt);\r
+                        imgBuffer[ (tempPt.z * sliceSize) + indexY + x] = 0;\r
+                    }\r
+                }\r
+\r
+                if ( (z - 1) >= 0) {\r
+\r
+                    if (imgBuffer[ ( (z - 1) * sliceSize) + indexY + x] == objValue) {\r
+                        tempPt = new Point3D(x, y, z - 1);\r
+                        stack.push(tempPt);\r
+                        imgBuffer[ (tempPt.z * sliceSize) + indexY + x] = 0;\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        return pixCount;\r
+    }\r
+    \r
+    /**\r
+     * Simple class to temporarily store the object's size, ID and seed index value. This class is used by the\r
+     * identifyObjects and deleteObjects methods of AlgorithmMorphology3D class.\r
+     */\r
+    private class MaskObject {\r
+\r
+        /** DOCUMENT ME! */\r
+        public short id = 0;\r
+\r
+        /** DOCUMENT ME! */\r
+        public int index = 0;\r
+\r
+        /** DOCUMENT ME! */\r
+        public int size = 0;\r
+\r
+        /**\r
+         * Creates a new intObject object.\r
+         * \r
+         * @param idx seed index. Index is the location in the image\r
+         * @param objectID the flood seed having a value >= 0.\r
+         * @param objectSize the number of voxels in the object\r
+         */\r
+        public MaskObject(final int idx, final short objectID, final int objectSize) {\r
+            index = idx;\r
+            id = objectID;\r
+            size = objectSize;\r
+        }\r
+    }\r
+    \r
+    private VOI maskToVOI(ModelImage img) {\r
+        final AlgorithmVOIExtraction voiAlgo = new AlgorithmVOIExtraction(img);\r
+        voiAlgo.run();\r
+        return voiAlgo.getAddedVOI();\r
+    }\r
+    \r
+    private boolean saveVOI(final ModelImage img, final VOI voi, final String outputDir, final String voiBasename) {\r
+        final boolean saveAllContours = true;\r
+        final boolean overwriteVOI = true;\r
+\r
+        FileVOI fileVOI;\r
+        String extension = voiExtension;\r
+\r
+        try {\r
+            fileVOI = new FileVOI(voiBasename + extension, outputDir, img);\r
+            fileVOI.writeXML(voi, saveAllContours, overwriteVOI);\r
+        } catch (final IOException error) {\r
+            MipavUtil.displayError("Error writing VOI" + error);\r
+            return false;\r
+        }\r
+        \r
+        return true;\r
+    }\r
+    \r
+    private boolean saveCoreStats(final String outputDir, final String dwiFile, final String adcFile, final String voiFile, final long numVoxels, final float[] imgResol) {\r
+        double voxelSize = imgResol[0] * imgResol[1] * imgResol[2];\r
+        double coreVol = largestObject.size * voxelSize;\r
+        System.err.println("Largest Object:\t" + largestObject.size + "\t" + coreVol);\r
+        \r
+        final String statsFile = outputDir + File.separator + outputBasename + "_stats.table";\r
+        \r
+        final String volUnitsStr = adcImage.getFileInfo(0).getVolumeUnitsOfMeasureStr();\r
+        \r
+        CSVPrinter csvPrinter = null;\r
+        BufferedWriter writer = null;\r
+        try {\r
+            writer = Files.newBufferedWriter(Paths.get(statsFile));\r
+            \r
+            csvPrinter = new CSVPrinter(writer, CSVFormat.TDF.withHeader("Base Dir", "DWI File", "ADC File", "Core VOI", "Core Voxel Count", "Core Volume " + volUnitsStr));\r
+            csvPrinter.printRecord(outputDir, dwiFile, adcFile, voiFile, numVoxels, coreVol);\r
+        } catch (final IOException e) {\r
+            e.printStackTrace();\r
+            MipavUtil.displayError("Error writing core stats file: " + statsFile);\r
+            return false;\r
+        } finally {\r
+            try {\r
+                if (csvPrinter != null) {\r
+                    csvPrinter.flush();\r
+                    csvPrinter.close();\r
+                }\r
+                if (writer != null) {\r
+                    writer.close();\r
+                }\r
+            } catch (final IOException e) {\r
+                // do nothing\r
+            }\r
+        }\r
+        \r
+        return true;\r
+    }\r
+}\r
diff --git a/mipav/src/plugins/PlugInDialogStrokeSegmentation.java b/mipav/src/plugins/PlugInDialogStrokeSegmentation.java
new file mode 100644 (file)
index 0000000..e0db82b
--- /dev/null
@@ -0,0 +1,627 @@
+import gov.nih.mipav.plugins.JDialogStandaloneScriptablePlugin;\r
+\r
+import gov.nih.mipav.model.algorithms.*;\r
+import gov.nih.mipav.model.file.FileIO;\r
+import gov.nih.mipav.model.file.FileUtility;\r
+import gov.nih.mipav.model.scripting.*;\r
+import gov.nih.mipav.model.scripting.parameters.ParameterFactory;\r
+import gov.nih.mipav.model.structures.*;\r
+\r
+import gov.nih.mipav.view.*;\r
+import gov.nih.mipav.view.dialogs.AlgorithmParameters;\r
+import gov.nih.mipav.view.dialogs.JDialogScriptableBase;\r
+\r
+import java.awt.*;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.WindowEvent;\r
+import java.io.File;\r
+\r
+import javax.swing.*;\r
+\r
+\r
+public class PlugInDialogStrokeSegmentation extends JDialogStandaloneScriptablePlugin implements AlgorithmInterface {\r
+    private JRadioButton dirMethodRadio;\r
+    private JRadioButton fileMethodRadio;\r
+    \r
+    private JTextField dirFileField;\r
+    \r
+    private String dirFileString;\r
+    \r
+    private String outputDir;\r
+    \r
+    private JTextField adcImageFileField;\r
+    private JTextField dwiImageFileField;\r
+    \r
+    private JTextField adcThresholdField;\r
+\r
+    private boolean adcImageMultifile = false;\r
+    private ModelImage adcImage;\r
+    \r
+    private boolean dwiImageMultifile = false;\r
+    private ModelImage dwiImage;\r
+    \r
+    private int adcThreshold = 620;\r
+    \r
+    private PlugInAlgorithmStrokeSegmentation segAlgo = null;\r
+    \r
+    private static final String svnVersion = "$Rev$";\r
+\r
+    private static final String svnLastUpdate = "$Date$";\r
+    \r
+    private static final String pluginVersion = MipavUtil.getSVNChangedDate(svnLastUpdate);\r
+    \r
+    private static final String adcFileStub = "Baseline_ADC";\r
+    private static final String dwiFileStub = "Baseline_DWI";\r
+    \r
+    private static final String PREF_PLUGIN_STROKE_SEG_LAST_DIR = "PlugInStrokeSegLastDir";\r
+    private static final String PREF_PLUGIN_STROKE_SEG_LAST_ADC = "PlugInStrokeSegLastADC";\r
+    private static final String PREF_PLUGIN_STROKE_SEG_LAST_DWI = "PlugInStrokeSegLastDWI";\r
+    \r
+    private String lastDir = "";\r
+    private String lastDwi = "";\r
+    private String lastAdc = "";\r
+\r
+    /**\r
+     * Constructor used for instantiation during script execution (required for dynamic loading).\r
+     */\r
+    public PlugInDialogStrokeSegmentation() {\r
+        super(false);\r
+        \r
+        if (Preferences.isPreferenceSet(PREF_PLUGIN_STROKE_SEG_LAST_DIR)) {\r
+            lastDir = Preferences.getProperty(PREF_PLUGIN_STROKE_SEG_LAST_DIR);\r
+            if (lastDir == null) {\r
+                lastDir = "";\r
+            }\r
+        }\r
+        if (Preferences.isPreferenceSet(PREF_PLUGIN_STROKE_SEG_LAST_ADC)) {\r
+            lastAdc = Preferences.getProperty(PREF_PLUGIN_STROKE_SEG_LAST_ADC);\r
+            if (lastAdc == null) {\r
+                lastAdc = "";\r
+            }\r
+        }\r
+        if (Preferences.isPreferenceSet(PREF_PLUGIN_STROKE_SEG_LAST_DWI)) {\r
+            lastDwi = Preferences.getProperty(PREF_PLUGIN_STROKE_SEG_LAST_DWI);\r
+            if (lastDwi == null) {\r
+                lastDwi = "";\r
+            }\r
+        }\r
+        \r
+        init();\r
+    }\r
+    \r
+    /**\r
+     * Closes dialog box when the OK button is pressed and calls the algorithm.\r
+     *\r
+     * @param  event  Event that triggers function.\r
+     */\r
+    public void actionPerformed(ActionEvent event) {\r
+        String command = event.getActionCommand();\r
+\r
+        if (command.equals("OK")) {\r
+            if (setVariables()) {\r
+                callAlgorithm();\r
+            }\r
+        } else if (command.equals("BrowseDir")) {\r
+            if (browseDir()) {\r
+                dirMethodRadio.setSelected(true);\r
+            }\r
+        } else if (command.equals("BrowseDWI")) {\r
+            if (browseDWIImage()) {\r
+                fileMethodRadio.setSelected(true);\r
+            }\r
+        } else if (command.equals("BrowseADC")) {\r
+            if (browseADCImage()) {\r
+                fileMethodRadio.setSelected(true);\r
+            }\r
+        } else if (command.equals("DirMethod")) {\r
+            // TODO\r
+        } else if (command.equals("FileMethod")) {\r
+            // TODO\r
+        } else if (command.equals("Cancel")) {\r
+            this.windowClosing(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));\r
+        } else {\r
+            super.actionPerformed(event);\r
+        }\r
+    } \r
+\r
+    /**\r
+     * This method is required if the AlgorithmPerformed interface is implemented. It is called by the algorithm when it\r
+     * has completed or failed to to complete, so that the dialog can be display the result image and/or clean up.\r
+     *\r
+     * @param  algorithm  Algorithm that caused the event.\r
+     */\r
+    public void algorithmPerformed(AlgorithmBase algorithm) {\r
+        if (algorithm instanceof PlugInAlgorithmStrokeSegmentation) {\r
+            Preferences.debug("Stroke segmentation Elapsed: " + algorithm.getElapsedTime());\r
+\r
+            if (algorithm.isCompleted()) {\r
+                insertScriptLine();\r
+            }\r
+\r
+            if (algorithm != null) {\r
+                algorithm.finalize();\r
+                algorithm = null;\r
+            }\r
+            \r
+            // if running from a script, the script runner manages the adc/dwi image cleanup\r
+            if (!ScriptRunner.getReference().isRunning()) {\r
+                if (adcImage != null) {\r
+                    adcImage.disposeLocal();\r
+                    adcImage = null;\r
+                }\r
+                if (dwiImage != null) {\r
+                    dwiImage.disposeLocal();\r
+                    dwiImage = null;\r
+                }\r
+            }\r
+\r
+            if (isExitRequired()) {\r
+                System.exit(0);\r
+            } else {\r
+                dispose();\r
+            }\r
+        }\r
+\r
+    }\r
+    \r
+    protected boolean setVariables() {\r
+        String adcPath = null;\r
+        String dwiPath = null;\r
+        \r
+        boolean doDirMethod = dirMethodRadio.isSelected();\r
+        \r
+        if (doDirMethod) {\r
+            dirFileString = dirFileField.getText();\r
+            final File dirFile = new File(dirFileString);\r
+            \r
+            File[] dirContents = dirFile.listFiles();\r
+            \r
+            for (File file : dirContents) {\r
+                String nameNoExt = FileUtility.stripExtension(file.getName());\r
+                if (nameNoExt.equalsIgnoreCase(adcFileStub)) {\r
+                    if (file.isDirectory()) {\r
+                        File[] subContents = file.listFiles();\r
+                        if (subContents.length > 0) {\r
+                            adcPath = subContents[0].getAbsolutePath();\r
+                            adcImageMultifile = true;\r
+                        } else {\r
+                            MipavUtil.displayError("No files found in ADC directory: " + file.getAbsolutePath());\r
+                            return false;\r
+                        }\r
+                    } else if (file.getName().endsWith(".img")) {\r
+                        adcPath = file.getAbsolutePath();\r
+                    }\r
+                } else if (nameNoExt.equalsIgnoreCase(dwiFileStub)) {\r
+                    if (file.isDirectory()) {\r
+                        File[] subContents = file.listFiles();\r
+                        if (subContents.length > 0) {\r
+                            dwiPath = subContents[0].getAbsolutePath();\r
+                            dwiImageMultifile = true;\r
+                        } else {\r
+                            MipavUtil.displayError("No files found in DWI directory: " + file.getAbsolutePath());\r
+                            return false;\r
+                        }\r
+                    } else if (file.getName().endsWith(".img")) {\r
+                        dwiPath = file.getAbsolutePath();\r
+                    }\r
+                }\r
+            }\r
+            outputDir = dirFile.getAbsolutePath() + File.separator;\r
+        } else {\r
+            adcPath = adcImageFileField.getText();\r
+            dwiPath = dwiImageFileField.getText();\r
+            outputDir = new File(adcPath).getParentFile().getAbsolutePath() + File.separator;\r
+        }\r
+        \r
+        if (adcPath != null && !adcPath.equals("")) {\r
+            final File adcFile = new File(adcPath);\r
+            adcImage = openImage(adcFile, adcImageMultifile);\r
+            \r
+            if (adcImage == null) {\r
+                MipavUtil.displayError("Error opening ADC volume from file: " + adcPath);\r
+                return false;\r
+            }\r
+        } else {\r
+            MipavUtil.displayError("No ADC volume selected.");\r
+            return false;\r
+        }\r
+        \r
+        \r
+        if (dwiPath != null && !dwiPath.equals("")) {\r
+            final File dwiFile = new File(dwiPath);\r
+            dwiImage = openImage(dwiFile, dwiImageMultifile);\r
+            \r
+            if (dwiImage == null) {\r
+                MipavUtil.displayError("Error opening DWI volume from file: " + dwiPath);\r
+                return false;\r
+            }\r
+        } else {\r
+            MipavUtil.displayError("No DWI volume selected.");\r
+            return false;\r
+        }\r
+        \r
+        adcThreshold = Integer.parseInt(adcThresholdField.getText());\r
+        \r
+        if (dirMethodRadio.isSelected() && !outputDir.equals("")) {\r
+            Preferences.setProperty(PREF_PLUGIN_STROKE_SEG_LAST_DIR, outputDir);\r
+        } else if (fileMethodRadio.isSelected()) {\r
+            if (!dwiPath.equals("")) {\r
+                Preferences.setProperty(PREF_PLUGIN_STROKE_SEG_LAST_DWI, dwiPath);\r
+            }\r
+            if (!adcPath.equals("")) {\r
+                Preferences.setProperty(PREF_PLUGIN_STROKE_SEG_LAST_ADC, adcPath);\r
+            }\r
+        }\r
+        \r
+        return true;\r
+    }\r
+\r
+    \r
+    /**\r
+     * Once all the necessary variables are set, call the stroke segmentation algorithm\r
+     */\r
+    protected void callAlgorithm() {\r
+\r
+        try {\r
+            segAlgo = new PlugInAlgorithmStrokeSegmentation(dwiImage, adcImage, adcThreshold, outputDir);\r
+\r
+            // This is very important. Adding this object as a listener allows the algorithm to\r
+            // notify this object when it has completed or failed. See algorithm performed event.\r
+            // This is made possible by implementing AlgorithmedPerformed interface\r
+            segAlgo.addListener(this);\r
+            createProgressBar(dwiImage.getImageName(), " ...", segAlgo);\r
+\r
+            setVisible(false); // Hide dialog\r
+\r
+            if (isRunInSeparateThread()) {\r
+\r
+                // Start the thread as a low priority because we wish to still\r
+                // have user interface work fast.\r
+                if (segAlgo.startMethod(Thread.MIN_PRIORITY) == false) {\r
+                    MipavUtil.displayError("A thread is already running on this object");\r
+                }\r
+            } else {\r
+                segAlgo.run();\r
+            }\r
+        } catch (OutOfMemoryError x) {\r
+            if (dwiImage != null) {\r
+                dwiImage.disposeLocal();\r
+                dwiImage = null;\r
+            }\r
+            \r
+            if (adcImage != null) {\r
+                adcImage.disposeLocal();\r
+                adcImage = null;\r
+            }\r
+\r
+            MipavUtil.displayError("Stroke segmentation: unable to allocate enough memory");\r
+\r
+            return;\r
+        }\r
+\r
+    } // end callAlgorithm()\r
+\r
+    protected void setGUIFromParams() {\r
+        adcImage = scriptParameters.retrieveImage("adc_image");\r
+        dwiImage = scriptParameters.retrieveImage("dwi_image");\r
+        \r
+        adcThreshold = scriptParameters.getParams().getInt("adc_threshold");\r
+        \r
+        outputDir = adcImage.getImageDirectory() + File.separator;\r
+    }\r
+\r
+    protected void storeParamsFromGUI() throws ParserException {\r
+        scriptParameters.storeImage(adcImage, "adc_image");\r
+        scriptParameters.storeImage(dwiImage, "dwi_image");\r
+        scriptParameters.getParams().put(ParameterFactory.newParameter("adc_threshold", adcThreshold));\r
+    }\r
+    \r
+    /**\r
+     * Perform any actions required after the running of the algorithm is complete.\r
+     */\r
+    protected void doPostAlgorithmActions() {\r
+        // nothing to do\r
+    }\r
+   \r
+    private void init() {\r
+        setForeground(Color.black);\r
+        setTitle("Stroke Segmentation " + pluginVersion);\r
+\r
+        GridBagConstraints gbc = new GridBagConstraints();\r
+        gbc.gridwidth = 1;\r
+        gbc.gridheight = 1;\r
+        gbc.anchor = GridBagConstraints.WEST;\r
+        gbc.weightx = 1;\r
+        gbc.insets = new Insets(3, 3, 3, 3);\r
+        gbc.fill = GridBagConstraints.HORIZONTAL;\r
+        gbc.gridx = 0;\r
+        gbc.gridy = 0;\r
+\r
+        JPanel mainPanel = new JPanel(new GridBagLayout());\r
+        mainPanel.setForeground(Color.black);\r
+        mainPanel.setBorder(buildTitledBorder("Input parameters"));\r
+        \r
+        JLabel labelThreshold = new JLabel("ADC threshold value");\r
+        labelThreshold.setForeground(Color.black);\r
+        labelThreshold.setFont(serif12);\r
+        mainPanel.add(labelThreshold, gbc);\r
+        \r
+        adcThresholdField = new JTextField(10);\r
+        adcThresholdField.setText("" + adcThreshold);\r
+        gbc.fill = GridBagConstraints.NONE;\r
+        gbc.gridx++;\r
+        mainPanel.add(adcThresholdField, gbc);\r
+        \r
+        gbc.fill = GridBagConstraints.HORIZONTAL;\r
+        \r
+        gbc.gridy++;\r
+        gbc.gridx = 0;\r
+        \r
+        gbc.gridwidth = 3;\r
+        \r
+        dirMethodRadio = new JRadioButton("Select directory with Baseline_ADC and Baseline_DWI files/subdirectories");\r
+        dirMethodRadio.setForeground(Color.black);\r
+        dirMethodRadio.setFont(serif12);\r
+        dirMethodRadio.setActionCommand("DirMethod");\r
+        dirMethodRadio.addActionListener(this);\r
+        mainPanel.add(dirMethodRadio, gbc);\r
+        \r
+        gbc.gridwidth = 1;\r
+        gbc.gridy++;\r
+        gbc.gridx = 0;\r
+        \r
+        JLabel labelDir = new JLabel("Parent directory");\r
+        labelDir.setForeground(Color.black);\r
+        labelDir.setFont(serif12);\r
+        mainPanel.add(labelDir, gbc);\r
+        \r
+        dirFileField = new JTextField(50);\r
+        dirFileField.setText(lastDir);\r
+        gbc.gridx++;\r
+        mainPanel.add(dirFileField, gbc);\r
+        \r
+        JButton dirFileButton = new JButton("Browse");\r
+        dirFileButton.setActionCommand("BrowseDir");\r
+        dirFileButton.addActionListener(this);\r
+        dirFileButton.setForeground(Color.black);\r
+        dirFileButton.setFont(serif12B);\r
+        gbc.gridx++;\r
+        mainPanel.add(dirFileButton, gbc);\r
+        \r
+        gbc.gridy++;\r
+        gbc.gridx = 0;\r
+        \r
+        gbc.gridwidth = 3;\r
+        \r
+        fileMethodRadio = new JRadioButton("Select individual ADC and DWI files");\r
+        fileMethodRadio.setForeground(Color.black);\r
+        fileMethodRadio.setFont(serif12);\r
+        fileMethodRadio.setActionCommand("FileMethod");\r
+        fileMethodRadio.addActionListener(this);\r
+        mainPanel.add(fileMethodRadio, gbc);\r
+        \r
+        ButtonGroup methodGroup = new ButtonGroup();\r
+        methodGroup.add(dirMethodRadio);\r
+        methodGroup.add(fileMethodRadio);\r
+        \r
+        // default to dir method, unless a previous value is set for the dwi/adc files but not the dir field\r
+        if (lastDir.equals("") && (!lastDwi.equals("") || !lastAdc.equals(""))) {\r
+            fileMethodRadio.setSelected(true);\r
+        } else {\r
+            dirMethodRadio.setSelected(true);\r
+        }\r
+        \r
+        gbc.gridwidth = 1;\r
+        \r
+        gbc.gridy++;\r
+        gbc.gridx = 0;\r
+\r
+        JLabel labelDWI = new JLabel("DWI image");\r
+        labelDWI.setForeground(Color.black);\r
+        labelDWI.setFont(serif12);\r
+        mainPanel.add(labelDWI, gbc);\r
+        \r
+        dwiImageFileField = new JTextField(50);\r
+        dwiImageFileField.setText(lastDwi);\r
+        gbc.gridx++;\r
+        mainPanel.add(dwiImageFileField, gbc);\r
+        \r
+        JButton dwiImageFileButton = new JButton("Browse");\r
+        dwiImageFileButton.setActionCommand("BrowseDWI");\r
+        dwiImageFileButton.addActionListener(this);\r
+        dwiImageFileButton.setForeground(Color.black);\r
+        dwiImageFileButton.setFont(serif12B);\r
+        gbc.gridx++;\r
+        mainPanel.add(dwiImageFileButton, gbc);\r
+        \r
+        gbc.gridy++;\r
+        gbc.gridx = 0;\r
+        \r
+        JLabel labelADC = new JLabel("ADC image");\r
+        labelADC.setForeground(Color.black);\r
+        labelADC.setFont(serif12);\r
+        mainPanel.add(labelADC, gbc);\r
+        \r
+        adcImageFileField = new JTextField(50);\r
+        adcImageFileField.setText(lastAdc);\r
+        gbc.gridx++;\r
+        mainPanel.add(adcImageFileField, gbc);\r
+        \r
+        JButton adcImageFileButton = new JButton("Browse");\r
+        adcImageFileButton.setActionCommand("BrowseADC");\r
+        adcImageFileButton.addActionListener(this);\r
+        adcImageFileButton.setForeground(Color.black);\r
+        adcImageFileButton.setFont(serif12B);\r
+        gbc.gridx++;\r
+        mainPanel.add(adcImageFileButton, gbc);\r
+\r
+        getContentPane().add(mainPanel, BorderLayout.CENTER);\r
+\r
+        // Build the Panel that holds the OK and CANCEL Buttons\r
+        JPanel OKCancelPanel = new JPanel();\r
+\r
+        // size and place the OK button\r
+        buildOKButton();\r
+        OKCancelPanel.add(OKButton, BorderLayout.WEST);\r
+\r
+        // size and place the CANCEL button\r
+        buildCancelButton();\r
+        OKCancelPanel.add(cancelButton, BorderLayout.EAST);\r
+        getContentPane().add(OKCancelPanel, BorderLayout.SOUTH);\r
+\r
+        pack();\r
+        setVisible(true);\r
+        setResizable(true);\r
+        System.gc();\r
+\r
+    } // end init()\r
+    \r
+    private boolean browseDir() {\r
+        String initDir = dirFileField.getText();\r
+        if (initDir.equals("") || !(new File(initDir).exists())) {\r
+            initDir = ViewUserInterface.getReference().getDefaultDirectory();\r
+        }\r
+        \r
+        final JFileChooser chooser = new JFileChooser(initDir);\r
+\r
+        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);\r
+        chooser.setDialogTitle("Choose output directory for Validation Tool files");\r
+        final int returnValue = chooser.showOpenDialog(this);\r
+        if (returnValue == JFileChooser.APPROVE_OPTION) {\r
+            dirFileField.setText(chooser.getSelectedFile().getAbsolutePath() + File.separator);\r
+\r
+            dirFileString = chooser.getSelectedFile().getAbsolutePath() + File.separator;\r
+            \r
+            return true;\r
+        }\r
+        \r
+        return false;\r
+    }\r
+    \r
+    private boolean browseADCImage() {\r
+        final ViewFileChooserBase fileChooser = new ViewFileChooserBase(true, false);\r
+        fileChooser.setMulti(ViewUserInterface.getReference().getLastStackFlag());\r
+\r
+        final JFileChooser chooser = fileChooser.getFileChooser();\r
+        chooser.setCurrentDirectory(new File(ViewUserInterface.getReference().getDefaultDirectory()));\r
+\r
+        // default to TECH filter\r
+        int filter = ViewImageFileFilter.TECH;\r
+\r
+        try {\r
+            filter = Integer.parseInt(Preferences.getProperty(Preferences.PREF_FILENAME_FILTER));\r
+        } catch (final NumberFormatException nfe) {\r
+\r
+            // an invalid value was set in preferences -- so don't\r
+            // use it!\r
+            filter = -1;\r
+        }\r
+\r
+        chooser.addChoosableFileFilter(new ViewImageFileFilter(ViewImageFileFilter.GEN));\r
+        chooser.addChoosableFileFilter(new ViewImageFileFilter(ViewImageFileFilter.TECH));\r
+        chooser.addChoosableFileFilter(new ViewImageFileFilter(ViewImageFileFilter.MICROSCOPY));\r
+        chooser.addChoosableFileFilter(new ViewImageFileFilter(ViewImageFileFilter.MISC));\r
+\r
+        if (filter != -1) {\r
+            // it seems that the set command adds the filter\r
+            // again...\r
+            // chooser.addChoosableFileFilter(new\r
+            // ViewImageFileFilter(filter));\r
+\r
+            // if filter is something we already added, then remove\r
+            // it before\r
+            // setting it..... (kludgy, kludgy....)\r
+            final javax.swing.filechooser.FileFilter found = ViewOpenFileUI.findFilter(chooser, filter);\r
+\r
+            if (found != null) {\r
+                chooser.removeChoosableFileFilter(found);\r
+            }\r
+\r
+            // initially set to the preferences\r
+            chooser.setFileFilter(new ViewImageFileFilter(filter));\r
+        }\r
+\r
+        final int returnVal = chooser.showOpenDialog(this);\r
+\r
+        if (returnVal == JFileChooser.APPROVE_OPTION) {\r
+            boolean isMultiFile = fileChooser.isMulti();\r
+\r
+            final File file = chooser.getSelectedFile();\r
+            ViewUserInterface.getReference().setDefaultDirectory(file.getParent());\r
+            \r
+            adcImageFileField.setText(file.getAbsolutePath());\r
+            adcImageMultifile = isMultiFile;\r
+\r
+            return true;\r
+        }\r
+        \r
+        return false;\r
+    }\r
+    \r
+    private boolean browseDWIImage() {\r
+        final ViewFileChooserBase fileChooser = new ViewFileChooserBase(true, false);\r
+        fileChooser.setMulti(ViewUserInterface.getReference().getLastStackFlag());\r
+\r
+        final JFileChooser chooser = fileChooser.getFileChooser();\r
+        chooser.setCurrentDirectory(new File(ViewUserInterface.getReference().getDefaultDirectory()));\r
+\r
+        // default to TECH filter\r
+        int filter = ViewImageFileFilter.TECH;\r
+\r
+        try {\r
+            filter = Integer.parseInt(Preferences.getProperty(Preferences.PREF_FILENAME_FILTER));\r
+        } catch (final NumberFormatException nfe) {\r
+\r
+            // an invalid value was set in preferences -- so don't\r
+            // use it!\r
+            filter = -1;\r
+        }\r
+\r
+        chooser.addChoosableFileFilter(new ViewImageFileFilter(ViewImageFileFilter.GEN));\r
+        chooser.addChoosableFileFilter(new ViewImageFileFilter(ViewImageFileFilter.TECH));\r
+        chooser.addChoosableFileFilter(new ViewImageFileFilter(ViewImageFileFilter.MICROSCOPY));\r
+        chooser.addChoosableFileFilter(new ViewImageFileFilter(ViewImageFileFilter.MISC));\r
+\r
+        if (filter != -1) {\r
+            // it seems that the set command adds the filter\r
+            // again...\r
+            // chooser.addChoosableFileFilter(new\r
+            // ViewImageFileFilter(filter));\r
+\r
+            // if filter is something we already added, then remove\r
+            // it before\r
+            // setting it..... (kludgy, kludgy....)\r
+            final javax.swing.filechooser.FileFilter found = ViewOpenFileUI.findFilter(chooser, filter);\r
+\r
+            if (found != null) {\r
+                chooser.removeChoosableFileFilter(found);\r
+            }\r
+\r
+            // initially set to the preferences\r
+            chooser.setFileFilter(new ViewImageFileFilter(filter));\r
+        }\r
+\r
+        final int returnVal = chooser.showOpenDialog(this);\r
+\r
+        if (returnVal == JFileChooser.APPROVE_OPTION) {\r
+            boolean isMultiFile = fileChooser.isMulti();\r
+\r
+            final File file = chooser.getSelectedFile();\r
+            ViewUserInterface.getReference().setDefaultDirectory(file.getParent());\r
+            \r
+            dwiImageFileField.setText(file.getAbsolutePath());\r
+            dwiImageMultifile = isMultiFile;\r
+\r
+            return true;\r
+        }\r
+        \r
+        return false;\r
+    }\r
+    \r
+    private ModelImage openImage(final File file, final boolean isMultiFile) {\r
+        final FileIO fileIO = new FileIO();\r
+        fileIO.setQuiet(true);\r
+\r
+        return fileIO.readImage(file.getName(), file.getParent() + File.separator, isMultiFile, null);\r
+    }\r
+}\r
diff --git a/mipav/src/plugins/PlugInStrokeSegmentation.java b/mipav/src/plugins/PlugInStrokeSegmentation.java
new file mode 100644 (file)
index 0000000..3cfdac0
--- /dev/null
@@ -0,0 +1,10 @@
+import gov.nih.mipav.plugins.*;\r
+\r
+\r
+public class PlugInStrokeSegmentation implements PlugInGeneric {\r
+    public static final String[] CATEGORY = {"Stroke"};\r
+    \r
+    public void run() {\r
+        new PlugInDialogStrokeSegmentation();\r
+    }\r
+}\r