Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Porting hazen to python 3.9 #143

Merged
merged 72 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
a214d3d
Adds WIP miniforge environment.yml - builds hazen in 3.9, but 23 test…
tomaroberts Nov 15, 2021
add45f8
Fix .astype(int) causing error
tomaroberts Nov 15, 2021
7dc6c48
Remove superfluous commented lines.
tomaroberts Nov 15, 2021
866dc22
Alters findContours syntax - spatial_resolution function now outputs
tomaroberts Nov 15, 2021
67f03cf
Adds env.yml for testing matplotlib version
tomaroberts Nov 19, 2021
3f65bb7
Adds env.yml for testing old opencv version
tomaroberts Nov 19, 2021
bd14c80
Adds requirements.txt for testing Docker hazen3.6 with latest numpy
tomaroberts Nov 19, 2021
f241076
Correct for 90 degree rotation of square/rectangles
tomaroberts Nov 22, 2021
e4ca073
Amend tests to reflect 90 degree rotation of square/rectangles
tomaroberts Nov 22, 2021
30e2d10
Adjusts hazen code to account for cv.minAreaRect() changes between Op…
tomaroberts Nov 22, 2021
74e9d8f
Create tests_development_py3.9.yml
tomaroberts Nov 22, 2021
1629d08
Update test_spatial_resolution to reflect new OpenCV version values
tomaroberts Nov 22, 2021
a130416
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
c6a40f1
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
58a773c
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
ce38a1a
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
d486335
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
0dd3646
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
20db76a
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
141be57
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
4c37ef5
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
c8a5fe1
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
6832cb1
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
fab1169
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
75b3d73
Update tests_development_py3.9.yml
tomaroberts Nov 22, 2021
8862afe
Testing
tomaroberts Nov 22, 2021
06c7473
Testing
tomaroberts Nov 22, 2021
7c17690
Testing
tomaroberts Nov 22, 2021
eeac324
Test install any psycopg2-binary version
tomaroberts Nov 22, 2021
8394ae7
Try build with psycopg2-binary removed from env.yml
tomaroberts Nov 22, 2021
599c17c
Test install psycopg2-binary via pip section in env.yml
tomaroberts Nov 22, 2021
017a4bf
Testing
tomaroberts Nov 22, 2021
139a8b6
Adds default bash shell to .yml
tomaroberts Nov 22, 2021
dd348a9
Update werkzeug version for Actions testing
tomaroberts Nov 23, 2021
60ad5fd
Remove old tests_development.yml / replace with finalised python3.9 v…
tomaroberts Nov 23, 2021
f9aa2f0
wip: Updating README.md
tomaroberts Nov 23, 2021
cb3f154
Remove old WIP files
tomaroberts Nov 24, 2021
7ae24e6
Update requirements.txt - will not work on M1 / to be tested on other…
tomaroberts Nov 24, 2021
d25f0be
Bugfix: missing = sign
tomaroberts Nov 24, 2021
238d39b
Changed GitHub Actions to python 3.9
tomaroberts Nov 24, 2021
2ca01a3
Revert .astype(int) change
tomaroberts Nov 29, 2021
3cadc1d
Adds Ibn Al-Haytham image
tomaroberts Nov 29, 2021
f1b2f91
Revert "Revert .astype(int) change"
tomaroberts Nov 29, 2021
6fdf4fe
Add snr/ghosting example images to assets
tomaroberts Nov 29, 2021
4c4488a
Update README.md
tomaroberts Nov 29, 2021
9e6f7e5
Make horizontal lines consistent
tomaroberts Nov 29, 2021
ad6a085
Adds enhance version of Ibn
tomaroberts Nov 29, 2021
d62e953
setuptools & wheel required so opencv install works
tomaroberts Nov 29, 2021
1c47ba4
Revert "Adds enhance version of Ibn"
tomaroberts Dec 1, 2021
fc2f215
Adds assert_allclose to shape tests / removes round_tuple
tomaroberts Dec 6, 2021
8450728
Merge branch 'readme-dev' into hazen-3.9-miniforge
tomaroberts Dec 6, 2021
5ba9d59
Update Git workflows python to v3.9.7
tomaroberts Dec 6, 2021
06caf01
m2r package hotfix
tomaroberts Dec 6, 2021
a90cea1
m2r version change test
tomaroberts Dec 6, 2021
2156519
mistune<2.0.0 test
tomaroberts Dec 6, 2021
fa390d3
Changes workflows versions to python 3.9
tomaroberts Dec 7, 2021
c8e9d63
Update test_development.yml with os/python matrix
tomaroberts Dec 7, 2021
0f53026
Indentation fix
tomaroberts Dec 7, 2021
d990adf
Remove macos from matrix (for now)
tomaroberts Dec 7, 2021
408de45
Remove specific python version from env.yml
tomaroberts Dec 7, 2021
16d6610
python 3.9 - os.matrix test
tomaroberts Dec 7, 2021
f9adb8c
os.matrix python3.10 test
tomaroberts Dec 7, 2021
65c20cb
Adds auto-update-conda, remove client-env, base true
tomaroberts Dec 7, 2021
fd7949e
Remove conda – test workflows with python3.9
tomaroberts Dec 7, 2021
3e7612a
Try os.matrix macos / python versions
tomaroberts Dec 7, 2021
cef5417
Drop macos from tests
tomaroberts Dec 7, 2021
72064a9
Remove scikit / numpy exact versions
tomaroberts Dec 7, 2021
ca99266
Update cli-test / tests_release
tomaroberts Dec 7, 2021
4bca6b1
Adds pip list for debugging
tomaroberts Dec 7, 2021
043c278
Update requirements.txt with fixed versions
tomaroberts Dec 7, 2021
43ba38f
Update README.md – important addition of openssl instructions
tomaroberts Dec 7, 2021
1de9e1a
Merge branch 'release/0.5.0' into hazen-3.9-miniforge
tomaroberts Dec 7, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/cli-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Set up Python 3.6
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.6
python-version: 3.9.7
tomaroberts marked this conversation as resolved.
Show resolved Hide resolved

