Skip to content

Commit

Permalink
feat(eyevolume): enable use of EyeVolume in eyelab
Browse files Browse the repository at this point in the history
  • Loading branch information
Oli4 committed Mar 25, 2022
1 parent c273e44 commit 8479628
Show file tree
Hide file tree
Showing 23 changed files with 543 additions and 241 deletions.
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ repos:
- id: trailing-whitespace
repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
- hooks:
- id: isort
args: ["--profile", "black"]
name: isort (python)
repo: https://github.com/pycqa/isort
rev: 5.10.1
- hooks:
- id: black
repo: https://github.com/psf/black
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

<!--next-version-placeholder-->

## v0.5.0 (2022-03-02)
### Feature
* **eyevolume.py:** Enable custom intensity transformation for OCT data ([`761dd5a`](https://github.com/MedVisBonn/eyepy/commit/761dd5a4a360ca46d79ff2074b2644b4c2fd8ca4))

## v0.4.1 (2022-02-17)
### Fix
* **EyeVolume:** Fix B-scan iteration; enable setting layer heights from EyeBscan ([`f982d68`](https://github.com/MedVisBonn/eyepy/commit/f982d687a7834922866377b70a755e253c785880))
Expand Down
49 changes: 49 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!

cff-version: 1.2.0
title: eyepy
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- given-names: Olivier
family-names: Morelle
email: oli4morelle@gmail.com
orcid: 'https://orcid.org/0000-0001-6404-2726'
affiliation: >-
University of Bonn - Bonn-Aachen International
Center for Information Technology (b-it) /
University Hospital Bonn - Department of
Ophthalmology
identifiers:
- type: url
value: 'https://github.com/MedVisBonn/eyepy/tree/v0.5.0'
repository-code: 'https://github.com/MedVisBonn/eyepy/tree/v0.5.0'
abstract: >-
Eye imaging data from different device vendors
often come in different file formats. Additionally,
public datasets often have their own way of storing
data for distribution. The lack of documentation
for many device-specific formats is a problem
already when working with a single data source
while the pure amount of different formats slows
down everyone trying to use data from multiple
sources.
This Python package aims at providing a unified
interface to a wide variety of eye imaging formats
and the execution of standard tasks such as data
visualization and quantification on ETDRS grids.
Thereby, it enables the development of
device-independent software and algorithms.
keywords:
- OCT
- Drusen
- Heidelberg
- HEYEX
- ETDRS
license: MIT
version: v0.5.0
date-released: '2022-03-02'
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pin the version in your project requirements.
* Quantify voxel annotations on a customizable circular grid
* Plot annotated localizer
* Plot annotated B-scans
* Save and load EyeVolume objects

## Getting Started

Expand Down Expand Up @@ -81,9 +82,9 @@ import eyepy as ep
# Import example data
ev = ep.data.load("drusen_patient")
drusen_map = ep.drusen(ev.layers["RPE"], ev.layers["BM"], ev.shape, minimum_height=2)
ev.set_volume_map("drusen", drusen_map)
ev.add_voxel_annotation(drusen_map, name="drusen")

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

# Configure quantification grid for drusen quantification
ev.volume_maps["drusen"].radii = [1.5, 2.5]
Expand Down Expand Up @@ -117,3 +118,4 @@ ax.axis("off")
# 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
21 changes: 9 additions & 12 deletions eyepy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,25 @@

__author__ = """Olivier Morelle"""
__email__ = "oli4morelle@gmail.com"
__version__ = "0.4.1"
__version__ = "0.5.0"

from eyepy.core import (
EyeEnface,
EyeVolume,
EyeBscan,
EyeData,
EyeVolumeMeta,
EyeBscanLayerAnnotation,
EyeBscanMeta,
EyeEnface,
EyeEnfaceAreaAnnotation,
EyeEnfaceMeta,
EyeVolumeVoxelAnnotation,
EyeVolume,
EyeVolumeLayerAnnotation,
EyeVolumeMeta,
EyeVolumeVoxelAnnotation,
)

from eyepy.io import (
import_heyex_xml,
import_heyex_vol,
import_bscan_folder,
import_duke_mat,
import_heyex_vol,
import_heyex_xml,
import_retouch,
)

from eyepy.quantification import drusen

import eyepy.data as data
23 changes: 19 additions & 4 deletions eyepy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,22 @@
line_kwargs = {"linewidth": 0.2, "linestyle": "-", "color": "green"}

# Colors for different Layers
# x = sns.color_palette("husl", 17)
# color_palette = sns.color_palette(x[::3] + x[1::3] + x[2::3])
# layer_colors = {key: color_palette[value] for key, value in SEG_MAPPING.items()}
layer_colors = defaultdict(lambda: "red")
_layer_colors = {
"BM": "F77189",
"RPE": "97A431",
"PR1": "36ADA4",
"EZ": "36ADA4",
"PR2": "A48CF4",
"IZ": "A48CF4",
"ELM": "E68332",
"ONL": "50B131",
"OPL": "38AABF",
"INL": "E866F4",
"IPL": "BB9832",
"GCL": "34AF84",
"RNFL": "3BA3EC",
"NFL": "3BA3EC",
"ILM": "F668C2",
}

layer_colors = defaultdict(lambda: "FF0000", **_layer_colors)
9 changes: 4 additions & 5 deletions eyepy/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# from annotations import Annotation, LayerAnnotation
from .eyemeta import EyeVolumeMeta, EyeBscanMeta, EyeEnfaceMeta
from .eyebscan import EyeBscan
from .eyedata import EyeData
from .eyevolume import EyeVolume, EyeVolumeVoxelAnnotation, EyeVolumeLayerAnnotation
from .eyeenface import EyeEnface
from .eyebscan import EyeBscan, EyeBscanLayerAnnotation
from .eyeenface import EyeEnface, EyeEnfaceAreaAnnotation
from .eyemeta import EyeBscanMeta, EyeEnfaceMeta, EyeVolumeMeta
from .eyevolume import EyeVolume, EyeVolumeLayerAnnotation, EyeVolumeVoxelAnnotation
76 changes: 45 additions & 31 deletions eyepy/core/eyebscan.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,66 @@
import collections
from typing import List

import matplotlib.pyplot as plt
import numpy as np

from eyepy import config
import matplotlib.pyplot as plt
from eyepy.core.utils import DynamicDefaultDict


class EyeBscanLayers:
def __init__(self, eyebscan):
self.index = eyebscan.index
self.volume = eyebscan._volume
class EyeBscanLayerAnnotation:
def __init__(self, eyevolumelayerannotation, index):
self.eyevolumelayerannotation = eyevolumelayerannotation
self.volume = eyevolumelayerannotation.volume
self.index = index

def __getitem__(self, item):
return self.volume.layers[item].data[-(self.index + 1)]
@property
def name(self):
return self.eyevolumelayerannotation.meta["name"]

def __setitem__(self, key, value):
self.volume.layers[key].data[-(self.index + 1), :] = value
@name.setter
def name(self, value: str):
self.eyevolumelayerannotation.meta["name"] = value

@property
def data(self):
return self.eyevolumelayerannotation.data[-(self.index + 1), :]

@data.setter
def data(self, value):
self.eyevolumelayerannotation.data[-(self.index + 1), :] = value

@property
def knots(self):
return self.eyevolumelayerannotation.knots[self.index]

@knots.setter
def knots(self, value: List):
self.eyevolumelayerannotation.knots[self.index] = value


class EyeBscan:
def __init__(self, volume: "EyeVolume", index: int):
self.index = index
self._volume = volume
self.volume = volume

self._bscan_layers = EyeBscanLayers(self)
self.layers = DynamicDefaultDict(
lambda x: EyeBscanLayerAnnotation(self.volume.layers[x], self.index)
)
self.area_maps = DynamicDefaultDict(
lambda x: self.volume.volume_maps[x].data[self.index]
)

@property
def meta(self):
return self._volume.meta["bscan_meta"][self.index]
return self.volume.meta["bscan_meta"][self.index]

@property
def data(self):
return self._volume.data[self.index]

@property
def layers(self):
return self._bscan_layers
return self.volume.data[self.index]

@property
def ascan_maps(self):
return self._volume.ascan_maps[self.index]

@property
def area_maps(self):
return {
name: self._volume.volume_maps[name].data[self.index]
for name in self._volume.volume_maps.keys()
}
return self.volume.ascan_maps[self.index]

@property
def shape(self):
Expand Down Expand Up @@ -89,12 +104,12 @@ def plot(
if layers is None:
layers = []
elif layers is True:
layers = self.layers.keys()
layers = self.volume.layers.keys()

if areas is None:
areas = []
elif areas is True:
areas = self.area_maps.keys()
areas = self.volume.volume_maps.keys()

if ascans is None:
ascans = []
Expand Down Expand Up @@ -138,11 +153,10 @@ def plot(
cmap="Reds",
interpolation="none",
)

for layer in layers:
color = config.layer_colors[layer]

layer_data = self.layers[layer]
layer_data = self.layers[layer].data
# Adjust layer height to plotted region
layer_data = layer_data - region[0].start
# Remove layer if outside of region
Expand All @@ -153,7 +167,7 @@ def plot(

ax.plot(
layer_data,
color=color,
color="#" + color,
label=layer,
**layer_kwargs,
)
60 changes: 0 additions & 60 deletions eyepy/core/eyedata.py

This file was deleted.

0 comments on commit 8479628

Please sign in to comment.