Skip to content

Commit

Permalink
Merge pull request #224 from saalfeldlab/channel-selection-in-opener-…
Browse files Browse the repository at this point in the history
…dialog

Channel selection in opener dialog
  • Loading branch information
hanslovsky authored Jun 18, 2019
2 parents 8f06bca + a8ebdce commit cc640cd
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -162,8 +163,6 @@ public class GenericBackendDialogN5 implements Closeable

private final ObjectProperty<AxisOrder> axisOrder = new SimpleObjectProperty<>();

private final ChannelInformation channelInformation = new ChannelInformation();

private final BooleanBinding isReady = isN5Valid
.and(isDatasetValid)
.and(datasetUpdateFailed.not());
Expand Down Expand Up @@ -305,14 +304,22 @@ public void updateDatasetInfo(final String group, final DatasetInfo info)

final DataType dataType = N5Types.getDataType(n5, group);

this.datasetInfo.minProperty().set(Optional.ofNullable(n5.getAttribute(
group,
MIN_KEY,
Double.class)).orElse(N5Types.minForType(dataType)));
this.datasetInfo.maxProperty().set(Optional.ofNullable(n5.getAttribute(
group,
MAX_KEY,
Double.class)).orElse(N5Types.maxForType(dataType)));
// TODO handle array case! for now just try and set to 0, 1 in case of failure
// TODO probably best to always handle min and max as array and populate acoording
// to n5 meta data
try {
this.datasetInfo.minProperty().set(Optional.ofNullable(n5.getAttribute(
group,
MIN_KEY,
Double.class)).orElse(N5Types.minForType(dataType)));
this.datasetInfo.maxProperty().set(Optional.ofNullable(n5.getAttribute(
group,
MAX_KEY,
Double.class)).orElse(N5Types.maxForType(dataType)));
} catch (final ClassCastException e) {
this.datasetInfo.minProperty().set(0.0);
this.datasetInfo.maxProperty().set(1.0);
}
} catch (final IOException e)
{
ExceptionNode.exceptionDialog(e).show();
Expand Down Expand Up @@ -473,11 +480,6 @@ private static <I extends IntegerType<I> & NativeType<I>> void findMaxId(

}

public ChannelInformation getChannelInformation()
{
return this.channelInformation;
}

private Node initializeNode(
final Node rootNode,
final String datasetPromptText,
Expand Down Expand Up @@ -524,6 +526,7 @@ public String identifier()
public <T extends RealType<T> & NativeType<T>, V extends AbstractVolatileRealType<T, V> & NativeType<V>>
List<ChannelSourceState<T, V, RealComposite<V>, VolatileWithSet<RealComposite<V>>>> getChannels(
final String name,
final int[] channelSelection,
final GlobalCache globalCache,
final int priority) throws Exception
{
Expand All @@ -534,43 +537,30 @@ List<ChannelSourceState<T, V, RealComposite<V>, VolatileWithSet<RealComposite<V>
final double[] offset = asPrimitiveArray(offset());
final AffineTransform3D transform = N5Helpers.fromResolutionAndOffset(resolution, offset);
final long numChannels = datasetAttributes.get().getDimensions()[axisOrderProperty().get().channelIndex()];
final int channelsPerSource = channelInformation.channelsPerSourceProperty().get();
final boolean revertChannels = channelInformation.revertChannelAxisProperty().get();

LOG.debug("Got channel info: num channels={} channels per source={} revert channel order? {}", numChannels, channelsPerSource, revertChannels);

List<ChannelSourceState<T, V, RealComposite<V>, VolatileWithSet<RealComposite<V>>>> sources = new ArrayList<>();
for (int channelMin = 0; channelMin < numChannels; channelMin += channelsPerSource) {

final long channelMax = Math.min(channelMin + channelInformation.channelsPerSourceProperty().get(), numChannels) - 1;
String sourceName = channelMin <= 0 && channelMax >= numChannels - 1
? name
: getChannelSourceName(name, channelMin, channelMax, numChannels, revertChannels);
final N5ChannelDataSource<T, V> source = N5ChannelDataSource.valueExtended(
meta,
transform,
globalCache,
sourceName,
priority,
axisOrder.get().channelIndex(),
channelMin,
channelMax,
revertChannels,
Double.NaN
);
LOG.debug("Got channel info: num channels={} channels selection={}", numChannels, channelSelection);

ARGBCompositeColorConverter<V, RealComposite<V>, VolatileWithSet<RealComposite<V>>> converter =
ARGBCompositeColorConverter.imp1((int) source.numChannels(), min().get(), max().get());
final N5ChannelDataSource<T, V> source = N5ChannelDataSource.valueExtended(
meta,
transform,
globalCache,
name + "-" + Arrays.toString(channelSelection),
priority,
axisOrder.get().channelIndex(),
IntStream.of(channelSelection).mapToLong(i -> i).toArray(),
Double.NaN
);

final ARGBCompositeColorConverter<V, RealComposite<V>, VolatileWithSet<RealComposite<V>>> converter =
ARGBCompositeColorConverter.imp1((int) source.numChannels(), min().get(), max().get());

ChannelSourceState<T, V, RealComposite<V>, VolatileWithSet<RealComposite<V>>> state = new ChannelSourceState<>(
source,
converter,
new ARGBCompositeAlphaAdd(),
sourceName);
sources.add(state);
ChannelSourceState<T, V, RealComposite<V>, VolatileWithSet<RealComposite<V>>> state = new ChannelSourceState<>(
source,
converter,
new ARGBCompositeAlphaAdd(),
source.getName());

}
final List<ChannelSourceState<T, V, RealComposite<V>, VolatileWithSet<RealComposite<V>>>> sources = Collections.singletonList(state);
LOG.debug("Returning {} channel source states", sources.size());
return sources;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.janelia.saalfeldlab.fx.ui.Exceptions;
import org.janelia.saalfeldlab.fx.ui.MatchSelection;
import org.janelia.saalfeldlab.fx.util.InvokeOnJavaFXApplicationThread;
import org.janelia.saalfeldlab.n5.DataType;
import org.janelia.saalfeldlab.n5.DatasetAttributes;
import org.janelia.saalfeldlab.paintera.Paintera;
import org.janelia.saalfeldlab.paintera.PainteraBaseView;
Expand All @@ -60,11 +61,14 @@
import org.scijava.plugin.Plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.touk.throwing.ThrowingBiFunction;
import pl.touk.throwing.ThrowingFunction;

import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
Expand All @@ -85,12 +89,11 @@ public BiConsumer<PainteraBaseView, String> onAction() {
return (pbv, projectDirectory) -> {
try (final GenericBackendDialogN5 dialog = fs.backendDialog(pbv.getPropagationQueue())) {
N5OpenSourceDialog osDialog = new N5OpenSourceDialog(pbv, dialog);
dialog.getChannelInformation().bindTo(osDialog.metaPanel.channelInformation());
osDialog.setHeaderFromBackendType("N5");
Optional<GenericBackendDialogN5> backend = osDialog.showAndWait();
if (backend == null || !backend.isPresent())
return;
N5OpenSourceDialog.addSource(osDialog.getName(), osDialog.getType(), dialog, pbv, projectDirectory);
N5OpenSourceDialog.addSource(osDialog.getName(), osDialog.getType(), dialog, osDialog.getChannelSelection(), pbv, projectDirectory);
fs.containerAccepted();
} catch (Exception e1) {
LOG.debug("Unable to open n5 dataset", e1);
Expand All @@ -110,12 +113,11 @@ public BiConsumer<PainteraBaseView, String> onAction() {
return (pbv, projectDirectory) -> {
try (final GenericBackendDialogN5 dialog = hdf5.backendDialog(pbv.getPropagationQueue())) {
N5OpenSourceDialog osDialog = new N5OpenSourceDialog(pbv, dialog);
dialog.getChannelInformation().bindTo(osDialog.metaPanel.channelInformation());
osDialog.setHeaderFromBackendType("HDF5");
Optional<GenericBackendDialogN5> backend = osDialog.showAndWait();
if (backend == null || !backend.isPresent())
return;
N5OpenSourceDialog.addSource(osDialog.getName(), osDialog.getType(), dialog, pbv, projectDirectory);
N5OpenSourceDialog.addSource(osDialog.getName(), osDialog.getType(), dialog, osDialog.getChannelSelection(), pbv, projectDirectory);
hdf5.containerAccepted();
} catch (Exception e1) {
LOG.debug("Unable to open hdf5 dataset", e1);
Expand All @@ -135,12 +137,11 @@ public BiConsumer<PainteraBaseView, String> onAction() {
final GoogleCloud googleCloud = new GoogleCloud();
try (final GenericBackendDialogN5 dialog = googleCloud.backendDialog(pbv.getPropagationQueue())) {
final N5OpenSourceDialog osDialog = new N5OpenSourceDialog(pbv, dialog);
dialog.getChannelInformation().bindTo(osDialog.metaPanel.channelInformation());
osDialog.setHeaderFromBackendType("Google Cloud");
Optional<GenericBackendDialogN5> backend = osDialog.showAndWait();
if (backend == null || !backend.isPresent())
return;
N5OpenSourceDialog.addSource(osDialog.getName(), osDialog.getType(), dialog, pbv, projectDirectory);
N5OpenSourceDialog.addSource(osDialog.getName(), osDialog.getType(), dialog, osDialog.getChannelSelection(), pbv, projectDirectory);
}
} catch (Exception e1) {
LOG.debug("Unable to open google cloud dataset", e1);
Expand Down Expand Up @@ -232,6 +233,10 @@ public N5OpenSourceDialog(final PainteraBaseView viewer, final GenericBackendDia
typeChoiceButton.getItems().setAll(cmi);
typeChoiceButton.setOnAction(e -> {typeChoiceButton.show(); matcher.requestFocus();});
this.metaPanel.bindDataTypeTo(this.typeChoice);
backendDialog.datsetAttributesProperty().addListener((obs, oldv, newv) -> Optional
.ofNullable(newv)
.map(ThrowingFunction.unchecked(this::updateType))
.ifPresent(this.typeChoice::set));

final ObservableObjectValue<DatasetAttributes> attributesProperty = backendDialog.datsetAttributesProperty();
final ObjectBinding<long[]> dimensionsProperty = Bindings.createObjectBinding(() -> attributesProperty.get().getDimensions().clone(), attributesProperty);
Expand Down Expand Up @@ -267,6 +272,8 @@ public MetaPanel.TYPE getType() {
return typeChoice.getValue();
}

public int[] getChannelSelection() { return metaPanel.channelInformation().getChannelSelectionCopy(); }

public String getName() {
return nameField.getText();
}
Expand All @@ -288,7 +295,7 @@ public Consumer<Collection<String>> combiner() {
)));
}

public GenericBackendDialogN5 getBackend() {
private GenericBackendDialogN5 getBackend() {
return this.backendDialog;
}

Expand All @@ -300,10 +307,11 @@ private static final double[] revert(final double[] array) {
return reverted;
}

public static void addSource(
private static void addSource(
final String name,
final MetaPanel.TYPE type,
final GenericBackendDialogN5 dataset,
final int[] channelSelection,
final PainteraBaseView viewer,
final String projectDirectory) throws Exception {
LOG.debug("Type={}", type);
Expand All @@ -315,7 +323,7 @@ public static void addSource(
switch (type) {
case RAW:
LOG.trace("Adding raw data");
addRaw(name, dataset, viewer);
addRaw(name, channelSelection, dataset, viewer);
break;
case LABEL:
addLabel(name, dataset, viewer, projectDirectory);
Expand All @@ -328,6 +336,7 @@ public static void addSource(
private static <T extends RealType<T> & NativeType<T>, V extends AbstractVolatileRealType<T, V> & NativeType<V>> void
addRaw(
final String name,
final int[] channelSelection,
final GenericBackendDialogN5 dataset,
PainteraBaseView viewer) throws Exception {
if (dataset.axisOrderProperty().get().hasTime())
Expand All @@ -340,9 +349,13 @@ public static void addSource(
{
LOG.debug("Axis order {} has channel at index {}", dataset.axisOrderProperty().get(), dataset.axisOrderProperty().get().channelIndex());
List<ChannelSourceState<T, V, RealComposite<V>, VolatileWithSet<RealComposite<V>>>> channels =
dataset.getChannels(name, viewer.getGlobalCache(), viewer.getGlobalCache().getNumPriorities() - 1);
dataset.getChannels(
name,
channelSelection,
viewer.getGlobalCache(),
viewer.getGlobalCache().getNumPriorities() - 1);
LOG.debug("Got {} channel sources", channels.size());
InvokeOnJavaFXApplicationThread.invoke(() -> channels.forEach(viewer::addChannelSource));
InvokeOnJavaFXApplicationThread.invoke(() -> channels.forEach(viewer::addState));
LOG.debug("Added {} channel sources", channels.size());
}
else {
Expand Down Expand Up @@ -399,4 +412,23 @@ InterruptibleFunction<HashWrapper<long[]>, long[]> uniqueLabelLoaders(
return labels;
});
}

private MetaPanel.TYPE updateType(final DatasetAttributes attributes) throws Exception {

if (attributes == null)
return null;

if (this.backendDialog.isLabelMultisetType()) {
return MetaPanel.TYPE.LABEL;
}

switch (attributes.getDataType()) {
case UINT64:
case UINT32:
case INT64:
return MetaPanel.TYPE.LABEL;
default:
return MetaPanel.TYPE.RAW;
}
}
}
Loading

0 comments on commit cc640cd

Please sign in to comment.