Skip to content

Commit

Permalink
feat: reset and estimate min max intensity
Browse files Browse the repository at this point in the history
  • Loading branch information
cmhulbert committed Jun 3, 2024
1 parent 031b917 commit 5bcd3f7
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 40 deletions.
2 changes: 2 additions & 0 deletions src/main/kotlin/org/janelia/saalfeldlab/paintera/Paintera.kt
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ class Paintera : Application() {
styleable.stylesheets.add("style/interpolation.css")
styleable.stylesheets.add("style/sam.css")
styleable.stylesheets.add("style/paint.css")
styleable.stylesheets.add("style/raw-source.css")
}

private fun registerPainteraStylesheets(styleable: Parent) {
Expand All @@ -335,6 +336,7 @@ class Paintera : Application() {
styleable.stylesheets.add("style/interpolation.css")
styleable.stylesheets.add("style/sam.css")
styleable.stylesheets.add("style/paint.css")
styleable.stylesheets.add("style/raw-source.css")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.janelia.saalfeldlab.paintera.control.modes

import io.github.oshai.kotlinlogging.KotlinLogging
import javafx.beans.value.ChangeListener
import javafx.collections.FXCollections
import javafx.collections.ObservableList
Expand All @@ -12,6 +11,7 @@ import net.imglib2.realtransform.AffineTransform3D
import net.imglib2.type.numeric.RealType
import net.imglib2.util.Intervals
import net.imglib2.util.Util
import org.janelia.saalfeldlab.bdv.fx.viewer.ViewerPanelFX
import org.janelia.saalfeldlab.fx.actions.ActionSet
import org.janelia.saalfeldlab.fx.actions.ActionSet.Companion.installActionSet
import org.janelia.saalfeldlab.fx.actions.ActionSet.Companion.removeActionSet
Expand All @@ -24,7 +24,6 @@ import org.janelia.saalfeldlab.paintera.paintera
import org.janelia.saalfeldlab.paintera.state.raw.ConnectomicsRawState
import org.janelia.saalfeldlab.util.*

private val LOG = KotlinLogging.logger {}

object RawSourceMode : AbstractToolMode() {

Expand All @@ -35,47 +34,68 @@ object RawSourceMode : AbstractToolMode() {
override val allowedActions = AllowedActions.NAVIGATION

private val minMaxIntensityThreshold = painteraActionSet("Min/Max Intensity Threshold") {
verifyAll(KEY_PRESSED, "Source State is Raw Source State ") { activeSourceStateProperty.get() is ConnectomicsRawState<*, *> }
KEY_PRESSED(KeyCode.SHIFT, KeyCode.Y) {
graphic = { ScaleView().apply { styleClass += "intensity-reset-min-max" } }
onAction {
val rawSource = activeSourceStateProperty.get() as ConnectomicsRawState<*, *>
resetIntensityMinMax(rawSource)
}
}
KEY_PRESSED(KeyCode.Y) {
graphic = { ScaleView().also { it.styleClass += "enter-shape-interpolation" } }
verify("Source State is Raw Source State ") { activeSourceStateProperty.get() is ConnectomicsRawState<*, *> }
lateinit var viewer: ViewerPanelFX
graphic = { ScaleView().apply { styleClass += "intensity-estimate-min-max" } }
verify("Last focused viewer found") { paintera.baseView.lastFocusHolder.value?.viewer()?.also { viewer = it } != null }
onAction {
val viewer = paintera.activeViewer.value!!
val globalToViewerTransform = AffineTransform3D().also { viewer.state.getViewerTransform(it) }
val viewerInterval = Intervals.createMinSize(0, 0, 0, viewer.width.toLong(), viewer.height.toLong(), 1L)

val rawSource = activeSourceStateProperty.get() as ConnectomicsRawState<*, *>
val scaleLevel = viewer.state.bestMipMapLevel
val sourceToGlobalTransform = rawSource.getDataSource().getSourceTransformCopy(0, scaleLevel)

val dataSource = rawSource.getDataSource().getDataSource(0, scaleLevel) as RandomAccessibleInterval<RealType<*>>

val extension = Util.getTypeFromInterval(dataSource).createVariable()
try {
extension.setReal(Double.NaN)
} catch( e : Exception) {
extension.setZero()
}

val screenSource = dataSource
.extendValue(extension)
.interpolateNearestNeighbor()
.affineReal(globalToViewerTransform.concatenate(sourceToGlobalTransform))
.raster()
.interval(viewerInterval)

var min = Double.NaN
var max = Double.NaN
LoopBuilder.setImages(screenSource).forEachPixel {
val value = it.realDouble
if (!value.isNaN()) {
if (value < min || min.isNaN()) min = value
if (value > max || max.isNaN()) max = value
}
}
if (!min.isNaN()) rawSource.converter().minProperty().set(min)
if (!max.isNaN()) rawSource.converter().maxProperty().set(max)
estimateIntensityMinMax(rawSource, viewer)
}
}
}

fun estimateIntensityMinMax(rawSource: ConnectomicsRawState<*, *>, viewer: ViewerPanelFX) {
val globalToViewerTransform = AffineTransform3D().also { viewer.state.getViewerTransform(it) }
val viewerInterval = Intervals.createMinSize(0, 0, 0, viewer.width.toLong(), viewer.height.toLong(), 1L)

val scaleLevel = viewer.state.bestMipMapLevel
val dataSource = rawSource.getDataSource().getDataSource(0, scaleLevel) as RandomAccessibleInterval<RealType<*>>

val sourceToGlobalTransform = rawSource.getDataSource().getSourceTransformCopy(0, scaleLevel)


val extension = Util.getTypeFromInterval(dataSource).createVariable()
try {
extension.setReal(Double.NaN)
} catch (e: Exception) {
extension.setZero()
}

val screenSource = dataSource
.extendValue(extension)
.interpolateNearestNeighbor()
.affineReal(globalToViewerTransform.concatenate(sourceToGlobalTransform))
.raster()
.interval(viewerInterval)

var min = Double.NaN
var max = Double.NaN
LoopBuilder.setImages(screenSource).forEachPixel {
val value = it.realDouble
if (!value.isNaN()) {
if (value < min || min.isNaN()) min = value
if (value > max || max.isNaN()) max = value
}
}
if (!min.isNaN()) rawSource.converter().minProperty().set(min)
if (!max.isNaN()) rawSource.converter().maxProperty().set(max)
}

fun resetIntensityMinMax(rawSource: ConnectomicsRawState<*, *>) {
val dataSource = rawSource.getDataSource().getDataSource(0, 0) as RandomAccessibleInterval<RealType<*>>
val extension = Util.getTypeFromInterval(dataSource).createVariable()

rawSource.converter().minProperty().set(extension.minValue)
rawSource.converter().maxProperty().set(extension.maxValue)
}

override val modeActions: List<ActionSet> = listOf(minMaxIntensityThreshold)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ import javafx.scene.layout.Priority
import javafx.scene.layout.TilePane
import javafx.scene.paint.Color
import javafx.stage.Modality
import net.imglib2.type.numeric.RealType
import org.janelia.saalfeldlab.net.imglib2.converter.ARGBColorConverter
import org.janelia.saalfeldlab.fx.extensions.TitledPaneExtensions
import org.janelia.saalfeldlab.fx.ui.NamedNode
import org.janelia.saalfeldlab.fx.ui.NumericSliderWithField
import org.janelia.saalfeldlab.fx.util.DoubleStringFormatter
import org.janelia.saalfeldlab.paintera.control.modes.RawSourceMode
import org.janelia.saalfeldlab.paintera.paintera
import org.janelia.saalfeldlab.paintera.state.raw.ConnectomicsRawState
import org.janelia.saalfeldlab.paintera.ui.PainteraAlerts
import org.janelia.saalfeldlab.util.Colors
import org.slf4j.LoggerFactory
import java.lang.invoke.MethodHandles

class RawSourceStateConverterNode(private val converter: ARGBColorConverter<*>) {
class RawSourceStateConverterNode(private val converter: ARGBColorConverter<*>, private val state: ConnectomicsRawState<out RealType<*>, *>) {

private val colorProperty = SimpleObjectProperty(Color.WHITE)

Expand Down Expand Up @@ -51,6 +55,23 @@ class RawSourceStateConverterNode(private val converter: ARGBColorConverter<*>)
val colorPickerBox = HBox(picker)
HBox.setHgrow(picker, Priority.ALWAYS)
tilePane.children.add(colorPickerBox)
val resetMinMax = Button("Reset Min / Max")
val estimateMinMax = Button("Estimate Min / Max")

listOf(resetMinMax, estimateMinMax).forEach {
HBox.setHgrow(it, Priority.ALWAYS)
it.maxWidth = Double.MAX_VALUE
}

resetMinMax.onAction = EventHandler { RawSourceMode.resetIntensityMinMax(state) }
estimateMinMax.onAction = EventHandler {
paintera.baseView.lastFocusHolder.value?.viewer()?.let { viewer ->
RawSourceMode.estimateIntensityMinMax(state, viewer)
}
}
val thresholdHBox = HBox(resetMinMax, estimateMinMax)

tilePane.children.add(thresholdHBox)

val min = this.min.asString()
val max = this.max.asString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ open class ConnectomicsRawState<D, T>(
override fun preferencePaneNode(): Node {
val node = super.preferencePaneNode()
val box = node as? VBox ?: VBox(node)
box.children.add(RawSourceStateConverterNode(converter).converterNode)
box.children.add(RawSourceStateConverterNode(converter, this).converterNode)

val backendMeta = backend.createMetaDataNode()
val metaDataContents = VBox(backendMeta)
Expand Down

0 comments on commit 5bcd3f7

Please sign in to comment.