Support view attributes
constantinpape opened this issue · comments
The bigdataviever attributes are stored in the metadata like this:
<ViewSetups>
<Attributes name="channel">
<Channel>
<id>0</id>
<name>0</name>
</Channel>
<Channel>
<id>1</id>
<name>1</name>
</Channel>
</Attributes>
<Attributes name="illumination">
<Illumination>
<id>0</id>
<name>0</name>
</Illumination>
</Attributes>
<ViewSetup>
<id>0</id>
....
<attributes>
<channel>0</channel>
<illumination>0</illumination>
</attributes>
</ViewSetup>
<ViewSetup>
<id>1</id>
....
<attributes>
<channel>1</channel>
<illumination>0</illumination>
</attributes>
</ViewSetup>
</ViewSetups>
So there is a group of existing attributes specified by <Attributes name=...>
and each ViewSetup
then maps to the corresponding attributes in <attributes>
.
In order to support this, I will add a dictionary argument attributes
to make_bdv
,
that maps the ViewSetup
that is written to its attributes.
To be compatible to the old code, this will default to attributes={"channel": None}
, which translates to: the attribute for this ViewSetup
is channel and increase the counter by 1.
If a user wants to add custom attributes (i.e. other attributes than channel), the first call to make_bdv
must have all the attribute names in the attributes
dict that is passed.
Otherwise subsequent calls that introduce new attribute names will fail.
As far as I know make_bdv does not report if a ViewID already exists in a data set (it would just append a "Channel").
I would like to be able to run a converter script multiple times without having to write the data over and over again if it is already present.
So if I call make_bdv with a set of attributes that already exists, the default should be to skip writing it. However, there should also be a possibility to overwrite an existing entry.
Could you implement this s well?
As far as I know make_bdv does not report if a ViewID already exists in a data set (it would just append a "Channel").
No it does report it, and asks the user if the view-id should be over-written, see https://github.com/constantinpape/pybdv/blob/master/pybdv/converter.py#L38-L42.
I think what you propose is a better solution though:
Add argument overwrite=False
to make_bdv
, if False
, just skip existing setup-ids, if True
over-write them.
Would this work for you?
exactly !
exactly !
Great, will implement it and let you know once all is there.
I have another one, quite important for multiple-view BDV:
displaysettings
I think your code as it is now should already support it. I will have a closer look and play a bit...
I have another one, quite important for multiple-view BDV:
displaysettings
What do you mean by displaysettings
? Is this part of the bdv.xml
spec?
I think your code as it is now should already support it. I will have a closer look and play a bit...
Sounds good, let me know what you find.
Hi Constantin,
displaysettings was implemented by Nico bigdataviewer/bigdataviewer-playground#111 (comment)
It basically stores the display state in BDV, so you can pre-load the contrast adjustment, LUT assignments etc. Probably there will be a few more attributes coming. So let's keep it flexible:
Could you simply make type-dependent assignments to the attribute tags?
- int -> set the id and name as it is right now.
- dict -> fill the attribute field with the tags defined in that dict.
I guess there should be easy ways to create xml fileds and tags from a dict...
So for displaysettings, I would provide: {"channel": 0, "tile": 1, "displaysettings": {"min": 55, "max":234}}
And you would also need to change all functions in transformations.py to accept the setupID as a parameter to access the proper values.
This would be extremely helpful to be able just to update the transform and not write a new data container. Or, even better make_bdv
's overwrite mode should by default just rewrite the xml entry for the ViewSetup not the data container.
Could you simply make type-dependent assignments to the attribute tags?
* int -> set the id and name as it is right now. * dict -> fill the attribute field with the tags defined in that dict.
I guess there should be easy ways to create xml fileds and tags from a dict...
So for displaysettings, I would provide:
{"channel": 0, "tile": 1, "displaysettings": {"min": 55, "max":234}}
Yes, this is easy to implement, I will give it a go later.
This would be extremely helpful to be able just to update the transform and not write a new data container. Or, even better
make_bdv
's overwrite mode should by default just rewrite the xml entry for the ViewSetup not the data container.
I would rather provide a new function for this then change make_bdv
; let's discuss this in person though.
Ok, thought about this a bit more and it's a bit more tricky.
We need some way of uniquely identifying an attribute setup.
When we just pass an int (=id) this is easy.
If we pass full dicts and don't require any specific fields, this is more difficult.
I would opt for always requiring the dict to contain {'id': <some number>}
Ok, will support passing dicts for attributes with two restrictions:
- needs to contain the field
id
- cannot be nested, i.e. no other dicts or lists etc as values
I have implemented this now in #15. However, I only allow passing attributes that are dict of dicts, e.g.
attributes = {'channel': {'id': 0, 'name': 'Channel1'}, 'displaysettings': {'id': 0, 'contrast_minimum': 0., ...}}
(Supporting both int
and dict
attributes made the code much more difficult to understand and is not necessary, I think).
@martinschorb could you give it a try and see if this works for you?
hmm,
I give it:
In[8]: attributes
Out[8]: {'channel': {'id': 0}}
and end up with
<Attributes name="channel">
<Channel>
<id>{'id': 0}</id>
<name>{'id': 0}</name>
</Channel>
then it crashes...
File "C:\Software\Anaconda3\envs\pyEM\lib\site-packages\pybdv\converter.py", line 322, in make_bdv
attributes_ = validate_attributes(xml_path, attributes, setup_id, overwrite_)
File "C:\Software\Anaconda3\envs\pyEM\lib\site-packages\pybdv\metadata.py", line 371, in validate_attributes
xml_ids = [int(child.find('id').text) for child in attribute]
File "C:\Software\Anaconda3\envs\pyEM\lib\site-packages\pybdv\metadata.py", line 371, in <listcomp>
xml_ids = [int(child.find('id').text) for child in attribute]
ValueError: invalid literal for int() with base 10: "{'id': 0}"
Found the problem:
print(child.tag) for child in attribute
Channel
Capital C in Channel
in the Attributes
definition, whereas smallcaps channel
in the ViewSetup attributes
hmm OK, the 'name' attrib of the xml tag 'Attributes' should be correct (smallcaps)...
But the tag clearly isn't. So I guess that's why the find does not find anything...?
@martinschorb sorry I don't quite understand what is not working for you.
I just tried this example:
import numpy as np
from pybdv import make_bdv
from pybdv.metadata import get_attributes
data = np.random.rand(1, 128, 128)
attrs1 = {'channel': {'id': 0}, 'tile': {'id': 0}}
make_bdv(data, 'test', attributes=attrs1)
data = np.random.rand(1, 128, 128)
attrs2 = {'channel': {'id': 1}, 'tile': {'id': 0}}
make_bdv(data, 'test', attributes=attrs2)
attrs_out1 = get_attributes('test.xml', 0)
print("Read 1:", attrs_out1)
attrs_out2 = get_attributes('test.xml', 1)
print("Read 2:", attrs_out2)
and it works fine.
Could you post a minimal example of the issue you are having please?
Oh, I am calling make_bdv
with overwrite = False
in this case. Maybe this helps locating the problem. But this will change anyway with the new overwrite options.
Oh, I am calling
make_bdv
withoverwrite = False
in this case
Ok, I think this explains it: You probably wrote the original file with an older pybdv version where the attributes were not properly supported yet. It does not over-write the attributes and so you have the broken xml.
For me, it works:
import h5py
from pybdv import make_bdv
from pybdv.metadata import get_attributes
with h5py.File('./fm_r1.h5', 'r') as f:
data = f['t00000/s00/0/cells'][:]
make_bdv(data, 'martin', attributes={'channel': {'id': 0},
'displaysettings': {'id': 0, 'alpha': 0.5}})
attrs = get_attributes('martin.xml', 0)
print(attrs)
But this will change anyway with the new overwrite options.
Yes, I will update the code for this now and let you know once it's there.
It also happens when I delete all files and rewrite them...
Now even earlier (with the displaysettings
)
Can you try with the code snippet I have posted above and can you make sure you are using the latest commit: 7d9f987
Same problem
File "<ipython-input-5-5ecb2a962fcb>", line 11, in <module>
attrs = get_attributes('martin.xml', 0)
File "C:\Software\Anaconda3\envs\pyEM\lib\site-packages\pybdv\metadata.py", line 455, in get_attributes
attributes = {att.tag: int(att.text) for att in attributes}
File "C:\Software\Anaconda3\envs\pyEM\lib\site-packages\pybdv\metadata.py", line 455, in <dictcomp>
attributes = {att.tag: int(att.text) for att in attributes}
ValueError: invalid literal for int() with base 10: "{'id': 0}"
If I install it through pip it should be the latest commit... right?
pip install git+https://github.com/constantinpape/pybdv
I am never sure what pip does ....
Can you try the following:
- clone the repository
- navaigate into the root folder of the repo
- run
pip install -e .
This should install the latest version
ran a manual install using python setup.py install
Same result
OK
now it runs
Also the initial make_bdv
needs to be the most recent version.
I still had an old file in there...
ran a manual install using
python setup.py install
Yeah, that's even worse then pip :D
(Python installing is pretty broken ...)
OK
now it runs
Ok, great :)
Also the initial
make_bdv
needs to be the most recent version.I still had an old file in there...
Yes, this has all changed quite a bit recently.
So I think this is working now :).
Feel free to reopen if any other issues come up.
Hi,
I just realized that Displaysettings
is capitalized in the attributes definition while in Nico's exported files it is in smallcaps.
bigdataviewer/bigdataviewer-playground#111
This is a problem with XML, correct?
Maybe you can align with him to make sure there is a consistent definition of the attributes (small/capitalized/...)
I just realized that
Displaysettings
is capitalized in the attributes definition while in Nico's exported files it is in smallcaps.bigdataviewer/bigdataviewer-playground#111
This is a problem with XML, correct?
This is just in the attrib
(at least that's how it is called in python), so I am not really sure if this will cause any major issues. But it should still be done consistently!
I capitalize it, because that's the way it's done for all the other BDV attribute names.
I will comment in the playground issue to see if we can coordinate this.
Sorry my bad, this is actually different for the field names.
So yes, this will def. cause incompatibilities in XML and we need to do it the same way.