Skip to content

Commit

Permalink
Serve deeplasia via flask (#1)
Browse files Browse the repository at this point in the history
* Implement basic flask app

* Update docker and adopt waitress for serving

* Add docs for API usage

* Add devcontainer configuration

* Add OpenAPI specification

* Add error responses to api specification

* Serve api spec file

* Reduce image size by 1GB and improve build time

* Update name of x-ray image property

* Add dependabot

* Add docker build workflow

* Update README.md

* Add html file to display OpenAPI spec

---------

Co-authored-by: Sebastian Rassmann <[email protected]>
  • Loading branch information
ChristophB and sRassmann authored Nov 7, 2023
1 parent 3e74b19 commit cce5886
Show file tree
Hide file tree
Showing 32 changed files with 779 additions and 1,610 deletions.
21 changes: 21 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "Python 3.9",
"image": "mcr.microsoft.com/devcontainers/python:3.9",
"runArgs": [
// "--gpus",
// "all",
"--env-file",
".devcontainer/.env"
],
"postCreateCommand": "pip install -r requirements.txt",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-azuretools.vscode-docker",
"mhutchie.git-graph",
"42Crunch.vscode-openapi"
]
}
}
}
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.devcontainer
models
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
38 changes: 38 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Build Docker Image

on:
workflow_dispatch:
release:
types: [created]

jobs:
build-and-publish-docker-image:
name: Build Docker image and publish to GitHub Container Registry
runs-on: ubuntu-latest
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,5 @@ modesl/*

/imgs/*
/models/*

*.env
19 changes: 9 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
FROM python:3.9
FROM python:3.9-slim

COPY . /app
ENV DEEPLASIA_THREADS=4

WORKDIR /app
# If your company uses a self-signed CA:
# ENV PIP_TRUSTED_HOST=download.pytorch.org

RUN ["apt-get", "update"]
RUN ["apt-get", "-y", "install", "vim"]
WORKDIR /app

#Install necessary packages from requirements.txt with no cache dir allowing for installation on machine with very little memory on board
COPY requirements.txt /app/.
RUN pip install -r requirements.txt

#Exposing the default streamlit port
COPY . /app

EXPOSE 8080

#Running the streamlit app
ENTRYPOINT ["streamlit", "run", "--server.maxUploadSize=20", "--server.port=8080"]
CMD ["main.py"]
CMD [ "waitress-serve", "app:app"]
File renamed without changes.
121 changes: 121 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Deeplasia Service

Deeplasia is a prior-free deep learning approach to asses bone age in children and adolescents.
This repository contains a RESTfull service with a simple API to process X-ray images and predict bone age in months.

Please refer for more information:

* http://www.deeplasia.de/
* https://github.com/aimi-bonn/Deeplasia

[![Build Docker Image](https://github.com/CrescNet/deeplasia-service/actions/workflows/build.yml/badge.svg)](https://github.com/CrescNet/deeplasia-service/actions/workflows/build.yml)

## How to Use

In order to run this application, you must provide the deep learning models. Please contact use to get them.

Use the environment variable `DEEPLASIA_THREADS` to limit the number of threads used by [PyTorch](https://pytorch.org/) (defaults to 4 threads).

### Run in Conda Environment

**Requirements:**

* [Conda](https://docs.conda.io) must be installed
* Deep learning models are located in the directory `./models`

Run the following CLI commands and navigate to <http://localhost:5000/>.

```sh
conda create -n flask_ba python=3.9
conda activate flask_ba
pip install -r requirements.txt
python flask run
```

### Run with Docker

**Requirements:**

* [Docker](https://docs.docker.com/engine/install/) must be installed
* Deep learning models are not included in the image and must be mounted on container start

You can use our pre built Docker image to run the application:

```sh
docker run -p 8080:8080 -v ./models:/app/models ghcr.io/CrescNet/deeplasia-service
```

Or you can build the image yourself (clone this repository first):

```bash
docker build -t deeplasia-service .
docker run -p 8080:8080 -v ./models:/app/models deeplasia-service
```

Navigate to <http://localhost:8080/>

#### Limiting CPU usage

To [limit the CPU usage of the docker container](https://docs.docker.com/config/containers/resource_constraints/), add the following flags to the docker run cmd:

```sh
--cpus=<number_of_cpus>
```

Note, that this should match the number of threads specified with environment variable `DEEPLASIA_THREADS`.

e.g.:

```sh
docker run -p 8080:8080 --cpus=2 -e "DEEPLASIA_THREADS=2" -v ./models:/app/models ghcr.io/CrescNet/deeplasia-service
```

## API

Please refer to `deeplasia-api.yml` for an [OpenAPI](https://www.openapis.org/) specification of the API.

### Request

In python the request can be conducted as follows:

```python
import requests

url = "http://localhost:8080/predict"

test_img = "/path/to/xray.png"
files = { "file": open(test_img, "rb") }

data = {
"sex": "female", # specify if known, else is predicted
"use_mask": True # default is true
}

resp = requests.post(url, files=files, json=data)
resp.json()
```

Gives something like:

```json
{
"bone_age": 164.9562530517578,
"sex_predicted": false,
"used_sex": "female"
}
```

## Predicting Sex

The canonical way would be as described in previous sections, with using the predicted mask and specifying the sex.
If, however, the sex happens to be unknown (or unsure for e.g. errors of inserting the data) the sex can also be predicted.

## Usage of Masks

Skipping the masking by the predicted mask is meant to be a debugging feature, if the results with the mask are not convincing
(e.g. predicting 127 months as age), one could re-conduct bone age prediction without the mask and see if makes a difference.
We might think about storing the masks as a visual control as well as logging features in general in the future.

## License

The code in this repository and the image `deeplasia-service` are licensed under CC BY-NC-SA 4.0 DEED.
34 changes: 0 additions & 34 deletions Readme.md

This file was deleted.

Loading

0 comments on commit cce5886

Please sign in to comment.