Skip to content

Instantly share code, notes, and snippets.

@hanslovsky
Last active November 4, 2019 01:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hanslovsky/46ccde43851b34a072143e4487e90ecb to your computer and use it in GitHub Desktop.
Save hanslovsky/46ccde43851b34a072143e4487e90ecb to your computer and use it in GitHub Desktop.
Evaluation of mutex watersheds for cremi and lauritzen
_convert() {
ITERATION=$1
SETUP=$2
THRESHOLD=$3
SAMPLE=$4
CONTAINER="/nrs/saalfeld/hanslovskyp/experiments/quasi-isotropic-predictions/affinities-glia/neuron_ids-unlabeled-unmask-background/predictions/CREMI/sample_${SAMPLE}.n5"
BASE_PATH="volumes/predictions/neuron_ids-unlabeled-unmask-background/${SETUP}/${ITERATION}"
if [ -z "${THRESHOLD}" ]; then
SOURCE="${BASE_PATH}/mutex-watershed-merged"
else
SOURCE="${BASE_PATH}/mutex-watershed-threshold=${THRESHOLD}-merged"
fi
TARGET="${SOURCE}-multiscale"
if [ -d "${CONTAINER}/${TARGET}" ]; then
echo "${CONTAINER}/${TARGET} already exists. Skipping..."
exit 0
fi
paintera-convert \
to-paintera \
--block-size=64,64,64 \
--scale 2 2 2 2 2 \
--output-container="${CONTAINER}" \
--container="${CONTAINER}" \
--dataset="${SOURCE}" \
--overwrite-existing \
--target-dataset="${TARGET}"
echo "Successfully converted into dataset ${TARGET} in container ${CONTAINER}."
}
mkdir -p $HOME/.local/tmp/mutex-watersheds-eval/logs
for ITERATION in 500000; do
for SETUP in 0 3; do
for THRESHOLD in 0.5 '' 0.3 0.7 0.1 0.9; do
for SAMPLE in 2 1 0 A C B; do
_convert $ITERATION $SETUP "$THRESHOLD" $SAMPLE
done
done
done
done
_evaluate() {
ITERATION=$1
SETUP=$2
SAMPLE=$3
THRESHOLD=$4
if [ -z "$5" ]; then
WITH_GLIA=''
WITH_GLIA_ARG='--with-glia=false'
else
WITH_GLIA='-with-glia'
WITH_GLIA_ARG='--with-glia=true'
fi
if [ -z "$THRESHOLD" ]; then
eval-mutex-watersheds \
--sample=$SAMPLE \
--setup=$SETUP \
--iteration=$ITERATION \
--border-threshold=175 \
--overwrite-existing \
"${WITH_GLIA_ARG}" \
1> "$HOME/.local/tmp/mutex-watersheds-eval/logs/eval.log.sample=${SAMPLE}_setup=${SETUP}_iteration=${ITERATION}${WITH_GLIA}" \
2>&1
eval-mutex-watersheds \
--sample=$SAMPLE \
--setup=$SETUP \
--iteration=$ITERATION \
--border-threshold=175 \
--ignore-training-mask \
--overwrite-existing \
"${WITH_GLIA_ARG}" \
1> "$HOME/.local/tmp/mutex-watersheds-eval/logs/eval.log.sample=${SAMPLE}_setup=${SETUP}_iteration=${ITERATION}${WITH_GLIA}_ignore-training-mask" \
2>&1
else
eval-mutex-watersheds \
--sample=$SAMPLE \
--setup=$SETUP \
--iteration=$ITERATION \
--threshold=$THRESHOLD \
--border-threshold=175 \
--overwrite-existing \
"${WITH_GLIA_ARG}" \
1> "$HOME/.local/tmp/mutex-watersheds-eval/logs/eval.log.sample=${SAMPLE}_setup=${SETUP}_iteration=${ITERATION}_threshold=${THRESHOLD}${WITH_GLIA}" \
2>&1
eval-mutex-watersheds \
--sample=$SAMPLE \
--setup=$SETUP \
--iteration=$ITERATION \
--threshold=$THRESHOLD \
--border-threshold=175 \
--ignore-training-mask \
--overwrite-existing \
"${WITH_GLIA_ARG}" \
1> "$HOME/.local/tmp/mutex-watersheds-eval/logs/eval.log.sample=${SAMPLE}_setup=${SETUP}_iteration=${ITERATION}_threshold=${THRESHOLD}${WITH_GLIA}_ignore-training-mask" \
2>&1
fi
}
_evaluate_all_thresholds() {
ITERATION=$1
SETUP=$2
SAMPLE=$3
WITH_GLIA="$4"
for THRESHOLD in 0.5 '' 0.3 0.7 0.1 0.9; do
_evaluate $ITERATION $SETUP $SAMPLE "$THRESHOLD" "$WITH_GLIA"
done
}
mkdir -p $HOME/.local/tmp/mutex-watersheds-eval/logs
for ITERATION in 500000; do
for SETUP in 0 3; do
for SAMPLE in A B C 0 1 2; do
_evaluate_all_thresholds $ITERATION $SETUP $SAMPLE 1 &
done
done
done
wait
  • mutex-watershed-threshold=0.5-merged-multiscale
    • 20585282
    • 16428617
    • 16647665
    • 20525818
    • 3737 potentially easy to fix merge
    • 11168539
    • 3788453 probably many merges
    • 14199231
    • 17548588
    • 20515317
    • 15526596
    • 15501213 potentially easy to fix merge
