Closed jluethi closed 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?
@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.
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.
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.
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.
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.
The fix is available on pypi, in version 0.1.6
.
I'll run this to see whether the final image shows up correctly.
Here is a quick test of parsing the 10-wells dataset, after the fix.
Well:
FOV E/08:
Are there any other tests to look at? Otherwise this issue can be closed.
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.
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):
And this is what we get:
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:
@mfranzon Is that used to load the correct image data for each FOV? Or how is the image chosen for a given FOV?