Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ngff v0.4 #139

Merged
merged 5 commits into from Jan 24, 2022
Merged

Ngff v0.4 #139

merged 5 commits into from Jan 24, 2022

Conversation

@manzt
Copy link
Member

manzt commented Jan 11, 2022

Thanks for opening this PR! Is it ready for review?

@will-moore
Copy link
Collaborator Author

Yes, thanks. I think the current PR is ready for review. However, the 0.4 axes spec PR is still open, so it could change (although probably not in the limited parts that affect this PR).

But I have been holding off generating larger HCS example datasets in 0.4 spec, so there aren't any public examples for testing (just been testing this PR on smaller local plates). But I could go ahead and do a medium plate if that would be useful?

Actually, I meant to ask you about supporting some of the other axes features that are added in ome/ngff#57, such as transformations (scaling and affine transforms).
I think scaling would be covered by adding a scale bar (unless the X and Y are scaled differently) but I don't know if affine transform is beyond the feature scope of vizarr?
Cheers,

@manzt
Copy link
Member

manzt commented Jan 12, 2022

Great. I'll work on the review.

Actually, I meant to ask you about supporting some of the other axes features that are added in ome/ngff#57, such as transformations (scaling and affine transforms). I think scaling would be covered by adding a scale bar (unless the X and Y are scaled differently) but I don't know if affine transform is beyond the feature scope of vizarr?

I'll need to take a look at the spec more closely, but there are two things that stand out to me which are likely related but we work on in separate PRs:

  • 1.) Vizarr should have a scale bar
  • 2.) Vizarr should (in theory) handle affine transformations

It's probably a bit too low-level, but affine transformations are configurable per-layer via an API we already have (https://github.com/hms-dbmi/vizarr/blob/main/example/spatial_transformations.ipynb). On a first pass, we can hopefully map the transformation defined in NGFF to this representation.

Copy link
Member

@manzt manzt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Just some minor suggestions, thanks @will-moore !

Let's try to get this merged and then we can focus on follow-up PRs for scale bar and transformations.

src/io.ts Outdated
Comment on lines 131 to 147
let labels = getAxisLabels(data[0], config.axis_labels);
let channel_axis = labels.indexOf('c');
if (!config.axis_labels && multiscales && multiscales[0].axes) {
const axes = getNgffAxes(multiscales);
labels = getNgffAxisLabels(axes);
channel_axis = axes.findIndex((axis) => axis.type === 'channel');
}
const tileSize = guessTileSize(data[0]);
const loader = data.map((d) => new ZarrPixelSource(d, labels, tileSize));
const [base] = loader;

