Skip to content

Commit

Permalink
Merge pull request #15 from RomainUSA/master
Browse files Browse the repository at this point in the history
Universal Labeling Merging and Separating algorithm deployment
  • Loading branch information
juanprietob authored Jul 16, 2021
2 parents a0ca35c + ee54ef1 commit e5aa75c
Show file tree
Hide file tree
Showing 18 changed files with 823 additions and 114 deletions.
49 changes: 49 additions & 0 deletions Docker/Dockerfile_ULMS
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
FROM python:3.7

RUN apt-get update && apt-get install -y \
git \
libx11-6 \
libgl1 \
libopengl0 \
libegl1 \
wget


RUN mkdir /app
WORKDIR /app

RUN wget https://github.com/RomainUSA/fly-by-cnn/releases/download/2.1.0/requirements.txt
RUN pip3 install -r /app/requirements.txt

RUN wget https://github.com/RomainUSA/fly-by-cnn/archive/refs/tags/2.1.0.zip
RUN unzip 2.1.0.zip
RUN rm -rf 2.1.0.zip


RUN mkdir /app/models
WORKDIR /app/models

RUN wget https://github.com/RomainUSA/fly-by-cnn/releases/download/2.1.0/nnLU_model_5.hdf5
RUN wget https://github.com/RomainUSA/fly-by-cnn/releases/download/2.1.0/model_features.zip
RUN unzip model_features.zip
RUN rm -rf model_features.zip


WORKDIR /app
RUN wget https://github.com/RomainUSA/fly-by-cnn/releases/download/2.1.0/groundtruth.zip
RUN unzip groundtruth.zip
RUN rm -rf groundtruth.zip

RUN mkdir /app/data
RUN mkdir /app/data/input
RUN mkdir /app/data/uid
RUN mkdir /app/data/out_tmp
RUN mkdir /app/data/merged
RUN mkdir /app/data/out







50 changes: 50 additions & 0 deletions Docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,53 @@ This example will extract the normals and the depth map as a separate component
```
python /app/fly-by-cnn/src/py/fly_by_features.py --surf my_surf.(vtk,.stl,.obj) --subdivision 2 --resolution 512 --out out.nrrd --radius 2 --use_z 1 --split_z 1"
```


## Running Universal Labeling, Merging and Separating

To get the docker:

```
docker pull dcbia/ulms:latest
```

To run the docker:

**For the Universal Labeling:**

Input:
- Dental crown model that contains the teeth of a patient (vtk file)

Output:
- Dental crown model with the universal labels on each tooth (vtk file)

```
docker run --rm -v */my/input/file*:/app/data/input/P1_teeth.vtk -v */my/output/folder*:/app/data/out python3 fly-by-cnn-2.1.0/src/py/universal_labeling.py --surf /app/data/input/P1_teeth.vtk --label_GT_dir /app/groundtruth --model_ft /app/models/model_features --model_LU /app/models/nnLU_model_5.hdf5 --out_feature /app/data/feature.nrrd --out /app/data/out
```

**For the Merging and Separating:**

Inputs:
- Dental crown model that contains the teeth of a patient with the universal labels on each tooth (vtk file)
- The root canal segmentation file (.nii)

Output:
- A merged model with the teeth and roots labeled based on the universal labels and individual teeth and roots (vtk files)

```
docker run --rm -v */my/input/file*:/app/data/input/P1_teeth_uid.vtk -v '*/my/input/file*:/app/data/input/lower_P1_scan_lower_RCSeg.nii.gz -v */my/output/folder*:/app/data/out ulms:latest bash fly-by-cnn-2.1.0/src/sh/compute_MergingSeparating.sh --src_code fly-by-cnn-2.1.0/src --input_file_uid /app/data/input/P1_teeth_uid.vtk --input_file_root /app/data/input/lower_P1_scan_lower_RCSeg.nii.gz --out_tmp /app/data/out_tmp --out_merge /app/data/merged --out_separate /app/data/out
```

