fractal-analytics-platform / fractal-tasks-core

Main tasks for the Fractal analytics platform
https://fractal-analytics-platform.github.io/fractal-tasks-core/
BSD 3-Clause "New" or "Revised" License
14 stars 6 forks source link

Yokogawa image placement from metadata is wrong for 10 well, 5x5 site example #100

Closed jluethi closed 2 years ago

jluethi commented 2 years ago

I ran the 10 well, 5x5 sites, constant Z example (https://github.com/fractal-analytics-platform/fractal/issues/213, Grid number 4, /data/active/fractal/3D/PelkmansLab/CardiacMultiplexing/Cycle1_5x5_10wells_constantZ). It parses and makes the MIPs, all the image data appears to be in the Zarr file. But the arrangement of the field of views is wrong.

This is what we would expect for the F08 well (towards the bottom left of the plate):

Bildschirmfoto 2022-09-23 um 17 33 09

And this is what we get:

Bildschirmfoto 2022-09-23 um 17 31 50

I checked the metadata files, they have correct positions that would go from top left to bottom right in the expected pattern and they are parsed correctly. I also checked a few of the images and the images on disk correspond to what we would expect to see in that well.

Surprisingly, all the FOVs contain data though and it's a 5x5 arrangement, as the metadata predicts. My suspicion is that the matching of FOV from the filename to the actual image files from disk is not happening correctly.

The ROI table contains a field_id index column that corresponds to the F001, F002, F003 part of the filename:

182591157-7caa225e-0c1b-4166-b479-6189ad77b340

@mfranzon Is that used to load the correct image data for each FOV? Or how is the image chosen for a given FOV?

tcompa commented 2 years ago

We're looking into this with @mfranzon.

Question: are you using /data/active/fractal/3D/PelkmansLab/CardiacMultiplexing/Cycle1_5x5_10wells_constantZ/MeasurementData.mlf as an input?

If so, I would expect the second column (y_micrometer) to be 1517.7 (rather than -1517.7), as in

<?xml version="1.0" encoding="utf-8"?>
<bts:MeasurementData bts:Version="1.0" xmlns:bts="http://www.yokogawa.co.jp/BTS/BTSSchema/1.0">
<bts:MeasurementRecord bts:Type="IMG" bts:Time="2020-08-12T18:07:25.008+02:00" bts:Column="9" bts:Row="2" bts:TimePoint="3" bts:FieldIndex="1" bts:ZIndex="1" bts:TimelineIndex="2" bts:ActionIndex="1" bts:Action="3D" bts:X="-1448.3" bts:Y="1517.7" bts:Z="-2.0" bts:Ch="1">20200812-CardiomyocyteDifferentiation14-Cycle1_B09_T0001F001L02A01Z01C01.tif</bts:MeasurementRecord>
<bts:MeasurementRecord bts:Type="IMG" bts:Time="2020-08-12T18:07:25.008+02:00" bts:Column="9" bts:Row="2" bts:TimePoint="3" bts:FieldIndex="1" bts:ZIndex="1" bts:TimelineIndex="2" bts:ActionIndex="1" bts:Action="3D" bts:X="-1448.3" bts:Y="1517.7" bts:Z="-2.0" bts:Ch="2">20200812-CardiomyocyteDifferentiation14-Cycle1_B09_T0001F001L02A01Z01C02.tif</bts:MeasurementRecord>
<bts:MeasurementRecord bts:Type="IMG" bts:Time="2020-08-12T18:07:25.291+02:00" bts:Column="9" bts:Row="2" bts:TimePoint="3" bts:FieldIndex="1" bts:ZIndex="2" bts:TimelineIndex="2" bts:ActionIndex="1" bts:Action="3D" bts:X="-1448.3" bts:Y="1517.7" bts:Z="-1.0" bts:Ch="1">20200812-CardiomyocyteDifferentiation14-Cycle1_B09_T0001F001L02A01Z02C01.tif</bts:MeasurementRecord>
...

This is a direct consequence of

        # we mirror the y coordinate to fit with the field layout
        y_micrometer = -float(record.get("{%s}Y" % ns["bts"]))

in metadata_parsing.py.

@jluethi, can you explain the logic for this line?

jluethi commented 2 years ago

@tcompa Yes, that's the metadata file I'm using.

The coordinate system in the metadata file seems to have its minimum in the lower left corner (low x are left, low y are bottom). But in our logic of building the arrays, we want the upper left corner to be the minimum (i.e. low x are left, low y are top).

The top-left FOV (typically FOV 1 in our grid data) should have the minimum x & y position. Thus, we invert the Y-Axis during parsing to ensure this is the case.

=> The values in the parsed table are consistent with how we'd like to place the FOVs in a large array.

x & y positions don't start at 0 though and in the non-grid case, it's not always FOV 1 that will be top left.

jluethi commented 2 years ago

For the planned refactor of the parsing for https://github.com/fractal-analytics-platform/fractal-tasks-core/issues/91, I will need to slightly adapt that table. Most likely, that means creating additional columns with "shifted" positions that guarantee that there is no overlap.

Do we assume in downstream processing that our top-left corner is 0, 0, 0? If so, would make sense to apply the shifts to the ROIs as part of this processing, not as part of the saving of ROIs to AnnData tables. In that way, all the logic of shifting ROI values would be in one place, which should make it easier to debug.

tcompa commented 2 years ago

Let's separate two things: the origin position and the shifts. Shifts do not matter here, and let's discuss them in the other issues (we will get rid of the shift towards (0,0,0), and the only ones that will matter are those that we decide to add for other reasons - like treating overlaps or off-by-one errors).

Concerning the origin position:

But in our logic of building the arrays, we want the upper left corner to be the minimum (i.e. low x are left, low y are top).

What are the reasons for this requirement?

Right now, our ROIs are working with a bottom-left origin (that is, the index of the minimum position is 0, both along X and Y), which explains the error described in this issue. This is also consistent with the metadata file, as far as I understand.

jluethi commented 2 years ago

As discussed in the call, bioimage analysis seems to have standardized around top left (0, 0, 0) origins. With the flipped Y positions, that should be working for us. What remains here is likely the matching of FOVs to the field_id integer.

tcompa commented 2 years ago

This is likely due to a bug in https://github.com/fractal-analytics-platform/fractal-tasks-core/blob/f3e8b40946af573a07d417edad28b185acb58ff9/fractal_tasks_core/lib_regions_of_interest.py#L119 now fixed with https://github.com/fractal-analytics-platform/fractal-tasks-core/commit/1a5bc8f7a1ed37cba920186aedcd1ee8f9c1065f

Notice that sorting that list is simply wrong, in general, and it only works correctly for a small wells (e.g. when there are only 9 FOVs, such that you don't have to sort ["FOV_1", .., "FOV_10"]).

In principle this should already work. I'll move ID-matching to a new issue.

tcompa commented 2 years ago

The fix is available on pypi, in version 0.1.6. I'll run this to see whether the final image shows up correctly.

tcompa commented 2 years ago

Here is a quick test of parsing the 10-wells dataset, after the fix.

Well: Screenshot from 2022-09-26 13-45-10

FOV E/08: Screenshot from 2022-09-26 13-47-20

Are there any other tests to look at? Otherwise this issue can be closed.

jluethi commented 2 years ago

This looks great!

I'm not aware of other issues & necessary tests for this. I'll test some of the search-first dataset later with this code, but would open new issues if something comes up.