// If explicit channel axis is provided, try to load as multichannel.
if ('channel_axis' in config || labels.includes('c')) {
if ('channel_axis' in config || channel_axis > -1) {
config = config as MultichannelConfig;
return loadMultiChannel(config, loader, Number(config.channel_axis ?? labels.indexOf('c')));
return loadMultiChannel(config, loader, Number(config.channel_axis ?? channel_axis));
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is starting to get pretty complicated. The issue is that there are multiple ways labels and channel_axis can be specified, and it's difficult to see what takes precedent over each. The way I see it:

Vizarr layer config > NGFF metadata > guessed labels

if that is the case, we could move this logic:

let data: ZarrArray[];
let labels: [...string, 'y', 'x'];
let channel_axis: number = -1;

if (node instance of ZarrGroup) {
  // ...
  data = await loadMultiscales(node, attrs.multiscales);
  if (attrs.multiscales[0].axes) {
    const axes = getNgffAxes(attrs.multiscales);
	labels = getNgffAxisLabels(axes);
	channel_axis = axes.findIndex((axis) => axis.type === 'channel');
  }
}

// ... 
// explicit override in config > ngff > guessed from data shape
labels = config.axis_labels ?? (labels ?? guessAxisLabels(data[0]));
channel_axis = config.channel_axis ?? (channel_axis ?? labels.indexOf('c'));

src/utils.ts Outdated
Comment on lines 130 to 141
if (multiscales[0].axes) {
axes = multiscales[0].axes.map((axis) => {
// axis may be string 'x' (v0.3) or object
let name: string = axis instanceof String ? (axis as string) : (axis as Ome.Axis).name;
let type = getDefaultType(name);
if (!(axis instanceof String) && (axis as Ome.Axis).type) {
type = (axis as Ome.Axis).type as string;
}
return { name, type };
});
}
return axes;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of type assertions here which are an escape-hatch from the TS type checker (so if the types ever change this might break). Perhaps something like this would be more explicit/safe:

Suggested change
if (multiscales[0].axes) {
axes = multiscales[0].axes.map((axis) => {
// axis may be string 'x' (v0.3) or object
let name: string = axis instanceof String ? (axis as string) : (axis as Ome.Axis).name;
let type = getDefaultType(name);
if (!(axis instanceof String) && (axis as Ome.Axis).type) {
type = (axis as Ome.Axis).type as string;
}
return { name, type };
});
}
return axes;
if (multiscales[0].axes) {
axes = multiscales[0].axes.map((axis) => {
// axis may be string 'x' (v0.3) or object
if (typeof axis === 'string') {
return { name: axis, type: getDefaultType(axis) }
}
const { name, type } = axis;
return { name, type: type ?? getDefaultType(name) };
});
}

types/ome.ts Outdated
interface Multiscale {
datasets: { path: string }[];
version?: string;
axes?: string[];
axes?: (string | Axis)[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is allowed that these types are interleaved? e.g. ['x', { name: 'y', type: 'space' }] ?

Otherwise the type here would be string[] | Axis[]

@will-moore
Copy link
Collaborator Author

Thanks. Those changes seem to be working well locally - think I still need to fix TypeScript...

@manzt
Copy link
Member

manzt commented Jan 13, 2022

Just checked this out and seeing how hard it is to please TS. Also the nested ternaries aren't great. What if we had a helper function for this? Keep the implementation in io.ts...

type Labels = [...string[], 'y', 'x'];
function getAxisLabelsAndChannelAxis(
  config: ImageLayerConfig,
  ngffAxes: Ome.Axis[] | undefined,
  arr: ZarrArray,
): { labels: Labels, channel_axis: number } {

	// type cast string[] to Labels
	const maybeAxisLabels = config.axis_labels as undefined | Labels;
	// ensure numeric if provided
	const maybeChannelAxis = 'channel_axis' in config ?  Number(config.channel_axis) : undefined;

	// Use ngff axes metadata if labels or channel axis aren't explicitly provided
	if (ngffAxes) {
		const labels = maybeAxisLabels ?? getNgffAxisLabels(ngffAxes);
		const channel_axis = maybeChannelAxis ?? ngffAxes.findIndex((axis) => axis.type === 'channel');
		return { labels, channel_axis }
	}

	// create dummy axis labels if not provided and try to guess channel_axis if missing
	const labels = maybeAxisLabels ?? getAxisLabels(arr);
	const channel_axis = maybeChannelAxis ?? labels.indexOf('c');
	return { labels, channel_axis }
}

and then in the main body

export async function createSourceData(config: ImageLayerConfig): Promise<SourceData> {
  const node = await open(config.source);
  let data: ZarrArray[];
  let axes: Ome.Axis[] | undefined;
  
  if (node instanceof ZarrGroup) {
    // ...
    data = await loadMultiscales(node, attrs.multiscales);
    if (attrs.multiscales[0].axes) {
      axes = getNgffAxes(attrs.multiscales);
    }
  } else {
    data = [node];
  }
  const { channel_axis, labels } = getAxisLabelsAndChannelAxis(config, axes, data[0]);
  // ...
}

@will-moore
Copy link
Collaborator Author

Thanks @manzt that looks good.
It's working with all the images I've been testing on.

However, for some reason, plates have stopped working. I had them working locally earlier on this PR, but it seems that a PR change isn't responsible since I also see the same error when I revert to older commits and also at https://hms-dbmi.github.io/vizarr/?source=https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.1/plates/422.zarr
Any ideas?
Older vizarr https://hms-dbmi.github.io/vizarr/v0.1 still works with that plate.

Thanks

@manzt
Copy link
Member

manzt commented Jan 24, 2022

However, for some reason, plates have stopped working. I had them working locally earlier on this PR, but it seems that a PR change isn't responsible since I also see the same error when I revert to older commits and also at hms-dbmi.github.io/vizarr/?source=https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.1/plates/422.zarr
Any ideas?

This is really surprising since at one point those plates where working on vizarr v0.2 & v0.3. The console errors in Chrome are very cryptic, so I tried other browsers to debug (Firefox 98 & Safari 15.1) but those actually seem to work...

What version of Chrome are you using? I'm wondering if something happened with the latest update to Chrome that for some reason breaks this layer. Have you seen this before, @ilan-gold?

@manzt manzt merged commit 1b98016 into hms-dbmi:main Jan 24, 2022
@will-moore
Copy link
Collaborator Author

Ah, yes: https://deploy-preview-139--vizarr.netlify.app/?source=https://minio-dev.openmicroscopy.org/idr/v0.4/2022-01-24/plates/idr0004/1752.zarr is working fine for me in Firefox.
I have Chrome 97.0.4692.71 (Official Build) (x86_64)

@ilan-gold
Copy link
Collaborator

Somethign must have happened in Chrome, if you can see the error on the hms-dbmi website. That's not to say there's not something wrong with what we have, but it was working before. I do understand the error, although I can't figure out why it's being thrown since usampler2D is defined. @manzt Can you point me to a working plate for the viv 0.12 PR so I can see if that resolves things?

@manzt
Copy link
Member

manzt commented Jan 24, 2022

Somethign must have happened in Chrome, if you can see the error on the hms-dbmi website.

are all pinned builds of vizarr corresponding to the tagged releases. So to see a regression in v0.2 & v0.3 & main branch (which wasn't there before) is most likely a browser related.

@will-moore. We are working to get the latest viv release integrated in Vizarr, which has some changes to our shaders anyways. So as long as the Firefox workaround is ok at the moment, we will try to get latest Viv release added before digging into this further. It could end up that this just gets resolved with upgrading Viv/deck.gl.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants