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

ImageDisplay incorrectly repeats first time point of 5D (xyzct) ImgPlus #65

Open
bhoeckendorf opened this issue Jan 20, 2017 · 14 comments

Comments

@bhoeckendorf
Copy link

I'm using the following code to display a 5D (xyzct) ImgPlus in ImageJ. When scrolling through time, the display repeats the first time point instead of switching to the newly selected time points. I have verified that the data is loaded properly, i.e. the axes of the ImgPlus instance have the expected order and length and traversing over the time axis yields time-varying values.

Dataset data = datasetService.create( imgPlus );
displayService.createDisplay( data );
@rimadoma
Copy link

Out of interest, does the same thing happen if you call uiService.show(data)?

@bhoeckendorf
Copy link
Author

Yes, the time slider still has no apparent effect.
Thanks for the shortcut though, I'll use UIService from now on instead of DatasetService and DisplayService.

@rimadoma
Copy link

I reckon this happens because the Dataset doesn't have any metadata about the axes, e.g. which ones are x,y,c,z and t. Try calling

Dataset data = datasetService.create( imgPlus );
data.setAxes(new CalibratedAxis[] { new DefaultLinearAxis(Axes.X),
	new DefaultLinearAxis(Axes.Y), new DefaultLinearAxis(Axes.CHANNEL),
	new DefaultLinearAxis(Axes.Z), new DefaultLinearAxis(Axes.TIME) });
uiService.show(data);

@bhoeckendorf
Copy link
Author

I ran the following code to first print the AxisTypes of the ImgPlus and then manually assign the same AxesTypes to the Dataset as per your suggestion. The axes of the ImgPlus are configured properly and the problem persists.

Code:

for (int i = 0; i < img.numDimensions(); ++i)
{
    Axis ax = img.axis( i );
    if (ax instanceof CalibratedAxis)
    {
        AxisType axType = (( CalibratedAxis ) ax).type();
        System.out.println(axType);
    } else {
        System.out.println("!instanceof CalibratedAxis");
    }
}

final Dataset data = datasetService.create( img );
data.setAxes(new CalibratedAxis[] { new DefaultLinearAxis( Axes.X),
        new DefaultLinearAxis(Axes.Y), new DefaultLinearAxis(Axes.Z),
        new DefaultLinearAxis(Axes.CHANNEL), new DefaultLinearAxis(Axes.TIME) });

uiService.show( img );

Output:

X
Y
Z
Channel
Time

@rimadoma
Copy link

You have uiService.show( img ); which should be uiService.show( data );. The (legacy) UI is quite particular about the order of axes in a hyperstack: it needs to be xyczt, not xyzct :)

@bhoeckendorf
Copy link
Author

Apologies, just noticed this myself and already fixed and re-run the test. Same result.

@bhoeckendorf
Copy link
Author

I see, so then the problem is that ImageJ can not deal with the memory layout of my data. My images can be quite big and are stored in xyzct order on disk in a way that provides very efficient access. So reordering the dimensions seems like it would be a bit of a headache. Any suggestions?

@rimadoma
Copy link

rimadoma commented Jan 20, 2017

What I know from ImageJ2 architecture is that concerns about loading images/data from disk are abstracted away, and that the order of axes in an ImgPlus is a high level concern which doesn't affect how the image is loaded to memory.

I have some simple code that creates 5D hyperstacks for testing purposes, I hope it helps you to find an answer to your problem: https://github.com/bonej-org/BoneJ2/tree/master/Modern/testImgs/src/main/java/org/bonej/testImages

@ctrueden
Copy link
Member

ctrueden commented Jan 20, 2017

So reordering the dimensions seems like it would be a bit of a headache.

Have you tried Views.permute to change the order to XYCZT, then wrap as ImgPlus/Dataset?

It would be great for the ImageJ Legacy layer to do this automatically if given an ImgPlus or Dataset with different axis order, but I'm not sure whether it does currently.

So your code above would become:

ImgView<T> permutedImg = new ImgView<>(Views.permute(img, 2, 3));
final Dataset data = datasetService.create( permutedImg );
data.setAxes(new CalibratedAxis[] { new DefaultLinearAxis( Axes.X),
        new DefaultLinearAxis(Axes.Y), new DefaultLinearAxis(Axes.CHANNEL),
        new DefaultLinearAxis(Axes.Z), new DefaultLinearAxis(Axes.TIME) });

uiService.show( img );

I did not test it, though.

@rimadoma
Copy link

@ctrueden In my experience no, order has to be XYCZT or sliders / image display doesn't work properly

@ctrueden
Copy link
Member

In my experience no, order has to be XYCZT or sliders / image display doesn't work properly

I filed imagej/imagej-legacy#147 to track this idea. Note that imagej/imagej-legacy#146 may also be relevant here... if there is an actual bug beyond just the need for a hardcoded order.

@bhoeckendorf
Copy link
Author

Have you tried Views.permute to change the order to XYCZT, then wrap as ImgPlus/Dataset?

Thanks @ctrueden, this actually seems to work. Views.permute takes a RandomAccessibleInterval as argument and returns an IntervalView, neither of which know about the axis and sampling meta data of the ImgPlus. The initial result thus appears to be a 3D stack. After re-setting the meta data on the Dataset produced from the permuted volume, I now get the 5D display that I expected and I can browse all axes.

What I know from ImageJ2 architecture is that concerns about loading images/data from disk are abstracted away, and that the order of axes in an ImgPlus is a high level concern which doesn't affect how the image is loaded to memory.

Thanks for sharing your code, @rimadoma. Unfortunately I use a custom reader that loads 3D-5D blocks in xyzct order, which is also the order in which the images are stored. Hence, I do not see an easy way to handle this on the data loading side without doing some reordering of these blocks, which I'd really like to avoid.

Thankfully, using a permuted View seems to be a good-enough solution for now.

@ctrueden
Copy link
Member

ctrueden commented Jan 20, 2017

@bhoeckendorf Glad you got it working.

Some day, SCIFIO will be blockized! And on that day, we can have a party. And port your reader over.

@bhoeckendorf
Copy link
Author

@ctrueden Since you filed imagej/imagej-legacy#147: Its not too hard to do obviously, but I thought I'd share the code that fixes it for me anyway. I would open a pull request, but I'm not sure this is 'correct enough' for the general case. Also I don't know which class to apply this to. The hierarchy always seems a bit convoluted to me.

Dataset data;
// ImageJ expects xyczt order, loaded order is xyzct.
// Permute if needed, be mindful of squeezed singleton dimensions.
int zDim = 2, cDim = -1;
for ( int i = 0; i < img.numDimensions(); ++i ) {
    final Axis ax = img.axis( i );
    if ( ax instanceof CalibratedAxis ) {
        final AxisType axType = (( CalibratedAxis ) ax).type();
        if ( axType == Axes.CHANNEL ) {
            cDim = i;
            break;
        }
    }
}
if ( cDim != -1 && cDim != 2 ) {
    data = datasetService.create( Views.permute( img, zDim, cDim ) );
    final CalibratedAxis[] axes = new CalibratedAxis[ img.numDimensions() ];
    for ( int i = 0; i < axes.length; ++i ) {
        if ( i == zDim )
            axes[ i ] = ( CalibratedAxis ) img.axis( cDim );
        else if ( i == cDim )
            axes[ i ] = ( CalibratedAxis ) img.axis( zDim );
        else
            axes[ i ] = ( CalibratedAxis ) img.axis( i );
    }
    data.setAxes( axes );
} else {
    data = datasetService.create( img );
}

uiService.show( data );

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

No branches or pull requests

3 participants