N_NODES=${N_NODES:-10}
N_EXECUTORS_PER_NODE=${N_EXECUTORS_PER_NODE:-15}
BLOCK_SIZE=${BLOCK_SIZE:-64,64,64}
# N5 group that holds integer type labels
# N5_GROUP="/nrs/saalfeld/hanslovskyp/n5-examples"
BLOCK=${BLOCK:-03}
CONTAINER="${CONTAINER:-/nrs/saalfeld/hanslovskyp/experiments/quasi-isotropic-predictions/affinities-glia/neuron_ids-unlabeled-unmask-background/predictions/lauritzen/${BLOCK}/workspace.n5}"
if [ -z "${THRESHOLD}" ]; then
SOURCE_DATASET="${SOURCE_DATASET:-volumes/predictions/neuron_ids-unlabeled-unmask-background/0/500000/mutex-watershed-merged}"
TARGET_DATASET="${TARGET_DATASET:-${SOURCE_DATASET}-multiscale}"
else
SOURCE_DATASET="${SOURCE_DATASET:-volumes/predictions/neuron_ids-unlabeled-unmask-background/0/500000/mutex-watershed-threshold=${THRESHOLD}-merged}"
TARGET_DATASET="${TARGET_DATASET:-${SOURCE_DATASET}-multiscale}"
fi
# input dataset
DATASET="filtered/volumes/labels/multiset-mipmap-with-label-lists-per-block"
JAR="${JAR:-$HOME/paintera-conversion-helper-0.9.2-shaded.jar}"
CLASS="org.janelia.saalfeldlab.paintera.conversion.PainteraConvert"
N_EXECUTORS_PER_NODE=$N_EXECUTORS_PER_NODE \
$HOME/flintstone/flintstone.sh ${N_NODES} $JAR $CLASS \
to-paintera \
--block-size=$BLOCK_SIZE \
--scale 2 2 2 2 2 2 2 2 \
-m -1 -1 -1
5 4 3 2 \
--output-container="${CONTAINER}" \
--container="${CONTAINER}" \
--dataset="${SOURCE_DATASET}" \
--target-dataset="${TARGET_DATASET}"
BASE_PATH="/nrs/saalfeld/hanslovskyp/experiments/quasi-isotropic-predictions/affinities-glia/neuron_ids-unlabeled-unmask-background/predictions/CREMI"
SAMPLE="$1"
PREDICTION_CONTAINER="${BASE_PATH}/sample_${SAMPLE}.n5"
SETUP="$2"
THRESHOLD="$3"
DATASET_BASE="volumes/predictions/neuron_ids-unlabeled-unmask-background/$SETUP/500000"
if [ -z "$THRESHOLD" ]; then
MERGED_STRING="mutex-watershed-merged-multiscale"
WITH_GLIA_STRING="mutex-watershed-merged-with-glia"
else
MERGED_STRING="mutex-watershed-threshold=$THRESHOLD-merged-multiscale"
WITH_GLIA_STRING="mutex-watershed-threshold=$THRESHOLD-merged-with-glia"
fi
paintera \
--add-n5-container "/groups/saalfeld/home/hanslovskyp/data/from-arlo/interpolated-combined/sample_${SAMPLE}.n5" --resolution=36,36,360 -d volumes/raw \
--add-n5-container "/groups/saalfeld/home/hanslovskyp/data/from-arlo/interpolated-combined/sample_${SAMPLE}.n5" --revert-array-attributes -d volumes/labels/glia-downsampled volumes/labels/neuron_ids-downsampled \
--add-n5-container "${PREDICTION_CONTAINER}" -d "${DATASET_BASE}/affinities" "${DATASET_BASE}/glia" "${DATASET_BASE}/${MERGED_STRING}" "${DATASET_BASE}/${WITH_GLIA_STRING}" --channels 8,4,0 9,5,1 10,6,2 11,7,3
#!/usr/bin/env kscript
@file:KotlinOpts("-J-Xmx100g")
@file:MavenRepository("scijava.public", "https://maven.scijava.org/content/groups/public")
@file:DependsOn("org.janelia.saalfeldlab:n5:2.1.2")
@file:DependsOn("org.janelia.saalfeldlab:n5-imglib2:3.4.1")
@file:DependsOn("org.janelia.saalfeldlab:k5:0.1.0")
@file:DependsOn("org.apache.commons:commons-compress:1.18")
@file:DependsOn("info.picocli:picocli:4.0.2")
@file:DependsOn("net.imglib2:imglib2-algorithm:0.11.1")
@file:DependsOn("net.imglib2:imglib2-realtransform:2.2.1")
// use logback to set log level programmatically
@file:DependsOn("ch.qos.logback:logback-classic:1.3.0-alpha4")
@file:DependsOn("sc.fiji:bigdataviewer-core:7.0.0")
// @file:DependsOn("org.slf4j:slf4j-api:2.0.0-alpha0")
// @file:DependsOn("org.slf4j:slf4j-simple:2.0.0-alpha0")
import java.io.File
import java.lang.Runtime
import java.lang.Thread
import java.util.concurrent.Callable
import java.util.concurrent.Executors
import kotlin.system.exitProcess
import bdv.export.Downsample
import com.google.gson.JsonElement
import net.imglib2.FinalInterval
import net.imglib2.RandomAccessibleInterval
import net.imglib2.algorithm.gauss3.Gauss3
import net.imglib2.algorithm.util.Grids
import net.imglib2.converter.Converter
import net.imglib2.converter.Converters
import net.imglib2.converter.RealDoubleConverter
import net.imglib2.img.array.ArrayImgs
import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory
import net.imglib2.realtransform.AffineTransform3D
import net.imglib2.realtransform.RealViews
import net.imglib2.type.NativeType
import net.imglib2.type.numeric.RealType
import net.imglib2.type.numeric.integer.ByteType
import net.imglib2.type.numeric.integer.UnsignedByteType
import net.imglib2.type.numeric.real.DoubleType
import net.imglib2.type.numeric.real.FloatType
import net.imglib2.util.Fraction
import net.imglib2.util.Intervals
import net.imglib2.view.Views
import org.janelia.saalfeldlab.k5.K5
import org.janelia.saalfeldlab.n5.DatasetAttributes
import org.janelia.saalfeldlab.n5.DataType
import org.janelia.saalfeldlab.n5.GzipCompression
import org.janelia.saalfeldlab.n5.N5FSReader
import org.janelia.saalfeldlab.n5.N5FSWriter
import org.janelia.saalfeldlab.n5.N5Reader
import org.janelia.saalfeldlab.n5.imglib2.N5Utils
import picocli.CommandLine
// This is necessary to get auto-detection of n5 compression to work, for some reason.
Thread
.currentThread()
.setContextClassLoader(java.lang.invoke.MethodHandles.lookup().lookupClass().getClassLoader())
@CommandLine.Command(name = "qi-fy")
class CliArgs : Callable<Int> {
@CommandLine.Option(names = ["--container", "-c"], required = true)
lateinit var containerPath: File
@CommandLine.Option(names = ["--dataset", "-d"], required = true)
lateinit var dataset: String
@CommandLine.Option(names = ["--block-size"], required = false, defaultValue = "64", split = ",")
lateinit var blockSize: IntArray
@CommandLine.Option(names = ["--resolution"], required = false, defaultValue = "108,108,120", split = ",")
lateinit var resolution: DoubleArray
@CommandLine.Option(names = ["--offset"], required = false, defaultValue = "0,0,0", split = ",")
lateinit var offset: DoubleArray
@CommandLine.Option(names = ["--parallelism", "-p"], required = false)
var parallelism: Int? = null
override fun call(): Int {
val blockSize = if (this.blockSize.size == 3) blockSize else IntArray(3) { this.blockSize[0] }
val sourceContainer = with (K5) { containerPath.absolutePath.n5fsWriter() }
val sourceDataset = sourceContainer.firstMipmapLevelDataset(dataset)
val targetDataset = "$dataset-qified"
val scaleFactorsTargetToSource = arrayOf(Fraction(3, 1), Fraction(3, 1), Fraction(1, 3))
val offsetsInOriginalCoordinates = arrayOf(Fraction(1, 1), Fraction(1, 1), Fraction(0, 1))
val translationTargetToSource = offsetsInOriginalCoordinates.map { it.ratio }.toDoubleArray()
val scaleTargetToSource = scaleFactorsTargetToSource.map { it.ratio }.toDoubleArray()
val scaleFactorsSourceToTarget = scaleFactorsTargetToSource.map { it.clone().also { it.invert() } }
val source = sourceContainer.datasetAsDoubleType(sourceDataset)
println(LongArray(3) { source.dimension(it) }.map { it })
val targetSize = LongArray(3) { (source.dimension(it) * scaleFactorsSourceToTarget[it].ratio).toLong() }
targetSize[2] -= 2L
println(targetSize.map { it })
val targetInterval = FinalInterval(*targetSize)
val extendedSource = Views.extendBorder(source)
val interpolatedSource = Views.interpolate(extendedSource, NLinearInterpolatorFactory<DoubleType>())
val relevantTransform = AffineTransform3D()
// need to shift input by 1 in x and y and scale by a factor of 3 along z
relevantTransform.set(
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 3.0, 0.0)
val transformedSource = RealViews.affine(interpolatedSource, relevantTransform)
val attributes = DatasetAttributes(
targetSize,
blockSize,
DataType.FLOAT64,
GzipCompression())
sourceContainer.createDataset(targetDataset, attributes)
sourceContainer.setAttribute(targetDataset, "resolution", resolution)
sourceContainer.setAttribute(targetDataset, "offset", offset)
val parallelism = this.parallelism ?: Runtime.getRuntime().availableProcessors()
val es = Executors.newFixedThreadPool(parallelism)
val factors2D = IntArray(2) { scaleFactorsTargetToSource[it].ratio.toInt() }
val intervals = Grids.collectAllContainedIntervalsWithGridPositions(targetSize, blockSize)
val futures = intervals.map { block ->
val task = Callable<Unit> {
val target = ArrayImgs.doubles(*Intervals.dimensionsAsLongArray(block.a))
val targetOffset = Views.translate(target, *Intervals.minAsLongArray(block.a))
println("doing stuff for $targetOffset")
for (index in targetOffset.min(2) .. targetOffset.max(2)) {
Downsample.downsample(
Views.hyperSlice(transformedSource, 2, index),
Views.hyperSlice(targetOffset, 2, index),
factors2D)
}
N5Utils.saveBlock(targetOffset, sourceContainer, targetDataset, attributes, block.b)
null
}
es.submit(task)
}
futures.forEach { it.get() }
println("Done!")
println(targetInterval)
es.shutdown()
return 0
}
}
fun N5Reader.datasetAsDoubleType(dataset: String): RandomAccessibleInterval<DoubleType> {
return when (this.getDatasetAttributes(dataset).dataType) {
DataType.FLOAT32 -> open<FloatType>(dataset).asDoubleType()
DataType.FLOAT64 -> open<DoubleType>(dataset)
DataType.INT8 -> open<ByteType>(dataset).asDoubleType()
DataType.UINT8 -> open<UnsignedByteType>(dataset).asDoubleType()
else -> null as RandomAccessibleInterval<DoubleType>?
}!!
}
fun <T: NativeType<T>> N5Reader.open(dataset: String): RandomAccessibleInterval<T> = N5Utils.open<T>(this, dataset)
fun <T: RealType<T>> RandomAccessibleInterval<T>.asDoubleType() = Converters.convert(this, RealDoubleConverter(), DoubleType())
fun N5Reader.firstMipmapLevelDataset(group: String): String {
return if (datasetExists(group)) {
group
} else if (!exists(group)) {
throw Exception("$group does not exist in $this")
} else if (getAttribute(group, "painteraData", JsonElement::class.java) != null) {
firstMipmapLevelDataset("$group/data")
} else {
firstMipmapLevelDataset("$group/s0")
}
}
exitProcess(CommandLine(CliArgs()).execute(*args))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment