Skip to content

Commit

Permalink
docs(README-and-Cookbook): clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
Oli4 committed Feb 10, 2023
1 parent c2647c6 commit 183b317
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 132 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}

Documentation:
needs: Release
permissions:
contents: write
runs-on: ubuntu-latest
Expand All @@ -39,5 +40,6 @@ jobs:
mkdocs-gen-files \
mkdocs-literate-nav \
mkdocs-section-index \
pymdown-extensions
pymdown-extensions \
eyepie
- run: mkdocs gh-deploy --force
98 changes: 11 additions & 87 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# eyepy

[![Documentation](https://img.shields.io/badge/docs-eyepy-blue)](https://MedVisBonn.github.io/eyepy)
[![PyPI version](https://badge.fury.io/py/eyepie.svg)](https://badge.fury.io/py/eyepie)
[![DOI](https://zenodo.org/badge/292547201.svg)](https://zenodo.org/badge/latestdoi/292547201)

Here you can find the [Documentation](https://www.MedVisBonn.github.io/eyepy)
The `eyepy` python package provides a simple interface to import and process OCT volumes. Everything you import with one of our import functions becomes an `EyeVolume` object which provides a unified interface to the data. The `EyeVolume` object provides methods to plot the localizer image and B-scans as well as to compute and plot quantifications of voxel annotations such as drusen. Check out the [documentation](https://MedVisBonn.github.io/eyepy), especially the [Cookbook](https://medvisbonn.github.io/eyepy/cookbook/) chapter, for more information.

## Features

* Import HEYEY E2E, VOL and XML exports
Expand All @@ -21,100 +23,22 @@ Here you can find the [Documentation](https://www.MedVisBonn.github.io/eyepy)
### Installation
To install the latest version of eyepy run `pip install -U eyepie`. It is `eyepie` and not `eyepy` for installation with pip.

### Import Data
When you don't hava a supported OCT volume at hand you can check out our sample dataset to get familiar with `eyepy`.
```python
import eyepy as ep
# Import HEYEX XML export
ev = ep.data.load("drusen_patient")
```

`eyepy` currently supports the HEYEX E2E, XML and VOL export format. Support for additional formats is easy to implement.

```python
import eyepy as ep
# Import HEYEX VOL export
ev = ep.import_heyex_e2e("path/to/file.e2e")
# Import HEYEX XML export
ev = ep.import_heyex_xml("path/to/folder")
# Import HEYEX VOL export
ev = ep.import_heyex_vol("path/to/file.vol")
```

When only B-scans exist in a folder `eyepy` might still be able to import them. B-scans are expected to be ordered and distributed with equal distance on a quadratic area.
```python
import eyepy as ep
# Import B-scans from folder
ev = ep.import_bscan_folder("path/to/folder")
```

Public OCT datasets often have their own data formats. `eyepy` can already import volumes from two of the biggest OCT datasets.
```python
import eyepy as ep
# Import DUKE volume
ev = ep.import_duke_mat("path/to/volume.mat")
# Import RETOUCH volume
ev = ep.import_retouch("path/to/folder")
```

### The EyeVolume Object
When `eyepy` imports OCT data, it always returns an EyeVolume object. This object provides a unified interface to data imported from various sources.

You can use this object to perform common actions on the OCT volume such as:

+ Access Meta information from the loaded data if available `ev.meta`
+ Access an associated localizer image `ev.localizer`. When no localizer image is available `eyepy` generates one using a mean projection.
+ Access associated layer voxel and A-scan annotations
+ Plot annotated localizer image associated to the volume `ev.plot()`
+ Iterate over the volume to retrieve EyeBscan objects `for bscan in ev:`

### Compute Drusen and Quantify
Here we compute and quantify drusen for our sample data which has manual layer annotations for BM and RPE.

In the resulting plot on the left, the scale is the drusen height in voxel and on the right, the drusen volume in mm³

```python
import eyepy.core.utils
import matplotlib.pyplot as plt
import eyepy as ep

# Import example data
ev = ep.data.load("drusen_patient")
drusen_map = eyepy.core.utils.drusen(ev.layers["RPE"], ev.layers["BM"], ev.shape, minimum_height=2)
ev.add_voxel_annotation(drusen_map, name="drusen")

fig, axes = plt.subplots(1, 2, figsize=(5, 10))

# Configure quantification grid for drusen quantification
ev.volume_maps["drusen"].radii = [1.5, 2.5]
ev.volume_maps["drusen"].n_sectors = [4, 8]
ev.volume_maps["drusen"].offsets = [0, 45]

# Plot drusen projection and quantification
ev.plot(ax=axes[0], projections=["drusen"])
ev.plot(ax=axes[1], quantification="drusen")
axes[0].axis("off")
axes[1].axis("off")
```
![Example quantification](examples/drusen_quantification.jpeg)

To access the quantification as a dictionary use `ev.volume_maps["drusen"].quantification`

### Interact with individual B-scans
If you index into an EyeVolume object you get EyeBscan objects.

```python
import numpy as np

fig, ax = plt.subplots(1,1, figsize=(9,3))
bscan = ev[40]
bscan.plot(layers=["BM", "RPE"], areas=["drusen"], region=np.s_[150:300, :], ax=ax)
ax.axis("off")
```

![Example quantification](examples/bscan_visualization.jpeg)

# Related Projects:

+ [OCT-Converter](https://github.com/marksgraham/OCT-Converter): Extract raw optical coherence tomography (OCT) and fundus data from proprietary file formats. (.fds/.fda/.e2e/.img/.oct/.dcm)
+ [eyelab](https://github.com/MedVisBonn/eyelab): A GUI for annotation of OCT data based on eyepy
+ Projects by the [Translational Neuroimaging Laboratory](https://github.com/neurodial)
+ [LibOctData](https://github.com/neurodial/LibOctData)
+ [LibE2E](https://github.com/neurodial/LibE2E)
+ [OCT-Marker](https://github.com/neurodial/OCT-Marker)
+ [UOCTE](https://github.com/TSchlosser13/UOCTE) Unofficial continuation of https://bitbucket.org/uocte/uocte
+ [OCTAnnotate](https://github.com/krzyk87/OCTAnnotate)
+ [heyexReader](https://github.com/ayl/heyexReader)
+ [OCTExplorer](https://www.iibi.uiowa.edu/oct-reference) Iowa Reference Algorithm
126 changes: 109 additions & 17 deletions docs/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,44 @@ Here you learn how to use eyepy to perform common tasks.

## Import OCT data

Currently `eyepy` supports the HEYEX E2E, VOL and XML formats, as well as reading data from several public OCT datasets. It is recommended to use one of the following functions to import OCT data into `eyepy`:
Currently `eyepy` supports the HEYEX E2E, VOL and XML formats, as well as reading data from several public OCT datasets. All functions return a single `EyeVolume` object representing the data. E2E files may contain several OCT volumes which you can retrieve by setting the parameter `single` to `False`. While you can use Reader objects to parse the data and access specific information, it is recommended to use the provided import functions to get `EyeVolume` object which are a convenient interface to the data that provides a unified interface to data imported from various sources.

``` python
```python
import eyepy as ep

eye_volume = ep.import_bscan_folder("path/to/folder")
eye_volume = ep.import_duke_mat("path/to/file.mat")
eye_volume = ep.import_retouch("path/to/file.txt")
eye_volume = ep.import_heyex_xml("path/to/file.xml")
eye_volume = ep.import_heyex_vol("path/to/file.vol")
eye_volume = ep.import_heyex_e2e("path/to/file.e2e", single=True)
# Import HEYEX E2E export
ev = ep.import_heyex_e2e("path/to/file.e2e", single=True)
# Import HEYEX XML export
ev = ep.import_heyex_xml("path/to/folder")
# Import HEYEX VOL export
ev = ep.import_heyex_vol("path/to/file.vol")
```

All functions return a single `EyeVolume` object representing the data. E2E files may contain several oct volumes which you can retrieve by setting the parameter `single` to `False`:

!!! Warning "Missing scale information"
For the E2E file the scale of both localizer axes as well as the B-scan x-axes has not been identified yet and is hardcoded. When importing B-scans from folders there is also no scale information available.

When only B-scans exist in a folder `eyepy` might still be able to import them. B-scans are expected to be ordered and distributed with equal distance on a quadratic area.

```python
import eyepy as ep
# Import B-scans from folder
ev = ep.import_bscan_folder("path/to/folder")
```

Public OCT datasets often have their own data formats. `eyepy` can import volumes from the [AMD Dataset from Duke University](https://people.duke.edu/~sf59/RPEDC_Ophth_2013_dataset.htm) and the [RETOUCH Challenge](https://retouch.grand-challenge.org/).

```python
import eyepy as ep
# Import DUKE volume
ev = ep.import_duke_mat("path/to/volume.mat")
# Import RETOUCH volume
ev = ep.import_retouch("path/to/folder")
```

## Plot Localizer

Most OCT volumes come with a localizer image. This image can be plotted using the `plot` method of the `EyeVolume` object:

``` python
```python
eye_volume.plot()
```

Expand All @@ -50,24 +64,102 @@ eye_volume[0].plot(layers=["BM", "RPE"])

The plotting function is documented here: [EyeBscan.plot][eyepy.core.EyeBscan.plot]

## Modify Annotations

### Compute Drusen from Layer Annotations

Here we compute drusen for our sample data which has manual layer annotations for BM and RPE.

``` python
import eyepy.core.utils
import eyepy as ep

# Import example data
ev = ep.data.load("drusen_patient")
# Compute drusen
drusen_map = eyepy.core.utils.drusen(ev.layers["RPE"], ev.layers["BM"], ev.shape, minimum_height=2)
```

### Add / Remove Layer Annotations
Often OCT volumes come with layer annotations. They are added to the `EyeVolume` object during the import, but you can also manipulate them yourself using the `add_layer_annotation` and `remove_layer_annotation` methods. The following code can be used to layer annotations to `EyeVolume` objects. The `name` parameter is used to identify the layer.

```python
layer_heights = np.zeros(np.array(ev.shape)[[0,2]])
ev.add_layer_annotation(layer_heights, name="new_layer")
```

To remove a layer annotation use the `remove_layer_annotation` method. The following code removes the layer annotation we added above.

```python
ev.remove_layer_annotation("new_layer")
```

### Add / Remove Voxel Annotaitons
If you want to add voxel annotations to the EyeVolume object you can use the `add_voxel_annotation` method. The following code adds the drusen map we computed above to the EyeVolume object. The `name` parameter is used to identify the annotation.

```python
ev.add_voxel_annotation(drusen_map, name="drusen")
```

To remove an annotation use the `remove_voxel_annotation` method. The following code removes the drusen annotation we added above.

```python
ev.remove_voxel_annotation("drusen")
```

### ETDRS and Custom Quantification Grids

Quantifications on circular grids such as the ETDRS grid are common to quantify imaging data of the eye. With `eyepy` you can easily compute quantifications on such grids. The following code computes a quantification grid for the drusen annotation we added above.

```python
fig, axes = plt.subplots(1, 2, figsize=(5, 10))

# Configure quantification grid for drusen quantification
ev.volume_maps["drusen"].radii = [1.5, 2.5]
ev.volume_maps["drusen"].n_sectors = [4, 8]
ev.volume_maps["drusen"].offsets = [0, 45]

# Plot drusen projection and quantification
ev.plot(ax=axes[0], projections=["drusen"])
ev.plot(ax=axes[1], quantification="drusen")
axes[0].axis("off")
axes[1].axis("off")
```

The result looks like this: On the left, the scale is the drusen height in voxel and on the right, the drusen volume in mm³

![Example quantification](https://user-images.githubusercontent.com/5720058/218107881-841c224a-ca1c-465f-ab42-7aa3726fb991.jpeg)

To access the quantification as a dictionary use `ev.volume_maps["drusen"].quantification`

### Interact with individual B-scans
If you index into an EyeVolume object you get EyeBscan objects. Annotations you added to the respective `EyeVolume` object are also available in the `EyeBscan` object and can be visualized easily. The following code plots the 40th B-scan of the volume together with the layer annotations for BM and RPE and the computed drusen annotation:

```python
import numpy as np

fig, ax = plt.subplots(1,1, figsize=(9,3))
bscan = ev[40]
bscan.plot(layers=["BM", "RPE"], areas=["drusen"], region=np.s_[150:300, :], ax=ax)
ax.axis("off")
```

![B-scan visualization](https://user-images.githubusercontent.com/5720058/218107633-fdb51f92-7415-4673-aef5-f8cbedda970e.jpeg)

<!---
## Access Meta data
## Modify Annotations
### Add / Remove Layer Annotations
### Add / Remove Voxel Annotaitons
### Add / Remove A-scan Annotations
### Add / Remove Shape Annotations
### Compute Drusen from Layer Annotations
## Quantify Annotations
### ETDRS and Custom Quantification Grids
### Map between Localizer and OCT space
Expand Down
4 changes: 2 additions & 2 deletions docs/gen_ref_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
for path in sorted(Path("src/eyepy").rglob("*.py")): #
if str(path) in excluded:
continue
print(path)
#print(path)
module_path = path.relative_to(".").with_suffix("") #
doc_path = path.relative_to(".").with_suffix(".md") #
full_doc_path = Path("reference", doc_path) #
Expand All @@ -38,6 +38,6 @@

mkdocs_gen_files.set_edit_path(full_doc_path, path) #

print([x for x in nav.build_literate_nav()])
#print([x for x in nav.build_literate_nav()])
with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
nav_file.writelines(nav.build_literate_nav())
24 changes: 1 addition & 23 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,23 +1 @@
# Welcome to eyepy

## Installation
To install the latest version of eyepy run `pip install -U eyepie`. It is `eyepie` and not `eyepy` for installation with pip.

## Getting Started

## Development

### Add new file formats

## Related projects

+ [OCT-Converter](https://github.com/marksgraham/OCT-Converter): Extract raw optical coherence tomography (OCT) and fundus data from proprietary file formats. (.fds/.fda/.e2e/.img/.oct/.dcm)
+ [eyelab](https://github.com/MedVisBonn/eyelab): A GUI for annotation of OCT data
+ Projects by the [Translational Neuroimaging Laboratory](https://github.com/neurodial)
+ [LibOctData](https://github.com/neurodial/LibOctData)
+ [LibE2E](https://github.com/neurodial/LibE2E)
+ [OCT-Marker](https://github.com/neurodial/OCT-Marker)
+ [UOCTE](https://github.com/TSchlosser13/UOCTE) Unofficial continuation of https://bitbucket.org/uocte/uocte
+ [OCTAnnotate](https://github.com/krzyk87/OCTAnnotate)
+ [heyexReader](https://github.com/ayl/heyexReader)
+ [OCTExplorer](https://www.iibi.uiowa.edu/oct-reference) Iowa Reference Algorithm
--8<-- "README.md"
Binary file removed examples/bscan_visualization.jpeg
Binary file not shown.
Binary file removed examples/drusen_quantification.jpeg
Binary file not shown.
4 changes: 2 additions & 2 deletions src/eyepy/core/eyevolume.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ def add_voxel_annotation(self, voxel_map=None, meta=None, **kwargs):
self._volume_maps.append(voxel_annotation)
return voxel_annotation

def delete_voxel_annotations(self, name):
def remove_voxel_annotations(self, name):
"""
Args:
Expand Down Expand Up @@ -583,7 +583,7 @@ def add_layer_annotation(self, height_map=None, meta=None, **kwargs):
self._layers.append(layer_annotation)
return layer_annotation

def delete_layer_annotation(self, name):
def remove_layer_annotation(self, name):
"""
Args:
Expand Down

0 comments on commit 183b317

Please sign in to comment.