Skip to content

Commit

Permalink
Merge branch 'master' into v4
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieuboudreau committed Nov 11, 2021
2 parents 5ab224f + 5596b72 commit 92f1013
Show file tree
Hide file tree
Showing 20 changed files with 104 additions and 31 deletions.
14 changes: 14 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
fail_on_warning: false

conda:
environment: docs/environment.yml
2 changes: 1 addition & 1 deletion AxonDeepSeg/download_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def download_tests(destination=None):
destination = convert_path(destination)
test_files_destination = destination / "__test_files__"

url_tests = "https://github.com/axondeepseg/data-testing/archive/refs/tags/r20210625.zip"
url_tests = "https://github.com/axondeepseg/data-testing/archive/refs/tags/r20210906b.zip"
files_before = list(Path.cwd().iterdir())

if (
Expand Down
18 changes: 13 additions & 5 deletions AxonDeepSeg/morphometrics/compute_morphometrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,19 @@ def get_axon_morphometrics(im_axon, path_folder=None, im_myelin=None, pixel_size
# Perimeter of axonmyelin instance (outer perimeter of myelin) in micrometers
axonmyelin_perimeter = prop_axonmyelin.perimeter * pixelsize

stats['myelin_thickness'] = myelin_thickness
stats['myelin_area'] = myelin_area
stats['axonmyelin_area'] = axonmyelin_area
stats['axonmyelin_perimeter'] = axonmyelin_perimeter
stats['gratio'] = (axon_diam / 2) / (axon_diam / 2 + myelin_thickness)
try:
stats['gratio'] = (axon_diam / 2) / (axon_diam / 2 + myelin_thickness)
stats['myelin_thickness'] = myelin_thickness
stats['myelin_area'] = myelin_area
stats['axonmyelin_area'] = axonmyelin_area
stats['axonmyelin_perimeter'] = axonmyelin_perimeter
except ZeroDivisionError:
print(f"ZeroDivisionError caught on invalid object #{idx}.")
stats['gratio'] = float('NaN')
stats['myelin_thickness'] = float('NaN')
stats['myelin_area'] = float('NaN')
stats['axonmyelin_area'] = float('NaN')
stats['axonmyelin_perimeter'] = float('NaN')

else:
print(
Expand Down
4 changes: 2 additions & 2 deletions AxonDeepSeg/morphometrics/launch_morphometrics_computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,10 @@ def main(argv=None):
try:
# Export to excel
if morph_filename.endswith('.xlsx'):
pd.DataFrame(x).to_excel(current_path_target.parent / morph_filename)
pd.DataFrame(x).to_excel(current_path_target.parent / morph_filename, na_rep='NaN')
# Export to csv
else:
pd.DataFrame(x).to_csv(current_path_target.parent / morph_filename)
pd.DataFrame(x).to_csv(current_path_target.parent / morph_filename, na_rep='NaN')

# Generate the index image
indexes_outfile = current_path_target.parent /(str(current_path_target.with_suffix("")) +
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Based on a convolutional neural network architecture. Pixels are classified as e

For more information, see the [documentation website](http://axondeepseg.readthedocs.io/).

![alt tag](https://github.com/neuropoly/axondeepseg/blob/master/docs/source/_static/fig0.png)
![alt tag](https://raw.githubusercontent.com/axondeepseg/doc-figures/main/index/fig0.png)



Expand Down
34 changes: 32 additions & 2 deletions ads_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import pandas as pd
import imageio

VERSION = "0.2.18"
VERSION = "0.2.19"

class ADSsettings:
"""
Expand All @@ -54,6 +54,7 @@ def __init__(self, ads_control):
self.use_custom_resolution = False # Unused
self.custom_resolution = 0.07 # Unused
self.zoom_factor = 1.0
self.axon_shape = "circle"

def on_settings_button(self, event):
"""
Expand All @@ -65,20 +66,44 @@ def on_settings_button(self, event):

# Add the overlap value to the settings menu
sizer_overlap_value = wx.BoxSizer(wx.HORIZONTAL)
overlap_value_tooltip = wx.ToolTip("Represents the number of pixels that overlap two patches of the image when "
"applying the prediction model")
sizer_overlap_value.Add(wx.StaticText(self.settings_frame, label="Overlap value (pixels): "))
self.overlap_value_spinCtrl = wx.SpinCtrl(self.settings_frame, min=0, max=100, initial=self.overlap_value)
self.overlap_value_spinCtrl.Bind(wx.EVT_SPINCTRL, self.on_overlap_value_changed)
self.overlap_value_spinCtrl.SetToolTip(overlap_value_tooltip)
sizer_overlap_value.Add(self.overlap_value_spinCtrl, flag=wx.SHAPED, proportion=1)
frame_sizer_h.Add(sizer_overlap_value)

# Add the zoom factor to the settings menu
sizer_zoom_factor = wx.BoxSizer(wx.HORIZONTAL)
zoom_factor_tooltip = wx.ToolTip("When applying the model, the pixel size of the image will be "
"multiplied by this number. The zoom factor does not affect the computation of morphometrics.")
sizer_zoom_factor.Add(wx.StaticText(self.settings_frame, label="Zoom factor: "))
self.zoom_factor_spinCtrlDouble = wx.SpinCtrlDouble(self.settings_frame, initial=self.zoom_factor, inc=0.0001)
self.zoom_factor_spinCtrlDouble.Bind(wx.EVT_SPINCTRLDOUBLE, self.on_zoom_factor_changed)
self.zoom_factor_spinCtrlDouble.SetToolTip(zoom_factor_tooltip)
sizer_zoom_factor.Add(self.zoom_factor_spinCtrlDouble, flag=wx.SHAPED, proportion=1)
frame_sizer_h.Add(sizer_zoom_factor)

# Add the axon shape selection
axon_shape_choices = ["circle", "ellipse"]
sizer_axon_shape = wx.BoxSizer(wx.HORIZONTAL)
axon_shape_tooltip = wx.ToolTip('Select what is the shape of the axons that will be considered when computing '
'the morphometrics. "circle" will use the equivalent diameter (diameter of a circle with the same area as the axon). '
'"ellipse" will use minor axis of a fitted ellipse as diameter.')
sizer_axon_shape.Add(wx.StaticText(self.settings_frame, label="Axon shape: "))
self.axon_shape_combobox = wx.ComboBox(
self.settings_frame,
choices=axon_shape_choices,
size=(100, 20),
value=self.axon_shape
)
self.axon_shape_combobox.Bind(wx.EVT_COMBOBOX, self.on_axon_shape_combobox_item_selected)
self.axon_shape_combobox.SetToolTip(axon_shape_tooltip)
sizer_axon_shape.Add(self.axon_shape_combobox, flag=wx.SHAPED, proportion=1)
frame_sizer_h.Add(sizer_axon_shape)

# Add the done button
sizer_done_button = wx.BoxSizer(wx.HORIZONTAL)
done_button = wx.Button(self.settings_frame, label="Done")
Expand All @@ -95,6 +120,9 @@ def on_overlap_value_changed(self, event):
def on_zoom_factor_changed(self, event):
self.zoom_factor = self.zoom_factor_spinCtrlDouble.GetValue()

def on_axon_shape_combobox_item_selected(self, event):
self.axon_shape = self.axon_shape_combobox.GetStringSelection()

def on_done_button(self, event):
# TODO: make sure every setting is saved
self.settings_frame.Close()
Expand Down Expand Up @@ -660,7 +688,9 @@ def on_compute_morphometrics_button(self, event):

# Compute statistics
stats_array, index_image_array = compute_morphs.get_axon_morphometrics(im_axon=pred_axon, im_myelin=pred_myelin,
pixel_size=pixel_size, return_index_image=True)
pixel_size=pixel_size,
axon_shape=self.settings.axon_shape,
return_index_image=True)
for stats in stats_array:

x = np.append(x,
Expand Down
10 changes: 10 additions & 0 deletions docs/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# File: docs/environment.yaml

name: docs
channels:
- conda-forge
- defaults
dependencies:
- sphinx==4.2.0
- sphinx_rtd_theme>=0.2.4
- recommonmark
Binary file removed docs/source/_static/GUI_image.png
Binary file not shown.
Binary file removed docs/source/_static/bad_mask_example.png
Binary file not shown.
Binary file removed docs/source/_static/fig0.png
Binary file not shown.
Binary file removed docs/source/_static/fig1.png
Binary file not shown.
Binary file removed docs/source/_static/fig2.png
Binary file not shown.
Binary file removed docs/source/_static/fig3.png
Binary file not shown.
Binary file removed docs/source/_static/good_mask_example.png
Binary file not shown.
Binary file removed docs/source/_static/image_example.png
Binary file not shown.
17 changes: 11 additions & 6 deletions docs/source/documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ Once your virtual environment is installed and activated, install the AxonDeepSe
.. WARNING :: When re-installing the application, the ``default_SEM_model``, ``default_TEM_model`` and ``model_seg_pns_bf`` folders in ``AxonDeepSeg/models`` will be deleted and re-downloaded. Please do not store valuable data in these folders.
.. raw:: html

<iframe width="700" height="394" src="https://www.youtube.com/embed/7RwZihR0HWk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>



Testing the installation
------------------------
.. WARNING :: Ensure that the virtual environment is activated.
Expand Down Expand Up @@ -133,7 +139,7 @@ Graphical User Interface (GUI) (optional)

AxonDeepSeg can be run via a Graphical User Interface (GUI) instead of the Terminal command line. This GUI is a plugin for the software `FSLeyes <https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FSLeyes>`_. Beyond the convenience of running AxonDeepSeg with the click of a button, this GUI is also an excellent way to manually correct output segmentations (if need to).

.. image:: _static/GUI_image.png
.. image:: https://raw.githubusercontent.com/axondeepseg/doc-figures/main/introduction/GUI_image.png

Launch FSLeyes ::

Expand All @@ -150,8 +156,7 @@ In case, you find trouble installing FSLeyes plugin for ADS you could refer the

.. raw:: html

<div style="position: relative; padding-bottom: 5%; height: 0; overflow: hidden; max-width: 100%; height: auto;">
<iframe width="700" height="394" src="https://www.youtube.com/embed/qzWeG5vaVyo" frameborder="0" allowfullscreen></iframe>
<iframe width="700" height="394" src="https://www.youtube.com/embed/ImElcp9_k6Y" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


.. NOTE :: For some users, the ADScontrol tab will not appear after first installing the plugin.
Expand Down Expand Up @@ -560,21 +565,21 @@ These options and detailed procedures are described in the section below "Manual

Here are examples of an image, a good manual mask and a bad manual mask.

.. figure:: _static/image_example.png
.. figure:: https://raw.githubusercontent.com/axondeepseg/doc-figures/main/introduction/image_example.png
:width: 750px
:align: center
:alt: Image example

Image example

.. figure:: _static/good_mask_example.png
.. figure:: https://raw.githubusercontent.com/axondeepseg/doc-figures/main/introduction/good_mask_example.png
:width: 750px
:align: center
:alt: Good manual mask example

Good manual mask example

.. figure:: _static/bad_mask_example.png
.. figure:: https://raw.githubusercontent.com/axondeepseg/doc-figures/main/introduction/bad_mask_example.png
:width: 750px
:align: center
:alt: Bad manual mask example
Expand Down
8 changes: 4 additions & 4 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Welcome to the AxonDeepSeg framework. In this site you will find the documentati

AxonDeepSeg is a segmentation software for microscopy data of nerve fibers. It is based on a convolutional neural network.

.. image:: _static/fig0.png
.. image:: https://raw.githubusercontent.com/axondeepseg/doc-figures/main/index/fig0.png
:scale: 35 %
:alt: alternate text
:align: center
Expand All @@ -19,7 +19,7 @@ AxonDeepSeg is a segmentation software for microscopy data of nerve fibers. It i
* AxonDeepSeg pipeline:

.. image:: _static/fig1.png
.. image:: https://raw.githubusercontent.com/axondeepseg/doc-figures/main/index/fig1.png
:scale: 35 %
:alt: alternate text
:align: center
Expand All @@ -28,7 +28,7 @@ AxonDeepSeg is a segmentation software for microscopy data of nerve fibers. It i
* Segmentation of axon/myelin from various species/contrasts:

.. image:: _static/fig2.png
.. image:: https://raw.githubusercontent.com/axondeepseg/doc-figures/main/index/fig2.png
:scale: 35 %
:alt: alternate text
:align: center
Expand All @@ -37,7 +37,7 @@ AxonDeepSeg is a segmentation software for microscopy data of nerve fibers. It i
* Segmentation of full slice histology and morphometrics extraction:

.. image:: _static/fig3.png
.. image:: https://raw.githubusercontent.com/axondeepseg/doc-figures/main/index/fig3.png
:scale: 35 %
:alt: alternate text
:align: center
Expand Down
5 changes: 0 additions & 5 deletions readthedocs.yml

This file was deleted.

5 changes: 0 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ def run(self):
'models/default_TEM_model/*',
'data_test/*'],
},
extras_require={
'docs': ['sphinx>=1.6',
'sphinx_rtd_theme>=0.2.4',
'recommonmark'],
},
include_package_data=True,
entry_points={
'console_scripts': [
Expand Down
16 changes: 16 additions & 0 deletions test/morphometrics/test_compute_morphometrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def setup(self):
pred_myelin_path = self.test_folder_path / ('image' + str(myelin_suffix))
self.pred_myelin = imageio_imread(pred_myelin_path, as_gray=True)

# Image to test NaN output
bad_pred_axon_path = self.test_folder_path / ('invalid_gratio' + str(axon_suffix))
self.bad_pred_axon = imageio_imread(bad_pred_axon_path, as_gray=True)
bad_pred_myelin_path = self.test_folder_path / ('invalid_gratio' + str(myelin_suffix))
self.bad_pred_myelin = imageio_imread(bad_pred_myelin_path, as_gray=True)

# simulated image of axon myelin where axon shape is circle; `plane angle = 0`
self.path_sim_image_circle = (
self.testPath /
Expand Down Expand Up @@ -175,6 +181,16 @@ def test_get_axon_morphometrics_with_myelin_mask_with_axon_as_ellipse(self):

assert stats_array[1]['gratio'] == pytest.approx(0.78, rel=0.01)

@pytest.mark.unit
def test_get_axon_morphometrics_with_invalid_gratio_with_axon_as_ellipse(self):
stats_array = get_axon_morphometrics(
self.bad_pred_axon,
str(self.test_folder_path),
im_myelin=self.bad_pred_myelin,
axon_shape=self.axon_shape
)
assert np.isnan(stats_array[0]['gratio'])

@pytest.mark.unit
def test_get_axon_morphometrics_with_myelin_mask_simulated_axons(self):
path_pred = (
Expand Down

0 comments on commit 92f1013

Please sign in to comment.