From 27cebbb13c00f0ff1f021527cf4123f893989683 Mon Sep 17 00:00:00 2001 From: Colin Fuller Date: Sun, 3 Mar 2024 16:03:17 -0500 Subject: [PATCH] Upgrade various dependencies - Remove ImgLib code that was hard to upgrade in favor of plain bio-formats/imageJ - Remove omero code since I no longer have access to an omero server to test. --- build.gradle | 19 +- .../OmeroBrowsingWindow.form | 149 ------ .../OmeroBrowsingWindow.kt | 161 ------ .../OmeroBrowsingWindowController.kt | 287 ----------- .../analysistoolsinterface/OmeroListener.kt | 9 - .../SegmentationController.kt | 212 ++++---- .../SegmentationWindow.form | 416 ++++++++-------- .../SegmentationWindow.kt | 405 ++++++++++----- .../filter/GaussianFilter.kt | 44 +- .../imageanalysistools/fitting/ImageObject.kt | 369 ++++++++------ .../imageanalysistools/frontend/DirUtils.kt | 155 +++--- .../imageanalysistools/image/ImageSet.kt | 220 ++++----- .../image/ImgLibPixelData.kt | 200 -------- .../image/PixelDataFactory.kt | 103 ++-- .../image/ReadOnlyImageImpl.kt | 464 +++++++++++------- .../image/io/ImageWriter.kt | 84 ++-- .../image/io/ImgLibImageReader.kt | 66 --- .../image/io/omero/OmeroServerConnection.kt | 49 -- .../image/io/omero/OmeroServerImageReader.kt | 194 -------- .../image/io/omero/OmeroServerInfo.kt | 21 - .../meta/AnalysisMetadataXMLWriter.kt | 57 ++- .../imageanalysistools/util/SortedDeque.kt | 432 ---------------- 22 files changed, 1407 insertions(+), 2709 deletions(-) delete mode 100644 src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindow.form delete mode 100644 src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindow.kt delete mode 100644 src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindowController.kt delete mode 100644 src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroListener.kt delete mode 100644 src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ImgLibPixelData.kt delete mode 100644 src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/ImgLibImageReader.kt delete mode 100644 src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerConnection.kt delete mode 100644 src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerImageReader.kt delete mode 100644 src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerInfo.kt delete mode 100644 src/main/kotlin/edu/stanford/cfuller/imageanalysistools/util/SortedDeque.kt diff --git a/build.gradle b/build.gradle index e3e0fdd..0430035 100644 --- a/build.gradle +++ b/build.gradle @@ -7,19 +7,17 @@ plugins { repositories { mavenCentral() maven { - url "https://maven.imagej.net/content/repositories/public" + url "https://maven.scijava.org/content/groups/public" } } def kotlinVersion = "1.9.22" -def DepVersionLoci = "4.4.5" -def DepVersionIJ = "2.0.0-beta4" -def DepVersionImgLib = "2.0.0-beta5" def DepVersionCommonsMath = "3.1.1" def DepVersionJRuby = "1.7.0" def VersionInfoFilename = "src/main/resources/edu/stanford/cfuller/imageanalysistools/resources/version_info.xml" +def bioFormatsVersion = "6.13.0" -def Version = "6.0.0" +def Version = "7.0.0" task updateVersionInfo(type: Exec) { commandLine "sh", "-c", ( @@ -33,15 +31,12 @@ dependencies { testImplementation "junit:junit:4.10" testImplementation "io.kotlintest:kotlintest:1.3.7" implementation "org.apache.commons:commons-math3:$DepVersionCommonsMath" - implementation "loci:scifio:$DepVersionLoci" - implementation "loci:bio-formats:$DepVersionLoci" - implementation "loci:ome_tools:$DepVersionLoci" - implementation "net.imagej:ij-core:$DepVersionIJ" - implementation "net.imglib2:imglib2:$DepVersionImgLib" - implementation "net.imglib2:imglib2-io:$DepVersionImgLib" - implementation "net.imglib2:imglib2-algorithms:$DepVersionImgLib" implementation "org.jruby:jruby:$DepVersionJRuby" implementation "javax.xml.bind:jaxb-api:2.3.1" + implementation "net.imagej:ij:1.54h" + implementation "ome:formats-bsd:$bioFormatsVersion" + implementation "ome:formats-gpl:$bioFormatsVersion" + } build.dependsOn updateVersionInfo diff --git a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindow.form b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindow.form deleted file mode 100644 index 169a613..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindow.form +++ /dev/null @@ -1,149 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindow.kt b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindow.kt deleted file mode 100644 index 7fccaa6..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindow.kt +++ /dev/null @@ -1,161 +0,0 @@ -package edu.stanford.cfuller.analysistoolsinterface - -import java.util.prefs.Preferences - -import javax.swing.GroupLayout -import javax.swing.JTree -import javax.swing.LayoutStyle - -/** - - * @author cfuller - */ -class OmeroBrowsingWindow -/** Creates new form OmeroBrowsingWindow */ -(internal var controller: OmeroBrowsingWindowController) : javax.swing.JFrame() { - val host: String - get() = this.serverTextField!!.text - - val username: String - get() = this.usernameTextField!!.text - - val password: CharArray - get() = this.passwordField!!.password - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. - */ - private // //GEN-BEGIN:initComponents - fun initComponents() { - - jScrollPane1 = javax.swing.JScrollPane() - omeroBrowsingTree = javax.swing.JTree() - usernameTextField = javax.swing.JTextField() - passwordField = javax.swing.JPasswordField() - serverTextField = javax.swing.JTextField() - jLabel1 = javax.swing.JLabel() - jLabel2 = javax.swing.JLabel() - jLabel3 = javax.swing.JLabel() - loginButton = javax.swing.JButton() - imageButton = javax.swing.JButton() - loadImagesButton = javax.swing.JButton() - - defaultCloseOperation = javax.swing.WindowConstants.DISPOSE_ON_CLOSE - - jScrollPane1!!.setViewportView(omeroBrowsingTree) - - serverTextField!!.text = "171.65.20.207" - - jLabel1!!.text = "Omero Server:" - - jLabel2!!.text = "Username:" - - jLabel3!!.text = "Password:" - - loginButton!!.text = "Login" - loginButton!!.addActionListener { evt -> loginButtonActionPerformed(evt) } - - imageButton!!.text = "Use selected" - imageButton!!.addActionListener { evt -> imageButtonActionPerformed(evt) } - - loadImagesButton!!.text = "Load Images" - loadImagesButton!!.addActionListener { evt -> loadImagesButtonActionPerformed(evt) } - - val layout = GroupLayout(contentPane) - contentPane.layout = layout - layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGap(11, 11, 11) - .addComponent(jScrollPane1!!, GroupLayout.PREFERRED_SIZE, 278, GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addComponent(serverTextField!!, GroupLayout.PREFERRED_SIZE, 246, GroupLayout.PREFERRED_SIZE) - .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addComponent(jLabel2) - .addComponent(jLabel3)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, false) - .addComponent(passwordField, GroupLayout.Alignment.TRAILING) - .addComponent(usernameTextField!!, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 144, java.lang.Short.MAX_VALUE.toInt()) - .addComponent(loginButton, GroupLayout.Alignment.TRAILING))) - .addGroup(GroupLayout.Alignment.TRAILING, layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addComponent(loadImagesButton) - .addComponent(imageButton))) - .addComponent(jLabel1)) - .addContainerGap()) - ) - layout.setVerticalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel1) - .addGap(8, 8, 8) - .addComponent(serverTextField!!, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(usernameTextField!!, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel2)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(passwordField!!, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel3)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(loginButton) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 67, java.lang.Short.MAX_VALUE.toInt()) - .addComponent(loadImagesButton) - .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(imageButton)) - .addComponent(jScrollPane1!!, GroupLayout.PREFERRED_SIZE, 282, GroupLayout.PREFERRED_SIZE)) - .addContainerGap()) - ) - - pack() - }// //GEN-END:initComponents - - private fun loginButtonActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_loginButtonActionPerformed - - Preferences.userNodeForPackage(this.javaClass).put("omeroserver_hostname", this.serverTextField!!.text) - - this.controller.loginButtonPressed() - }//GEN-LAST:event_loginButtonActionPerformed - - private fun loadImagesButtonActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_loadImagesButtonActionPerformed - this.controller.loadImages() - }//GEN-LAST:event_loadImagesButtonActionPerformed - - private fun imageButtonActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_imageButtonActionPerformed - this.controller.doneButtonPressed() - }//GEN-LAST:event_imageButtonActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private var imageButton: javax.swing.JButton? = null - private var jLabel1: javax.swing.JLabel? = null - private var jLabel2: javax.swing.JLabel? = null - private var jLabel3: javax.swing.JLabel? = null - private var jScrollPane1: javax.swing.JScrollPane? = null - private var loadImagesButton: javax.swing.JButton? = null - private var loginButton: javax.swing.JButton? = null - var omeroBrowsingTree: JTree? = null - private set - private var passwordField: javax.swing.JPasswordField? = null - private var serverTextField: javax.swing.JTextField? = null - private var usernameTextField: javax.swing.JTextField? = null - - companion object { - internal val serialVersionUID = 1L - } - // End of variables declaration//GEN-END:variables - - init { - initComponents() - this.serverTextField!!.text = Preferences.userNodeForPackage(this.javaClass).get("omeroserver_hostname", this.serverTextField!!.text) - } -} diff --git a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindowController.kt b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindowController.kt deleted file mode 100644 index 9cfd4e4..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroBrowsingWindowController.kt +++ /dev/null @@ -1,287 +0,0 @@ -package edu.stanford.cfuller.analysistoolsinterface - -import java.util.ArrayList -import javax.swing.tree.DefaultTreeModel -import javax.swing.tree.DefaultMutableTreeNode -import Glacier2.CannotCreateSessionException -import Glacier2.PermissionDeniedException -import omero.ServerError -import omero.api.GatewayPrx -import omero.api.IQueryPrx -import omero.api.ServiceFactoryPrx -import omero.model.Dataset -import omero.model.Project -import omero.sys.ParametersI -import omero.rtypes.* - - -/** - - * @author cfuller - */ -class OmeroBrowsingWindowController { - - internal var obw: OmeroBrowsingWindow = OmeroBrowsingWindow(this) - internal var selectedImageIds: MutableList = ArrayList() - internal var client: omero.client? = null - internal var serviceFactory: ServiceFactoryPrx? = null - internal var queryService: IQueryPrx? = null - internal var gateway: GatewayPrx? = null - internal var lastDatasetAccessed: Dataset? = null - - internal var listener: OmeroListener? = null - - var username: String = "" - internal set - var hostname: String = "" - internal set - internal var password: CharArray = CharArray(0) - - fun openNewBrowsingWindow(l: OmeroListener) { - this.listener = l - this.obw = OmeroBrowsingWindow(this) - this.obw.omeroBrowsingTree!!.model = DefaultTreeModel(null) - this.obw.isVisible = true - this.selectedImageIds = ArrayList() - this.client = null - this.serviceFactory = null - this.queryService = null - this.gateway = null - this.lastDatasetAccessed = null - } - - fun doneButtonPressed() { - val selectedNode = this.obw.omeroBrowsingTree!!.selectionPath.lastPathComponent as DefaultMutableTreeNode - val selected = selectedNode.userObject as Holder - if (selected.isAnImage) { - this.selectedImageIds.add((selected as ImageHolder).image.id.value) - } - if (selected.isADataset) { - this.loadImages() - val children = selectedNode.children() - while (children.hasMoreElements()) { - val child = children.nextElement() as DefaultMutableTreeNode - val ih = child.userObject as ImageHolder - this.selectedImageIds.add(ih.image.id.value) - } - } - this.obw.dispose() - this.listener?.imageIdsHaveBeenSelected(this.selectedImageIds) - this.client!!.closeSession() - } - - fun getSelectedImageIds(): List { - return this.selectedImageIds - } - - fun loginButtonPressed() { - this.hostname = obw.host - this.username = obw.username - this.password = obw.password - - if (this.client != null) { - this.client!!.closeSession() - } - - this.client = omero.client(hostname) - try { - this.serviceFactory = this.client!!.createSession(username, String(password)) - this.gateway = this.serviceFactory!!.createGateway() - } catch (ex: CannotCreateSessionException) { - LoggingUtilities.severe(ex.message ?: "Cannot create session.") - } catch (ex: PermissionDeniedException) { - LoggingUtilities.severe(ex.message ?: "Permission denied.") - } catch (ex: ServerError) { - LoggingUtilities.severe(ex.toString()) - } - - val projectList = this.projectList - java.util.Collections.sort(projectList, ProjectComparator()) - val root = DefaultMutableTreeNode(this.username) - - for (p in projectList) { - val pNode = DefaultMutableTreeNode(ProjectHolder(p)) - val pdLinks = p.copyDatasetLinks() - val datasets = ArrayList() - - for (pdl in pdLinks) { - datasets.add(pdl.child) - } - - java.util.Collections.sort(datasets, DatasetComparator()) - - for (d in datasets) { - val dNode = DefaultMutableTreeNode(DatasetHolder(d)) - pNode.add(dNode) - - } - root.add(pNode) - } - this.obw.omeroBrowsingTree!!.model = DefaultTreeModel(root) - } - - - /** Gets the list of projects associated with the current user from the server. - - * Will close the connection to the server and return on exception. - - * @return a `List` of `Project` objects or null on unexpected failure. - */ - //return new Vector(); - val projectList: List - get() { - var rv: List<*>? = null - - try { - if (this.queryService == null) - this.queryService = this.serviceFactory!!.queryService - - val query_string = "select p from Project p left outer join fetch p.datasetLinks dsl left outer join fetch dsl.child ds where p.details.owner.omeName = :name" - val p = ParametersI() - p.add("id", rlong(1L)) - rv = queryService!!.findAllByQuery( - query_string, - ParametersI().add("name", rstring(this.username))) - - } catch (e: Exception) { - LoggingUtilities.severe("encountered exception while reading datasets") - this.client!!.closeSession() - } - - val toReturn = java.util.ArrayList() - for (prj in rv!!) { - toReturn.add(prj as Project) - } - return toReturn - } - - fun loadImages() { - try { - val n = this.obw.omeroBrowsingTree!!.selectionPath.lastPathComponent as DefaultMutableTreeNode - val h = n.userObject as Holder - if (!h.isADataset) { - return - } - var d = (h as DatasetHolder).dataset - d = this.gateway!!.getDataset(d.id.value, true) - val images = d.linkedImageList() - java.util.Collections.sort(images, ImageComparator()) - images.forEach { - n.add(DefaultMutableTreeNode(ImageHolder(it))) - } - this.obw.omeroBrowsingTree!!.repaint() - } catch (ex: ServerError) { - LoggingUtilities.severe(ex.toString()) - } - } - - fun getPassword(): String { - val pw = String(this.password) - nullifyPassword(this.password) - return pw - } - - /** A comparator for omero images, allowing sorting for display. - - */ - private class ImageComparator : java.util.Comparator, java.io.Serializable { - override fun compare(a: omero.model.Image, b: omero.model.Image): Int { - return a.name.value.compareTo(b.name.value) - } - - companion object { - internal const val serialVersionUID = 1L - } - } - - /** A comparator for omero projects, allowing sorting for display. - - */ - private class ProjectComparator : java.util.Comparator, java.io.Serializable { - override fun compare(a: Project, b: Project): Int { - return a.name.value.compareTo(b.name.value) - } - - companion object { - internal const val serialVersionUID = 1L - } - } - - /** A comparator for omero datasets, allowing sorting for display. - - */ - private class DatasetComparator : java.util.Comparator, java.io.Serializable { - override fun compare(a: Dataset, b: Dataset): Int { - return a.name.value.compareTo(b.name.value) - } - - companion object { - internal const val serialVersionUID = 1L - } - } - - - private open class Holder { - var isADataset: Boolean = false - internal set - var isAnImage: Boolean = false - internal set - var isAProject: Boolean = false - internal set - } - - /** A class that holds a dataset and adds a toString method so these can be - * used to populate the tree for display. - */ - private class DatasetHolder(var dataset: Dataset) : Holder() { - init { - isADataset = true - isAnImage = false - isAProject = false - } - - override fun toString(): String { - return this.dataset.name.value - } - - } - - private class ImageHolder(i_in: omero.model.Image) : Holder() { - var image: omero.model.Image - internal set - - init { - this.image = i_in - isADataset = false - isAnImage = true - isAProject = false - } - override fun toString(): String { - return this.image.name.value - } - } - - private class ProjectHolder(p_in: Project) : Holder() { - var project: Project - internal set - - init { - this.project = p_in - isADataset = false - isAnImage = false - isAProject = true - } - - override fun toString(): String { - return this.project.name.value - } - } - - companion object { - fun nullifyPassword(password: CharArray) { - for (i in password.indices) { - password[i] = 0.toChar() - } - } - } -} diff --git a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroListener.kt b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroListener.kt deleted file mode 100644 index b19155c..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/OmeroListener.kt +++ /dev/null @@ -1,9 +0,0 @@ -package edu.stanford.cfuller.analysistoolsinterface - -/** - - * @author cfuller - */ -interface OmeroListener { - fun imageIdsHaveBeenSelected(ids: List) -} diff --git a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationController.kt b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationController.kt index 5a5aa6e..bb07f34 100644 --- a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationController.kt +++ b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationController.kt @@ -2,39 +2,33 @@ package edu.stanford.cfuller.analysistoolsinterface import edu.stanford.cfuller.imageanalysistools.frontend.AnalysisController import edu.stanford.cfuller.imageanalysistools.frontend.DataSummary -import edu.stanford.cfuller.imageanalysistools.method.Method -import edu.stanford.cfuller.imageanalysistools.meta.parameters.ParameterDictionary -import edu.stanford.cfuller.imageanalysistools.meta.AnalysisMetadata import edu.stanford.cfuller.imageanalysistools.meta.AnalysisMetadataParserFactory import java.io.File import java.io.IOException import java.util.prefs.Preferences -import javax.swing.DefaultComboBoxModel import javax.swing.JFileChooser import javax.swing.filechooser.FileNameExtensionFilter import javax.xml.parsers.DocumentBuilderFactory import javax.xml.parsers.ParserConfigurationException -import org.w3c.dom.Node import org.w3c.dom.NodeList import org.xml.sax.SAXException /** - + * * @author cfuller */ -class SegmentationController : TaskController(), OmeroListener { +class SegmentationController : TaskController() { internal var sw: SegmentationWindow = SegmentationWindow(this) - internal var omeroImageIds: List = listOf() - internal var omeroBrowser: OmeroBrowsingWindowController? = null override fun startTask() { - this.omeroBrowser = null sw = SegmentationWindow(this) sw.addWindowListener(this) initializeMethods() val imageFilename = Preferences.userNodeForPackage(this::class.java).get("imageFile", "") - val parameterFilename = Preferences.userNodeForPackage(this::class.java).get("parameterFile", "") - val selectedMethodIndex = Preferences.userNodeForPackage(this::class.java).getInt("selectedIndex", 0) + val parameterFilename = + Preferences.userNodeForPackage(this::class.java).get("parameterFile", "") + val selectedMethodIndex = + Preferences.userNodeForPackage(this::class.java).getInt("selectedIndex", 0) sw.imageFilename = imageFilename sw.parameterFilename = parameterFilename sw.selectedMethodIndex = selectedMethodIndex @@ -43,11 +37,6 @@ class SegmentationController : TaskController(), OmeroListener { sw.isVisible = true } - override fun imageIdsHaveBeenSelected(ids: List) { - this.omeroImageIds = ids - this.sw.useOmeroServer = true - } - fun browseForParameterFile() { val path = this.sw.parameterFilename var fc: JFileChooser? = null @@ -57,15 +46,15 @@ class SegmentationController : TaskController(), OmeroListener { fc = JFileChooser() } fc.fileSelectionMode = JFileChooser.FILES_ONLY - fc.addChoosableFileFilter(FileNameExtensionFilter("Parameter files (.xml, .rb)", "xml", "rb")) + fc.addChoosableFileFilter( + FileNameExtensionFilter("Parameter files (.xml, .rb)", "xml", "rb") + ) val retVal = fc.showOpenDialog(this.sw) - if (retVal == JFileChooser.APPROVE_OPTION) { val selected = fc.selectedFile.absolutePath - sw.parameterFilename = selected } } @@ -86,12 +75,10 @@ class SegmentationController : TaskController(), OmeroListener { val selected = fc.selectedFile.absolutePath - sw.imageFilename = selected } } - fun onMethodSelect() { val index = this.sw.selectedMethodIndex @@ -102,111 +89,100 @@ class SegmentationController : TaskController(), OmeroListener { } else { this.sw.setCustomMethodFieldEnabled(false) } - - } - - fun useOmeroDataSource() { - try { - Class.forName("omero.api.GatewayPrx") - } catch (e: ClassNotFoundException) { - LoggingUtilities.warning("Could not open OMERO data source; OMERO client plugin missing.") - this.sw.status = STATUS_OMERO_ERR + STATUS_READY - this.sw.disableOmero() - return - } - - this.omeroBrowser = OmeroBrowsingWindowController() - omeroBrowser!!.openNewBrowsingWindow(this) } fun runButtonPressed() { - if (!(this.sw.status == STATUS_READY || this.sw.status == STATUS_OMERO_ERR + STATUS_READY)) return - + if (!(this.sw.status == STATUS_READY || this.sw.status == STATUS_OMERO_ERR + STATUS_READY)) + return val parameterFilename = this.sw.parameterFilename val imageFilename = this.sw.imageFilename Preferences.userNodeForPackage(this.javaClass).put("imageFile", imageFilename) Preferences.userNodeForPackage(this.javaClass).put("parameterFile", parameterFilename) - Preferences.userNodeForPackage(this.javaClass).putInt("selectedIndex", this.sw.selectedMethodIndex) + Preferences.userNodeForPackage(this.javaClass) + .putInt("selectedIndex", this.sw.selectedMethodIndex) this.sw.status = STATUS_PROCESSING - java.awt.EventQueue.invokeLater(Runnable { - val mi = sw.methodComboBoxModel.getElementAt(sw.selectedMethodIndex) as MethodInfo - - var c: Class<*>? = mi.methodClass - - val am = AnalysisMetadataParserFactory.createParserForFile(parameterFilename).parseFileToAnalysisMetadata(parameterFilename) - - val pd = am.inputParameters - - if (c == null && !pd!!.hasKey("method_name")) { - try { - c = Class.forName(sw.customClassName) - } catch (e: ClassNotFoundException) { - LoggingUtilities.warning("Custom class not found with name: " + sw.customClassName) - sw.status = STATUS_READY - return@Runnable - } - - } - - pd!!.addIfNotSet("method_name", c!!.name) - if (File(imageFilename).isDirectory) { - pd.addIfNotSet("local_directory", imageFilename) - } else { - val wholeFilename = File(imageFilename) - val name = wholeFilename.name - val dir = wholeFilename.parent - if (!pd.hasKey("common_filename_tag")) { - pd.addIfNotSet("common_filename_tag", name) - } else { - pd.setValueForKey("image_extension", name) - } - pd.addIfNotSet("local_directory", dir) - } + java.awt.EventQueue.invokeLater( + Runnable { + val mi = + sw.methodComboBoxModel.getElementAt(sw.selectedMethodIndex) as + MethodInfo + + var c: Class<*>? = mi.methodClass + + val am = + AnalysisMetadataParserFactory.createParserForFile(parameterFilename) + .parseFileToAnalysisMetadata(parameterFilename) + + val pd = am.inputParameters + + if (c == null && !pd!!.hasKey("method_name")) { + try { + c = Class.forName(sw.customClassName) + } catch (e: ClassNotFoundException) { + LoggingUtilities.warning( + "Custom class not found with name: " + sw.customClassName + ) + sw.status = STATUS_READY + return@Runnable + } + } - pd.addIfNotSet("max_threads", Integer.toString(Runtime.getRuntime().availableProcessors())) - - if (sw.useOmeroServer) { - pd.setValueForKey("use_omero", "true") - pd.addIfNotSet("omero_hostname", omeroBrowser!!.hostname) - pd.addIfNotSet("omero_username", omeroBrowser!!.username) - pd.addIfNotSet("omero_password", omeroBrowser!!.getPassword()) - var omeroImageIdString: String? = null - for (l in omeroImageIds) { - if (omeroImageIdString == null) { - omeroImageIdString = "" + pd!!.addIfNotSet("method_name", c!!.name) + if (File(imageFilename).isDirectory) { + pd.addIfNotSet("local_directory", imageFilename) } else { - omeroImageIdString += " " + val wholeFilename = File(imageFilename) + val name = wholeFilename.name + val dir = wholeFilename.parent + if (!pd.hasKey("common_filename_tag")) { + pd.addIfNotSet("common_filename_tag", name) + } else { + pd.setValueForKey("image_extension", name) + } + pd.addIfNotSet("local_directory", dir) } - omeroImageIdString += java.lang.Long.toString(l) - } - pd.addIfNotSet("omero_image_ids", omeroImageIdString!!) - } - - - Thread(Runnable { - if (!sw.summarizeDataOnly()) { - val ac = AnalysisController() - ac.addAnalysisLoggingHandler(sw.logHandler) - ac.runLocal(am) - } - - sw.status = STATUS_SUMMARY - - try { - DataSummary.SummarizeData(pd.getValueForKey("local_directory") + File.separator + AnalysisController.DATA_OUTPUT_DIR, pd.getValueForKey("local_directory") + File.separator + AnalysisController.PARAMETER_OUTPUT_DIR) - } catch (e: IOException) { - LoggingUtilities.severe("Encountered error while summarizing data.") + pd.addIfNotSet( + "max_threads", + Integer.toString(Runtime.getRuntime().availableProcessors()) + ) + + Thread( + Runnable { + if (!sw.summarizeDataOnly()) { + val ac = AnalysisController() + ac.addAnalysisLoggingHandler(sw.logHandler) + ac.runLocal(am) + } + + sw.status = STATUS_SUMMARY + + try { + DataSummary.SummarizeData( + pd.getValueForKey("local_directory") + + File.separator + + AnalysisController.DATA_OUTPUT_DIR, + pd.getValueForKey("local_directory") + + File.separator + + AnalysisController.PARAMETER_OUTPUT_DIR + ) + } catch (e: IOException) { + LoggingUtilities.severe( + "Encountered error while summarizing data." + ) + } + + sw.status = STATUS_READY + } + ) + .start() } - - sw.status = STATUS_READY - }).start() - }) + ) } private class MethodInfo(displayName: String, className: String?) { @@ -221,10 +197,11 @@ class SegmentationController : TaskController(), OmeroListener { try { this.methodClass = Class.forName(className) } catch (e: ClassNotFoundException) { - LoggingUtilities.warning("Valid class not found for method with name: " + displayName) + LoggingUtilities.warning( + "Valid class not found for method with name: " + displayName + ) this.methodClass = null } - } else { this.methodClass = null } @@ -233,16 +210,20 @@ class SegmentationController : TaskController(), OmeroListener { override fun toString(): String { return this.displayName } - } private fun initializeMethods() { val model = sw.methodComboBoxModel - val methodResourceLocation = this.javaClass.classLoader.getResource(METHOD_XML_FILENAME)!!.toString() + val methodResourceLocation = + this.javaClass.classLoader.getResource(METHOD_XML_FILENAME)!!.toString() var methodNodes: NodeList? = null try { - methodNodes = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(methodResourceLocation).getElementsByTagName(METHOD_XML_TAG) + methodNodes = + DocumentBuilderFactory.newInstance() + .newDocumentBuilder() + .parse(methodResourceLocation) + .getElementsByTagName(METHOD_XML_TAG) } catch (e: SAXException) { LoggingUtilities.severe("Encountered exception while parsing tasks xml file.") return @@ -263,7 +244,8 @@ class SegmentationController : TaskController(), OmeroListener { } companion object { - internal val METHOD_XML_FILENAME = "edu/stanford/cfuller/analysistoolsinterface/resources/methods.xml" + internal val METHOD_XML_FILENAME = + "edu/stanford/cfuller/analysistoolsinterface/resources/methods.xml" internal val METHOD_XML_TAG = "method" internal val DISPLAY_ATTR_NAME = "displayname" internal val CLASS_ATTR_NAME = "class" diff --git a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationWindow.form b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationWindow.form index 77feab0..e2354ed 100644 --- a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationWindow.form +++ b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationWindow.form @@ -1,225 +1,203 @@
- - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationWindow.kt b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationWindow.kt index 3905bc9..a8c97d8 100644 --- a/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationWindow.kt +++ b/src/main/kotlin/edu/stanford/cfuller/analysistoolsinterface/SegmentationWindow.kt @@ -1,6 +1,5 @@ package edu.stanford.cfuller.analysistoolsinterface -import java.util.logging.Formatter import java.util.logging.Handler import java.util.logging.LogRecord import javax.swing.DefaultComboBoxModel @@ -10,11 +9,11 @@ import javax.swing.LayoutStyle import javax.swing.SwingUtilities /** - + * * @author cfuller */ class SegmentationWindow -/** Creates new form SegmentationWindow */ +/** Creates new form SegmentationWindow */ (internal var controller: SegmentationController) : javax.swing.JFrame() { var methodComboBoxModel: DefaultComboBoxModel internal set @@ -58,25 +57,12 @@ class SegmentationWindow return this.summarizeDataOnlyCheckBox!!.isSelected } - var useOmeroServer: Boolean - get() = this.useOmeroServerCheckBox!!.isSelected - set(use) { - this.useOmeroServerCheckBox!!.isSelected = use - } - - fun disableOmero() { - this.useOmeroServer = false - this.omeroButton!!.isEnabled = false - this.useOmeroServerCheckBox!!.isEnabled = false - } - - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the Form Editor. + /** + * This method is called from within the constructor to initialize the form. WARNING: Do NOT + * modify this code. The content of this method is always regenerated by the Form Editor. */ - private // //GEN-BEGIN:initComponents + private // //GEN-BEGIN:initComponents fun initComponents() { methodComboBox = javax.swing.JComboBox() @@ -94,13 +80,14 @@ class SegmentationWindow summarizeDataOnlyCheckBox = javax.swing.JCheckBox() jScrollPane1 = javax.swing.JScrollPane() loggingTextArea = javax.swing.JTextArea() - omeroButton = javax.swing.JButton() - useOmeroServerCheckBox = javax.swing.JCheckBox() defaultCloseOperation = javax.swing.WindowConstants.DISPOSE_ON_CLOSE title = "Segmentation and Quantification" - methodComboBox!!.model = javax.swing.DefaultComboBoxModel(arrayOf("Item 1", "Item 2", "Item 3", "Item 4")) + methodComboBox!!.model = + javax.swing.DefaultComboBoxModel( + arrayOf("Item 1", "Item 2", "Item 3", "Item 4") + ) methodComboBox!!.addActionListener { evt -> methodComboBoxActionPerformed(evt) } jLabel2!!.text = "Method:" @@ -108,7 +95,9 @@ class SegmentationWindow jLabel3!!.text = "Parameter file:" parameterBrowseButton!!.text = "Browse" - parameterBrowseButton!!.addActionListener { evt -> parameterBrowseButtonActionPerformed(evt) } + parameterBrowseButton!!.addActionListener { evt -> + parameterBrowseButtonActionPerformed(evt) + } jLabel4!!.text = "File or directory to process:" @@ -123,7 +112,9 @@ class SegmentationWindow statusLabel!!.text = "Ready" summarizeDataOnlyCheckBox!!.text = "Summarize data only" - summarizeDataOnlyCheckBox!!.addActionListener { evt -> summarizeDataOnlyCheckBoxActionPerformed(evt) } + summarizeDataOnlyCheckBox!!.addActionListener { evt -> + summarizeDataOnlyCheckBoxActionPerformed(evt) + } jScrollPane1!!.preferredSize = java.awt.Dimension(100, 80) @@ -132,115 +123,295 @@ class SegmentationWindow loggingTextArea!!.rows = 5 jScrollPane1!!.setViewportView(loggingTextArea) - omeroButton!!.text = "Browse OMERO" - omeroButton!!.addActionListener { evt -> omeroButtonActionPerformed(evt) } - - useOmeroServerCheckBox!!.text = "Use OMERO server" - val layout = GroupLayout(contentPane) contentPane.layout = layout layout.setHorizontalGroup( layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addComponent(jLabel2) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, false) - .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(jLabel4!!, GroupLayout.PREFERRED_SIZE, 187, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, java.lang.Short.MAX_VALUE.toInt()) - .addComponent(imageBrowseButton)) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel3) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, java.lang.Short.MAX_VALUE.toInt()) - .addComponent(parameterBrowseButton)) - .addComponent(parameterFileTextField!!, GroupLayout.PREFERRED_SIZE, 284, GroupLayout.PREFERRED_SIZE) - .addComponent(methodComboBox!!, GroupLayout.Alignment.TRAILING, 0, 287, java.lang.Short.MAX_VALUE.toInt()) - .addComponent(customMethodTextField, GroupLayout.Alignment.TRAILING)) - .addComponent(summarizeDataOnlyCheckBox) - .addGroup(layout.createSequentialGroup() - .addComponent(runButton) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel5) - .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(statusLabel)) - .addComponent(imageTextField!!, GroupLayout.PREFERRED_SIZE, 284, GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addComponent(useOmeroServerCheckBox) - .addGap(18, 18, 18) - .addComponent(omeroButton!!, GroupLayout.PREFERRED_SIZE, 121, GroupLayout.PREFERRED_SIZE))) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1!!, GroupLayout.DEFAULT_SIZE, 451, java.lang.Short.MAX_VALUE.toInt()) - .addContainerGap()) + .addGroup( + layout.createSequentialGroup() + .addContainerGap() + .addGroup( + layout.createParallelGroup( + GroupLayout.Alignment.LEADING + ) + .addComponent(jLabel2) + .addGroup( + layout.createParallelGroup( + GroupLayout + .Alignment + .LEADING, + false + ) + .addGroup( + GroupLayout + .Alignment + .TRAILING, + layout.createSequentialGroup() + .addComponent( + jLabel4!!, + GroupLayout + .PREFERRED_SIZE, + 187, + GroupLayout + .PREFERRED_SIZE + ) + .addPreferredGap( + LayoutStyle + .ComponentPlacement + .RELATED, + GroupLayout + .DEFAULT_SIZE, + java.lang + .Short + .MAX_VALUE + .toInt() + ) + .addComponent( + imageBrowseButton + ) + ) + .addGroup( + layout.createSequentialGroup() + .addComponent( + jLabel3 + ) + .addPreferredGap( + LayoutStyle + .ComponentPlacement + .RELATED, + GroupLayout + .DEFAULT_SIZE, + java.lang + .Short + .MAX_VALUE + .toInt() + ) + .addComponent( + parameterBrowseButton + ) + ) + .addComponent( + parameterFileTextField!!, + GroupLayout + .PREFERRED_SIZE, + 284, + GroupLayout + .PREFERRED_SIZE + ) + .addComponent( + methodComboBox!!, + GroupLayout + .Alignment + .TRAILING, + 0, + 287, + java.lang.Short + .MAX_VALUE + .toInt() + ) + .addComponent( + customMethodTextField, + GroupLayout + .Alignment + .TRAILING + ) + ) + .addComponent(summarizeDataOnlyCheckBox) + .addGroup( + layout.createSequentialGroup() + .addComponent(runButton) + .addPreferredGap( + LayoutStyle + .ComponentPlacement + .RELATED + ) + .addComponent(jLabel5) + .addPreferredGap( + LayoutStyle + .ComponentPlacement + .UNRELATED + ) + .addComponent(statusLabel) + ) + .addComponent( + imageTextField!!, + GroupLayout.PREFERRED_SIZE, + 284, + GroupLayout.PREFERRED_SIZE + ) + ) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + jScrollPane1!!, + GroupLayout.DEFAULT_SIZE, + 451, + java.lang.Short.MAX_VALUE.toInt() + ) + .addContainerGap() + ) ) layout.setVerticalGroup( layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jLabel2) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(methodComboBox!!, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(customMethodTextField!!, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(jLabel3) - .addComponent(parameterBrowseButton)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(parameterFileTextField!!, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addGap(29, 29, 29) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(imageBrowseButton) - .addComponent(jLabel4)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(imageTextField!!, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(useOmeroServerCheckBox) - .addComponent(omeroButton)) - .addGap(9, 9, 9) - .addComponent(summarizeDataOnlyCheckBox) - .addGap(18, 18, 18) - .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(runButton) - .addComponent(statusLabel) - .addComponent(jLabel5))) - .addComponent(jScrollPane1!!, GroupLayout.DEFAULT_SIZE, 390, java.lang.Short.MAX_VALUE.toInt())) - .addContainerGap()) + .addGroup( + layout.createSequentialGroup() + .addContainerGap() + .addGroup( + layout.createParallelGroup( + GroupLayout.Alignment.LEADING + ) + .addGroup( + layout.createSequentialGroup() + .addComponent(jLabel2) + .addPreferredGap( + LayoutStyle + .ComponentPlacement + .RELATED + ) + .addComponent( + methodComboBox!!, + GroupLayout + .PREFERRED_SIZE, + GroupLayout + .DEFAULT_SIZE, + GroupLayout + .PREFERRED_SIZE + ) + .addPreferredGap( + LayoutStyle + .ComponentPlacement + .RELATED + ) + .addComponent( + customMethodTextField!!, + GroupLayout + .PREFERRED_SIZE, + GroupLayout + .DEFAULT_SIZE, + GroupLayout + .PREFERRED_SIZE + ) + .addGap(18, 18, 18) + .addGroup( + layout.createParallelGroup( + GroupLayout + .Alignment + .BASELINE + ) + .addComponent( + jLabel3 + ) + .addComponent( + parameterBrowseButton + ) + ) + .addPreferredGap( + LayoutStyle + .ComponentPlacement + .RELATED + ) + .addComponent( + parameterFileTextField!!, + GroupLayout + .PREFERRED_SIZE, + GroupLayout + .DEFAULT_SIZE, + GroupLayout + .PREFERRED_SIZE + ) + .addGap(29, 29, 29) + .addGroup( + layout.createParallelGroup( + GroupLayout + .Alignment + .BASELINE + ) + .addComponent( + imageBrowseButton + ) + .addComponent( + jLabel4 + ) + ) + .addPreferredGap( + LayoutStyle + .ComponentPlacement + .RELATED + ) + .addComponent( + imageTextField!!, + GroupLayout + .PREFERRED_SIZE, + GroupLayout + .DEFAULT_SIZE, + GroupLayout + .PREFERRED_SIZE + ) + .addGap(18, 18, 18) + .addComponent( + summarizeDataOnlyCheckBox + ) + .addGap(18, 18, 18) + .addGroup( + layout.createParallelGroup( + GroupLayout + .Alignment + .BASELINE + ) + .addComponent( + runButton + ) + .addComponent( + statusLabel + ) + .addComponent( + jLabel5 + ) + ) + ) + .addComponent( + jScrollPane1!!, + GroupLayout.DEFAULT_SIZE, + 390, + java.lang.Short.MAX_VALUE.toInt() + ) + ) + .addContainerGap() + ) ) pack() - }// //GEN-END:initComponents + } // //GEN-END:initComponents - private fun parameterBrowseButtonActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_parameterBrowseButtonActionPerformed + private fun parameterBrowseButtonActionPerformed( + evt: java.awt.event.ActionEvent + ) { // GEN-FIRST:event_parameterBrowseButtonActionPerformed this.controller.browseForParameterFile() - }//GEN-LAST:event_parameterBrowseButtonActionPerformed + } // GEN-LAST:event_parameterBrowseButtonActionPerformed - private fun imageBrowseButtonActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_imageBrowseButtonActionPerformed + private fun imageBrowseButtonActionPerformed( + evt: java.awt.event.ActionEvent + ) { // GEN-FIRST:event_imageBrowseButtonActionPerformed this.controller.browseForImageFileOrDirectory() - }//GEN-LAST:event_imageBrowseButtonActionPerformed + } // GEN-LAST:event_imageBrowseButtonActionPerformed - private fun methodComboBoxActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_methodComboBoxActionPerformed + private fun methodComboBoxActionPerformed( + evt: java.awt.event.ActionEvent + ) { // GEN-FIRST:event_methodComboBoxActionPerformed this.controller.onMethodSelect() - }//GEN-LAST:event_methodComboBoxActionPerformed + } // GEN-LAST:event_methodComboBoxActionPerformed - private fun runButtonActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_runButtonActionPerformed + private fun runButtonActionPerformed( + evt: java.awt.event.ActionEvent + ) { // GEN-FIRST:event_runButtonActionPerformed this.controller.runButtonPressed() + } // GEN-LAST:event_runButtonActionPerformed - - }//GEN-LAST:event_runButtonActionPerformed - - private fun summarizeDataOnlyCheckBoxActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_summarizeDataOnlyCheckBoxActionPerformed + private fun summarizeDataOnlyCheckBoxActionPerformed( + evt: java.awt.event.ActionEvent + ) { // GEN-FIRST:event_summarizeDataOnlyCheckBoxActionPerformed // TODO add your handling code here: - }//GEN-LAST:event_summarizeDataOnlyCheckBoxActionPerformed - - private fun omeroButtonActionPerformed(evt: java.awt.event.ActionEvent) {//GEN-FIRST:event_omeroButtonActionPerformed - this.controller.useOmeroDataSource() - }//GEN-LAST:event_omeroButtonActionPerformed - + } // GEN-LAST:event_summarizeDataOnlyCheckBoxActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private var customMethodTextField: javax.swing.JTextField? = null @@ -253,16 +424,13 @@ class SegmentationWindow private var jScrollPane1: javax.swing.JScrollPane? = null private var loggingTextArea: javax.swing.JTextArea? = null private var methodComboBox: javax.swing.JComboBox<*>? = null - private var omeroButton: javax.swing.JButton? = null private var parameterBrowseButton: javax.swing.JButton? = null private var parameterFileTextField: javax.swing.JTextField? = null private var runButton: javax.swing.JButton? = null private var statusLabel: javax.swing.JLabel? = null private var summarizeDataOnlyCheckBox: javax.swing.JCheckBox? = null - private var useOmeroServerCheckBox: javax.swing.JCheckBox? = null // End of variables declaration//GEN-END:variables - private class TextAreaLogHandler(private var textArea: JTextArea?) : Handler() { var formatter = java.util.logging.SimpleFormatter() @@ -270,8 +438,7 @@ class SegmentationWindow this.textArea = null } - override fun flush() { - } + override fun flush() {} override fun publish(record: LogRecord) { val s = formatter.format(record) diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/filter/GaussianFilter.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/filter/GaussianFilter.kt index 1d5bd2c..2b8b6b8 100644 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/filter/GaussianFilter.kt +++ b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/filter/GaussianFilter.kt @@ -1,37 +1,33 @@ package edu.stanford.cfuller.imageanalysistools.filter -import ij.plugin.filter.GaussianBlur -import net.imglib2.algorithm.gauss.GaussFloat -import edu.stanford.cfuller.imageanalysistools.image.WritableImage -import edu.stanford.cfuller.imageanalysistools.image.ImgLibPixelData -import edu.stanford.cfuller.imageanalysistools.image.ImagePlusPixelData import edu.stanford.cfuller.imageanalysistools.image.ImageFactory +import edu.stanford.cfuller.imageanalysistools.image.ImagePlusPixelData +import edu.stanford.cfuller.imageanalysistools.image.WritableImage +import ij.plugin.filter.GaussianBlur /** * A Filter that applies a gaussian blur to a 2D Image. * - * * This Filter does not use a reference Image. * - * * The argument passed to the apply method is the Image to be blurred. - + * * @author Colin J. Fuller */ class GaussianFilter : Filter() { - //TODO: deal with more than 2 dimensional blur. (Or make that be the job of other filters and rename this one -2D?) + // TODO: deal with more than 2 dimensional blur. (Or make that be the job of other filters and + // rename this one -2D?) internal var width: Int = 0 - /** - * Constructs a GaussianFilter with a default size blur. - */ + /** Constructs a GaussianFilter with a default size blur. */ init { this.width = 5 } /** - * Applies the GaussianFilter to the specified Image, blurring it by convolution with a Gaussian function. - * @param im The Image to process, which will be blurred. + * Applies the GaussianFilter to the specified Image, blurring it by convolution with a Gaussian + * function. + * @param im The Image to process, which will be blurred. */ override fun apply(im: WritableImage) { val kernelSize = this.width @@ -46,18 +42,6 @@ class GaussianFilter : Filter() { return } - // if we're dealing with an ImgLib image, use the ImgLib gaussian filtering to avoid duplication of image data. - if (im.pixelData is ImgLibPixelData) { - val pd = im.pixelData as ImgLibPixelData - val imP = pd.img - // TODO(colin): figure out proper null handling - val numDim = imP!!.numDimensions() - val sigmas = DoubleArray(numDim, { 0.0 }) - sigmas[0] = this.width.toDouble() - sigmas[1] = this.width.toDouble() // only filter in x-y - GaussFloat(sigmas, imP) - return - } val imP = im.toImagePlus() val gb = GaussianBlur() @@ -65,15 +49,17 @@ class GaussianFilter : Filter() { imP.setSliceWithoutUpdate(i + 1) gb.blur(imP.processor, width.toDouble()) } - //only recopy if not an ImagePlusPixelData underneath, which would make duplicating unnecessary + // only recopy if not an ImagePlusPixelData underneath, which would make duplicating + // unnecessary if (im.pixelData !is ImagePlusPixelData) { im.copy(ImageFactory.create(imP)) } } /** - * Sets the width of the Gaussian filter. This is the standard deviation of the Gaussian function in units of pixels. - * @param width The width of the Gaussian to be used for filtering, in pixels. + * Sets the width of the Gaussian filter. This is the standard deviation of the Gaussian + * function in units of pixels. + * @param width The width of the Gaussian to be used for filtering, in pixels. */ fun setWidth(width: Int) { this.width = width diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/fitting/ImageObject.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/fitting/ImageObject.kt index 636962d..e7e3d3f 100644 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/fitting/ImageObject.kt +++ b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/fitting/ImageObject.kt @@ -4,24 +4,23 @@ import edu.stanford.cfuller.imageanalysistools.frontend.LoggingUtilities import edu.stanford.cfuller.imageanalysistools.image.Image import edu.stanford.cfuller.imageanalysistools.image.ImageCoordinate import edu.stanford.cfuller.imageanalysistools.meta.parameters.ParameterDictionary -import org.apache.commons.math3.geometry.euclidean.threed.Vector3D -import org.apache.commons.math3.linear.RealVector - +import edu.stanford.cfuller.imageanalysistools.util.Base64BinaryAdapter import java.io.ByteArrayOutputStream import java.io.ObjectOutputStream import java.io.Serializable import java.io.StringWriter - -import edu.stanford.cfuller.imageanalysistools.util.Base64BinaryAdapter import javax.xml.stream.XMLOutputFactory import javax.xml.stream.XMLStreamException import javax.xml.stream.XMLStreamWriter +import org.apache.commons.math3.geometry.euclidean.threed.Vector3D +import org.apache.commons.math3.linear.RealVector /** * An object in an image with some spatial extent that can be fit to some function. - - * Classes that extend ImageObject will supply methods that perform the fit to a specific functional form. - + * + * Classes that extend ImageObject will supply methods that perform the fit to a specific functional + * form. + * * @author Colin J. Fuller */ abstract class ImageObject : Serializable { @@ -29,8 +28,9 @@ abstract class ImageObject : Serializable { internal var sizeInPixels: Int = 0 /** - * Gets the label of this object; this corresponds to its greylevel value in the original image mask. - * @return The label of this object. + * Gets the label of this object; this corresponds to its greylevel value in the original image + * mask. + * @return The label of this object. */ var label: Int = 0 internal set @@ -40,11 +40,13 @@ abstract class ImageObject : Serializable { internal var yValues: DoubleArray? = null internal var zValues: DoubleArray? = null /** - * Gets the internal array containing the greylevel values of the pixels in the box containing this object. - - * No specific order is guaranteed beyond that it must be the same order as the x-, y-, and z- coordinates. - - * @return An array containing the values of each pixel in this object's box. + * Gets the internal array containing the greylevel values of the pixels in the box containing + * this object. + * + * No specific order is guaranteed beyond that it must be the same order as the x-, y-, and z- + * coordinates. + * + * @return An array containing the values of each pixel in this object's box. */ var functionValues: DoubleArray? = null internal set @@ -53,26 +55,26 @@ abstract class ImageObject : Serializable { /** * Gets the fitted parameters, one set per channel in the original image of the object. - * @return A List containing FitParameters objects, one for each channel in the original object. + * @return A List containing FitParameters objects, one for each channel in the original object. */ var fitParametersByChannel: MutableList? = null internal set /** * Gets the R^2 values for the fit in each channel. - * @return a List containing one R^2 value for the fit in each channel. + * @return a List containing one R^2 value for the fit in each channel. */ var fitR2ByChannel: MutableList? = null internal set /** * Gets an error estimate for the fitting of the position of the object in each channel. - * @return a List containing one fit error estimate for each channel. + * @return a List containing one fit error estimate for each channel. */ var fitErrorByChannel: MutableList? = null internal set /** * Gets the number of photons above background from the object in each channel. - * @return a List containing the number of photons collected in each channel. + * @return a List containing the number of photons collected in each channel. */ var nPhotonsByChannel: MutableList? = null internal set @@ -85,46 +87,48 @@ abstract class ImageObject : Serializable { internal var parentBoxMax: ImageCoordinate? = null /** - * Returns a reference to the parent Image of this ImageObject (that is, the image used to create it). - - * @return A reference to the parent Image. + * Returns a reference to the parent Image of this ImageObject (that is, the image used to + * create it). + * + * @return A reference to the parent Image. */ var parent: Image? = null internal set /** - * Returns a reference to the mask of the original image that was used to create this ImageObject. - - * @return A reference to the mask Image. + * Returns a reference to the mask of the original image that was used to create this + * ImageObject. + * + * @return A reference to the mask Image. */ var mask: Image? = null internal set /** - * Gets the ID of the image from which the object was taken (this could, for instance, be the filename of the original image). - - * @return A string that is the ID of the original image. + * Gets the ID of the image from which the object was taken (this could, for instance, be the + * filename of the original image). + * + * @return A string that is the ID of the original image. */ /** - * Sets the ID of the image from which the object was taken (this could, for instance, be the filename of the original image). - * @param imageID A string to which to set the ID of the image. + * Sets the ID of the image from which the object was taken (this could, for instance, be the + * filename of the original image). + * @param imageID A string to which to set the ID of the image. */ var imageID: String? = null internal var hadFittingError: Boolean = false /** * Gets information on whether this ImageObject had its position corrected successfully. - * @return true if the position was corrected successfully, false if it failed or was not corrected at all. + * @return true if the position was corrected successfully, false if it failed or was not + * corrected at all. */ /** * Sets whether this ImageObject had its position corrected successfully. - * @param success Whether the position has been corrected successfully. + * @param success Whether the position has been corrected successfully. */ var correctionSuccessful: Boolean = false - - /** - * Initializes the fields of an ImageObject to default or null values. - */ + /** Initializes the fields of an ImageObject to default or null values. */ protected fun init() { this.parentBoxMin = null this.parentBoxMax = null @@ -144,20 +148,21 @@ abstract class ImageObject : Serializable { this.correctionSuccessful = false } - /** * Initializes the fields of an ImageObject based on supplied image and parameter data. - - * Both the mask and the parent will be unmodified, except for boxing a region of interest, so no other thread should - * be using these images at the same time. - - * @param label The numerical label of the object in the mask. + * + * Both the mask and the parent will be unmodified, except for boxing a region of interest, so + * no other thread should be using these images at the same time. + * + * @param label The numerical label of the object in the mask. * * - * @param mask An image mask, containing a unique greylevel for each object; the greylevel of the object being initialized should correspond to the parameter [.label]. + * @param mask An image mask, containing a unique greylevel for each object; the greylevel of + * the object being initialized should correspond to the parameter [.label]. * * - * @param parent The original imgae that the mask corresponds to. + * @param parent The original imgae that the mask corresponds to. * * - * @param p A [ParameterDictionary] containing the parameters for the current analysis. In particular, this routine makes use of the various box size parameters. + * @param p A [ParameterDictionary] containing the parameters for the current analysis. In + * particular, this routine makes use of the various box size parameters. */ protected fun init(label: Int, mask: Image, parent: Image, p: ParameterDictionary) { this.fitParametersByChannel = null @@ -175,13 +180,23 @@ abstract class ImageObject : Serializable { this.centroidInMask = Vector3D(0.0, 0.0, 0.0) this.imageID = null this.hadFittingError = true - this.numberOfChannels = p.getIntValueForKey("num_wavelengths") // use this so that if there's extra wavelengths not to be quantified at the end, these won't skew the initial guess + this.numberOfChannels = + p.getIntValueForKey( + "num_wavelengths" + ) // use this so that if there's extra wavelengths not to be quantified at the end, + // these won't skew the initial guess for (i in mask) { if (mask.getValue(i) == label.toFloat()) { sizeInPixels++ - this.centroidInMask = this.centroidInMask.add( - Vector3D(i[ImageCoordinate.X].toDouble(), i[ImageCoordinate.Y].toDouble(), i[ImageCoordinate.Z].toDouble())) + this.centroidInMask = + this.centroidInMask.add( + Vector3D( + i[ImageCoordinate.X].toDouble(), + i[ImageCoordinate.Y].toDouble(), + i[ImageCoordinate.Z].toDouble() + ) + ) } } @@ -191,8 +206,10 @@ abstract class ImageObject : Serializable { this.centroidInMask = this.centroidInMask.scalarMultiply(1.0 / sizeInPixels) - var xcoord = Math.round(this.centroidInMask.x - p.getIntValueForKey("half_box_size")).toInt() - var ycoord = Math.round(this.centroidInMask.y - p.getIntValueForKey("half_box_size")).toInt() + var xcoord = + Math.round(this.centroidInMask.x - p.getIntValueForKey("half_box_size")).toInt() + var ycoord = + Math.round(this.centroidInMask.y - p.getIntValueForKey("half_box_size")).toInt() if (xcoord < 0) { xcoord = 0 } @@ -201,8 +218,10 @@ abstract class ImageObject : Serializable { } this.parentBoxMin = ImageCoordinate.createCoordXYZCT(xcoord, ycoord, 0, 0, 0) - xcoord = Math.round(this.centroidInMask.x + p.getIntValueForKey("half_box_size")).toInt() + 1 - ycoord = Math.round(this.centroidInMask.y + p.getIntValueForKey("half_box_size")).toInt() + 1 + xcoord = + Math.round(this.centroidInMask.x + p.getIntValueForKey("half_box_size")).toInt() + 1 + ycoord = + Math.round(this.centroidInMask.y + p.getIntValueForKey("half_box_size")).toInt() + 1 if (xcoord > mask.dimensionSizes[ImageCoordinate.X]) { xcoord = mask.dimensionSizes[ImageCoordinate.X] } @@ -210,17 +229,20 @@ abstract class ImageObject : Serializable { ycoord = mask.dimensionSizes[ImageCoordinate.Y] } - this.parentBoxMax = ImageCoordinate.createCoordXYZCT( - xcoord, ycoord, - parent.dimensionSizes[ImageCoordinate.Z], - parent.dimensionSizes[ImageCoordinate.C], - parent.dimensionSizes[ImageCoordinate.T]) + this.parentBoxMax = + ImageCoordinate.createCoordXYZCT( + xcoord, + ycoord, + parent.dimensionSizes[ImageCoordinate.Z], + parent.dimensionSizes[ImageCoordinate.C], + parent.dimensionSizes[ImageCoordinate.T] + ) - //handle either 2D or 3D masks + // handle either 2D or 3D masks - //2D case: + // 2D case: if (mask.dimensionSizes[ImageCoordinate.Z] == 1) { - //find the max intensity pixel in each channel and use this to refine the box + // find the max intensity pixel in each channel and use this to refine the box this.maxIntensityZCoordByChannel = IntArray(parent.dimensionSizes[ImageCoordinate.C]) var minZOverall = parent.dimensionSizes[ImageCoordinate.Z] var maxZOverall = 0 @@ -232,7 +254,9 @@ abstract class ImageObject : Serializable { var maxCoord: ImageCoordinate? = null for (ic in parent) { if (ic[ImageCoordinate.X] != Math.round(this.centroidInMask.x).toInt() || - ic.get(dimensionConstant = ImageCoordinate.Y) != Math.round(this.centroidInMask.y).toInt()) { + ic.get(dimensionConstant = ImageCoordinate.Y) != + Math.round(this.centroidInMask.y).toInt() + ) { continue } if (parent.getValue(ic) > maxValue) { @@ -243,8 +267,10 @@ abstract class ImageObject : Serializable { } if (maxCoord == null) continue - if (maxCoord[ImageCoordinate.Z] > maxZOverall) maxZOverall = maxCoord[ImageCoordinate.Z] - if (maxCoord[ImageCoordinate.Z] < minZOverall) minZOverall = maxCoord[ImageCoordinate.Z] + if (maxCoord[ImageCoordinate.Z] > maxZOverall) + maxZOverall = maxCoord[ImageCoordinate.Z] + if (maxCoord[ImageCoordinate.Z] < minZOverall) + minZOverall = maxCoord[ImageCoordinate.Z] this.maxIntensityZCoordByChannel!![c] = maxCoord[ImageCoordinate.Z] maxCoord.recycle() @@ -254,7 +280,8 @@ abstract class ImageObject : Serializable { if (minZOverall > maxZOverall) { minZOverall = 0 maxZOverall = 0 - java.util.logging.Logger.getLogger("edu.stanford.cfuller.colocalization3d").warning("Problem when calculating Z range of image stack.") + java.util.logging.Logger.getLogger("edu.stanford.cfuller.colocalization3d") + .warning("Problem when calculating Z range of image stack.") } val zAverage = (minZOverall + maxZOverall) / 2 @@ -267,36 +294,44 @@ abstract class ImageObject : Serializable { this.parentBoxMax!![ImageCoordinate.C] = parent.dimensionSizes[ImageCoordinate.C] zcoord = zAverage + p.getIntValueForKey("half_z_size") + 1 - if (zcoord > parent.dimensionSizes[ImageCoordinate.Z]) zcoord = parent.dimensionSizes[ImageCoordinate.Z] + if (zcoord > parent.dimensionSizes[ImageCoordinate.Z]) + zcoord = parent.dimensionSizes[ImageCoordinate.Z] this.parentBoxMax!![ImageCoordinate.Z] = zcoord - - } else { //3D mask - var zcoord = Math.round(this.centroidInMask.z - p.getIntValueForKey("half_z_size")).toInt() + } else { // 3D mask + var zcoord = + Math.round(this.centroidInMask.z - p.getIntValueForKey("half_z_size")).toInt() if (zcoord < 0) { zcoord = 0 } this.parentBoxMin!![ImageCoordinate.Z] = zcoord - zcoord = Math.round(this.centroidInMask.z + p.getIntValueForKey("half_z_size").toDouble() + 1.0).toInt() + zcoord = + Math.round( + this.centroidInMask.z + + p.getIntValueForKey("half_z_size").toDouble() + + 1.0 + ) + .toInt() this.parentBoxMax!![ImageCoordinate.Z] = zcoord } } - /** * Fits the object to the ImageObject's functional form in order to determine its position. - * @param p The parameters for the current analysis. + * @param p The parameters for the current analysis. */ abstract fun fitPosition(p: ParameterDictionary) /** - * Nullifies the references to the parent and mask image, and the internal storage of the image data and coordinates to free them for garbage collection. - - * This should only be called after fitting has been completed, as fitting will no longer be possible without the original image data. + * Nullifies the references to the parent and mask image, and the internal storage of the image + * data and coordinates to free them for garbage collection. + * + * This should only be called after fitting has been completed, as fitting will no longer be + * possible without the original image data. */ fun nullifyImages() { - //this.parent.dispose(); + // this.parent.dispose(); this.parent = null - //this.mask.dispose(); + // this.mask.dispose(); this.mask = null this.xValues = null this.yValues = null @@ -305,7 +340,8 @@ abstract class ImageObject : Serializable { } /** - * Cleans up the ImageCoordinates used internally by ImageCoordinates and recycles them for future use. + * Cleans up the ImageCoordinates used internally by ImageCoordinates and recycles them for + * future use. * @throws Throwable */ @Throws(Throwable::class) @@ -321,45 +357,46 @@ abstract class ImageObject : Serializable { // super.finalize() // TODO(colin): why can't I call super.finalize()? } - /** - * Sets the relevant region of interest in the parent image to be the region that boxes this object. + * Sets the relevant region of interest in the parent image to be the region that boxes this + * object. */ fun boxImages() { this.parent!!.setBoxOfInterest(this.parentBoxMin!!, this.parentBoxMax!!) this.mask!!.setBoxOfInterest(this.parentBoxMin!!, this.parentBoxMax!!) } - /** - * Clears the region of interest in the parent image. - */ + /** Clears the region of interest in the parent image. */ fun unboxImages() { this.parent!!.clearBoxOfInterest() this.mask!!.clearBoxOfInterest() } /** - * Gets the internal array containing the x-coordinates of the pixels in the box containing this object. - * No specific order is guaranteed beyond that it must be the same order as the y- and z- coordinates and function values. - * @return An array containing the x-coordinates of each pixel in this object's box. + * Gets the internal array containing the x-coordinates of the pixels in the box containing this + * object. No specific order is guaranteed beyond that it must be the same order as the y- and + * z- coordinates and function values. + * @return An array containing the x-coordinates of each pixel in this object's box. */ fun getxValues(): DoubleArray { return xValues!! } /** - * Gets the internal array containing the y-coordinates of the pixels in the box containing this object. - * No specific order is guaranteed beyond that it must be the same order as the x- and z- coordinates and function values. - * @return An array containing the x-coordinates of each pixel in this object's box. + * Gets the internal array containing the y-coordinates of the pixels in the box containing this + * object. No specific order is guaranteed beyond that it must be the same order as the x- and + * z- coordinates and function values. + * @return An array containing the x-coordinates of each pixel in this object's box. */ fun getyValues(): DoubleArray { return yValues!! } /** - * Gets the internal array containing the z-coordinates of the pixels in the box containing this object. - * No specific order is guaranteed beyond that it must be the same order as the x- and y- coordinates and function values. - * @return An array containing the x-coordinates of each pixel in this object's box. + * Gets the internal array containing the z-coordinates of the pixels in the box containing this + * object. No specific order is guaranteed beyond that it must be the same order as the x- and + * y- coordinates and function values. + * @return An array containing the x-coordinates of each pixel in this object's box. */ fun getzValues(): DoubleArray { return zValues!! @@ -367,17 +404,20 @@ abstract class ImageObject : Serializable { /** * Gets information on whether this object has finished fitting with no errors. - * @return true if fitting finished normally, false if there was an error or the object was not yet fit. + * @return true if fitting finished normally, false if there was an error or the object was not + * yet fit. */ fun finishedFitting(): Boolean { return !this.hadFittingError } /** - * Gets the position of this object in the specified channel. - * Do not modify the returned RealVector, as it is a reference to the internally stored vector. - * @param channel The index of the channel, either by order in the original multiwavelength image, or in the order specified for split wavelength images. - * @return A RealVector containing the x,y,and z coordinates of the position, or null if it has not yet been determined, or the channel is out of range. + * Gets the position of this object in the specified channel. Do not modify the returned + * RealVector, as it is a reference to the internally stored vector. + * @param channel The index of the channel, either by order in the original multiwavelength + * image, or in the order specified for split wavelength images. + * @return A RealVector containing the x,y,and z coordinates of the position, or null if it has + * not yet been determined, or the channel is out of range. */ fun getPositionForChannel(channel: Int): RealVector? { if (channel >= this.positionsByChannel.size) { @@ -387,10 +427,13 @@ abstract class ImageObject : Serializable { } /** - * Gets the corrected position of this object in the specified channel (if there is a corrected position). - * Do not modify the returned RealVector, as it is a reference to the internally stored vector. - * @param channel The index of the channel, either by order in the original multiwavelength image, or in the order specified for split wavelength images. - * @return A RealVector containing the x,y,and z coordinates of the position, or null if it has not yet been determined, or the channel is out of range. + * Gets the corrected position of this object in the specified channel (if there is a corrected + * position). Do not modify the returned RealVector, as it is a reference to the internally + * stored vector. + * @param channel The index of the channel, either by order in the original multiwavelength + * image, or in the order specified for split wavelength images. + * @return A RealVector containing the x,y,and z coordinates of the position, or null if it has + * not yet been determined, or the channel is out of range. */ fun getCorrectedPositionForChannel(channel: Int): RealVector? { if (!this.correctedPositionsByChannel.containsKey(channel)) { @@ -400,20 +443,28 @@ abstract class ImageObject : Serializable { } /** - * Applies the specified correction vector to the position of the object to generate a corrected position that can be accessed using [.getCorrectedPositionForChannel]. - * @param channel The index of the channel, either by order in the original multiwavelength image, or in the order specified for split wavelength images. - * @param correction a RealVector containing a correction that will be subtracted from the position of the object in the specified channel. + * Applies the specified correction vector to the position of the object to generate a corrected + * position that can be accessed using [.getCorrectedPositionForChannel]. + * @param channel The index of the channel, either by order in the original multiwavelength + * image, or in the order specified for split wavelength images. + * @param correction a RealVector containing a correction that will be subtracted from the + * position of the object in the specified channel. */ fun applyCorrectionVectorToChannel(channel: Int, correction: RealVector) { - this.correctedPositionsByChannel.put(channel, this.getPositionForChannel(channel)!!.subtract(correction)) + this.correctedPositionsByChannel.put( + channel, + this.getPositionForChannel(channel)!!.subtract(correction) + ) } /** - * Gets the vector difference between the position of the object in two channels. - * Note that there is no unit conversion here, and the distance is returned in image units of pixels or sections. - * @param channel0 The index of one channel to use for the difference. - * @param channel1 The index of the other channel to use for the difference. - * @return The vector difference between the two channels, as channel1 - channel0, or null if either channel is out of range or has not yet been fit. + * Gets the vector difference between the position of the object in two channels. Note that + * there is no unit conversion here, and the distance is returned in image units of pixels or + * sections. + * @param channel0 The index of one channel to use for the difference. + * @param channel1 The index of the other channel to use for the difference. + * @return The vector difference between the two channels, as channel1 - channel0, or null if + * either channel is out of range or has not yet been fit. */ fun getVectorDifferenceBetweenChannels(channel0: Int, channel1: Int): RealVector? { val key = this.numberOfChannels * channel0 + channel1 @@ -430,12 +481,14 @@ abstract class ImageObject : Serializable { } /** - * Gets the vector difference between the corrected positions of the object in two channels. - * If there is no corrected position for a channel, its uncorrected position is used. - * Note that there is no unit conversion here, and the distance is returned in image units of pixels or sections. - * @param channel0 The index of one channel to use for the difference. - * @param channel1 The index of the other channel to use for the difference. - * @return The vector difference between the two channels, as channel1 - channel0, or null if either channel is out of range or has not yet been fit. + * Gets the vector difference between the corrected positions of the object in two channels. If + * there is no corrected position for a channel, its uncorrected position is used. Note that + * there is no unit conversion here, and the distance is returned in image units of pixels or + * sections. + * @param channel0 The index of one channel to use for the difference. + * @param channel1 The index of the other channel to use for the difference. + * @return The vector difference between the two channels, as channel1 - channel0, or null if + * either channel is out of range or has not yet been fit. */ fun getCorrectedVectorDifferenceBetweenChannels(channel0: Int, channel1: Int): RealVector { var c0: RealVector? = this.correctedPositionsByChannel[channel0] @@ -446,18 +499,29 @@ abstract class ImageObject : Serializable { } /** - * Gets the scalar difference between the position of the object in two channels. - * Units are converted from image units to real units using the supplied vector of conversions. - * @param channel0 The index of one channel to use for the difference. - * @param channel1 The index of the other channel to use for the difference. - * @param pixelToDistanceConversions A vector containing the number of realspace distance units per pixel or section, one element per dimension. - * @return The scalar distance between the position of the object in each channel (that is, the length of the vector representing the vector distance), or null if either channel is out of range or has not yet been fit. + * Gets the scalar difference between the position of the object in two channels. Units are + * converted from image units to real units using the supplied vector of conversions. + * @param channel0 The index of one channel to use for the difference. + * @param channel1 The index of the other channel to use for the difference. + * @param pixelToDistanceConversions A vector containing the number of realspace distance units + * per pixel or section, one element per dimension. + * @return The scalar distance between the position of the object in each channel (that is, the + * length of the vector representing the vector distance), or null if either channel is out of + * range or has not yet been fit. */ - fun getScalarDifferenceBetweenChannels(channel0: Int, channel1: Int, pixelToDistanceConversions: RealVector): Double? { + fun getScalarDifferenceBetweenChannels( + channel0: Int, + channel1: Int, + pixelToDistanceConversions: RealVector + ): Double? { val key = this.numberOfChannels * channel0 + channel1 if (!this.scalarDifferencesBetweenChannels.containsKey(key)) { - val vecDifference = this.getVectorDifferenceBetweenChannels(channel0, channel1) ?: return null - this.scalarDifferencesBetweenChannels.put(key, vecDifference.ebeMultiply(pixelToDistanceConversions).norm) + val vecDifference = + this.getVectorDifferenceBetweenChannels(channel0, channel1) ?: return null + this.scalarDifferencesBetweenChannels.put( + key, + vecDifference.ebeMultiply(pixelToDistanceConversions).norm + ) } return this.scalarDifferencesBetweenChannels[key] } @@ -465,10 +529,12 @@ abstract class ImageObject : Serializable { fun writeToXMLString(): String { val sw = StringWriter() try { - val xsw = XMLOutputFactory.newFactory().createXMLStreamWriter(sw) + val xsw = XMLOutputFactory.newInstance().createXMLStreamWriter(sw) this.writeToXML(xsw) } catch (e: XMLStreamException) { - LoggingUtilities.logger.severe("Exception encountered while writing XML correction output: " + e.message) + LoggingUtilities.logger.severe( + "Exception encountered while writing XML correction output: " + e.message + ) } return sw.toString() @@ -486,20 +552,38 @@ abstract class ImageObject : Serializable { xsw.writeCharacters("\n") xsw.writeStartElement(FIT_ELEMENT) xsw.writeAttribute(R2_ATTR, java.lang.Double.toString(this.fitR2ByChannel!![i])) - xsw.writeAttribute(ERROR_ATTR, java.lang.Double.toString(this.fitErrorByChannel!![i])) - xsw.writeAttribute(N_PHOTONS_ATTR, java.lang.Double.toString(this.nPhotonsByChannel!![i])) + xsw.writeAttribute( + ERROR_ATTR, + java.lang.Double.toString(this.fitErrorByChannel!![i]) + ) + xsw.writeAttribute( + N_PHOTONS_ATTR, + java.lang.Double.toString(this.nPhotonsByChannel!![i]) + ) xsw.writeCharacters("\n") xsw.writeStartElement(PARAMETERS_ELEMENT) - xsw.writeCharacters(this.fitParametersByChannel!![i].toString().replace(";", ",").replace("}", "").replace("{", "")) - xsw.writeEndElement() //parameters + xsw.writeCharacters( + this.fitParametersByChannel!![i] + .toString() + .replace(";", ",") + .replace("}", "") + .replace("{", "") + ) + xsw.writeEndElement() // parameters xsw.writeCharacters("\n") xsw.writeStartElement(POSITION_ELEMENT) - xsw.writeCharacters(this.getPositionForChannel(i)!!.toString().replace(";", ",").replace("}", "").replace("{", "")) - xsw.writeEndElement() //position + xsw.writeCharacters( + this.getPositionForChannel(i)!! + .toString() + .replace(";", ",") + .replace("}", "") + .replace("{", "") + ) + xsw.writeEndElement() // position xsw.writeCharacters("\n") - xsw.writeEndElement() //fit + xsw.writeEndElement() // fit xsw.writeCharacters("\n") - xsw.writeEndElement() //channel + xsw.writeEndElement() // channel xsw.writeCharacters("\n") } xsw.writeStartElement(SERIAL_ELEMENT) @@ -509,22 +593,27 @@ abstract class ImageObject : Serializable { val oos = ObjectOutputStream(bytesOutput) oos.writeObject(this) } catch (e: java.io.IOException) { - LoggingUtilities.logger.severe("Exception encountered while serializing ImageObject: " + e.message) + LoggingUtilities.logger.severe( + "Exception encountered while serializing ImageObject: " + e.message + ) } val adapter = Base64BinaryAdapter() xsw.writeCharacters(adapter.marshal(bytesOutput.toByteArray())) - xsw.writeEndElement() //serial + xsw.writeEndElement() // serial xsw.writeCharacters("\n") - xsw.writeEndElement() //object + xsw.writeEndElement() // object xsw.writeCharacters("\n") } catch (e: XMLStreamException) { - LoggingUtilities.logger.severe("Exception encountered while writing XML correction output: " + e.message) + LoggingUtilities.logger.severe( + "Exception encountered while writing XML correction output: " + e.message + ) } } companion object { - //TODO: maintain the notion that the ImageObject has some location in real space, but reduce - //dependence on 5D images. + // TODO: maintain the notion that the ImageObject has some location in real space, but + // reduce + // dependence on 5D images. const val serialVersionUID = 5L val OBJECT_ELEMENT = "image_object" diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/frontend/DirUtils.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/frontend/DirUtils.kt index 603fadd..91a4152 100644 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/frontend/DirUtils.kt +++ b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/frontend/DirUtils.kt @@ -1,72 +1,70 @@ package edu.stanford.cfuller.imageanalysistools.frontend import edu.stanford.cfuller.imageanalysistools.image.ImageSet -import edu.stanford.cfuller.imageanalysistools.image.io.omero.OmeroServerImageReader -import edu.stanford.cfuller.imageanalysistools.image.io.omero.OmeroServerInfo import edu.stanford.cfuller.imageanalysistools.meta.parameters.ParameterDictionary /** - * Utilities for getting files from a directory or OMERO data source and matching them together into sets of the different channels of the same image. - + * Utilities for getting files from a directory or OMERO data source and matching them together into + * sets of the different channels of the same image. + * * @author Colin J. Fuller */ object DirUtils { /** - * Gets filenames of the images to be processed (either from a directory or OMERO data source), along with a display name (i.e. name without a full path) for each. - * @param params The ParameterDictionary containing the parameters to be used for the analysis; the data directory or OMERO data source will be pulled from here. + * Gets filenames of the images to be processed (either from a directory or OMERO data source), + * along with a display name (i.e. name without a full path) for each. + * @param params The ParameterDictionary containing the parameters to be used for the analysis; + * the data directory or OMERO data source will be pulled from here. * * - * @return A List containing ImageSets, one per file. + * @return A List containing ImageSets, one per file. */ fun makeMultiwavelengthFileSets(params: ParameterDictionary): List { val outputSets = java.util.Vector() - if (params.hasKey("use_omero") && params.getBooleanValueForKey("use_omero")) { - val imageIds = params.getValueForKey("omero_image_ids")!! - .split(" ".toRegex()) - .dropLastWhile(String::isEmpty) - .toTypedArray() + val directory = java.io.File(params.getValueForKey("local_directory")) + var commonFilenameTag = "" + var imageExtension = "" + if (params.hasKey("image_extension")) { + imageExtension = params.getValueForKey("image_extension")!! + } + if (params.hasKey("common_filename_tag")) { + commonFilenameTag = params.getValueForKey("common_filename_tag")!! + } - for (id in imageIds) { - val currSet = ImageSet(params) - currSet.addImageWithOmeroId(java.lang.Long.parseLong(id)) - outputSets.add(currSet) - } - } else { - val directory = java.io.File(params.getValueForKey("local_directory")) - var commonFilenameTag = "" - var imageExtension = "" - if (params.hasKey("image_extension")) { - imageExtension = params.getValueForKey("image_extension")!! - } - if (params.hasKey("common_filename_tag")) { - commonFilenameTag = params.getValueForKey("common_filename_tag")!! + for (f in directory.listFiles()!!) { + if (f.isDirectory) { + continue } - - for (f in directory.listFiles()!!) { - if (f.isDirectory) { - continue - } - if (f.name.matches(".*Thumb.*".toRegex()) || !f.name.toLowerCase().matches((".*" + imageExtension.toLowerCase() + "$").toRegex()) || !f.name.matches(".*$commonFilenameTag.*".toRegex())) { - continue - } - println(f.absolutePath) - val set = ImageSet(params) - set.addImageWithFilename(f.absolutePath) - outputSets.add(set) + if (f.name.matches(".*Thumb.*".toRegex()) || + !f.name.toLowerCase() + .matches( + (".*" + imageExtension.toLowerCase() + "$").toRegex() + ) || + !f.name.matches(".*$commonFilenameTag.*".toRegex()) + ) { + continue } + println(f.absolutePath) + val set = ImageSet(params) + set.addImageWithFilename(f.absolutePath) + outputSets.add(set) } return outputSets } /** - * Gets a list of String arrays each containing a set of filenames that correspond to image files for the color channels of a split channel image. - * @param params The ParameterDictionary containing the parameters for the analysis. The directory or OMERO source and channel names will be taken from here. - * @return A List containing ImageSets. Each set can load the Images for the channels of an image. + * Gets a list of String arrays each containing a set of filenames that correspond to image + * files for the color channels of a split channel image. + * @param params The ParameterDictionary containing the parameters for the analysis. The + * directory or OMERO source and channel names will be taken from here. + * @return A List containing ImageSets. Each set can load the Images for the channels of an + * image. */ fun makeSetsOfMatchingFiles(params: ParameterDictionary): List { - val setTags = params.getValueForKey("channel_name")!! - .split(" ".toRegex()) - .dropLastWhile(String::isEmpty) - .toTypedArray() + val setTags = + params.getValueForKey("channel_name")!! + .split(" ".toRegex()) + .dropLastWhile(String::isEmpty) + .toTypedArray() var numPerSet = 0 if (params.hasKey("number_of_channels")) { @@ -80,50 +78,32 @@ object DirUtils { val directory = java.io.File(params.getValueForKey("local_directory")) val idLookupByName = mutableMapOf() - if (params.hasKey("use_omero") && params.getBooleanValueForKey("use_omero")) { - val imageIds = params.getValueForKey("omero_image_ids")!! - .split(" ".toRegex()) - .dropLastWhile(String::isEmpty) - .toTypedArray() - val ir = OmeroServerImageReader() - try { - for (id in imageIds) { - val idL = java.lang.Long.parseLong(id) - val osi = OmeroServerInfo( - params.getValueForKey("omero_hostname")!!, - params.getValueForKey("omero_username")!!, - params.getValueForKey("omero_password")!!.toCharArray()) - val name = ir.getImageNameForOmeroId(idL, osi) - idLookupByName.put(name, idL) - } - } catch (e: java.io.IOException) { - LoggingUtilities.logger.severe("Exception encountered while accessing image on OMERO server: ") - e.printStackTrace() - } finally { - ir.closeConnection() + for (f in directory.listFiles()!!) { + if (f.isDirectory) { + continue } - } else { - for (f in directory.listFiles()!!) { - if (f.isDirectory) { - continue - } - //added hack here for case insensitive extension - var commonFilenameTag = "" - var imageExtension = "" + // added hack here for case insensitive extension + var commonFilenameTag = "" + var imageExtension = "" - if (params.hasKey("image_extension")) { - imageExtension = params.getValueForKey("image_extension")!! - } + if (params.hasKey("image_extension")) { + imageExtension = params.getValueForKey("image_extension")!! + } - if (params.hasKey("common_filename_tag")) { - commonFilenameTag = params.getValueForKey("common_filename_tag")!! - } + if (params.hasKey("common_filename_tag")) { + commonFilenameTag = params.getValueForKey("common_filename_tag")!! + } - if (f.name.matches(".*Thumb.*".toRegex()) || !f.name.toLowerCase().matches((".*" + imageExtension.toLowerCase() + "$").toRegex()) || !f.name.matches(".*$commonFilenameTag.*".toRegex())) { - continue - } - idLookupByName.put(f.absolutePath, null) + if (f.name.matches(".*Thumb.*".toRegex()) || + !f.name.toLowerCase() + .matches( + (".*" + imageExtension.toLowerCase() + "$").toRegex() + ) || + !f.name.matches(".*$commonFilenameTag.*".toRegex()) + ) { + continue } + idLookupByName.put(f.absolutePath, null) } for (name in idLookupByName.keys) { @@ -139,12 +119,7 @@ object DirUtils { tempSet = null break } - val id = idLookupByName[subName] - if (id != null) { - tempSet!!.addImageWithOmeroIdAndName(id, subName) - } else { - tempSet!!.addImageWithFilename(subName) - } + tempSet!!.addImageWithFilename(subName) } outputSets.add(tempSet) } diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ImageSet.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ImageSet.kt index 0e9616e..725d43c 100644 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ImageSet.kt +++ b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ImageSet.kt @@ -2,57 +2,55 @@ package edu.stanford.cfuller.imageanalysistools.image import edu.stanford.cfuller.imageanalysistools.frontend.LoggingUtilities import edu.stanford.cfuller.imageanalysistools.image.io.ImageReader -import edu.stanford.cfuller.imageanalysistools.image.io.omero.OmeroServerImageReader -import edu.stanford.cfuller.imageanalysistools.image.io.omero.OmeroServerInfo import edu.stanford.cfuller.imageanalysistools.meta.parameters.ParameterDictionary import edu.stanford.cfuller.imageanalysistools.util.FileHashCalculator - import java.util.* /** - * A set of related Images that will be processed together for analysis. For instance, multiple channels of the same Image might be collected - * into an ImageSet. Images in the set can be added by Image object, by filename, or by omero Id. - - * Optionally, one Image in the set can be designated as a marker Image, which might have some specific purpose in the analysis. For - * instance, the marker Image might be the Image to use for segmentation, while the others might just be quantified. - - * Images added by filename or omero Id must be loaded using [.loadAllImages] before being used or retrieved. - + * A set of related Images that will be processed together for analysis. For instance, multiple + * channels of the same Image might be collected into an ImageSet. Images in the set can be added by + * Image object or by filename. + * + * Optionally, one Image in the set can be designated as a marker Image, which might have some + * specific purpose in the analysis. For instance, the marker Image might be the Image to use for + * segmentation, while the others might just be quantified. + * * @author Colin J. Fuller */ class ImageSet : java.io.Serializable, Collection { private var images: MutableList = mutableListOf() /** - * Gets a stored Image that is the combine of each image in this set along the appropriate dimension if it has been previously supplied. - * (e.g. a color combine if all images represent channels) + * Gets a stored Image that is the combine of each image in this set along the appropriate + * dimension if it has been previously supplied. (e.g. a color combine if all images represent + * channels) * @return the combine of the Images in this set, if it has been set, or null if it has not. */ /** - * Stores an Image that is the combine of each image in this set along the appropriate dimension. - * (e.g. a color combine if all images represent channels) + * Stores an Image that is the combine of each image in this set along the appropriate + * dimension. (e.g. a color combine if all images represent channels) * @param combined the Image that is the combine of the images in the ImageSet. */ var combinedImage: Image? = null /** * Gets the index of the Image specified as the marker Image. - - * @return The index of the marker Image in the set, or null if one has not been specified. + * + * @return The index of the marker Image in the set, or null if one has not been specified. */ var markerIndex: Int? = null internal set /** * Gets the parameters associated with the ImageSet. - * @return A ParameterDictionary containing the analysis parameters. + * @return A ParameterDictionary containing the analysis parameters. */ var parameters: ParameterDictionary internal set /** * Constructs a new, empty ImageSet. - * @param p The parameters for the analysis. + * @param p The parameters for the analysis. */ constructor(p: ParameterDictionary) { this.images = ArrayList() @@ -62,9 +60,9 @@ class ImageSet : java.io.Serializable, Collection { } /** - * Copy constructor. - * Does not make copies of the Images; this method is primarily useful to allow different marker Images or simultaneous iteration. - * @param other The ImageSet to copy. + * Copy constructor. Does not make copies of the Images; this method is primarily useful to + * allow different marker Images or simultaneous iteration. + * @param other The ImageSet to copy. */ constructor(other: ImageSet) { this.images = ArrayList() @@ -74,90 +72,67 @@ class ImageSet : java.io.Serializable, Collection { this.combinedImage = other.combinedImage } - /** * Adds an Image to the ImageSet using its filename. - * @param filename The filename of the Image to add. + * @param filename The filename of the Image to add. */ fun addImageWithFilename(filename: String) { - val newImage = ImageHolder(null, filename, null) + val newImage = ImageHolder(null, filename) newImage.displayName = filename this.images.add(newImage) } /** - * Adds an Image to the ImageSet using an already constructed Image object and a filename associated with it. - * This can be used for Images that have already been loaded, in order to keep them associated with some sort of identifier. - * @param toAdd The (loaded) Image to add to the ImageSet. - * @param name The name to associate with the Image. + * Adds an Image to the ImageSet using an already constructed Image object and a filename + * associated with it. This can be used for Images that have already been loaded, in order to + * keep them associated with some sort of identifier. + * @param toAdd The (loaded) Image to add to the ImageSet. + * @param name The name to associate with the Image. */ fun addImageWithImageAndName(toAdd: Image, name: String) { - val imh = ImageHolder(toAdd, null, null) + val imh = ImageHolder(toAdd, null) imh.displayName = name this.images.add(imh) } /** * Adds an Image to the ImageSet using an already constructed Image object. - * @param toAdd The Image to add to the ImageSet. + * @param toAdd The Image to add to the ImageSet. */ fun addImageWithImage(toAdd: Image) { - this.images.add(ImageHolder(toAdd, null, null)) - } - - /** - * Adds an Image to the ImageSet using its omero Id. - * The necessary information for connecting to the omero server on which the image resides should be in the - * ParameterDictionary used to construct the ImageSet in the parameters "omero_hostname", "omero_username", and "omero_password". - * @param omeroId The ID of the Image to add to the ImageSet. - */ - fun addImageWithOmeroId(omeroId: Long) { - this.images.add(ImageHolder(null, null, omeroId)) - } - - fun addImageWithOmeroIdAndName(omeroId: Long, name: String) { - val imh = ImageHolder(null, null, omeroId) - imh.displayName = name - this.images.add(imh) + this.images.add(ImageHolder(toAdd, null)) } /** * Gets an Image from the set given its exact filename. - * @param filename The filename of the Image to retrieve. - * @return The retrieved Image, or null if no Image with this filename is contained in the ImageSet or the Image has not yet been loaded. + * @param filename The filename of the Image to retrieve. + * @return The retrieved Image, or null if no Image with this filename is contained in the + * ImageSet or the Image has not yet been loaded. */ fun getImageForName(filename: String): Image? { - return images - .firstOrNull { it.filename != null && it.filename == filename } - ?.image + return images.firstOrNull { it.filename != null && it.filename == filename }?.image } /** * Gets an Image from the set whose filename matches the supplied regular expression. - * @param regexp The regular expression to match; must follow the syntax in java.util.regex.Pattern. - * @return The retrieved Image, or null if no Image with this filename is contained in the ImageSet or the Image has not yet been loaded. + * @param regexp The regular expression to match; must follow the syntax in + * java.util.regex.Pattern. + * @return The retrieved Image, or null if no Image with this filename is contained in the + * ImageSet or the Image has not yet been loaded. */ fun getImageForNameMatching(regexp: String): Image? { return images - .firstOrNull { it.filename != null && it.filename?.matches(regexp.toRegex()) ?: false } - ?.image - } - - /** - * Gets an Image from the set whose omero Id matches the supplied Id. - * @param id The omero Id of the Image to retrieve. - * @return The retrieved Image, or null if no Image with this id is contained in the ImageSet or the Image has not yet been loaded. - */ - fun getImageForOmeroId(id: Long): Image? { - return images - .firstOrNull { it.omeroId != null && it.omeroId == id } + .firstOrNull { + it.filename != null && it.filename?.matches(regexp.toRegex()) ?: false + } ?.image } /** - * Gets the Image specified as the marker Image. - * The marker Image might be the one to use for segmentation, for instance. - * @return The marker Image, or null if a marker Image has not been specified or it has not yet been loaded. + * Gets the Image specified as the marker Image. The marker Image might be the one to use for + * segmentation, for instance. + * @return The marker Image, or null if a marker Image has not been specified or it has not yet + * been loaded. */ val markerImage: Image? get() { @@ -166,7 +141,7 @@ class ImageSet : java.io.Serializable, Collection { /** * Gets the filename of the marker Image. - * @return the filename, or null if there is no marker image. + * @return the filename, or null if there is no marker image. */ val markerImageName: String? get() { @@ -174,10 +149,10 @@ class ImageSet : java.io.Serializable, Collection { } /** - * Gets the Image specified as the marker Image. If one has not been specified, - * the zeroth image in the set will be chosen as the default and returned. - * The marker Image might be the one to use for segmentation, for instance. - * @return The marker Image, or an Image selected as the default. + * Gets the Image specified as the marker Image. If one has not been specified, the zeroth image + * in the set will be chosen as the default and returned. The marker Image might be the one to + * use for segmentation, for instance. + * @return The marker Image, or an Image selected as the default. */ val markerImageOrDefault: Image? get() { @@ -187,32 +162,34 @@ class ImageSet : java.io.Serializable, Collection { /** * Checks if the ImageSet has a marker Image specified. - * @return true if a valid marker Image has been specified; false otherwise. + * @return true if a valid marker Image has been specified; false otherwise. */ fun hasMarkerImage(): Boolean { return this.markerIndex != null } /** - * Gets the Image at the specified index. - * The index refers to the order in which the Image was added to the ImageSet. (Starts at 0.) - * @param index The index of the Image to return. - * @return The retrieved Image, or null if the index was not valid. + * Gets the Image at the specified index. The index refers to the order in which the Image was + * added to the ImageSet. (Starts at 0.) + * @param index The index of the Image to return. + * @return The retrieved Image, or null if the index was not valid. */ fun getImageForIndex(index: Int): Image? { try { return this.images[index].image } catch (e: IndexOutOfBoundsException) { - LoggingUtilities.logger.warning("Request for Image at index $index in ImageSet was out of bounds.") + LoggingUtilities.logger.warning( + "Request for Image at index $index in ImageSet was out of bounds." + ) return null } } /** - * Specifies that the Image at the provided index is the marker Image. - * The index refers to the order in which the Image was added to the ImageSet. (Starts at 0.) - * Supply -1 for the index as a shortcut for the most recently added Image. - * @param index The index of the Image to designate as the marker Image. + * Specifies that the Image at the provided index is the marker Image. The index refers to the + * order in which the Image was added to the ImageSet. (Starts at 0.) Supply -1 for the index as + * a shortcut for the most recently added Image. + * @param index The index of the Image to designate as the marker Image. */ fun setMarkerImage(index: Int) { try { @@ -223,22 +200,23 @@ class ImageSet : java.io.Serializable, Collection { this.markerIndex = index this.images[this.markerIndex!!] } catch (e: IndexOutOfBoundsException) { - LoggingUtilities.logger.warning("Request to set marker image to index $index in ImageSet was out of bounds.") + LoggingUtilities.logger.warning( + "Request to set marker image to index $index in ImageSet was out of bounds." + ) this.markerIndex = null } } /** * Gets the number of Images in the ImageSet. - * @return The number of Images in the ImageSet. + * @return The number of Images in the ImageSet. */ val imageCount: Int get() = this.images.size /** - * Loads all the Images in the ImageSet. - * This will ensure that any Images that were added by filename or omero Id are retrieved and stored as Image objects, - * which can then be processed. + * Loads all the Images in the ImageSet. This will ensure that any Images that were added by + * filename are retrieved and stored as Image objects, which can then be processed. */ @Throws(java.io.IOException::class) fun loadAllImages() { @@ -248,16 +226,6 @@ class ImageSet : java.io.Serializable, Collection { if (imh.displayName == null) { imh.displayName = imh.image?.metadata?.getImageName(0) } - } else if (imh.omeroId != null) { - val osir = OmeroServerImageReader() - val osi = OmeroServerInfo( - this.parameters.getValueForKey("omero_hostname")!!, - this.parameters.getValueForKey("omero_username")!!, - this.parameters.getValueForKey("omero_password")!!.toCharArray()) - - val names = osir.loadImageFromOmeroServer(imh.omeroId!!, osi) - imh.image = osir.read(names!![1]!!) - imh.displayName = names[0] } else { imh.filename?.let { filename -> val ir = ImageReader() @@ -271,8 +239,8 @@ class ImageSet : java.io.Serializable, Collection { /** * Gets the display name associated with the requested Image. - * @param index The index of the Image in the ImageSet to use. - * @return The display name of the requested Image, or null if the Image does not exist. + * @param index The index of the Image in the ImageSet to use. + * @return The display name of the requested Image, or null if the Image does not exist. */ fun getImageNameForIndex(index: Int): String? { if (index >= 0 && index < this.size) { @@ -284,8 +252,8 @@ class ImageSet : java.io.Serializable, Collection { /** * Gets the image filename associated with the requested Image. - * @param index The index of the Image in the ImageSet to use. - * @return The filename of the requested Image, or null if the Image does not exist. + * @param index The index of the Image in the ImageSet to use. + * @return The filename of the requested Image, or null if the Image does not exist. */ fun getFilenameForIndex(index: Int): String? { if (index >= 0 && index < this.size) { @@ -297,8 +265,8 @@ class ImageSet : java.io.Serializable, Collection { /** * Gets the file hash associated with the requested Image. - * @param index The index of the Image in the ImageSet to use. - * @return The value of the hash associated with the image, or null if the Image does not exist. + * @param index The index of the Image in the ImageSet to use. + * @return The value of the hash associated with the image, or null if the Image does not exist. */ fun getImageHashForIndex(index: Int): String? { if (index >= 0 && index < this.size) { @@ -310,8 +278,8 @@ class ImageSet : java.io.Serializable, Collection { /** * Gets the algorithm used to hash the requested Image. - * @param index The index of the Image in the ImageSet to use. - * @return The hash algorithm associated with the image, or null if the Image does not exist. + * @param index The index of the Image in the ImageSet to use. + * @return The hash algorithm associated with the image, or null if the Image does not exist. */ fun getImageHashAlgorithmForIndex(index: Int): String? { if (index >= 0 && index < this.size) { @@ -324,8 +292,8 @@ class ImageSet : java.io.Serializable, Collection { /** * Disposes of the memory-intensive portions of Images. * - * Useful for programs that retain long-term references to the ImageSet for things like naming, but don't need - * the pixel data for the full lifetime of the ImageSet. + * Useful for programs that retain long-term references to the ImageSet for things like naming, + * but don't need the pixel data for the full lifetime of the ImageSet. */ fun disposeImages() { for (imh in this.images) { @@ -334,10 +302,8 @@ class ImageSet : java.io.Serializable, Collection { } /** - * Calculates the hash of all the Images in the image set. For images added - * from files, this will calculate an actual hash of the file. For omero images, - * this will produce something based on the server and omero id. For added Image - * objects, this will do nothing. + * Calculates the hash of all the Images in the image set. For images added from files, this + * will calculate an actual hash of the file. For added Image objects, this will do nothing. */ fun hashAllImages() { for (imh in this.images) { @@ -345,21 +311,18 @@ class ImageSet : java.io.Serializable, Collection { try { imh.setImageHash( FileHashCalculator.ALG_DEFAULT, - FileHashCalculator.calculateHash( - FileHashCalculator.ALG_DEFAULT, it)) + FileHashCalculator.calculateHash(FileHashCalculator.ALG_DEFAULT, it) + ) } catch (e: java.io.IOException) { - LoggingUtilities.logger.warning("Unable to calculate hash on image: " + imh.filename + "\n" + e.message) + LoggingUtilities.logger.warning( + "Unable to calculate hash on image: " + imh.filename + "\n" + e.message + ) } } - imh.omeroId?.let { - imh.setImageHash( - "omero://" + this.parameters.getValueForKey("omero_hostname"), - java.lang.Long.toString(imh.omeroId!!)) - } } } - private class ImageHolder(var image: Image?, var filename: String?, var omeroId: Long?) { + private class ImageHolder(var image: Image?, var filename: String?) { var displayName: String? = null var hashAlgorithm: String? = null internal set @@ -407,12 +370,16 @@ class ImageSet : java.io.Serializable, Collection { override fun next(): Image { if (currIndex >= toIterate.size) { - throw NoSuchElementException("No more Images in ImageSet. Attempted to access Image at index $currIndex.") + throw NoSuchElementException( + "No more Images in ImageSet. Attempted to access Image at index $currIndex." + ) } val toReturn = toIterate.getImageForIndex(currIndex) currIndex++ - return toReturn ?: throw NoSuchElementException( - "No more Images in ImageSet. Attempted to access Image at index ${currIndex - 1}.") + return toReturn + ?: throw NoSuchElementException( + "No more Images in ImageSet. Attempted to access Image at index ${currIndex - 1}." + ) } } @@ -423,5 +390,4 @@ class ImageSet : java.io.Serializable, Collection { return ImageSet(ParameterDictionary.emptyDictionary()) } } - } diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ImgLibPixelData.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ImgLibPixelData.kt deleted file mode 100644 index fb19a64..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ImgLibPixelData.kt +++ /dev/null @@ -1,200 +0,0 @@ -package edu.stanford.cfuller.imageanalysistools.image - -import ij.ImagePlus - -import net.imglib2.img.ImgPlus -import net.imglib2.img.Img -import net.imglib2.type.numeric.real.FloatType -import net.imglib2.img.planar.PlanarImgFactory -import net.imglib2.RandomAccess - -/** - * A type of WritablePixelData that uses an ImgLib2 ImgPlus as its underlying representation. - * @author Colin J. Fuller - */ -class ImgLibPixelData private constructor() : WritablePixelData() { - /** - * Gets the underlying ImgLib Img object. - * @return the Img object that is used to store the pixel data (not a copy). - */ - var img: ImgPlus? = null - private set - - private var ra: RandomAccess? = null - - /* (non-Javadoc) - * @see edu.stanford.cfuller.imageanalysistools.image.PixelData#getDimensionOrder() - */ - override var dimensionOrder: String = ImgLibPixelData.defaultDimensionOrder - set(value: String) { - super.dimensionOrder = value - } - - internal var has_x: Boolean = false - internal var has_y: Boolean = false - internal var has_z: Boolean = false - internal var has_c: Boolean = false - internal var has_t: Boolean = false - - internal var xi: Int = 0 - internal var yi: Int = 0 - internal var zi: Int = 0 - internal var ci: Int = 0 - internal var ti: Int = 0 - - fun init(size_x: Int, size_y: Int, size_z: Int, size_c: Int, size_t: Int, dimensionOrder: String) { - var size_x = size_x - var size_y = size_y - var size_z = size_z - var size_c = size_c - var size_t = size_t - - this.dimensionOrder = dimensionOrder.toUpperCase() - - this.xi = this.dimensionOrder.indexOf("X") - this.yi = this.dimensionOrder.indexOf("Y") - this.zi = this.dimensionOrder.indexOf("Z") - this.ci = this.dimensionOrder.indexOf("C") - this.ti = this.dimensionOrder.indexOf("T") - - this.img?.let { img -> - val dimensionSizeArray = LongArray(numDims) - java.util.Arrays.fill(dimensionSizeArray, 0L) - img.dimensions(dimensionSizeArray) - - val numImpglDims = this.img!!.numDimensions() - - if (this.xi < numImpglDims) { - this.has_x = true - size_x = dimensionSizeArray[this.xi].toInt() - } - if (this.yi < numImpglDims) { - this.has_y = true - size_y = dimensionSizeArray[this.yi].toInt() - } - if (this.zi < numImpglDims) { - this.has_z = true - size_z = dimensionSizeArray[this.zi].toInt() - } - if (this.ci < numImpglDims) { - this.has_c = true - size_c = dimensionSizeArray[this.ci].toInt() - } - if (this.ti < numImpglDims) { - this.has_t = true - size_t = dimensionSizeArray[this.ti].toInt() - } - } - if (this.img == null) { - this.has_x = true - this.has_y = true - this.has_z = true - this.has_c = true - this.has_t = true - } - - this.sizeX = size_x - this.sizeY = size_y - this.sizeZ = size_z - this.sizeC = size_c - this.sizeT = size_t - } - - /** - * This ensures that the dimension order for the ImgPl object does not have - * any unknown dimension types. - */ - private fun fixDimensionOrder() { - for (i in 0..this.dimensionOrder.length - 1) { - if (i >= this.img!!.numDimensions()) break - val currChar = this.dimensionOrder[i] - if (currChar == 'X') { - this.img!!.setAxis(net.imglib2.meta.Axes.X, i) - } else if (currChar == 'Y') { - this.img!!.setAxis(net.imglib2.meta.Axes.Y, i) - } else if (currChar == 'Z') { - this.img!!.setAxis(net.imglib2.meta.Axes.Z, i) - } else if (currChar == 'C') { - this.img!!.setAxis(net.imglib2.meta.Axes.CHANNEL, i) - } else if (currChar == 'T') { - this.img!!.setAxis(net.imglib2.meta.Axes.TIME, i) - } - } - } - - private fun initNewImgPlus() { - this.dimensionOrder = ImgLibPixelData.defaultDimensionOrder - val imgf = PlanarImgFactory() - val dims = longArrayOf(this.sizeX.toLong(), this.sizeY.toLong(), this.sizeZ.toLong(), this.sizeC.toLong(), this.sizeT.toLong()) - val im = imgf.create(dims, FloatType()) - this.img = ImgPlus(im) - this.fixDimensionOrder() - this.ra = this.img!!.randomAccess() - } - - /** - * Creates a new ImgLibPixelData from an existing ImgPlus and a specified dimension order. - * @param imPl The ImgPlus to use. This will not be copied, but used and potentially modified in place. - * @param dimensionOrder a String containing the five characters XYZCT in the order they are in the image (if some dimensions are not present, the can be specified in any order) - */ - constructor(imPl: ImgPlus, dimensionOrder: String) : this() { - this.img = imPl - this.init(1, 1, 1, 1, 1, dimensionOrder) - this.fixDimensionOrder() - this.ra = this.img!!.randomAccess() - } - - - /* (non-Javadoc) - * @see edu.stanford.cfuller.imageanalysistools.image.PixelData#getPixel(int, int, int, int, int) - */ - override fun getPixel(x: Int, y: Int, z: Int, c: Int, t: Int): Float { - this.ra!!.setPosition(x, this.xi) - this.ra!!.setPosition(y, this.yi) - if (this.has_z) this.ra!!.setPosition(z, this.zi) - if (this.has_c) this.ra!!.setPosition(c, this.ci) - if (this.has_t) this.ra!!.setPosition(t, this.ti) - return this.ra!!.get().get() - } - - /* (non-Javadoc) - * @see edu.stanford.cfuller.imageanalysistools.image.PixelData#setPixel(int, int, int, int, int, float) - */ - override fun setPixel(x: Int, y: Int, z: Int, c: Int, t: Int, value: Float) { - this.ra!!.setPosition(x, this.xi) - this.ra!!.setPosition(y, this.yi) - if (this.has_z) this.ra!!.setPosition(z, this.zi) - if (this.has_c) this.ra!!.setPosition(c, this.ci) - if (this.has_t) this.ra!!.setPosition(t, this.ti) - this.ra!!.get().set(value) - } - - /* (non-Javadoc) - * @see edu.stanford.cfuller.imageanalysistools.image.PixelData#toImagePlus() - */ - override fun toImagePlus(): ImagePlus { - val ippd = ImagePlusPixelData(this.sizeX, this.sizeY, this.sizeZ, this.sizeC, this.sizeT, loci.formats.FormatTools.FLOAT, this.dimensionOrder) - val a_copy = ImageFactory.createWritable(ReadOnlyImageImpl.createNewMetadata(), ippd) - for (ic in a_copy) { - a_copy.setValue( - ic, - this.getPixel( - ic[ImageCoordinate.X], ic[ImageCoordinate.Y], - ic[ImageCoordinate.Z], ic[ImageCoordinate.C], ic[ImageCoordinate.T])) - } - return a_copy.toImagePlus() - } - - /* (non-Javadoc) - * @see edu.stanford.cfuller.imageanalysistools.image.PixelData#getDimensionOrder() - */ - override val dataType: Int - get() = loci.formats.FormatTools.FLOAT - - companion object { - private val serialVersionUID = 1435719453195463339L - private val defaultDimensionOrder = ImageCoordinate.defaultDimensionOrder - internal val numDims = 5 - } -} - diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/PixelDataFactory.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/PixelDataFactory.kt index b02043f..6ea726d 100644 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/PixelDataFactory.kt +++ b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/PixelDataFactory.kt @@ -1,72 +1,91 @@ package edu.stanford.cfuller.imageanalysistools.image -import ij.ImagePlus -import net.imglib2.img.ImgPlus -import net.imglib2.type.numeric.real.FloatType - - /** * A factory to construct PixelData objects; the choice of implementation will be made based on the * properties of the data. * @author Colin J. Fuller */ -//TODO: reimplement to handle images other than 5D. +// TODO: reimplement to handle images other than 5D. -/** - * Constructs a new default PixelDataFactory. - */ +/** Constructs a new default PixelDataFactory. */ class PixelDataFactory { companion object { /** - * Construct a new pixeldata with some sensible default settings (XYZCT ordering, float datatype). + * Construct a new pixeldata with some sensible default settings (XYZCT ordering, float + * datatype). */ fun createPixelData(sizes: ImageCoordinate): WritablePixelData { return createPixelData(sizes, loci.formats.FormatTools.FLOAT, "XYZCT") } /** - * Constructs a new pixeldata object using an [ImageCoordinate] to specify the size of the pixeldata. - * @param sizes An ImageCoordinate that specifies the size of the pixeldata in all 5 (XYZCT) dimensions. - * @param data_type An integer that specifies the numeric type of the on-disk representation of this data; valid values are from [loci.formats.FormatTools] - * @param dimensionOrder A string made up of the characters "XYZCT" in any order that specifies the order of the dimensions in the on-disk representation. - * @return A new PixelData with the specified options. + * Constructs a new pixeldata object using an [ImageCoordinate] to specify the size of the + * pixeldata. + * @param sizes An ImageCoordinate that specifies the size of the pixeldata in all 5 (XYZCT) + * dimensions. + * @param data_type An integer that specifies the numeric type of the on-disk representation + * of this data; valid values are from [loci.formats.FormatTools] + * @param dimensionOrder A string made up of the characters "XYZCT" in any order that + * specifies the order of the dimensions in the on-disk representation. + * @return A new PixelData with the specified options. */ - - fun createPixelData(sizes: ImageCoordinate, data_type: Int, dimensionOrder: String): WritablePixelData { - return createPixelData(sizes[ImageCoordinate.X], sizes[ImageCoordinate.Y], sizes[ImageCoordinate.Z], sizes[ImageCoordinate.C], sizes[ImageCoordinate.T], data_type, dimensionOrder) + fun createPixelData( + sizes: ImageCoordinate, + data_type: Int, + dimensionOrder: String + ): WritablePixelData { + return createPixelData( + sizes[ImageCoordinate.X], + sizes[ImageCoordinate.Y], + sizes[ImageCoordinate.Z], + sizes[ImageCoordinate.C], + sizes[ImageCoordinate.T], + data_type, + dimensionOrder + ) } /** - * Convenience constructor for creating a PixelData object with individual dimension sizes instead of the sizes lumped into an ImageCoordinate. - * @param size_x Size of the pixel data in the X-dimension. - * @param size_y Size of the pixel data in the Y-dimension. - * @param size_z Size of the pixel data in the Z-dimension. - * @param size_c Size of the pixel data in the C-dimension. - * @param size_t Size of the pixel data in the T-dimension. - * @param data_type An integer that specifies the numeric type of the on-disk representation of this data; valid values are from [loci.formats.FormatTools] - * @param dimensionOrder A string made up of the characters "XYZCT" in any order that specifies the order of the dimensions in the on-disk representation. - * @return A new PixelData with the specified options. + * Convenience constructor for creating a PixelData object with individual dimension sizes + * instead of the sizes lumped into an ImageCoordinate. + * @param size_x Size of the pixel data in the X-dimension. + * @param size_y Size of the pixel data in the Y-dimension. + * @param size_z Size of the pixel data in the Z-dimension. + * @param size_c Size of the pixel data in the C-dimension. + * @param size_t Size of the pixel data in the T-dimension. + * @param data_type An integer that specifies the numeric type of the on-disk representation + * of this data; valid values are from [loci.formats.FormatTools] + * @param dimensionOrder A string made up of the characters "XYZCT" in any order that + * specifies the order of the dimensions in the on-disk representation. + * @return A new PixelData with the specified options. */ - fun createPixelData(size_x: Int, size_y: Int, size_z: Int, size_c: Int, size_t: Int, data_type: Int, dimensionOrder: String): WritablePixelData { - return ImagePlusPixelData(size_x, size_y, size_z, size_c, size_t, data_type, dimensionOrder) + fun createPixelData( + size_x: Int, + size_y: Int, + size_z: Int, + size_c: Int, + size_t: Int, + data_type: Int, + dimensionOrder: String + ): WritablePixelData { + return ImagePlusPixelData( + size_x, + size_y, + size_z, + size_c, + size_t, + data_type, + dimensionOrder + ) } /** * Creates a new ImagePlusPixelData from an existing ImagePlus. - * @param imPl The ImagePlus to use. This will not be copied, but used and potentially modified in place. - * @return A new PixelData with the specified options. + * @param imPl The ImagePlus to use. This will not be copied, but used and potentially + * modified in place. + * @return A new PixelData with the specified options. */ - fun createPixelData(imPl: ImagePlus): WritablePixelData { + fun createPixelData(imPl: ij.ImagePlus): WritablePixelData { return ImagePlusPixelData(imPl) } - - /** - * Creates a new PixelData from an existing ImgLib2 ImgPlus and a specified dimension order. - * @param imgpl The ImgPlus to use. This may not be copied, but used and potentially modified in place. - * @param dimensionOrder a String containing the five characters XYZCT in the order they are in the image (if some dimensions are not present, the can be specified in any order) - * @return A new PixelData with the specified options. - */ - fun createPixelData(imgpl: ImgPlus, dimensionOrder: String): WritablePixelData { - return ImgLibPixelData(imgpl, dimensionOrder) - } } } diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ReadOnlyImageImpl.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ReadOnlyImageImpl.kt index 0351231..d9cf2fb 100644 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ReadOnlyImageImpl.kt +++ b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/ReadOnlyImageImpl.kt @@ -2,17 +2,12 @@ package edu.stanford.cfuller.imageanalysistools.image import edu.stanford.cfuller.imageanalysistools.frontend.LoggingUtilities import edu.stanford.cfuller.imageanalysistools.image.io.ImageWriter -import ome.xml.model.primitives.PositiveInteger - +import ij.ImagePlus import ij.ImageStack import ij.process.FloatProcessor -import ij.process.ImageProcessor -import ij.ImagePlus -import loci.formats.meta.IMetadata -import net.imglib2.meta.Metadata - import java.awt.image.BufferedImage -import java.awt.image.WritableRaster +import loci.formats.meta.IMetadata +import ome.xml.model.primitives.PositiveInteger /** * A basic read-only implementation of [Image]. @@ -24,74 +19,78 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima /** * Constructs a new Image with the specified metadata and PixelData. * - * - * The metadata and PixelData should already be initialized (and should contain consistent values for things like dimension sizes). - * This constructor is primarily used by classes that read images in binary format; most users will probably want to use a different constructor. - * @param m An object containing the metadata associated with the Image (as a loci.formats.meta.IMetadata, to ease integration with the LOCI bio-formats library). - * @param p A PixelData object containing the actual values at each pixel in the Image. + * The metadata and PixelData should already be initialized (and should contain consistent + * values for things like dimension sizes). This constructor is primarily used by classes that + * read images in binary format; most users will probably want to use a different constructor. + * @param m An object containing the metadata associated with the Image (as a + * loci.formats.meta.IMetadata, to ease integration with the LOCI bio-formats library). + * @param p A PixelData object containing the actual values at each pixel in the Image. */ - internal final val defaultDimensionOrder = "xyzct" /** - * Gets the metadata associated with this Image. (The object returned is an [loci.formats.meta.IMetadata] to facilitate - * use with the LOCI bio-formats library. - * @return The metadata object associated with the Image. + * Gets the metadata associated with this Image. (The object returned is an + * [loci.formats.meta.IMetadata] to facilitate use with the LOCI bio-formats library. + * @return The metadata object associated with the Image. */ override final var metadata: loci.formats.meta.IMetadata = m protected set - /** - * Gets a PixelData instance that holds the image data. - */ + /** Gets a PixelData instance that holds the image data. */ override final var pixelData: PixelData = p protected set - var writablePixelData: WritablePixelData? = null // this is null if it can be written, and the same as pixelData otherwise + var writablePixelData: WritablePixelData? = + null // this is null if it can be written, and the same as pixelData otherwise /** * Returns an ImageCoordinate that contains the size of each dimension of the Image. * * This ImageCoordinate should not be modified by users, nor should it be recycled by users. - * @return An ImageCoordinate containing the size of each dimension of the Image. + * @return An ImageCoordinate containing the size of each dimension of the Image. */ - override final var dimensionSizes: ImageCoordinate = ImageCoordinate.createCoordXYZCT(p.sizeX, p.sizeY, p.sizeZ, p.sizeC, p.sizeT) + override final var dimensionSizes: ImageCoordinate = + ImageCoordinate.createCoordXYZCT(p.sizeX, p.sizeY, p.sizeZ, p.sizeC, p.sizeT) protected set /** - * Gets the (inclusive) lower bound of any region of interest currently set on this Image, or null if no region is currently - * set. + * Gets the (inclusive) lower bound of any region of interest currently set on this Image, or + * null if no region is currently set. * - * This is a reference to the actual internal ImageCoordinate and should not be modified or used after the region of interest has been cleared. - * As per the specification in [ImageCoordinate], users should *not* recycle the ImageCoordinate returned. - * @return The ImageCoordinate whose components are the lower bound on the region of interest, or null if there is no region of interest. + * This is a reference to the actual internal ImageCoordinate and should not be modified or used + * after the region of interest has been cleared. As per the specification in [ImageCoordinate], + * users should *not* recycle the ImageCoordinate returned. + * @return The ImageCoordinate whose components are the lower bound on the region of interest, + * or null if there is no region of interest. */ override final var boxMin: ImageCoordinate? = null protected set /** - * Gets the (exclusive) upper bound of any region of interest currently set on this Image, or null if no region is currently - * set. + * Gets the (exclusive) upper bound of any region of interest currently set on this Image, or + * null if no region is currently set. * - * This is a reference to the actual internal ImageCoordinate and should not be modified or used after the region of interest has been cleared. - * As per the specification in [ImageCoordinate], users should *not* recycle the ImageCoordinate returned. - * @return The ImageCoordinate whose components are the upper bound on the region of interest, or null if there is no region of interest. + * This is a reference to the actual internal ImageCoordinate and should not be modified or used + * after the region of interest has been cleared. As per the specification in [ImageCoordinate], + * users should *not* recycle the ImageCoordinate returned. + * @return The ImageCoordinate whose components are the upper bound on the region of interest, + * or null if there is no region of interest. */ override final var boxMax: ImageCoordinate? = null protected set /** * Queries whether the Image is currently boxed with a region of interest. - * @return true if there is currently a region of interest set, false otherwise. + * @return true if there is currently a region of interest set, false otherwise. */ override final var isBoxed: Boolean = false protected set protected var coordinateArrayStorage: Array? = null - /** - * Constructs a new Image that is a (deep) copy of the specified Image. The pixel data will be copied exactly, but no guarantee - * is made that the metadata will be completely copied; only the minimally necessary metadata (like dimension sizes) will definitely be copied. - * @param toCopy The Image to copy. + * Constructs a new Image that is a (deep) copy of the specified Image. The pixel data will be + * copied exactly, but no guarantee is made that the metadata will be completely copied; only + * the minimally necessary metadata (like dimension sizes) will definitely be copied. + * @param toCopy The Image to copy. */ constructor(toCopy: Image, shallow: Boolean) : this() { this.isBoxed = false @@ -103,8 +102,12 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima this.pixelData = toCopy.pixelData this.metadata = toCopy.metadata } else { - val newWritableData = PixelDataFactory.createPixelData( - toCopy.dimensionSizes, toCopy.pixelData.dataType, defaultDimensionOrder) + val newWritableData = + PixelDataFactory.createPixelData( + toCopy.dimensionSizes, + toCopy.pixelData.dataType, + defaultDimensionOrder + ) this.writablePixelData = newWritableData this.pixelData = newWritableData this.copy(toCopy) @@ -113,17 +116,19 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } /** - * Constructs a new Image with specified dimension sizes and all the pixel values set to some specified initial value. Bare-bones metadata - * will also be constructed. + * Constructs a new Image with specified dimension sizes and all the pixel values set to some + * specified initial value. Bare-bones metadata will also be constructed. * + * (Note that even though ImageCoordinates are zero-indexed, the ImageCoordinate's components + * should be the actual sizes of the dimensions, not the indices of those dimensions. For + * instance, for an 512 x by 512 y by 1 z plane by 1 color by 1 timepoint image, the + * ImageCoordinate specifying sizes should have components = (512, 512, 1, 1, 1), not (511, 511, + * 0, 0, 0).) * - * (Note that even though ImageCoordinates are zero-indexed, the ImageCoordinate's components should be the actual sizes of the dimensions, - * not the indices of those dimensions. For instance, for an 512 x by 512 y by 1 z plane by 1 color by 1 timepoint image, the ImageCoordinate - * specifying sizes should have components = (512, 512, 1, 1, 1), not (511, 511, 0, 0, 0).) - - * @param dimensionSizes An ImageCoordinate whose components will be the sizes of each dimension of the Image. + * @param dimensionSizes An ImageCoordinate whose components will be the sizes of each dimension + * of the Image. * * - * @param initialValue The initial value to which all the pixels will be set. + * @param initialValue The initial value to which all the pixels will be set. */ constructor(dimensionSizes: ImageCoordinate, initialValue: Float) : this() { this.isBoxed = false @@ -131,7 +136,12 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima this.boxMax = null this.coordinateArrayStorage = null this.dimensionSizes = ImageCoordinate.cloneCoord(dimensionSizes) - val newWritableData = PixelDataFactory.createPixelData(dimensionSizes, loci.formats.FormatTools.FLOAT, "XYZCT") + val newWritableData = + PixelDataFactory.createPixelData( + dimensionSizes, + loci.formats.FormatTools.FLOAT, + "XYZCT" + ) this.writablePixelData = newWritableData this.pixelData = newWritableData setupNewMetadata() @@ -143,7 +153,7 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima /** * Constructs a new Image from a java standard BufferedImage. - * @param bufferedImage The BufferedImage to convert to an Image. + * @param bufferedImage The BufferedImage to convert to an Image. */ constructor(bufferedImage: BufferedImage) : this() { val size_x = bufferedImage.width @@ -151,13 +161,19 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima val size_z = 1 val size_c = bufferedImage.raster.numBands val size_t = 1 - val dimensionSizes = ImageCoordinate.createCoordXYZCT(size_x, size_y, size_z, size_c, size_t) + val dimensionSizes = + ImageCoordinate.createCoordXYZCT(size_x, size_y, size_z, size_c, size_t) this.isBoxed = false this.boxMin = null this.boxMax = null this.coordinateArrayStorage = null this.dimensionSizes = ImageCoordinate.cloneCoord(dimensionSizes) - val newWritableData = PixelDataFactory.createPixelData(dimensionSizes, loci.formats.FormatTools.FLOAT, "XYZCT") + val newWritableData = + PixelDataFactory.createPixelData( + dimensionSizes, + loci.formats.FormatTools.FLOAT, + "XYZCT" + ) this.writablePixelData = newWritableData this.pixelData = newWritableData setupNewMetadata() @@ -173,11 +189,18 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima /** * Constructs a new Image from an ImageJ ImagePlus. - * @param imPl The ImagePlus to convert to an Image. + * @param imPl The ImagePlus to convert to an Image. */ constructor(imPl: ImagePlus) : this() { val dimensions = imPl.dimensions - val dimensionSizes = ImageCoordinate.createCoordXYZCT(dimensions[0], dimensions[1], dimensions[3], dimensions[2], dimensions[4]) + val dimensionSizes = + ImageCoordinate.createCoordXYZCT( + dimensions[0], + dimensions[1], + dimensions[3], + dimensions[2], + dimensions[4] + ) this.isBoxed = false this.boxMin = null this.boxMax = null @@ -192,12 +215,15 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima // TODO(colin): this should be an ImageFactory.createEmpty() method instead of a constructor I // think - protected constructor() : this( - loci.common.services.ServiceFactory() - .getInstance(loci.formats.services.OMEXMLService::class.java) - .createOMEXMLMetadata(), - PixelDataFactory.createPixelData(ImageCoordinate.createCoordXYZCT(1, 1, 1, 1, 1)) - ) { } + protected constructor() : + this( + loci.common.services.ServiceFactory() + .getInstance(loci.formats.services.OMEXMLService::class.java) + .createOMEXMLMetadata(), + PixelDataFactory.createPixelData( + ImageCoordinate.createCoordXYZCT(1, 1, 1, 1, 1) + ) + ) {} /** * Sets the region of interest in the Image, copying the provided image coordinates. @@ -210,20 +236,24 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima /** * Sets the region of interest in the Image. * - * The region of interest must be a (possibly high-dimensional) rectangle and is represented by two points, the lower bound of the coordinates - * and the upper bound of the coordinates (conceptually like the upper-left and lower-right corners for the 2D case). The box will include the - * lower bound but exclude the upper bound. + * The region of interest must be a (possibly high-dimensional) rectangle and is represented by + * two points, the lower bound of the coordinates and the upper bound of the coordinates + * (conceptually like the upper-left and lower-right corners for the 2D case). The box will + * include the lower bound but exclude the upper bound. * - * The region of interest will control what area of the Image will be iterated over using foreach-style iteration, or - * any of the methods of an [ImageIterator]. This will be the sole region iterated over until [.clearBoxOfInterest] is - * called, or this method is called again with a new region of interest. A new region of interest specified will replace any existing - * region; it is not possible to construct complex regions by successively building with several rectangles. + * The region of interest will control what area of the Image will be iterated over using + * foreach-style iteration, or any of the methods of an [ImageIterator]. This will be the sole + * region iterated over until [.clearBoxOfInterest] is called, or this method is called again + * with a new region of interest. A new region of interest specified will replace any existing + * region; it is not possible to construct complex regions by successively building with several + * rectangles. * - * As per the specification in [ImageCoordinate], users are still responsible for recycling the ImageCoordinate parameters; they are - * not retained and recycled by this class. - * @param boxMin The (inclusive) lower coordinate bound of the boxed region of interest. - * @param boxMax The (exclusive) upper coordinate bound of the boxed region of interest. - * @param copy Indicates whether to copy the input coordinates; if set to false, do not recycle or further modify the inputs. + * As per the specification in [ImageCoordinate], users are still responsible for recycling the + * ImageCoordinate parameters; they are not retained and recycled by this class. + * @param boxMin The (inclusive) lower coordinate bound of the boxed region of interest. + * @param boxMax The (exclusive) upper coordinate bound of the boxed region of interest. + * @param copy Indicates whether to copy the input coordinates; if set to false, do not recycle + * or further modify the inputs. */ override fun setBoxOfInterest(boxMin: ImageCoordinate, boxMax: ImageCoordinate, copy: Boolean) { if (this.boxMin != null) { @@ -238,23 +268,20 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima this.boxMin = boxMin this.boxMax = boxMax - //bounds checking - boxMin - .asSequence() - .filter { boxMin[it] < 0 } - .forEach { boxMin[it] = 0 } + // bounds checking + boxMin.asSequence().filter { boxMin[it] < 0 }.forEach { boxMin[it] = 0 } - boxMax - .asSequence() - .filter { boxMax[it] > this.dimensionSizes[it] } - .forEach { boxMax[it] = this.dimensionSizes[it] } + boxMax.asSequence().filter { boxMax[it] > this.dimensionSizes[it] }.forEach { + boxMax[it] = this.dimensionSizes[it] + } this.isBoxed = true } /** - * Clears any existing region of interest that has been set on this Image. - * This will cause any foreach-style iteration or ImageIterator-controlled iteration to iterate over the entire image. + * Clears any existing region of interest that has been set on this Image. This will cause any + * foreach-style iteration or ImageIterator-controlled iteration to iterate over the entire + * image. */ override fun clearBoxOfInterest() { this.boxMax?.recycle() @@ -267,16 +294,21 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima /** * Creates a new Image that is a sub-image of the Image. * - * The new Image will be created with bare-bones metadata and its pixel values set to the pixel values of this Image in the specifed region. - * The new Image will have the dimension sizes specified (as if the Image constructor were called with this size parameter), and start at the specified - * coordinate in the current Image (inclusive). If the start point + new dimension sizes goes outside the current Image, then the new Image will be filled - * with zeros at any coordinate outside the current Image. + * The new Image will be created with bare-bones metadata and its pixel values set to the pixel + * values of this Image in the specifed region. The new Image will have the dimension sizes + * specified (as if the Image constructor were called with this size parameter), and start at + * the specified coordinate in the current Image (inclusive). If the start point + new dimension + * sizes goes outside the current Image, then the new Image will be filled with zeros at any + * coordinate outside the current Image. * - * As per the specification in [ImageCoordinate], users are responsible for recycling the ImageCoordinate parameters; they are not retained - * or recycled by this class. - * @param newDimensions An ImageCoordinate containing the size of the new sub-image in each dimension. - * @param startPoint The (inclusive) point in the current Image where the sub-image will start (this will become coordinate (0,0,0,...) in the new Image). - * @return A new Image with pixel values set to those of the specified sub region of the current Image. + * As per the specification in [ImageCoordinate], users are responsible for recycling the + * ImageCoordinate parameters; they are not retained or recycled by this class. + * @param newDimensions An ImageCoordinate containing the size of the new sub-image in each + * dimension. + * @param startPoint The (inclusive) point in the current Image where the sub-image will start + * (this will become coordinate (0,0,0,...) in the new Image). + * @return A new Image with pixel values set to those of the specified sub region of the current + * Image. */ override fun subImage(newDimensions: ImageCoordinate, startPoint: ImageCoordinate): Image { val toReturn = ImageFactory.createWritable(newDimensions, 0.0f) @@ -297,24 +329,28 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima /** * Writes the Image to a file. * - * The output format of the Image will be guessed from the extension. Valid formats are those that the LOCI bio-formats library can write. - * (The recommended option is the ome-tiff format, which should have the extension .ome.tif). - * @param filename The full absolute path to the file to which the Image is to be written. The parent directory must exist. + * The output format of the Image will be guessed from the extension. Valid formats are those + * that the LOCI bio-formats library can write. (The recommended option is the ome-tiff format, + * which should have the extension .ome.tif). + * @param filename The full absolute path to the file to which the Image is to be written. The + * parent directory must exist. */ override fun writeToFile(filename: String) { try { ImageWriter(this).write(filename, this.pixelData) } catch (e: java.io.IOException) { - LoggingUtilities.logger.severe("Error while writing image to file: $filename Skipping write and continuing.") + LoggingUtilities.logger.severe( + "Error while writing image to file: $filename Skipping write and continuing." + ) e.printStackTrace() } } /** - * Gets the value of the Image at the coordinate specified. No bounds checking is performed. - * @param coord An ImageCoordinate specifying the location of the value to retrieve. + * Gets the value of the Image at the coordinate specified. No bounds checking is performed. + * @param coord An ImageCoordinate specifying the location of the value to retrieve. * * - * @return The value of the Image at the specified location as a float. + * @return The value of the Image at the specified location as a float. */ override fun getValue(coord: ImageCoordinate): Float { return pixelData.getPixel( @@ -322,13 +358,15 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima coord.quickGet(ImageCoordinate.Y), coord.quickGet(ImageCoordinate.Z), coord.quickGet(ImageCoordinate.C), - coord.quickGet(ImageCoordinate.T)) + coord.quickGet(ImageCoordinate.T) + ) } /** * Checks to see whether a specified ImageCoordinate represents a location in the Image. - * @param c The ImageCoordinate to check - * @return true, if the ImageCoordinate lies within this Image in every dimension, false otherwise. + * @param c The ImageCoordinate to check + * @return true, if the ImageCoordinate lies within this Image in every dimension, false + * otherwise. */ override fun inBounds(c: ImageCoordinate): Boolean { val sizes = this.dimensionSizes @@ -340,14 +378,16 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } /** - * Method for converting a single Image with non-singleton specified dimension into a List of Images with - * singleton specified dimension, each containing the Image for a single point along that dimension. + * Method for converting a single Image with non-singleton specified dimension into a List of + * Images with singleton specified dimension, each containing the Image for a single point along + * that dimension. * - * Images will be returned in the List in the order of their dimension index in the original Image. - * @return A List of Images, each with one point from the original Image. + * Images will be returned in the List in the order of their dimension index in the original + * Image. + * @return A List of Images, each with one point from the original Image. */ override fun split(dimension: Int): List { - //handle special case of color, where we need to rename channels in the metadata + // handle special case of color, where we need to rename channels in the metadata if (dimension == ImageCoordinate.C) { return this.splitChannels() } @@ -376,24 +416,27 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima return split_ro } - /** - * Convenience method for converting a single Image with non-singleton color dimension into a List of Images with - * singleton color dimension, each containing the Image for a single color channel. + * Convenience method for converting a single Image with non-singleton color dimension into a + * List of Images with singleton color dimension, each containing the Image for a single color + * channel. * - * Images will be returned in the List in the order of their color dimension index in the original Image. - * @return A List of Images, each with one color channel from the original Image. + * Images will be returned in the List in the order of their color dimension index in the + * original Image. + * @return A List of Images, each with one color channel from the original Image. */ override fun splitChannels(): List { - val series_number = 0 // if this Image was created from a multi-series image, this will get metadata from the first series. + val series_number = + 0 // if this Image was created from a multi-series image, this will get metadata + // from the first series. val split = java.util.Vector() var ic = ImageCoordinate.createCoordXYZCT(0, 0, 0, 0, 0) /* We need to label the channels individually with some sort of identifier in their metadata. Try first the * name; if null, then try the excitation/emission wavelengths; if null then fall back on the channel ID, which * will be something nondescript but unique. - * Whatever the result, this will set the metadata property using setChannelName so that this can be retrieved - * later. + * Whatever the result, this will set the metadata property using setChannelName so that this can be retrieved + * later. */ var useName = false var useWavelengths = false @@ -402,13 +445,14 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima val exW = this.metadata.getChannelExcitationWavelength(series_number, 0) val emW = this.metadata.getChannelEmissionWavelength(series_number, 0) - //check if name is ok + // check if name is ok if (name0 != null && name0 !== "") { useName = true useWavelengths = false useID = false - //check if wavelengths are ok - } else if (exW != null && exW.value > 0 || emW != null && emW.value > 0) { + // check if wavelengths are ok + } else if (exW != null && exW.value().toInt() > 0 || emW != null && emW.value().toInt() > 0 + ) { useName = false useWavelengths = true useID = false @@ -420,14 +464,19 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima ic[ImageCoordinate.C] = 1 val newChannelImage = ImageFactory.createWritable(ic, 0.0f) - //set the channel name for this new single channel image + // set the channel name for this new single channel image var channelName = "" try { if (useID) { channelName = this.metadata.getChannelID(series_number, i) } if (useWavelengths) { - channelName = this.metadata.getChannelExcitationWavelength(series_number, i).toString() + "/" + this.metadata.getChannelEmissionWavelength(series_number, i) + channelName = + this.metadata + .getChannelExcitationWavelength(series_number, i) + .toString() + + "/" + + this.metadata.getChannelEmissionWavelength(series_number, i) } if (useName) { channelName = this.metadata.getChannelName(series_number, i) @@ -456,23 +505,27 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima return split_ro } - /** * Converts the Image to a [BufferedImage] for use with other java imaging clases. * - * Because the Buffered Image can only represent single-plane images (possibly with down-sampled color channels), this - * conversion may not convert all the Image data. + * Because the Buffered Image can only represent single-plane images (possibly with down-sampled + * color channels), this conversion may not convert all the Image data. * - * For multi-plane images, this will convert the first x-y plane only (that is, with c=0, t=0, z=0). If you want a different - * plane, create a single plane sub-image using [.subImage], and then convert that Image. + * For multi-plane images, this will convert the first x-y plane only (that is, with c=0, t=0, + * z=0). If you want a different plane, create a single plane sub-image using [.subImage], and + * then convert that Image. * * The output format is a 16-bit greyscale BufferedImage. - * @return A BufferedImage containing the pixel values from the first plane of the Image converted to 16-bit greyscale format. + * @return A BufferedImage containing the pixel values from the first plane of the Image + * converted to 16-bit greyscale format. */ override fun toBufferedImage(): java.awt.image.BufferedImage { - val toReturn = BufferedImage( - this.dimensionSizes[ImageCoordinate.X], this.dimensionSizes[ImageCoordinate.Y], - BufferedImage.TYPE_USHORT_GRAY) + val toReturn = + BufferedImage( + this.dimensionSizes[ImageCoordinate.X], + this.dimensionSizes[ImageCoordinate.Y], + BufferedImage.TYPE_USHORT_GRAY + ) val r = toReturn.raster @@ -492,10 +545,9 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima return toReturn } - /** * Converts the Image to an ImageJ ImagePlus. - * @return The converted Image. + * @return The converted Image. */ override fun toImagePlus(): ImagePlus { val pixelPlus = this.pixelData.toImagePlus() @@ -503,7 +555,7 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima // TODO(colin): this is always true; why was the code below required at some point? if (pixelPlus != null) return pixelPlus - //TODO: add multiple pixel formats + // TODO: add multiple pixel formats val n_planes = this.planeCount val width = this.dimensionSizes[ImageCoordinate.X] val height = this.dimensionSizes[ImageCoordinate.Y] @@ -520,10 +572,15 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima imPl.setDimensions( this.dimensionSizes[ImageCoordinate.C], this.dimensionSizes[ImageCoordinate.Z], - this.dimensionSizes[ImageCoordinate.T]) + this.dimensionSizes[ImageCoordinate.T] + ) for (ic in this) { - imPl.setPositionWithoutUpdate(ic[ImageCoordinate.C] + 1, ic[ImageCoordinate.Z] + 1, ic[ImageCoordinate.T] + 1) + imPl.setPositionWithoutUpdate( + ic[ImageCoordinate.C] + 1, + ic[ImageCoordinate.Z] + 1, + ic[ImageCoordinate.T] + 1 + ) val imP = imPl.processor imP.setf(ic[ImageCoordinate.X], ic[ImageCoordinate.Y], this.getValue(ic)) } @@ -532,29 +589,46 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } /** - * Gets the number of planes in this Image (that is the number of distinct (z, c, t) coordinates). - * @return The number of planes. + * Gets the number of planes in this Image (that is the number of distinct (z, c, t) + * coordinates). + * @return The number of planes. */ override val planeCount: Int - get() = this.dimensionSizes[ImageCoordinate.Z] * this.dimensionSizes[ImageCoordinate.T] * this.dimensionSizes[ImageCoordinate.C] - - - /** - * Selects a plane as active for iteration. This has the same effect as calling the setBoxOfInterest method - * with coordinates that would select only the given plane. This should not be used in conjunction with the setBoxOfInterest method, - * as internally this method uses the same boxing mechanism. To clear the selected plane, call clearBoxOfInterest. - * @param i The plane index to set as active; no guarantee is made as to which plane this is, except that this index + get() = + this.dimensionSizes[ImageCoordinate.Z] * + this.dimensionSizes[ImageCoordinate.T] * + this.dimensionSizes[ImageCoordinate.C] + + /** + * Selects a plane as active for iteration. This has the same effect as calling the + * setBoxOfInterest method with coordinates that would select only the given plane. This should + * not be used in conjunction with the setBoxOfInterest method, as internally this method uses + * the same boxing mechanism. To clear the selected plane, call clearBoxOfInterest. + * @param i The plane index to set as active; no guarantee is made as to which plane this is, + * except that this index + * ``` * will always refer to the same plane for a given Image, and iterating from i = 0 to getPlaneCount() - 1 will * visit all the planes in the Image. + * ``` */ override fun selectPlane(i: Int) { val tempMin = ImageCoordinate.createCoordXYZCT(0, 0, 0, 0, 0) - val tempMax = ImageCoordinate.createCoordXYZCT( - this.dimensionSizes[ImageCoordinate.X], this.dimensionSizes[ImageCoordinate.Y], 0, 0, 0) + val tempMax = + ImageCoordinate.createCoordXYZCT( + this.dimensionSizes[ImageCoordinate.X], + this.dimensionSizes[ImageCoordinate.Y], + 0, + 0, + 0 + ) // TODO(colin): I think this assumes xyzct pixel ordering? val z_index = i % this.dimensionSizes[ImageCoordinate.Z] - val c_index = (i - z_index) / this.dimensionSizes[ImageCoordinate.Z] % this.dimensionSizes[ImageCoordinate.C] - val t_index = ((i - z_index) / this.dimensionSizes[ImageCoordinate.Z] - c_index) / this.dimensionSizes[ImageCoordinate.C] + val c_index = + (i - z_index) / this.dimensionSizes[ImageCoordinate.Z] % + this.dimensionSizes[ImageCoordinate.C] + val t_index = + ((i - z_index) / this.dimensionSizes[ImageCoordinate.Z] - c_index) / + this.dimensionSizes[ImageCoordinate.C] tempMin[ImageCoordinate.Z] = z_index tempMin[ImageCoordinate.C] = c_index tempMin[ImageCoordinate.T] = t_index @@ -568,25 +642,32 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima /** * Queries whether this Image's implementation can support writing. - * @return false, as this read-only instance cannot be written. + * @return false, as this read-only instance cannot be written. */ override val isWritable: Boolean get() = false /** - * Gets a writable version of this Image. If the Image on which this method is - * called is already writable, then it is returned unchanged. Otherwise, - * a new object will be returned that contains a writable copy of the image data. + * Gets a writable version of this Image. If the Image on which this method is called is already + * writable, then it is returned unchanged. Otherwise, a new object will be returned that + * contains a writable copy of the image data. */ override val writableInstance: WritableImage get() = ImageFactory.writableImageInstance(this) - //this is included here for ease of initialization + // this is included here for ease of initialization private fun setValue(coord: ImageCoordinate, value: Float) { - writablePixelData!!.setPixel(coord.quickGet(ImageCoordinate.X), coord.quickGet(ImageCoordinate.Y), coord.quickGet(ImageCoordinate.Z), coord.quickGet(ImageCoordinate.C), coord.quickGet(ImageCoordinate.T), value) + writablePixelData!!.setPixel( + coord.quickGet(ImageCoordinate.X), + coord.quickGet(ImageCoordinate.Y), + coord.quickGet(ImageCoordinate.Z), + coord.quickGet(ImageCoordinate.C), + coord.quickGet(ImageCoordinate.T), + value + ) } - //this is included here for ease of initialization + // this is included here for ease of initialization private fun copy(other: Image) { for (i in this) { this.setValue(i, other.getValue(i)) @@ -594,32 +675,37 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } /** - * Checks to see whether a given object is contained in this Image. Collection interface method. + * Checks to see whether a given object is contained in this Image. Collection interface method. * - * This is equivalent to checking that the object is an instance of ImageCoordinate, and then calling [.inBounds]. - * @param element The object that will be checked. - * @return true if the Object is an ImageCoordinate and is inBounds, false otherwise. + * This is equivalent to checking that the object is an instance of ImageCoordinate, and then + * calling [.inBounds]. + * @param element The object that will be checked. + * @return true if the Object is an ImageCoordinate and is inBounds, false otherwise. */ override operator fun contains(element: ImageCoordinate): Boolean { return this.inBounds(element) } /** - * Checks to see whether a given collection of objects is contained in this Image. Collection interface method. + * Checks to see whether a given collection of objects is contained in this Image. Collection + * interface method. * - * This is equivalent to taking the logical AND of the call to [.contains] on each object in the Collection. - * @param elements The Collection of objects to check. - * @return true if every Object in the collection is an ImageCoordinate and inBounds, false otherwise. + * This is equivalent to taking the logical AND of the call to [.contains] on each object in the + * Collection. + * @param elements The Collection of objects to check. + * @return true if every Object in the collection is an ImageCoordinate and inBounds, false + * otherwise. */ override fun containsAll(elements: Collection): Boolean { return elements.all { this.contains(it) } } /** - * Checks to see if the Image is empty. Collection interface method. + * Checks to see if the Image is empty. Collection interface method. * - * An Image is considered empty if and only if any of the dimensions of the Image have size zero. - * @return true if this Image has any zero-size dimensions, false otherwise. + * An Image is considered empty if and only if any of the dimensions of the Image have size + * zero. + * @return true if this Image has any zero-size dimensions, false otherwise. */ override fun isEmpty(): Boolean { val dimSizes = this.dimensionSizes @@ -627,19 +713,23 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } /** - * Gets an Iterator over the ImageCoordinates in this Image. Collection interface method. - * @return an Iterator that will traverse all the coordinates in the Image (or all the coordinates in the region of interest if the + * Gets an Iterator over the ImageCoordinates in this Image. Collection interface method. + * @return an Iterator that will traverse all the coordinates in the Image (or all the + * coordinates in the region of interest if the + * ``` * Image is currently boxed with a region of interest. + * ``` */ override fun iterator(): Iterator { return edu.stanford.cfuller.imageanalysistools.image.ImageIterator5D(this) } /** - * Gets the size of the Image, or more specifically, the number of distinct ImageCoordinates making up this Image. Collection interface method. + * Gets the size of the Image, or more specifically, the number of distinct ImageCoordinates + * making up this Image. Collection interface method. * * The result is equivalent to multiplying the size of each dimension of the Image together. - * @return The size of this Image, in number of ImageCoordinates (or multidimensional pixels). + * @return The size of this Image, in number of ImageCoordinates (or multidimensional pixels). */ override val size: Int get() { @@ -652,11 +742,13 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } /** - * Immediately nullifies the portions of the image that consume a lot of memory. This may help programs that rapidly - * create and destroy many Images from running out of memory while the Images are being finalized. + * Immediately nullifies the portions of the image that consume a lot of memory. This may help + * programs that rapidly create and destroy many Images from running out of memory while the + * Images are being finalized. */ fun dispose() { - this.pixelData = PixelDataFactory.createPixelData(ImageCoordinate.createCoordXYZCT(0, 0, 0, 0, 0)) + this.pixelData = + PixelDataFactory.createPixelData(ImageCoordinate.createCoordXYZCT(0, 0, 0, 0, 0)) if (this.coordinateArrayStorage != null) { for (ic in this.coordinateArrayStorage ?: arrayOf()) { ic.recycle() @@ -665,7 +757,7 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } } - //recycle image coordinates + // recycle image coordinates @Throws(Throwable::class) protected open fun finalize() { dimensionSizes.recycle() @@ -682,7 +774,8 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } /** - * Sets up a new Metadata object that contains the bare minimum required for reading and writing the image. + * Sets up a new Metadata object that contains the bare minimum required for reading and writing + * the image. */ protected fun setupNewMetadata() { this.metadata = ReadOnlyImageImpl.createNewMetadata() @@ -696,12 +789,18 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima } /** - * Sets the metadata object to have the same pixel format and dimension order as the supplied PixelData. + * Sets the metadata object to have the same pixel format and dimension order as the supplied + * PixelData. * @param toUse the PixelData whose characteristics will be copied. */ protected fun setMetadataPixelCharacteristics(toUse: PixelData) { try { - this.metadata.setPixelsType(ome.xml.model.enums.PixelType.fromString(loci.formats.FormatTools.getPixelTypeString(this.pixelData!!.dataType)), 0) + this.metadata.setPixelsType( + ome.xml.model.enums.PixelType.fromString( + loci.formats.FormatTools.getPixelTypeString(this.pixelData!!.dataType) + ), + 0 + ) } catch (e: ome.xml.model.enums.EnumerationException) { e.printStackTrace() } @@ -709,7 +808,9 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima try { val dimensionOrder = this.pixelData?.dimensionOrder?.toUpperCase() ?: "XYZCT" this.metadata.setPixelsDimensionOrder( - ome.xml.model.enums.DimensionOrder.fromString(dimensionOrder), 0) + ome.xml.model.enums.DimensionOrder.fromString(dimensionOrder), + 0 + ) } catch (e: ome.xml.model.enums.EnumerationException) { e.printStackTrace() } @@ -742,9 +843,10 @@ open class ReadOnlyImageImpl(m: loci.formats.meta.IMetadata, p: PixelData) : Ima internal val serialVersionUID = 1L fun createNewMetadata(): loci.formats.meta.IMetadata { // TODO(colin): this function probably belongs somewhere else. - val metadata = loci.common.services.ServiceFactory() - .getInstance(loci.formats.services.OMEXMLService::class.java) - .createOMEXMLMetadata() + val metadata = + loci.common.services.ServiceFactory() + .getInstance(loci.formats.services.OMEXMLService::class.java) + .createOMEXMLMetadata() metadata.createRoot() metadata.setImageID("Image:0", 0) metadata.setPixelsID("Pixels:0", 0) diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/ImageWriter.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/ImageWriter.kt index 661af1f..a35a482 100644 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/ImageWriter.kt +++ b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/ImageWriter.kt @@ -1,78 +1,70 @@ package edu.stanford.cfuller.imageanalysistools.image.io -import java.io.File - -import ome.xml.model.enums.EnumerationException import edu.stanford.cfuller.imageanalysistools.frontend.LoggingUtilities import edu.stanford.cfuller.imageanalysistools.image.Image import edu.stanford.cfuller.imageanalysistools.image.PixelData -import edu.stanford.cfuller.imageanalysistools.image.ImgLibPixelData - -import net.imglib2.io.ImgSaver +import java.io.File +import ome.xml.model.enums.EnumerationException /** * This class writes an Image to disk as one of several possible formats. - + * * @author Colin J. Fuller */ - class ImageWriter(im: Image) { /** * Constructs an ImageWriter for a given Image. * - * - * The writer retains a reference to the Image, so it can be used multiple times for writing the same Image to disk. Only the Image's metadata will be used; - * pixel data is passed in separately, allowing several Images that share the same metadata to be written with the same writer. - * @param im The Image whose metadata will be used to write Images to disk. - */ - /** - * The image that will be written. + * The writer retains a reference to the Image, so it can be used multiple times for writing the + * same Image to disk. Only the Image's metadata will be used; pixel data is passed in + * separately, allowing several Images that share the same metadata to be written with the same + * writer. + * @param im The Image whose metadata will be used to write Images to disk. */ + /** The image that will be written. */ internal var toWrite: Image = im - @Throws(java.io.IOException::class) - private fun imgLibWrite(filename: String, p: ImgLibPixelData) { - val `is` = ImgSaver() - try { - `is`.saveImg(filename, p.img) - } catch (e: net.imglib2.io.ImgIOException) { - throw java.io.IOException(e) - } catch (e: net.imglib2.exception.IncompatibleTypeException) { - throw java.io.IOException(e) - } - } - /** * Writes the specified pixels to disk with the ImageWriter's Image's metadata. * - * The output format will be specified by the filename's extenstion and must be a valid writable extension for the LOCI bioformats library. - * The recommended format is ome-tiff, with extension ".ome.tif". - * @param filename The absolute path of the file to which the image will be written. The parent directory should exist. - * @param imagePixels The PixelData containing the values to write to disk. - * @throws java.io.IOException if there is an exception while opening or writing the file. + * The output format will be specified by the filename's extenstion and must be a valid writable + * extension for the LOCI bioformats library. The recommended format is ome-tiff, with extension + * ".ome.tif". + * @param filename The absolute path of the file to which the image will be written. The parent + * directory should exist. + * @param imagePixels The PixelData containing the values to write to disk. + * @throws java.io.IOException if there is an exception while opening or writing the file. */ @Throws(java.io.IOException::class) fun write(filename: String, imagePixels: PixelData) { - //until I phase out other types of pixel data - if (imagePixels is ImgLibPixelData) { - this.imgLibWrite(filename, imagePixels) - return - } + // until I phase out other types of pixel data val writer = loci.formats.ImageWriter() try { - toWrite.metadata.setPixelsDimensionOrder(ome.xml.model.enums.DimensionOrder.fromString(imagePixels.dimensionOrder.toUpperCase()), 0) - toWrite.metadata.setPixelsType(ome.xml.model.enums.PixelType.fromString(loci.formats.FormatTools.getPixelTypeString(imagePixels.dataType)), 0) - + toWrite.metadata.setPixelsDimensionOrder( + ome.xml.model.enums.DimensionOrder.fromString( + imagePixels.dimensionOrder.toUpperCase() + ), + 0 + ) + toWrite.metadata.setPixelsType( + ome.xml.model.enums.PixelType.fromString( + loci.formats.FormatTools.getPixelTypeString(imagePixels.dataType) + ), + 0 + ) } catch (e: EnumerationException) { - LoggingUtilities.logger.severe("Exception while trying to update dimension order of pixel data: " + e.message) + LoggingUtilities.logger.severe( + "Exception while trying to update dimension order of pixel data: " + e.message + ) } val m = toWrite.metadata writer.metadataRetrieve = toWrite.metadata try { val f = File(filename) if (f.exists()) { - // there seems to be some bug with adding to the end of an existing image rather than overwriting if + // there seems to be some bug with adding to the end of an existing image rather + // than overwriting if // it's there already; delete any existing file to avoid this. f.delete() } @@ -82,7 +74,13 @@ class ImageWriter(im: Image) { writer.savePlane(i, bytes) } } catch (e: loci.formats.FormatException) { - LoggingUtilities.logger.severe("Encountered exception " + e.toString() + " while writing " + filename + " skipping write and proceeding.") + LoggingUtilities.logger.severe( + "Encountered exception " + + e.toString() + + " while writing " + + filename + + " skipping write and proceeding." + ) e.printStackTrace() return } finally { diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/ImgLibImageReader.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/ImgLibImageReader.kt deleted file mode 100644 index b7cc60c..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/ImgLibImageReader.kt +++ /dev/null @@ -1,66 +0,0 @@ -package edu.stanford.cfuller.imageanalysistools.image.io - -import edu.stanford.cfuller.imageanalysistools.frontend.LoggingUtilities -import edu.stanford.cfuller.imageanalysistools.image.Image -import edu.stanford.cfuller.imageanalysistools.image.ImageFactory - -import edu.stanford.cfuller.imageanalysistools.image.PixelDataFactory - -import net.imglib2.io.ImgOpener -import net.imglib2.img.ImgPlus -import net.imglib2.img.Img -import net.imglib2.type.numeric.real.FloatType -import net.imglib2.img.array.ArrayImgFactory - -/** - * This class reads an Image from a file on disk. - * - * Uses the LOCI bio-formats library to do the reading (http://www.loci.wisc.edu/software/bio-formats), so - * all formats supported by that library are supported in this reader. - * - * For image formats that support multiple series per file (that is, multiple (X,Y,Z,C,T) 5D images per file), the reader - * will return only a single series at a time, keeps track of what series it has last read, and will return the subsequent series - * on subsequent calls to the read method. - * @author Colin J. Fuller - */ -class ImgLibImageReader : ImageReader() { - /** - * Reads an image file from disk to an Image. - * @param filename The full absolute filename of the image file to read. - * @return An Image containing the pixel data and (if supported by the format) metadata from the image file. - * @throws java.io.IOException if there is an error reading the Image file from disk. - */ - @Synchronized @Throws(java.io.IOException::class) - override fun read(filename: String): Image? { - val o = ImgOpener() - var reader: loci.formats.IFormatReader? = null - - try { - ImageReader.lock(filename) - } catch (e: InterruptedException) { - LoggingUtilities.logger.warning("interrupted while attempting to lock image file") - return null - } - - try { - reader = ImgOpener.createReader(filename, true) - } catch (e: loci.formats.FormatException) { - throw java.io.IOException(e) - } - val meta = reader!!.metadataStore as loci.formats.meta.IMetadata - var img: Img? = null - try { - img = o.openImg(reader, ArrayImgFactory(), FloatType()) - } catch (e: net.imglib2.io.ImgIOException) { - throw java.io.IOException(e) - } - - val p = PixelDataFactory.createPixelData(ImgPlus(img!!), meta.getPixelsDimensionOrder(0).toString()) - val toReturn = ImageFactory.create(meta, p) - lociReader.close() - seriesCounts.put(filename, 1) - currentSeries.put(filename, 1) - release(filename, Thread.currentThread()) - return toReturn - } -} diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerConnection.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerConnection.kt deleted file mode 100644 index d8eb79c..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerConnection.kt +++ /dev/null @@ -1,49 +0,0 @@ -package edu.stanford.cfuller.imageanalysistools.image.io.omero - -import omero.ServerError -import omero.client -import omero.api.GatewayPrx -import omero.api.ServiceFactoryPrx -import Glacier2.CannotCreateSessionException -import Glacier2.PermissionDeniedException - -/** - * @author cfuller - */ -class OmeroServerConnection(infoIn: OmeroServerInfo) { - var serviceFactory: ServiceFactoryPrx? = null - internal set - internal var readerClient: client? = null - var gateway: GatewayPrx? = null - internal set - - var isConnected: Boolean = false - internal set - - private val info: OmeroServerInfo = infoIn - - fun connect() { - try { - this.readerClient = client(this.info.hostname) - this.serviceFactory = this.readerClient!!.createSession(this.info.username, String(this.info.password)) - this.gateway = this.serviceFactory!!.createGateway() - this.isConnected = true - } catch (e: ServerError) { - java.util.logging.Logger.getLogger(OmeroServerConnection::class.java.toString()).severe("Exception while connecting to omero server") - } catch (e: CannotCreateSessionException) { - java.util.logging.Logger.getLogger(OmeroServerConnection::class.java.toString()).severe("Exception while connecting to omero server") - } catch (e: PermissionDeniedException) { - java.util.logging.Logger.getLogger(OmeroServerConnection::class.java.toString()).severe("Invalid username or password.") - } - } - - fun disconnect() { - if (this.readerClient != null) { - this.readerClient!!.closeSession() - this.readerClient = null - } - this.serviceFactory = null - this.gateway = null - this.isConnected = false - } -} diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerImageReader.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerImageReader.kt deleted file mode 100644 index cf5fbfd..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerImageReader.kt +++ /dev/null @@ -1,194 +0,0 @@ -package edu.stanford.cfuller.imageanalysistools.image.io.omero - -import edu.stanford.cfuller.imageanalysistools.frontend.LoggingUtilities -import edu.stanford.cfuller.imageanalysistools.image.Image -import edu.stanford.cfuller.imageanalysistools.image.io.ImageReader -import omero.ServerError -import omero.api.ExporterPrx - -import java.io.* - -/** - * An ImageReader that reads images from an OMERO server instead of files on disk. - - * @author Colin J. Fuller - */ - -//TODO: add functionality to have it remember login information, and redefine the read method to use this information, so this can be passed as an ImageReader and still function. - -class OmeroServerImageReader : ImageReader() { - internal var connection: OmeroServerConnection? = null - - /** - * Constructs a new OmeroServerImageReader with no specified server. - */ - init { - this.connection = null - } - - /** - * Attempts a connection to the server at the specified address using the provided username and password. - * Logs any errors in connecting, but returns normally regardless of whether the connection succeeded. - * @param info An OmeroServerInfo object containing the hostname, usenname, and password to use for the connection. - */ - private fun connectToServer(info: OmeroServerInfo) { - this.connection = OmeroServerConnection(info) - this.connection!!.connect() - } - - /** - * Closes the connection to the OMERO server. - */ - fun closeConnection() { - this.connection!!.disconnect() - } - - /** - * Reads an Image with the specified Id on the OMERO server using the provided address, username, and password. - * @param omeroserverImageId The ID that the OMERO server as assigned to the desired image; this image will be retrieved. - * * - * @param info An OmeroServerInfo object containing the hostname, usenname, and password to use for the connection. - * * - * @return An Image containing the data from the Image identified by ID. It is not guaranteed how much metadata, if any, will be retrieved. Null if there is a problem retrieving the Image. - * * - * @throws IOException If there is a problem with the temporary disk storage for the retrieved Image. - */ - @Throws(IOException::class) - fun readImageFromOmeroServer(omeroserverImageId: Long, info: OmeroServerInfo): Image? { - val temporaryImageFilename = this.loadImageFromOmeroServer(omeroserverImageId, info) ?: return null - return this.read(temporaryImageFilename[1]!!) - } - - /** - * Reads the name of an Image on the OMERO server given its Id. - * @param omeroserverImageId The ID that the OMERO server as assigned to the desired image; this image will be retrieved. - * @param info An OmeroServerInfo object containing the hostname, usenname, and password to use for the connection. - * @return A String containing the name of the Image. - * @throws IOException if the Image cannnot be accessed on the server. - */ - @Throws(IOException::class) - fun getImageNameForOmeroId(omeroserverImageId: Long, info: OmeroServerInfo): String { - try { - if (this.connection == null || !this.connection!!.isConnected) { - this.connectToServer(info) - } - return this.connection!!.gateway!!.getImage(omeroserverImageId).name.value - } catch (e: ServerError) { - throw IOException(e) - } - } - - /** - * Asynchronously loads the Image with the specified Id on the OMERO server using the provided address, username, and password, and stores it to a temporary file on disk. - * @param omeroserverImageId The ID that the OMERO server as assigned to the desired image; this image will be retrieved. - * @param info An OmeroServerInfo object containing the hostname, usenname, and password to use for the connection. - * @return An array containing two elements: first, the original name of the image on the OMERO server; second, the temporary filename to which the image is being stored. - */ - fun loadImageFromOmeroServer(omeroserverImageId: Long, info: OmeroServerInfo): Array? { - var tempfile: File? = null - var originalName: String? = null - try { - if (this.connection == null || !this.connection!!.isConnected) { - this.connectToServer(info) - } - originalName = this.getImageNameForOmeroId(omeroserverImageId, info) - this.closeConnection() - tempfile = File.createTempFile("omerodownload", ".ome.tif") - tempfile!!.deleteOnExit() - val fos = FileOutputStream(tempfile) - val out = BufferedOutputStream(fos) - val lockFilename = tempfile.absolutePath - val lockingThread = Thread(Runnable { - try { - if (connection == null || !connection!!.isConnected) { - try { - while (OmeroServerImageReader.numberOfConnections >= MAX_CONNECTIONS) { - Thread.sleep(SLEEP_TIME_MS) - } - } catch (e: InterruptedException) { - LoggingUtilities.logger.warning("interrupted while waiting on omero server connection") - return@Runnable - } - OmeroServerImageReader.incrementConnections() - connectToServer(info) - } - - val exporter = connection!!.serviceFactory!!.createExporter() - exporter.addImage(omeroserverImageId) - val lengthOfFile = exporter.generateTiff() - var currPos: Long = 0 - val chunkSize = 524288 - - while (currPos < lengthOfFile) { - val data = exporter.read(currPos, chunkSize) - currPos += data.size.toLong() - out.write(data) - } - exporter.close() - release(lockFilename, Thread.currentThread()) - } catch (serverError: ServerError) { - LoggingUtilities.logger.severe("Unable to retrieve image from omero server. " + serverError.toString()) - serverError.printStackTrace() - } catch (e: IOException) { - LoggingUtilities.logger.severe("Exception while writing temporary image file to disk.") - } finally { - closeConnection() - OmeroServerImageReader.decrementConnections() - connection = null - } - }) - - try { - ImageReader.lockWithThread(lockFilename, lockingThread) - } catch (e: InterruptedException) { - LoggingUtilities.logger.severe("interrupted while locking image file") - return null - } - lockingThread.start() - } catch (e: IOException) { - LoggingUtilities.logger.severe("Exception while processing omero server images") - } - val toReturn = arrayOfNulls(2) - toReturn[0] = originalName - toReturn[1] = if (tempfile != null) tempfile.absolutePath else null - return toReturn - } - - companion object { - internal val MAX_CONNECTIONS = 10 - internal val SLEEP_TIME_MS: Long = 10000 - @Volatile internal var currentConnections: Int = 0 - - init { - currentConnections = 0 - } - /** - * Adds a connection to the count of current connections to the OMERO server. - * Ensures that the server is not overloaded with connection requests. - */ - private fun incrementConnections() { - synchronized(OmeroServerImageReader::class.java) { - OmeroServerImageReader.currentConnections += 1 - } - } - - /** - * Removes a connection from the count of current connections to the OMERO server. - * Ensures that the server is not overloaded with connection requests. - */ - private fun decrementConnections() { - synchronized(OmeroServerImageReader::class.java) { - OmeroServerImageReader.currentConnections -= 1 - } - } - - /** - * Gets the number of current connections to the OMERO server. - * @return The current number of connections. - */ - private val numberOfConnections: Int - get() = synchronized(OmeroServerImageReader::class.java) { - return OmeroServerImageReader.currentConnections - } - } -} diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerInfo.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerInfo.kt deleted file mode 100644 index 2823189..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/image/io/omero/OmeroServerInfo.kt +++ /dev/null @@ -1,21 +0,0 @@ -package edu.stanford.cfuller.imageanalysistools.image.io.omero - -/** - * @author cfuller - */ -class OmeroServerInfo -/** - * Constructs a new OmeroServerInfo object given the information needed - * to connect to the server. - * @param hostname The IP address or resolvable hostname of the OMERO server. - * @param username The username to use to connect. - * @param password The password for the provided username. - */ -(val hostname: String, val username: String, val password: CharArray) { - @Throws(Throwable::class) - protected fun Finalize() { - for (i in this.password.indices) { - password[i] = '\u0000' - } - } -} diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/meta/AnalysisMetadataXMLWriter.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/meta/AnalysisMetadataXMLWriter.kt index feb8c6a..7a23c4a 100644 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/meta/AnalysisMetadataXMLWriter.kt +++ b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/meta/AnalysisMetadataXMLWriter.kt @@ -1,15 +1,14 @@ package edu.stanford.cfuller.imageanalysistools.meta +import edu.stanford.cfuller.imageanalysistools.frontend.LoggingUtilities +import edu.stanford.cfuller.imageanalysistools.image.ImageSet +import edu.stanford.cfuller.imageanalysistools.meta.parameters.Parameter +import edu.stanford.cfuller.imageanalysistools.meta.parameters.ParameterDictionary import java.io.PrintWriter import javax.xml.stream.XMLOutputFactory import javax.xml.stream.XMLStreamException import javax.xml.stream.XMLStreamWriter -import edu.stanford.cfuller.imageanalysistools.frontend.LoggingUtilities -import edu.stanford.cfuller.imageanalysistools.meta.parameters.ParameterDictionary -import edu.stanford.cfuller.imageanalysistools.meta.parameters.Parameter -import edu.stanford.cfuller.imageanalysistools.image.ImageSet - /** * Utilites for writing existing analysis parameters to XML files. * @@ -17,13 +16,14 @@ import edu.stanford.cfuller.imageanalysistools.image.ImageSet */ class AnalysisMetadataXMLWriter : AnalysisMetadataXMLParser() { /** - * Writes the parameters in a ParameterDictionary to an XML file that can be read at a later time by a ParameterXMLParser. - * @param pd The ParameterDictionary to write to XML. - * @param filename The full path and filename to the XML file that will be written. + * Writes the parameters in a ParameterDictionary to an XML file that can be read at a later + * time by a ParameterXMLParser. + * @param pd The ParameterDictionary to write to XML. + * @param filename The full path and filename to the XML file that will be written. */ fun writeParameterDictionaryToXMLFile(pd: ParameterDictionary, filename: String) { try { - val xsw = XMLOutputFactory.newFactory().createXMLStreamWriter(PrintWriter(filename)) + val xsw = XMLOutputFactory.newInstance().createXMLStreamWriter(PrintWriter(filename)) xsw.writeStartDocument() xsw.writeCharacters("\n") xsw.writeStartElement(AnalysisMetadataXMLParser.TAG_ANALYSIS_METADATA) @@ -42,12 +42,12 @@ class AnalysisMetadataXMLWriter : AnalysisMetadataXMLParser() { /** * Writes the data in an AnalysisMetadata object to an XML file. - * @param am The AnalysisMetadata to write to XML. - * @param filename The full path and filename to the XML file that will be written. + * @param am The AnalysisMetadata to write to XML. + * @param filename The full path and filename to the XML file that will be written. */ fun writeAnalysisMetadataToXMLFile(am: AnalysisMetadata, filename: String) { try { - val xsw = XMLOutputFactory.newFactory().createXMLStreamWriter(PrintWriter(filename)) + val xsw = XMLOutputFactory.newInstance().createXMLStreamWriter(PrintWriter(filename)) xsw.writeStartDocument() xsw.writeCharacters("\n") xsw.writeStartElement(AnalysisMetadataXMLParser.TAG_ANALYSIS_METADATA) @@ -67,16 +67,16 @@ class AnalysisMetadataXMLWriter : AnalysisMetadataXMLParser() { } /** - * Writes the parameters in a ParameterDictionary to an XML String representation. - * The ordering of all parameters with the same name will be preserved, but otherwise ordering - * is not guaranteed. - * @param pd ParameterDictionary to write to XML - * @return A String containing the XML representation of the ParameterDictionary + * Writes the parameters in a ParameterDictionary to an XML String representation. The ordering + * of all parameters with the same name will be preserved, but otherwise ordering is not + * guaranteed. + * @param pd ParameterDictionary to write to XML + * @return A String containing the XML representation of the ParameterDictionary */ fun writeParameterDictionaryToXMLString(pd: ParameterDictionary): String { val sw = java.io.StringWriter() try { - val xsw = XMLOutputFactory.newFactory().createXMLStreamWriter(sw) + val xsw = XMLOutputFactory.newInstance().createXMLStreamWriter(sw) this.writeParameterDictionaryToXMLStream(pd, xsw) } catch (e: XMLStreamException) { LoggingUtilities.logger.severe("Exception while writing parameter to XML: " + e.message) @@ -86,13 +86,13 @@ class AnalysisMetadataXMLWriter : AnalysisMetadataXMLParser() { /** * Writes a single parameter to an XML String representation. - * @param p ParameterDictionary to write to XML - * @return A String containing the XML representation of the ParameterDictionary + * @param p ParameterDictionary to write to XML + * @return A String containing the XML representation of the ParameterDictionary */ fun writeParameterToXMLString(p: Parameter): String { val sw = java.io.StringWriter() try { - val xsw = XMLOutputFactory.newFactory().createXMLStreamWriter(sw) + val xsw = XMLOutputFactory.newInstance().createXMLStreamWriter(sw) this.writeParameterToXMLStream(p, xsw) } catch (e: XMLStreamException) { LoggingUtilities.logger.severe("Exception while writing parameter to XML: " + e.message) @@ -150,8 +150,14 @@ class AnalysisMetadataXMLWriter : AnalysisMetadataXMLParser() { } xsw.writeAttribute(AnalysisMetadataXMLParser.ATTR_FILENAME, filename) if (`is`.getImageHashAlgorithmForIndex(i) != null) { - xsw.writeAttribute(AnalysisMetadataXMLParser.ATTR_HASH_ALG, `is`.getImageHashAlgorithmForIndex(i)) - xsw.writeAttribute(AnalysisMetadataXMLParser.ATTR_HASH, `is`.getImageHashForIndex(i)) + xsw.writeAttribute( + AnalysisMetadataXMLParser.ATTR_HASH_ALG, + `is`.getImageHashAlgorithmForIndex(i) + ) + xsw.writeAttribute( + AnalysisMetadataXMLParser.ATTR_HASH, + `is`.getImageHashForIndex(i) + ) } xsw.writeEndElement() xsw.writeCharacters("\n") @@ -190,7 +196,10 @@ class AnalysisMetadataXMLWriter : AnalysisMetadataXMLParser() { for (f in outputFilenames) { xsw.writeStartElement(AnalysisMetadataXMLParser.TAG_DATAFILE) xsw.writeAttribute(AnalysisMetadataXMLParser.ATTR_FILENAME, f) - xsw.writeAttribute(AnalysisMetadataXMLParser.ATTR_HASH_ALG, am.getOutputFileHashAlgorithm(f)) + xsw.writeAttribute( + AnalysisMetadataXMLParser.ATTR_HASH_ALG, + am.getOutputFileHashAlgorithm(f) + ) xsw.writeAttribute(AnalysisMetadataXMLParser.ATTR_HASH, am.getOutputFileHash(f)) xsw.writeEndElement() xsw.writeCharacters("\n") diff --git a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/util/SortedDeque.kt b/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/util/SortedDeque.kt deleted file mode 100644 index 8e0da09..0000000 --- a/src/main/kotlin/edu/stanford/cfuller/imageanalysistools/util/SortedDeque.kt +++ /dev/null @@ -1,432 +0,0 @@ -package edu.stanford.cfuller.imageanalysistools.util - -import java.util.ArrayList -import java.util.Deque -import java.util.NoSuchElementException - -/** - * This class represents a sorted priority deque. Adding an element will insert it according to its natural ordering. - * - * Depending on the arguments to the constructor, the SortedDeque can either have a fixed capacity or not. - * This capacity is distinct from the normal definition of capacity in that the add and offer based methods will not - * fail if the SortedDeque is at capacity, but will push elements off the end instead. - * - * If the capacity is - * not fixed, then the method addFirst and addLast do the same thing; the element will be added at its appropriate place in the ordering. - * - * In the case of a SortedDeque with fixed size, then the addFirst and addLast methods have the additional effect of (if at capacity) controlling from which - * end and element is removed; addFirst pushes an element off the end; addLast pushes an element off the front. - * @author Colin J. Fuller - */ -class SortedDeque>(internal var capacity: Int, internal var isFixedCapacity: Boolean) : Deque, MutableList { - internal var storage: ArrayList = ArrayList() - override val size: Int - get() = this.storage.size - - private class ReversingIterator(internal var it: ListIterator) : Iterator { - override fun hasNext(): Boolean { - return it.hasPrevious() - } - override fun next(): E { - return it.previous() - } - } - - /** - * Determines whether the SortedDeque is at capacity. - * @return true if this.size() == this.capacity() and this is a fixed capacity sorted Deque; false otherwise. - */ - val isAtCapacity: Boolean - get() = this.isFixedCapacity && this.size == this.capacity() - - /** - * Gets the capacity of the SortedDeque. If this is not a fixed capacity SortedDeque, may return either Integer.MAX_VALUE - * or the initial capacity specified in the constructor. - * @return The capacity (or initial capacity) of the SortedDeque. - */ - fun capacity(): Int { - return this.capacity - } - - /* (non-Javadoc) - * @see java.util.Collection#addAll(java.util.Collection) - */ - override fun addAll(elements: Collection): Boolean { - for (e in elements) { - this.add(e) - } - if (!elements.isEmpty()) return true - return false - } - - /* (non-Javadoc) - * @see java.util.Collection#clear() - */ - override fun clear() { - this.storage.clear() - } - - /* (non-Javadoc) - * @see java.util.Collection#containsAll(java.util.Collection) - */ - override fun containsAll(elements: Collection): Boolean { - return this.storage.containsAll(elements) - } - - /* (non-Javadoc) - * @see java.util.Collection#isEmpty() - */ - override fun isEmpty(): Boolean { - return this.storage.isEmpty() - } - - /* (non-Javadoc) - * @see java.util.Collection#removeAll(java.util.Collection) - */ - override fun removeAll(elements: Collection): Boolean { - return this.storage.removeAll(elements) - } - - /* (non-Javadoc) - * @see java.util.Collection#retainAll(java.util.Collection) - */ - override fun retainAll(elements: Collection): Boolean { - return this.storage.retainAll(elements) - } - - /* (non-Javadoc) - * @see java.util.Deque#add(java.lang.Object) - */ - override fun add(element: E): Boolean { - this.addLast(element) - return true - } - - /* (non-Javadoc) - * @see java.util.Deque#addFirst(java.lang.Object) - */ - override fun addFirst(e: E) { - this.add(0, e) - } - - /* (non-Javadoc) - * @see java.util.Deque#addLast(java.lang.Object) - */ - override fun addLast(e: E) { - this.add(this.size, e) - } - - /* (non-Javadoc) - * @see java.util.Deque#contains(java.lang.Object) - */ - override operator fun contains(element: E): Boolean { - return this.storage.contains(element) - //TODO: better performance for contains method, given that the list is sorted. - } - - /* (non-Javadoc) - * @see java.util.Deque#descendingIterator() - */ - override fun descendingIterator(): Iterator { - return ReversingIterator(this.storage.listIterator(this.storage.size)) - } - - /* (non-Javadoc) - * @see java.util.Deque#element() - */ - override fun element(): E { - return this.first - } - - /* (non-Javadoc) - * @see java.util.Deque#getFirst() - */ - override fun getFirst(): E { - if (this.storage.isEmpty()) { - throw NoSuchElementException("SortedDeque is empty.") - } - return storage[0] - } - - /* (non-Javadoc) - * @see java.util.Deque#getLast() - */ - override fun getLast(): E { - if (this.storage.isEmpty()) { - throw NoSuchElementException("SortedDeque is empty.") - } - return this.storage[this.storage.size - 1] - } - - /* (non-Javadoc) - * @see java.util.Deque#iterator() - */ - override fun iterator(): MutableIterator { - return this.storage.iterator() - } - - /* (non-Javadoc) - * @see java.util.Deque#offer(java.lang.Object) - */ - override fun offer(e: E): Boolean { - return this.offerLast(e) - } - - /* (non-Javadoc) - * @see java.util.Deque#offerFirst(java.lang.Object) - */ - override fun offerFirst(e: E): Boolean { - this.addFirst(e) - return true - } - - /* (non-Javadoc) - * @see java.util.Deque#offerLast(java.lang.Object) - */ - override fun offerLast(e: E): Boolean { - this.addLast(e) - return true - } - - /* (non-Javadoc) - * @see java.util.Deque#peek() - */ - override fun peek(): E { - return this.peekFirst()!! - } - - /* (non-Javadoc) - * @see java.util.Deque#peekFirst() - */ - override fun peekFirst(): E? { - if (this.storage.isEmpty()) { - return null - } - return this.storage[0] - } - - /* (non-Javadoc) - * @see java.util.Deque#peekLast() - */ - override fun peekLast(): E? { - if (this.storage.isEmpty()) { - return null - } - return this.storage[this.storage.size - 1] - } - - /* (non-Javadoc) - * @see java.util.Deque#poll() - */ - override fun poll(): E { - return this.pollFirst()!! - } - - /* (non-Javadoc) - * @see java.util.Deque#pollFirst() - */ - override fun pollFirst(): E? { - if (this.storage.isEmpty()) { - return null - } - return this.storage.removeAt(0) - } - - /* (non-Javadoc) - * @see java.util.Deque#pollLast() - */ - override fun pollLast(): E? { - if (this.storage.isEmpty()) { - return null - } - return this.storage.removeAt(this.storage.size - 1) - } - - /* (non-Javadoc) - * @see java.util.Deque#pop() - */ - override fun pop(): E { - return this.removeFirst() - } - - /* (non-Javadoc) - * @see java.util.Deque#push(java.lang.Object) - */ - override fun push(e: E) { - this.addFirst(e) - } - - /* (non-Javadoc) - * @see java.util.Deque#remove() - */ - override fun remove(): E { - return this.removeFirst() - } - - override fun removeAt(index: Int): E { - return this.storage.removeAt(index) - } - - /* (non-Javadoc) - * @see java.util.Deque#remove(java.lang.Object) - */ - override fun remove(element: E): Boolean { - return this.storage.remove(element) - } - - /* (non-Javadoc) - * @see java.util.Deque#removeFirst() - */ - override fun removeFirst(): E { - if (this.storage.isEmpty()) { - throw NoSuchElementException("SortedDeque is empty.") - } - return this.storage.removeAt(0) - } - - /* (non-Javadoc) - * @see java.util.Deque#removeFirstOccurrence(java.lang.Object) - */ - override fun removeFirstOccurrence(o: Any): Boolean { - return this.storage.remove(o) - } - - /* (non-Javadoc) - * @see java.util.Deque#removeLast() - */ - override fun removeLast(): E { - if (this.storage.isEmpty()) { - throw NoSuchElementException("SortedDeque is empty.") - } - return this.storage.removeAt(this.storage.size - 1) - } - - /* (non-Javadoc) - * @see java.util.Deque#removeLastOccurrence(java.lang.Object) - */ - override fun removeLastOccurrence(o: Any): Boolean { - val index = this.storage.lastIndexOf(o) - - if (index == -1) { - return false - } - - this.storage.removeAt(index) - return true - - } - - /* (non-Javadoc) - * @see java.util.List#add(int, java.lang.Object) - */ - override fun add(index: Int, element: E) { - var upperBound = this.size - var lowerBound = 0 - var currentGuess = this.size shr 1 - val previousIndex = currentGuess - 1 - val conditionLower = index <= currentGuess && (previousIndex < 0 || this[previousIndex] < element && this[currentGuess] >= element) - val conditionUpper = index > currentGuess && (previousIndex < 0 || this[previousIndex] <= element && this[currentGuess] > element) - while (!(conditionLower || conditionUpper)) { - if (this[currentGuess] < element) { - //case0: .get(currentGuess) < arg1 -> increase currentGuess halfway between current and upper bound - var increment = upperBound - currentGuess shr 1 - if (increment < 1) increment = 1 - lowerBound = currentGuess - currentGuess += increment - if (currentGuess >= this.size) { - currentGuess = this.size - break - } - } else if (this[currentGuess] > element) { - //case1: .get(currentGuess) > arg1 -> decrease currentGuess halfway between current and lower bound - var increment = currentGuess - lowerBound shr 1 - if (increment < 1) increment = 1 - upperBound = currentGuess - currentGuess -= increment - if (currentGuess <= 0) { - currentGuess = 0 - break - } - } else { - //case2: equality; insert here - break - } - } - this.storage.add(currentGuess, element) - if (this.isFixedCapacity && this.size > this.capacity) { - if (index < currentGuess || index == 0) { - this.removeAt(this.size) - } else { - this.removeAt(0) - } - } - } - - /* (non-Javadoc) - * @see java.util.List#addAll(int, java.util.Collection) - */ - override fun addAll(index: Int, elements: Collection): Boolean { - if (elements.isEmpty()) { - return false - } - for (e in elements) { - this.add(index, e) - } - return true - } - - /* (non-Javadoc) - * @see java.util.List#get(int) - */ - override fun get(index: Int): E { - return this.storage[index] - } - - /* (non-Javadoc) - * @see java.util.List#indexOf(java.lang.Object) - */ - override fun indexOf(element: E): Int { - return this.storage.indexOf(element) - } - - /* (non-Javadoc) - * @see java.util.List#lastIndexOf(java.lang.Object) - */ - override fun lastIndexOf(element: E): Int { - return this.storage.lastIndexOf(element) - } - - /* (non-Javadoc) - * @see java.util.List#listIterator() - */ - override fun listIterator(): MutableListIterator { - return this.storage.listIterator() - } - - /* (non-Javadoc) - * @see java.util.List#listIterator(int) - */ - override fun listIterator(index: Int): MutableListIterator { - return this.storage.listIterator(index) - } - - /* (non-Javadoc) - * @see java.util.List#set(int, java.lang.Object) - */ - override operator fun set(index: Int, element: E): E { - throw UnsupportedOperationException("Set is not supported by SortedDeque.") - } - - /* (non-Javadoc) - * @see java.util.List#subList(int, int) - */ - override fun subList(fromIndex: Int, toIndex: Int): MutableList { - val sub = SortedDeque(toIndex - fromIndex, this.isFixedCapacity) - sub.addAll(this.storage.subList(fromIndex, toIndex)) - return sub - } - - init { - this.storage = ArrayList(capacity) - } -}