**For the ULMS:**

Inputs:
- Dental crown model that contains the teeth of a patient (vtk file)
- The root canal segmentation file (.nii)

Output:
- A merged model with the teeth and roots labeled based on the universal labels and individual teeth and roots (vtk files)

```
docker run --rm -v */my/input/file*:/app/data/input/P1_teeth.vtk -v '*/my/input/file*:/app/data/input/lower_P1_scan_lower_RCSeg.nii.gz -v */my/output/folder*:/app/data/out ulms:latest bash fly-by-cnn-2.1.0/src/sh/compute_ULMS.sh --src_code fly-by-cnn-2.1.0/src --input_file_surf /app/data/input/P1_teeth.vtk
--label_GT_dir /app/groundtruth --model_ft /app/models/model_features --model_LU /app/models/nnLU_model_5.hdf5 --out_feature /app/data/feature.nrrd --output_dir_uid /app/data/uid --input_file_root /app/data/input/lower_P1_scan_lower_RCSeg.nii.gz --out_tmp /app/data/out_tmp --out_merge /app/data/merged --out_separate /app/data/out
```
101 changes: 101 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,104 @@ This example will extract the normals and the depth map as a separate component
python /app/fly-by-cnn/src/py/fly_by_features.py --surf my_surf.(vtk,.stl,.obj) --subdivision 2 --resolution 512 --out out.nrrd --radius 2 --use_z 1 --split_z 1"
```

## Running Universal Labeling, Merging and Separating algorithm

The easiest way to use the docker container.
[Docker/README.md](https://github.com/RomainUSA/fly-by-cnn/tree/master/Docker)

Here are the command lines to run the scripts:

**Universal Labeling:**

Input:
- Dental crown model that contains the teeth of a patient (vtk file)

Output:
- Dental crown model with the universal labels on each tooth (vtk file)

```
python3 fly-by-cnn/src/py/universal_labeling.py --help
```

```
usage: universal_labeling.py [-h] --surf SURF --label_groundtruth
LABEL_GROUNDTRUTH --model_feature MODEL_FEATURE
--model_LU MODEL_LU --out_feature OUT_FEATURE
[--out OUT]
Label the teeth from 2 to 16 or with the universal IDs used by clinicians
optional arguments:
-h, --help show this help message and exit
--surf SURF Input surface mesh to label (default: None)
--out OUT Output model with labels (default: out.vtk)
Label parameters:
--label_groundtruth LABEL_GROUNDTRUTH
directory of the template labels (default: None)
Prediction parameters:
--model_feature MODEL_FEATURE
path of the VGG19 model (default: None)
--model_LU MODEL_LU path of the LowerUpper model (default: None)
--out_feature OUT_FEATURE
out of the feature (default: None)
```

**Merging and Separating:**

Inputs:
- Dental crown model that contains the teeth of a patient with the universal labels on each tooth (vtk file)
- The root canal segmentation file (.nii)

Output:
- A merged model with the teeth and roots labeled based on the universal labels and individual teeth and roots (vtk files)

```
bash fly-by-cnn/src/sh/compute_MergingSeparating.sh --help
```

```
Program to run the Merging and Separating algorithm
Syntax: compute_MergingSeparating.sh [--options]
options:
--src_code Path of the source code
--input_file_uid Output directory of the teeth with the universal labels.
--input_file_root Input directory with the root canal segmentation files.
--out_tmp Temporary output folder.
--out_merge Output directory of the merged surfaces.
--out_separate Output directory of the separated surfaces.
```

**ULMS algortihm:**

Inputs:
- Dental crown model that contains the teeth of a patient (vtk file)
- The root canal segmentation file (.nii)

Output:
- A merged model with the teeth and roots labeled based on the universal labels and individual teeth and roots (vtk files)

```
bash fly-by-cnn/src/sh/compute_ULMS.sh --help
```

```
Program to run the Universal Labeling Merging and Separated algorithm
Syntax: compute_ULMS.sh [--options]
options:
--src_code Path of the source code
--input_file_surf Input file surface with only the teeth.
--label_GT_dir Folder containing the template for the Upper/Lower classification.
--model_ft Path to the feature model .
--model_LU Path to the LowerUpper classification model.
--out_ft Output of the fearure.
--output_dir_uid Output directory of the teeth with the universal labels.
--input_file_root Root canal segmentation file.
--out_tmp Temporary output folder.
--out_merge Output directory of the merged surfaces.
--out_separate Output directory of the separated surfaces.
```

30 changes: 30 additions & 0 deletions src/py/Classification_LU/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import os

from keras.models import Sequential
from keras.layers import LSTM, Dense, Input, Bidirectional
from tensorflow.keras.optimizers import *


def LSTM_model():
model = Sequential()

# Two bidirectional LSTM layers
model.add(Bidirectional(LSTM(64, return_sequences=True), input_shape=(64,512)))
model.add(Bidirectional(LSTM(64)))

# Sequence voting layer
model.add(Dense(64))

# Fully connected layer
model.add(Dense(1, activation='sigmoid'))

# Compile the model
model.compile(optimizer=Adam(lr = 1e-4), loss='binary_crossentropy', metrics=['accuracy'])
model.summary()

return model





88 changes: 88 additions & 0 deletions src/py/Classification_LU/testing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from model import *
from tensorflow.keras.models import load_model
from sklearn.metrics import classification_report

import argparse
import os
import itk

import numpy as np
import tensorflow as tf


parser = argparse.ArgumentParser(description='Predict an input with a trained neural network', formatter_class=argparse.ArgumentDefaultsHelpFormatter)

in_group = parser.add_mutually_exclusive_group(required=True)
in_group.add_argument('--features', type=str, help='Input features to be predict as an Upper or Lower teeth')
in_group.add_argument('--dir_features', type=str, help='Input dir features to be predict as an Upper or Lower teeth')

parser.add_argument('--load_model', type=str, help='Saved model', required=True)
parser.add_argument('--display', type=bool, help='display the prediction', default=False)

args = parser.parse_args()

inputFeatures = args.features
inputdirFeatures = args.dir_features
loadModelPath = args.load_model



def ReadImage(fName, image_dimension=2, pixel_dimension=-1):
if(image_dimension == 1):
if(pixel_dimension != -1):
ImageType = itk.Image[itk.Vector[itk.F, pixel_dimension], 2]
else:
ImageType = itk.VectorImage[itk.F, 2]
else:
if(pixel_dimension != -1):
ImageType = itk.Image[itk.Vector[itk.F, pixel_dimension], image_dimension]
else:
ImageType = itk.VectorImage[itk.F, image_dimension]

img_read = itk.ImageFileReader[ImageType].New(FileName=fName)
img_read.Update()
img = img_read.GetOutput()
return img

def process_scan(path):
img = ReadImage(path)
img_np = itk.GetArrayViewFromImage(img)
img_np = np.reshape(img_np, [s for s in img_np.shape if s != 1])
return img_np



if (args.dir_features):
features_paths = [os.path.join(inputdirFeatures, x) for x in os.listdir(inputdirFeatures) if not x.startswith(".")]
features_name = [y for y in os.listdir(inputdirFeatures) if not y.startswith(".")]
features = np.array([process_scan(path) for path in features_paths])

if (args.features):
features_name = inputFeatures.split("/")[-1]
features = np.array([process_scan(inputFeatures)])


model = load_model(loadModelPath)
predictions = model.predict(features)


y_true = []
for i in range(len(features_name)):
if features_name[i][:1]=="L":
y_true.append(0)

if features_name[i][:1]=="U":
y_true.append(1)

predictions[predictions>0.5]=1
predictions[predictions<=0.5]=0



if (args.display):
target_names = ['Lower', 'Upper']
print(classification_report(y_true, predictions, target_names=target_names))




Loading

0 comments on commit e5aa75c

Please sign in to comment.