Closed SaeedShurrab closed 1 year ago
Hey @SaeedShurrab
single=True
. It seems that there is only one volume in the file.ev.plot()
for example. For projecting annotations from OCT to localizer space eyepy assumes that B-scans are spaced equally and that a 2D projection of the Volume can be mapped to the Localizer via an affine transformation. The intention of this message is to tell the user that the B-scans are not perfectly aligned or have varying distances according to their positions in the meta data. It probably makes sense to use a higher threshold here because for the sample data for example the deviations are only very small and equal spacing can be assumed.yes, I tried to set single=True
but I get the same error.
the drusen patient worked very well.
do you think that the problem is with e2e file itself? it is around 50 Mb file for both eyes from a certain patient (1 study). Based on your experience, what is the expected size of E2E file?
This depends mainly on the number of B-scans. Bscans are stored with 2 bytes per pixel. Hence a single B-scan of size 512*496 would be 507904 bytes. So roughly half a MB. So I would assume that both your volume have fewer than 50 slices. If they should have a lot more than maybe there was a problem with the export.
The E2E readers uses a heuristic to determine wether a series contains a OCT volume. bscanmeta data has to be present in the series. Maybe there is no such bscanmeta data in your file or there are multiple identifiers for bscanmeta data and in your file another identifier is used than the one I know (10004).
Yes, it contains 25 slices per eye. Is there any workaround to check the presence of the metadata or the identifier?
You could try something like this:
types = defaultdict[list]
for chunk in e2ereader.parsed_file.chunks:
for folder in chunk.folders:
types[folder.header.type].append(folder.header.size)
for t, sizes in types.items():
print(f"Type {t}: {sum(sizes)/len(sizes)}")
The size of the Bscan meta ist 428 bytes.
Hi @Oli4 and @marksgraham,
I have got other files with the following extensions:
These are extension for raw HEYEX files stored on HEYEX server. Do both of you encounter such files? Is there any work-around to extract OCT scans from those files?. I found A c++ package called E2ELib but it was last updated 5 years ago, we have been trying to build but with no results.
Hey @SaeedShurrab,
Unfortunately, I have never worked with sdb edb or pdb files, maybe @marksgraham has worked with them before. But I am confident that we can extract the OCT data from your E2E file. Have you found a type of size 428 bytes it might contain the bscan meta data. Also I am going to make it possible to extract partial data. So you can extract only the Bscans without the meta data.
I tried to do that but I did not know how to do it exactly. Could you please elaborate on that.
The idea is to go through all data folders in your file and print the folder type and its size. So you first read the volume with the e2ereader = HeE2eReader("path/to/your/file.e2e")
and then you should be able to use the above code.
Dear @SaeedShurrab
I just released a new version of eyepy with an improved E2E reader. Would you mind updating your current version of eyepy to the latest version and running the following code with your E2E data and share the output here? This might help support your kind of E2E file and thereby improve the E2E reader
from eyepy.io import HeE2eReader
with HeE2eReader("yourfile.E2E") as e2ereader:
print(e2ereader.inspect(recursive=True))
Hi Oli,
Thanks for notifying me,
I will try it and update you. Indeed, I got a new 10 e2e sample from our clinical partner. I tried @marksgraham package on them. some files were read successfully and some were not. I have to try with yours. I will keep you posted.
Thanks
Hi Oli,
I tried to open the new files with your package and I get:
There is more than one image object. This is not expected.
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[9], line 1
----> 1 ev = ep.import_heyex_e2e("26052032.E2E")
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/__init__.py:41, in import_heyex_e2e(path)
37 logger.info(
38 f"There are {len(reader.series)} Series stored in the E2E file. If you want to read all of them, use the HeE2eReader class directly."
39 )
40 with reader as open_reader:
---> 41 ev = open_reader.volume
42 return ev
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:886, in HeE2eReader.volume(self)
879 @property
880 def volume(self) -> EyeVolume:
881 """ First EyeVolume object in the E2E file.
882
883 Returns:
884 EyeVolume object for the first Series in the e2e file.
885 """
--> 886 return self.series[0].get_volume()
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:443, in E2ESeriesStructure.get_volume(self)
439 bscan = sl.get_image()
440 i = ind // 2 if len(
441 self.get_bscan_meta()
442 ) != 1 else 0 # Slice id for single B-scan Volumes is 2 and not 0 in the e2e file.
--> 443 data[i] = bscan
445 volume_meta = self.get_meta()
446 localizer = self.get_localizer()
IndexError: index 0 is out of bounds for axis 0 with size 0
My files contain more than one object, for each eye an object. This might creat a problem.
Dear @SaeedShurrab
I just released a new version of eyepy with an improved E2E reader. Would you mind updating your current version of eyepy to the latest version and running the following code with your E2E data and share the output here? This might help support your kind of E2E file and thereby improve the E2E reader
from eyepy.io import HeE2eReader with HeE2eReader("yourfile.E2E") as e2ereader: print(e2ereader.inspect(recursive=True))
Dear @SaeedShurrab, The import functions work only for data that can be parsed to EyeVolume objects, please use the code above with you file and post the result here. This gives us some idea about the contents.
Dear @Oli4
I managed to extract these reports from the files that I have using your library. I have 10 reports for 10 e2e files. I wonder if these contain patient metadata or only the oct scans metadata. I work with real data for, so privacy counts. If no patient data are included in these reports I can post all reports.
Great that creating the reports worked for all files. The reports do not contain any patient information.
Great I provided you with metadata from 1 patient with 4 e2e files. Worthy to note that I managed to read the second file correctly with the OCT-Converter package from @marksgraham, while the remaining not.
E2EFileStructure
Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|
9011 | 1 | 11144 | 11144 | 11144 | True |
0 | 156 | 0 | 0 | 0 | True |
E2EPatientStructure(26929)
Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|
patient (9) | 1 | 131 | 131 | 131 | True |
31 | 1 | 217 | 217 | 217 | True |
diagnose (17) | 1 | 2 | 2 | 2 | True |
29 | 1 | 2 | 2 | 2 | True |
E2EStudyStructure(78232) Device: Heidelberg Retina Angiograph | Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|---|
device (9001) | 1 | 776 | 776 | 776 | True | |
1000 | 1 | 65 | 65 | 65 | True | |
measurements (7) | 2 | 68 | 68 | 68 | True | |
58 | 1 | 91 | 91 | 91 | True | |
13 | 1 | 200 | 200 | 200 | True | |
62 | 1 | 228 | 228 | 228 | False | |
10 | 1 | 91 | 91 | 91 | True | |
30 | 1 | 2 | 2 | 2 | True | |
53 | 1 | 97 | 97 | 97 | True |
E2EFileStructure
Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|
9011 | 1 | 8504 | 8504 | 8504 | True |
0 | 409 | 0 | 0 | 0 | True |
E2EPatientStructure(26929)
Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|
9010 | 1 | 11144 | 11144 | 11144 | True |
patient (9) | 1 | 131 | 131 | 131 | True |
31 | 1 | 217 | 217 | 217 | True |
diagnose (17) | 1 | 2 | 2 | 2 | True |
29 | 1 | 2 | 2 | 2 | True |
E2EStudyStructure(81048) Device: Heidelberg Retina Angiograph | Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|---|
device (9001) | 1 | 776 | 776 | 776 | True | |
1000 | 1 | 65 | 65 | 65 | True | |
measurements (7) | 2 | 68 | 68 | 68 | True | |
58 | 1 | 91 | 91 | 91 | True | |
13 | 1 | 200 | 200 | 200 | True | |
10 | 1 | 91 | 91 | 91 | True | |
30 | 1 | 2 | 2 | 2 | True | |
53 | 1 | 97 | 97 | 97 | True |
E2EFileStructure
Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|
9011 | 1 | 4784 | 4784 | 4784 | True |
0 | 430 | 0 | 0 | 0 | True |
E2EPatientStructure(26929)
Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|
9010 | 1 | 19344 | 19344 | 19344 | True |
patient (9) | 1 | 131 | 131 | 131 | True |
31 | 1 | 217 | 217 | 217 | True |
diagnose (17) | 1 | 2 | 2 | 2 | True |
29 | 1 | 2 | 2 | 2 | True |
E2EStudyStructure(81049) Device: Heidelberg Retina Angiograph | Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|---|
device (9001) | 1 | 776 | 776 | 776 | True | |
1000 | 1 | 65 | 65 | 65 | True | |
measurements (7) | 2 | 68 | 68 | 68 | True | |
58 | 1 | 91 | 91 | 91 | True | |
13 | 1 | 200 | 200 | 200 | True | |
10 | 1 | 91 | 91 | 91 | True | |
30 | 1 | 2 | 2 | 2 | True | |
53 | 1 | 97 | 97 | 97 | True |
E2EFileStructure
Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|
9011 | 1 | 464 | 464 | 464 | True |
0 | 466 | 0 | 0 | 0 | True |
E2EPatientStructure(26929)
Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|
9010 | 1 | 23824 | 23824 | 23824 | True |
patient (9) | 1 | 131 | 131 | 131 | True |
31 | 1 | 217 | 217 | 217 | True |
diagnose (17) | 1 | 2 | 2 | 2 | True |
29 | 1 | 2 | 2 | 2 | True |
E2EStudyStructure(90544) Device: Heidelberg Retina Angiograph | Type | Count | Mean Size | Min Size | Max Size | described |
---|---|---|---|---|---|---|
device (9001) | 1 | 776 | 776 | 776 | True | |
1000 | 1 | 65 | 65 | 65 | True | |
measurements (7) | 2 | 68 | 68 | 68 | True | |
58 | 1 | 91 | 91 | 91 | True | |
13 | 1 | 200 | 200 | 200 | True | |
62 | 1 | 228 | 228 | 228 | False | |
10 | 1 | 91 | 91 | 91 | True | |
30 | 1 | 2 | 2 | 2 | True | |
53 | 1 | 97 | 97 | 97 | True |
The reports for File 1 and File 2 are exactly the same even the Series IDs. Maybe you copied it twice. I assume that this is the File you could not read. All of the files contain multiple Series but probably only those with Scan Pattern "OCT ART Volume" have a localizer (fundus) image.
On file 3 for example you should be able to retrieve the volume with localizer with code like the following
from eyepy.io import HeE2eReader
with HeE2eReader("your_File3.E2E") as e2ereader:
eyevolume = e2ereader.series[1].get_volume()
For now you could iterate over all series in a file by yourself and extract the data from the Series.
from eyepy.io import HeE2eReader
with HeE2eReader("your_File3.E2E") as e2ereader:
for s in e2ereader.series:
try:
eyevolume = s.get_volume()
except:
# Extract data without localizer (have a look at the E2ESeries implementation)
pass
I will add some checks to eyepys import function, to make sure an EyeVolume is returned if any of the contained Series allows it and otherwise raise an Exception with a useful error message.
Hi Oli
First, I updated the table 1 in my previous comment
For your previous comment I tried this
from eyepy.io import HeE2eReader
with HeE2eReader("your_File3.E2E") as e2ereader:
eyevolume = e2ereader.series[1].get_volume()
on file 2 which I manged to read it gives:
IndexError Traceback (most recent call last)
Cell In[16], line 3
1 from eyepy.io import HeE2eReader
2 with HeE2eReader("26052022.E2E") as e2ereader:
----> 3 eyevolume = e2ereader.series[1].get_volume()
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:443, in E2ESeriesStructure.get_volume(self)
439 bscan = sl.get_image()
440 i = ind // 2 if len(
441 self.get_bscan_meta()
442 ) != 1 else 0 # Slice id for single B-scan Volumes is 2 and not 0 in the e2e file.
--> 443 data[i] = bscan
445 volume_meta = self.get_meta()
446 localizer = self.get_localizer()
IndexError: index 0 is out of bounds for axis 0 with size 0
on file 3 which I can not read i get this:
KeyError Traceback (most recent call last)
Cell In[17], line 3
1 from eyepy.io import HeE2eReader
2 with HeE2eReader("26052032.E2E") as e2ereader:
----> 3 eyevolume = e2ereader.series[1].get_volume()
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:446, in E2ESeriesStructure.get_volume(self)
443 data[i] = bscan
445 volume_meta = self.get_meta()
--> 446 localizer = self.get_localizer()
447 volume = EyeVolume(
448 data=data,
449 meta=volume_meta,
(...)
452 volume_meta, localizer.meta, data.shape),
453 )
455 layer_height_maps = self.get_layers()
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:533, in E2ESeriesStructure.get_localizer(self)
529 if len(folders) > 1:
530 logger.warning(
531 "There is more than one enface localizer image stored. This is not expected."
532 )
--> 533 transform = np.array(list(self.slo_data().transform) +
534 [0, 0, 1]).reshape((3, 3))
535 # transfrom localizer with transform from E2E file
536 transformed_localizer = warp(folders[0].data.data,
537 AffineTransform(transform),
538 order=1,
539 preserve_range=True)
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:503, in E2ESeriesStructure.slo_data(self)
502 def slo_data(self) -> Type10025:
--> 503 folders = self.folders[TypesEnum.slodata]
504 if len(folders) > 1:
505 logger.debug(
506 "There is more than one SLO data folder. This is not expected."
507 )
KeyError: <TypesEnum.slodata: 10025>
I tried this also for file 2 and 3:
from eyepy.io import HeE2eReader
with HeE2eReader("26052032.E2E") as e2ereader:
for s in e2ereader.series:
try:
eyevolume = s.get_volume()
except:
# Extract data without localizer (have a look at the E2ESeries implementation)
pass
and I get this:
There is more than one image object. This is not expected.
Dear @SaeedShurrab,
first I updated your comment with the reports to have the series sections collapsible. Now we have a better overview of what is contained in your files. The data is very heterogeneous, which is good for testing the assumptions I made when writing the Reader but also leads to more problems than I expected. First of all, you need to know what you want to extract from the files, you might not be able to parse the data to an EyeVolume object but retrieving images should always be possible.
from eyepy.io import HeE2eReader
from eyepy.io.he.e2e_format import TypesEnum
from eyepy.core.utils import from_vol_intensity
with HeE2eReader("yourFile.E2E") as e2ereader:
# Choose the series you are interested in
index = 0
series = e2ereader.series[index]
# You can also select the series based on the ID shown in the reports
#series_id = 12345
#series = [s for s in e2ereader.series if s.id == series_id][0]
# Extract fundus images. Therefore get all folders that contain fundus images by their ID (you find this ID in the report)
# Only the Series with scan pattern OCT ART volume in your files contain Fundus images.
folders = series.folders[1073741824] # or series.folders[TypesEnum.image]
fundus_images = [f.data.data for f in folders]
# Extrac B-scans
bscans = {}
for id, bscan_slice in series.slices.items():
folders = bscan_slice.folders[1073741824] # or bscan_slice.folders[TypesEnum.image]
# Extract raw B-scan data and apply intensity correction
bscans[id] = [from_vol_intensity(f.data.data) for f in folders]
If you see some data in the output of the inspect
function, you are interested in because of its size or name and want to access it you can simply access the folder as shown above for the images. Then you can do the following:
.data
attribute to get the parsed data according to its description in the E2E types section in the documentation hereget_bytes()
function instead of the data
attribute on the E2EFolder.Thank you very much for your feedback. I hope the code above allows you to extract everything you need from your files. Let me know if you run into any issues. I'll let you know when I implemented the changes and it would be great if you could test the new version with your files.
Best Olivier
Hi Oli,
Thanks for your reply. I will try everything above and update you. My ultimate goal is to extract all images from these files. I am looking also for sending you anonymized sample E2E files that we have. This would allow you to test directly on the files to figure out the problems.
If you do not mind of course.
Kind regards, Saeed
Having the file at hand would make debugging a lot easier. I could write a function that deletes all information from the Patient structure, the location where I know sensitive information is stored.
Sure I have to check first with the Principal Investigator and the medical partner, Then I will do it.
It would be better if you write the function and send it to me to apply to all files before sending.
Thanks Oli,
There is a new version of eyepy, that should fix both Errors you reported above. I would be very interested in the content of File2 E2ESeriesStructure(434684) (Series index 1). Could it be that there is a separate fundus image stored with every B-scan? Because there are twice as much images stored (82) than expected (41). This would be one of the cases where you have to extract the images manually.
Hi Oli,
Yes, You are right, each B-scan is associated with a fundus photo that indicates the depth at which the slice is taken like. Further, when I extracted the b-scans yesterday after implementing your last code, it returned a dictionary with keys like this: {0:[0,0,0.....], 2:[0,0,0.....], 4:[0,0,0.....], 6:[0,0,0.....], . . . . }
It might be the missing keys in the dictionary holds the IR scans. I will try your new release and update you.
Hallo Oli,
I have several points to discuss with you, I think it would be better to hold a zoom meeting if you do not mind of course. This is my email: saeed.shurrab@nyu.edu send an email to this email address to arrange.
Kind regards, Saeed
For some reason the slice indices increase in steps of two not only in this case but throughout all E2E files I have seen, and if there is only a single B-scan it has index 2 and not 0. I'll contact you
Dear Oli,
Please, check the following error when running:
ev = ep.import_heyex_e2e("E2E-file.E2E")
I get the following error:
AttributeError Traceback (most recent call last)
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:929, in HeE2eReader.volume(self)
928 try:
--> 929 return s.get_volume()
930 except Exception as e:
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:458, in E2ESeriesStructure.get_volume(self)
452 localizer = self.get_localizer()
453 volume = EyeVolume(
454 data=data,
455 meta=volume_meta,
456 localizer=localizer,
457 transformation=_compute_localizer_oct_transform(
--> 458 volume_meta, localizer.meta, data.shape),
459 )
461 layer_height_maps = self.get_layers()
AttributeError: 'NoneType' object has no attribute 'meta'
During handling of the above exception, another exception occurred:
TypeError Traceback (most recent call last)
Cell In[174], line 1
----> 1 ev = ep.import_heyex_e2e("E2E-file.E2E")
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/__init__.py:41, in import_heyex_e2e(path)
37 logger.info(
38 f"There are {len(reader.series)} Series stored in the E2E file. If you want to read all of them, use the HeE2eReader class directly."
39 )
40 with reader as open_reader:
---> 41 ev = open_reader.volume
42 return ev
File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:931, in HeE2eReader.volume(self)
929 return s.get_volume()
930 except Exception as e:
--> 931 logger.debug("".join(traceback.format_exception(e)))
932 raise ValueError(
933 "No Series in the E2E file can be parsed to a an EyeVolume object. You might be able to extract information manually from the E2ESeries objects (e2ereader.series)"
934 )
TypeError: format_exception() missing 2 required positional arguments: 'value' and 'tb'
Dear Oli,
Please, check the following error when running:
ev = ep.import_heyex_e2e("E2E-file.E2E")
I get the following error:AttributeError Traceback (most recent call last) File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:929, in HeE2eReader.volume(self) 928 try: --> 929 return s.get_volume() 930 except Exception as e: File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:458, in E2ESeriesStructure.get_volume(self) 452 localizer = self.get_localizer() 453 volume = EyeVolume( 454 data=data, 455 meta=volume_meta, 456 localizer=localizer, 457 transformation=_compute_localizer_oct_transform( --> 458 volume_meta, localizer.meta, data.shape), 459 ) 461 layer_height_maps = self.get_layers() AttributeError: 'NoneType' object has no attribute 'meta' During handling of the above exception, another exception occurred: TypeError Traceback (most recent call last) Cell In[174], line 1 ----> 1 ev = ep.import_heyex_e2e("E2E-file.E2E") File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/__init__.py:41, in import_heyex_e2e(path) 37 logger.info( 38 f"There are {len(reader.series)} Series stored in the E2E file. If you want to read all of them, use the HeE2eReader class directly." 39 ) 40 with reader as open_reader: ---> 41 ev = open_reader.volume 42 return ev File /scratch/sas10092/conda-envs/chexmsn-env/lib/python3.9/site-packages/eyepy/io/he/e2e_reader.py:931, in HeE2eReader.volume(self) 929 return s.get_volume() 930 except Exception as e: --> 931 logger.debug("".join(traceback.format_exception(e))) 932 raise ValueError( 933 "No Series in the E2E file can be parsed to a an EyeVolume object. You might be able to extract information manually from the E2ESeries objects (e2ereader.series)" 934 ) TypeError: format_exception() missing 2 required positional arguments: 'value' and 'tb'
The problem here was that the signature of the format_exception function changed in python 3.10. This is fixed now with eyepy 0.9.2 and also works for python < 3.10
Description
I tried to read my e2e file but I get the following error:
What I Did
response:
I tried also:
response:
What might be the problem??