- name: Install dependencies
run: |
Expand Down
22 changes: 16 additions & 6 deletions .github/workflows/tests_development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ on:
jobs:
test:
runs-on: ubuntu-latest
defaults:
tomaroberts marked this conversation as resolved.
Show resolved Hide resolved
run:
shell: bash -l {0}
# Service containers to run with `container-job`
services:
# Label used to access the service container
Expand All @@ -30,20 +33,27 @@ jobs:
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- uses: actions/checkout@v2

- name: Set up Python 3.6
uses: actions/setup-python@v2
- name: Set up Conda
tomaroberts marked this conversation as resolved.
Show resolved Hide resolved
uses: conda-incubator/setup-miniconda@v2.1.1
with:
python-version: 3.6
activate-environment: anaconda-client-env
environment-file: environment.yml
python-version: 3.9.7
auto-activate-base: false

- name: Conda info
run: conda info

- name: Conda list
run: conda list

- name: Install dependencies
- name: Install flake8 & pytest
run: |
python -m pip install --upgrade pip
pip install flake8 pytest pytest-cov
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi

- name: Setup flake8 annotations
uses: rbialon/flake8-annotations@v1
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Set up Python 3.6
- name: Set up Python 3.9
tomaroberts marked this conversation as resolved.
Show resolved Hide resolved
uses: actions/setup-python@v2
with:
python-version: 3.6
python-version: 3.9.7

- name: Install dependencies
run: |
Expand Down
161 changes: 135 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,79 @@
<!-- PROJECT HEADING -->
<br />
<p align="center">
<a href="https://github.com/github_username/repo_name">
<img src="https://raw.githubusercontent.com/GSTT-CSC/gstt-csc.github.io/main/assets/transparent-CSC-logo-cropped.png" alt="Logo" width="40%">
</a>
<h1 align="center">Hazen</h1>
<img src="https://raw.githubusercontent.com/GSTT-CSC/hazen/readme-dev/docs/assets/ibn-al-haytham.jpeg" alt="Ibn Al-Haytham">
</p>
<h1 align="center">hazen</h1>
<p align="center">
Quality assurance framework for Magnetic Resonance Imaging
<br />
<a href="https://github.com/github_username/repo_name"><strong>Explore the docs »</strong></a>
<br />
<br />
<a href="https://github.com/GSTT-CSC/hazen">View repo</a>
·
<a href="https://github.com/GSTT-CSC/hazen/issues">Report Bug</a>
·
<a href="https://github.com/GSTT-CSC/hazen/issues">Request Feature</a>
Quality assurance framework for Magnetic Resonance Imaging
<br />
<a href="https://github.com/github_username/repo_name"><strong>Explore the docs »</strong></a>
<br />
<br />
<a href="https://github.com/GSTT-CSC/hazen">View repo</a>
·
<a href="https://github.com/GSTT-CSC/hazen/issues">Report Bug</a>
·
<a href="https://github.com/GSTT-CSC/hazen/issues">Request Feature</a>
</p>
<p align="center">
<img src="https://github.com/GSTT-CSC/hazen/actions/workflows/tests_release.yml/badge.svg?branch=master">
<img src="https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/laurencejackson/ba102d5f3e592fcd50451c2eff8a803d/raw/hazen_pytest-coverage-comment.json">
</p>
<p align="center">Please <b>STAR</b> this repo to receive updates about new versions of hazen!</p>

