diff --git a/frontend/javascripts/libs/floodfill.js b/frontend/javascripts/libs/floodfill.js index c1a2aa42cde..ee2e1a04b97 100644 --- a/frontend/javascripts/libs/floodfill.js +++ b/frontend/javascripts/libs/floodfill.js @@ -25,12 +25,10 @@ export default function(options: FloodfillOptions) { let flooded = []; const visits = {}; - // $FlowFixMe Flow doesn't allow to use an array as the key - const visited = key => visits[key] === true; + const visited = key => visits[key.toString()] === true; const markAsVisited = key => { - // $FlowFixMe Flow doesn't allow to use an array as the key - visits[key] = true; + visits[key.toString()] = true; }; const member = getArgs => getter(getArgs[0], getArgs[1], getArgs[2]); diff --git a/frontend/javascripts/oxalis/model/sagas/volumetracing_saga.js b/frontend/javascripts/oxalis/model/sagas/volumetracing_saga.js index 64bff149fdb..24295129fe8 100644 --- a/frontend/javascripts/oxalis/model/sagas/volumetracing_saga.js +++ b/frontend/javascripts/oxalis/model/sagas/volumetracing_saga.js @@ -45,6 +45,7 @@ import { type Vector3, type ContourMode, ContourModeEnum, + OrthoViews, type OrthoView, type VolumeTool, VolumeToolEnum, @@ -348,10 +349,12 @@ function* inferSegmentInViewport(action: InferSegmentationInViewportAction): Sag const tensorArray = new Float32Array(new Uint8Array(cuboidData)).map( el => (el - mean) / stdDev, ); + // When interpreting the 3d data slice as a 2d slice, the x and y axis are flipped only on the YZ plane + const isXYflipped = activeViewport === OrthoViews.PLANE_YZ; return useWebworker - ? workerPredict(useGPU, tensorArray.buffer, inputExtent) - : mainThreadPredict(useGPU, tf, tensorArray.buffer, inputExtent); + ? workerPredict(useGPU, tensorArray.buffer, inputExtent, isXYflipped) + : mainThreadPredict(useGPU, tf, tensorArray.buffer, inputExtent, isXYflipped); }; function* currentPositionUpdater(): Saga { @@ -414,11 +417,11 @@ function* inferSegmentInViewport(action: InferSegmentationInViewportAction): Sag const seed = clickPosition; const seedPrediction = yield* call(getter, ...seed); if (seedPrediction) { - // The floodfill will run until aborted - floodfill({ getter, seed, onFlood }); - // Keep updating the current position - yield* call(currentPositionUpdater); + yield* fork(currentPositionUpdater); + + // The floodfill will run until aborted or exhausted + yield* call(floodfill, { getter, seed, onFlood }); } else { Toast.warning("Click position is classified as border, please click inside a segment instead."); } diff --git a/frontend/javascripts/oxalis/workers/tensorflow.impl.js b/frontend/javascripts/oxalis/workers/tensorflow.impl.js index b8526f76e77..3d4746e0d6c 100644 --- a/frontend/javascripts/oxalis/workers/tensorflow.impl.js +++ b/frontend/javascripts/oxalis/workers/tensorflow.impl.js @@ -6,6 +6,7 @@ export default async function predict( tf: Object, buffer: ArrayBuffer, inputExtent: number, + isXYflipped: boolean, ) { if (useGPU) { tf.setBackend("webgl"); @@ -21,7 +22,10 @@ export default async function predict( } let tensor = tf.tensor4d(tensorArray, [1, inputExtent, inputExtent, 1]); - tensor = tf.transpose(tensor, [0, 2, 1, 3]); + // The tensorflow model expects a flipped x/y-axis + if (!isXYflipped) { + tensor = tf.transpose(tensor, [0, 2, 1, 3]); + } const model = segmentationModel; const inferredTensor = model.predict(tensor); diff --git a/frontend/javascripts/oxalis/workers/tensorflow.worker.js b/frontend/javascripts/oxalis/workers/tensorflow.worker.js index dfacd438081..1b690b49bf7 100644 --- a/frontend/javascripts/oxalis/workers/tensorflow.worker.js +++ b/frontend/javascripts/oxalis/workers/tensorflow.worker.js @@ -20,8 +20,8 @@ if (typeof OffscreenCanvas !== "undefined") { const tf = require("@tensorflow/tfjs"); -function predictWrapper(useGPU, buffer: ArrayBuffer, inputExtent: number) { - return predict(useGPU, tf, buffer, inputExtent); +function predictWrapper(useGPU, buffer: ArrayBuffer, inputExtent: number, isXYflipped: boolean) { + return predict(useGPU, tf, buffer, inputExtent, isXYflipped); } export default expose(predictWrapper);