mutex-watershed-threshold=0.5-merged-multiscale
20585282
16428617
16647665
20525818
3737
potentially easy to fix merge11168539
3788453
probably many merges14199231
17548588
20515317
15526596
15501213
potentially easy to fix merge
Last active
November 4, 2019 01:54
-
-
Save hanslovsky/46ccde43851b34a072143e4487e90ecb to your computer and use it in GitHub Desktop.
Evaluation of mutex watersheds for cremi and lauritzen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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