---

## Overview

---
hazen is a software framework for performing automated analysis of magnetic resonance imaging (MRI) Quality Assurance data.

It provides automatic quantitative analysis for the following measurements of MRI phantom data:
- Signal-to-noise ratio (SNR)
- Spatial resolution
- Slice position and width
- Uniformity
- Ghosting
- MR Relaxometry

Some example outputs from hazen:

## Usage
| hazen snr | hazen ghosting |
| ------------------ | ------------------------------- |
| ![](docs/assets/snr-example.png) | ![](docs/assets/ghosting-example.png) |

---
### Docker
To use the docker version of hazen simply run the `hazen-app` script in a terminal. Docker must be installed on the host
system for this method to work, see [docker installation instructions](https://docs.docker.com/engine/install).
For ease of use it is recommended to copy the hazen-app script to location accessible on the path such as `/usr/local/bin`
so you can run it from any location.

e.g.
## Installation

### Prerequisites

- Python v3.9
- conda is required for hazen installation on Mac M1 silicon (arm64 architecture)
- Git
- Docker

### Install

First, clone this repo, then follow the instructions for your operating system. To clone:
```bash
git clone [email protected]:GSTT-CSC/hazen.git
```

#### Docker

We recommend using the Docker version of hazen as it is easy to get up-and-running and is linked to the most stable release. Refer to the [Docker installation instructions](https://docs.docker.com/engine/install) to install Docker on your host computer.

For ease of use, it is recommended to copy the `hazen-app` script to a location accessible on the path such as `/usr/local/bin`. This will allow you to run hazen from any location on your computer. Then, to use Docker hazen, simply run the `hazen-app` script appended with the function you want to use (e.g.: `snr`).

In Terminal:

```bash
cd hazen
cp ./hazen-app /usr/local/bin

./hazen-app snr tests/data/snr/Siemens/
# run hazen
hazen-app snr tests/data/snr/Siemens/

latest: Pulling from gsttmriphysics/hazen
Digest: sha256:18603e40b45f3af4bf45f07559a08a7833af92a6efe21cb7306f758e8eeab24a
Expand All @@ -55,15 +87,92 @@ docker.io/gsttmriphysics/hazen:latest
'snr_subtraction_normalised_seFoV250_2meas_slice5mm_tra_repeat_PSN_noDC_2_1': 2154.69}
```

#### Linux & MacOS

For developers, hazen can be installed using `pip`. We highly recommend using a virtual environment.

```bash
# Go to local hazen repo directory
cd hazen

# Create and activate a virtual environment
python3 -m venv ./hazen-venv
source hazen-venv/bin/activate

# Install requirements
pip install --upgrade pip setuptools wheel
pip install -r requirements.txt

# Install hazen
python setup.py install

# Run tests to ensure everything is working
pytest tests/
```

#### MacOS using M1 processors (Apple silicon arm64 architecture)

For developers using MacOS with M1 processors, installation via `conda` is required. This is because various Python packages have not been updated to install with `pip`. Installation of hazen via conda is a temporary workaround.

```bash
# Download and install conda (we have tested with both Miniforge and Miniconda):
brew update
brew upgrade
brew install miniforge
tomaroberts marked this conversation as resolved.
Show resolved Hide resolved

# Create and activate virtual environment
conda create --name hazen-venv --file environment.yml
conda activate hazen-venv

# Build hazen
cd hazen
python setup.py install

# Run tests to ensure everything is working
pytest tests/
```

---

## Usage

### Command Line

The CLI version of hazen is designed to be pointed at single folders containing DICOM file(s). Example datasets are provided in the `tests/data/` directory. If you are not using the Docker version of hazen, replace `hazen-app` with `hazen` in the following commands.

To perform an SNR measurement on the provided example Philips DICOMs:

`hazen-app snr tests/data/snr/Philips`

To perform a spatial resolution measurement on example data provided by the East Kent Trust:

`hazen-app spatial_resolution tests/data/resolution/philips`

To see the full list of available tools, enter:

`hazen-app -h`

The `--report` option provides additional information for some of the functions. For example, the user can gain additional insight into the performance of the snr function by entering:

`hazen-app snr tests/data/snr/Philips --report`

### Web Interface

WIP: we are developing a web interface for hazen.

---

## Contributors

Read CONTRIBUTING.md
If you want to contribute to the development of hazen, please take a look at: `CONTRIBUTING.md`.

---

## Users

Nothing to see here. Maybe see hazen/docs.
Please [raise an Issue](https://github.com/GSTT-CSC/hazen/issues) if you have any problems installing or running hazen.

We have used hazen with MRI data from a handful of different MRI scanners, including multiple different vendors. If your MRI data doesn't work with hazen, or the results are unexpected, please submit an Issue and we will investigate.



---
2 changes: 2 additions & 0 deletions contributors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ Dika Vilic
Elizabeth Gabriel
Jane Ansell
Neil Heraghty
Tom Roberts
Lucrezia Cester
Haris Shuaib
Binary file added docs/assets/ghosting-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/ibn-al-haytham.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/snr-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: hazen-3.9-miniforge
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this file do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, shouldn't have spread between two places. Bit more context in #142.

Basically, installing some of our libraries (mainly scikit-image) doesnt work on M1 silicon via pip. So instead I made the venv in miniforge (just a smaller version of conda). The environment.yml is the equivalent to our normal requirements.txt.

I specifically made most of it install via pip anyway, so it was easy to see which ones work via pip and which only work via conda.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for being a bit slow here. But doesn't the dockerisation of Hazen make this issue a moot point?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I should have made this comment on the issue when you first raised it.


dependencies:
- pip #=21.3.1
- python=3.9.7
- scikit-image=0.18.3
- pip:
- SQLAlchemy_Utils==0.33.11
- alembic==1.0.10
- celery==4.3.1
- numpy==1.21.0
- Werkzeug==0.15.6
- opencv-python==4.5.4.58
- Flask_Migrate==2.5.1
- pytest>=6.2
- coverage>=6.0.2
- Flask_Moment==0.7.0
- flask_heroku==0.1.9
- Flask==1.0.3
- pydicom==1.4.1
- WTForms==2.2.1
- Flask_Dropzone==1.5.3
- Flask_Mail==0.9.1
- Flask_Login==0.4.1
- Flask_Bootstrap4==4.0.2
- imutils==0.5.3
- matplotlib==3.4.3
- Flask_SQLAlchemy==2.4.0
- scipy==1.7.2
- docopt==0.6.2
- SQLAlchemy==1.3.3
- Flask_WTF==0.14.2
- flask_bootstrap==3.3.7.1
- PyJWT==1.7.1
- gunicorn==19.9.0
- psycopg2-binary==2.8.4
- arrow==0.13.2
- amqp==2.4.2
- sphinxcontrib-needs==0.4.3
- sphinxcontrib-napoleon
- sphinx_rtd_theme
- m2r
- python-dateutil
- email_validator
- colorlog

2 changes: 1 addition & 1 deletion hazenlib/slice_width.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ def get_initial_trapezoid_fit_and_coefficients(profile, slice_thickness):
n_ramp = 47
n_plateau = 55

trapezoid_centre = round(np.median(np.argwhere(profile < np.mean(profile)))).astype(int)
trapezoid_centre = int(round(np.median(np.argwhere(profile < np.mean(profile)))))
tomaroberts marked this conversation as resolved.
Show resolved Hide resolved

n_total = len(profile)
n_left_baseline = int(trapezoid_centre - round(n_plateau / 2) - n_ramp - 1)
Expand Down
14 changes: 12 additions & 2 deletions hazenlib/spatial_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,25 @@ def thresh_image(img, bound=150):


def find_square(img):
cnts = cv.findContours(img.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
cnts = cv.findContours(img.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)[0]
hshuaib90 marked this conversation as resolved.
Show resolved Hide resolved

for c in cnts[1]:
for c in cnts:
perimeter = cv.arcLength(c, True)
approx = cv.approxPolyDP(c, 0.1 * perimeter, True)
if len(approx) == 4:
# compute the bounding box of the contour and use the
# bounding box to compute the aspect ratio
rect = cv.minAreaRect(approx)

# OpenCV 4.5 adjustment
# - cv.minAreaRect() output tuple order changed since v3.4
# - swap rect[1] order & rotate rect[2] by -90
# – convert tuple>list>tuple to do this
rectAsList = list(rect)
rectAsList[1] = (rectAsList[1][1], rectAsList[1][0])
rectAsList[2] = rectAsList[2] - 90
rect = tuple(rectAsList)

box = cv.boxPoints(rect)
box = np.int0(box)
w, h = rect[1]
Expand Down
5 changes: 5 additions & 0 deletions hazenlib/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ def get_shape(self, shape):

if shape == 'rectangle' or shape == 'square':
(x,y), size, angle = cv.minAreaRect(contour)
# OpenCV v4.5 adjustment
# - cv.minAreaRect() output tuple order changed since v3.4
# - swap size order & rotate angle by -90
size = (size[1], size[0])
angle = angle-90
return (x,y), size, angle


Expand Down
Loading