Téléverser les fichiers vers "/"

This commit is contained in:
aslane 2024-04-20 20:54:53 +00:00
commit 60cfd4fc74
5 changed files with 1257 additions and 0 deletions

18
Dockerfile Normal file
View File

@ -0,0 +1,18 @@
# Utilisez une image de base officielle Python
FROM python:3.8-slim
# Définissez le répertoire de travail dans le conteneur
WORKDIR /app
# Copiez les fichiers de dépendances et installez les dépendances
COPY requirements.txt ./requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copiez le reste de l'application dans le répertoire de travail
COPY . .
# Exposez le port sur lequel streamlit s'exécute
EXPOSE 8501
# Commande pour exécuter l'application Streamlit
CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]

BIN
LCOCT_Stack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 KiB

View File

@ -0,0 +1,919 @@
#!/usr/bin/env python
# coding: utf-8
# In[ ]:
# Autor : Raoul MISSODEY
import pandas as pd
import imageio.v2 as iio
import pydicom as dicom
import os
from glob import glob
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans, MiniBatchKMeans
from sklearn.preprocessing import minmax_scale, MinMaxScaler
from pydicom.dataset import FileDataset, FileMetaDataset
from skimage.filters import threshold_multiotsu, gabor
from scipy.ndimage import gaussian_filter, sobel, prewitt
from skimage.segmentation import find_boundaries
from skimage.measure import label, regionprops
from skimage import morphology, color, feature, restoration
from functools import partial
import skimage.util
from skimage import (exposure, filters, io, measure, future,
morphology, restoration, segmentation, transform,
util)
from sklearn.ensemble import RandomForestClassifier
import scipy.ndimage as nd
from skimage.restoration import (denoise_tv_chambolle, denoise_bilateral,
denoise_wavelet, estimate_sigma)
from tqdm import tqdm
from skimage import filters, feature
from concurrent.futures import ThreadPoolExecutor
from sklearn.preprocessing import LabelEncoder
import matplotlib as mpl
from scipy.spatial.distance import cdist
from joblib import Parallel, delayed
import time
from scipy.ndimage import median_filter
from sklearn import decomposition
from skimage.filters import threshold_triangle, threshold_li
import skimage as ski
import pybaselines
# In[ ]:
class SegShrinkBScan(object) :
"""
This class is used to segment all images in the third dimension (here in the LCOCT case this class is used
to process the 1232 images.)
First, the data is preprocessed (normalization, denoising, and each batch of 5 images is averaged).
Then eigenvalues of the Hessian matrix are used in KMeans algorithm for first segmentation.
After that RandomForest is used to migrate pixels belonging to wrong classes.
Then with Parallel Compusing we segmentation all images obtained after preprocessing.
Finaly, results are postprocessed using morphology binary operation, closing, dilatation...
input :
my_data : the 3D dicom pixels array.
return :
mean of stratum thickness calcuted for each segmented image.
"""
def __init__(self, my_data,remove_num = 50, disk_num = 2, disk_num1 = None,
disk_num2 = None, octagon_num1=None, octagon_num2=None, **kwargs) :
self.my_data = my_data.copy()
self.shape = self.my_data.shape
if remove_num == None :
self.remove_num = 50
else :
self.remove_num = remove_num
if disk_num == None :
self.disk_num = 2
else :
self.disk_num = disk_num
self.disk_num1 = disk_num1
self.disk_num2 = disk_num2
self.octagon_num1 = octagon_num1
self.octagon_num2 = octagon_num2
# data normalization
self.da_norm = self.normalize(self.my_data)
self.vmin, self.vmax = np.percentile(self.da_norm, [1,99])
#stretching intensity levels.
self.stretched = exposure.rescale_intensity(
self.da_norm,
in_range=(self.vmin, self.vmax),
out_range=np.float32
)
self.denoised2 = median_filter((self.da_norm), size=3)
#self.shape1 = self.denoised2.shape
self.mean_data2 = self.Mean(self.denoised2, 5)
self.sigma_min = 0.5
self.sigma_max = 20
self.features_func2_pca = partial(feature.multiscale_basic_features,
intensity=False, edges=True, texture=True,
sigma_min=self.sigma_min, sigma_max=self.sigma_max,
channel_axis=None, num_workers=20)
self.features_func2 = partial(feature.multiscale_basic_features,
intensity=False, edges=False, texture=True,
sigma_min=self.sigma_min, sigma_max=self.sigma_max,
channel_axis=None, num_workers=20)
## poil detection results
self.da_pca_red = self.pca_hair(self.stretched)
self.features2_pca = self.features_func2_pca((self.da_pca_red[:,:,0]))
self.lab_v2_pca = self.kmeans_seg_pca(self.features2_pca)
self.t1 = threshold_triangle(self.features2_pca[:,:,8])
self.ftr = self.features2_pca[:,:,8]<self.t1
self.lab_pca = self.label_hair(self.ftr, self.lab_v2_pca)
self.zero_data = self.replace_pixel_poil(self.stretched, self.lab_pca)
#####################################
# features extraction
self.features2 = self.features_func2((self.mean_data2[:,:,60]))
self.shape2 = self.mean_data2.shape
self.lab_v2 = self.kmeans_seg(self.features2)
self.foret2 = RandomForestClassifier(n_estimators=100, n_jobs=-1,
max_depth=None, max_samples=2500)
self.foret2 = future.fit_segmenter(self.lab_v2,self.features2, self.foret2)
self.All_features2 = np.asarray(Parallel(n_jobs=1)(delayed(self.allfea2)((self.mean_data2[:,:,i]))
for i in range(self.shape2[2])))
self.All_seg2 = np.asarray(Parallel(n_jobs=1)(delayed(self.allseg2)(self.All_features2[j]) for j in range(self.shape2[2])))
self.All_seg2 = np.transpose(self.All_seg2, (1, 0, 2))
self.zero_seg2 = self.All_seg2.copy()
for self.i in range(self.zero_seg2.shape[1]) :
self.zero_seg2[:,self.i,:][np.where(self.zero_data2[:,self.i,:]==0)] = 0
self.D_v2 = np.asarray(Parallel(n_jobs=1)(delayed(self.sc_th2)(self.All_seg2[:,i,:], self.zero_seg2[:,i,:],self.remove_num, self.disk_num, self.disk_num1, self.disk_num2, self.octagon_num1, self.octagon_num2)
for i in range( self.All_seg2.shape[1])))
self.conts = Parallel(n_jobs=1)(delayed(self.return_contours)(self.All_seg2[:,i,:], self.zero_seg2[:,i,:], self.remove_num, self.disk_num, self.disk_num1, self.disk_num2, self.octagon_num1, self.octagon_num2)
for i in range(self.All_seg2.shape[1]))
self.D_v2_sc = np.nanmean(np.delete(self.D_v2[:,0], np.where((self.D_v2[:,0] > 30) | (self.D_v2[:,0] == 0) )))
self.D_v2_std = np.nanstd(np.delete(self.D_v2[:,0], np.where((self.D_v2[:,0] > 30) | (self.D_v2[:,0] == 0) )))
self.D_v2_perc = np.nanmean(self.D_v2[:,1])
def sc_shrink(self) :
# return stratum thickness value
return self.D_v2_sc
def image_percentage(self) :
# return stratum thickness value
return self.D_v2_perc
def image_std(self) :
# return stratum thickness value
return self.D_v2_std
def return_im_cont(self) :
return self.conts, self.mean_data2, self.D_v2[:,0]
def return_contours(self, one_seg, zer, remove_num, disk_num, disk_num1, disk_num2, octagon_num1, octagon_num2) :
self.i1 = (one_seg == 1).copy()
self.i1[110:,:] = 0
"""
self.i2 = morphology.binary_closing(morphology.dilation(morphology.remove_small_objects(self.i1, 50)
, footprint = morphology.disk(3))
, footprint = morphology.disk(6))
self.i2 = morphology.binary_closing(morphology.dilation(morphology.erosion(self.i1, footprint = morphology.octagon(0,1))
, footprint = morphology.disk(4))
, footprint = morphology.disk(6))
"""
if disk_num1 is None :
self.i2 = morphology.remove_small_objects(self.i1, remove_num)
self.i2[np.where(zer==0)] = 0
self.i2 = morphology.dilation(self.i2, footprint = morphology.disk(disk_num))
else :
self.i2 = morphology.erosion(self.i1, footprint = morphology.octagon(octagon_num1,octagon_num2))
self.i2[np.where(zer==0)] = 0
self.i2 = morphology.dilation(self.i2, footprint = morphology.disk(disk_num1))
self.i2 = morphology.binary_closing(self.i2, footprint = morphology.disk(disk_num2))
"""
self.i2 = morphology.binary_closing(morphology.dilation(morphology.erosion(self.i1, footprint = morphology.octagon(octagon_num1,octagon_num2))
, footprint = morphology.disk(disk_num1))
, footprint = morphology.disk(disk_num2))
"""
self.c = measure.find_contours(self.i2)
return self.c
# segmentation according to the region of interest
def sc_th2(self, one_seg, zer, remove_num, disk_num, disk_num1, disk_num2, octagon_num1, octagon_num2) :
self.i1 = (one_seg == 1).copy()
self.i1[110:,:] = 0
"""
self.i2 = morphology.binary_closing(morphology.dilation(morphology.remove_small_objects(self.i1, 50)
, footprint = morphology.disk(3))
, footprint = morphology.disk(6))
self.i2 = morphology.binary_closing(morphology.dilation(morphology.erosion(self.i1, footprint = morphology.octagon(0,1))
, footprint = morphology.disk(4))
, footprint = morphology.disk(6))
"""
if disk_num1 is None :
self.i2 = morphology.remove_small_objects(self.i1, remove_num)
self.i2[np.where(zer==0)] = 0
self.i2 = morphology.dilation(self.i2, footprint = morphology.disk(disk_num))
else :
self.i2 = morphology.erosion(self.i1, footprint = morphology.octagon(octagon_num1,octagon_num2))
self.i2[np.where(zer==0)] = 0
self.i2 = morphology.dilation(self.i2, footprint = morphology.disk(disk_num1))
self.i2 = morphology.binary_closing(self.i2, footprint = morphology.disk(disk_num2))
"""
self.i2 = morphology.binary_closing(morphology.dilation(morphology.erosion(self.i1, footprint = morphology.octagon(octagon_num1,octagon_num2))
, footprint = morphology.disk(disk_num1))
, footprint = morphology.disk(disk_num2))
"""
self.c = measure.find_contours(self.i2)
self.c_up = []
self.n_up = []
self.l_up = []
#if (len(self.c) > 1) :
#self.ma = max(self.c, key=len)
if ((len(self.c)==2) and (self.c[0][0][1] != self.c[0][-1][1])) :
self.c_up.append(self.c)
self.n_up.append(0)
self.l_up.append(one_seg.shape[1])
else :
for ma in self.c :
if (len(ma)) > 200 :
for w in range(10, 17) :
self.n = int(np.round(ma[:,1].min() + w))
self.l = int(np.round(ma[:,1].max() - w))
if (self.i2[:,self.n:self.l].shape[0] < 2 or self.i2[:,self.n:self.l].shape[1] < 2) :
pass
else :
self.c = measure.find_contours(self.i2[:,self.n:self.l])
if (len(self.c) == 2) :
break
while (len(self.c) > 2) :
self.c.remove(min(self.c, key=len))
#if (self.i2[:,self.n:self.l].shape[0] < 2 or self.i2[:,self.n:self.l].shape[1] < 2) :
# pass
#else :
if (len(self.c[0])>20):
self.c_up.append(self.c)
self.n_up.append(self.n)
self.l_up.append(self.l)
else :
pass
if ((len(self.n_up)==2) and (self.n_up[0] == self.n_up[1]) and (self.l_up[0] == self.l_up[1])) :
self.c_up = [self.c_up[0]]
self.n_up = [self.n_up[0]]
self.l_up = [self.l_up[0]]
self.B = []
for pos in range(len(self.c_up)) :
self.base, self.p = pybaselines.whittaker.iasls(self.c_up[pos][0][:,0], lam=3*1e3,
p=0.95, lam_1=0.01, max_iter=100, tol=0.001)
self.B.append(self.base)
self.split_mean = [np.mean(np.min(cdist(self.c_up[pos][0],self.c_up[pos][1]),axis=1)) for pos in range(len(self.c_up))]
#self.split_mean = [np.mean(np.min(cdist(np.column_stack(( self.B[pos], self.c_up[pos][0][:, 1])),
# self.c_up[pos][1]),axis=1)) for pos in range(len(self.c_up))]
self.split_mean = list(filter(lambda x: x >8, self.split_mean))
self.split_mean = list(filter(lambda x: x <30, self.split_mean))
self.im_percentage = sum([one_seg[:,self.n_up[pos]:self.l_up[pos]].shape[1]/one_seg.shape[1]
for pos in range(len(self.l_up))])
#while (len(self.c)!=2) :
# self.c = measure.find_contours(self.i2[:,self.n:self.l])
# self.ma = max(self.c, key=len)
# self.n = int(np.round(self.ma[:,1].min() + 10))
#self.l = int(np.round(self.ma[:,1].max() - 10))
#else :
# return 0
return np.nanmean(np.array(self.split_mean)), self.im_percentage
"""
def sc_th2(self, one_seg) :
self.i1 = (one_seg == 1).copy()
self.i1[120:,:] = 0
self.i2 = morphology.binary_closing(morphology.dilation(morphology.remove_small_objects(self.i1, 50)
, footprint = morphology.disk(3))
, footprint = morphology.disk(6))
self.c = measure.find_contours(self.i2)
if (len(self.c) > 1) :
self.ma = max(self.c, key=len)
for w in range(10, 17) :
self.n = int(np.round(self.ma[:,1].min() + w))
self.l = int(np.round(self.ma[:,1].max() - w))
self.c = measure.find_contours(self.i2[:,self.n:self.l])
if (len(self.c) == 2) :
break
#while (len(self.c)!=2) :
# self.c = measure.find_contours(self.i2[:,self.n:self.l])
# self.ma = max(self.c, key=len)
# self.n = int(np.round(self.ma[:,1].min() + 10))
#self.l = int(np.round(self.ma[:,1].max() - 10))
else :
return 0
return np.mean(np.min(cdist(self.c[0],self.c[1]),axis=1))
"""
def allfea2(self, a) :
return self.features_func2(a)
def allseg2(self, a) :
return future.predict_segmenter(a, self.foret2)
def kmeans_seg(self, features) :
self.F = np.concatenate((features[:,:,9].reshape(-1,1).T, features[:,:,7].reshape(-1,1).T), axis=0).T
self.kmeans = MiniBatchKMeans(n_clusters = 3, batch_size = 5000,max_iter=500,
n_init='auto', random_state = 0).fit(self.F)
self.idx = np.argsort(self.kmeans.cluster_centers_.sum(axis=1))
self.L = np.zeros_like(self.idx)
self.L[self.idx] = np.arange(3)
#self.kmeans.fit(self.F.get())
#self.L = self.kmeans.labels_ +1
#self.label_encoder = LabelEncoder()
#self.label_encoder.fit(self.L)
#self.lab = self.label_encoder.transform(self.L) + 1
self.lab_v = self.L[self.kmeans.labels_].reshape(self.shape[0], self.shape[1]) + 1
return self.lab_v
def pca_hair(self, data) :
self.da_pca = np.transpose(data[50:70,:,:].reshape((20,-1)), (1,0))
self.pca = decomposition.PCA(n_components=3, svd_solver='full')
self.pca_fit = self.pca.fit(self.da_pca)
self.da_pca_red = self.pca_fit.transform(self.da_pca).reshape(data[0,:,:].shape + (-1,))
return self.da_pca_red
def moyenne(self, images, stretched, axis) :
return np.array(np.ma.average(images, axis=axis, weights=images.astype(bool)).tolist()).astype(stretched.dtype)
def replace_pixel_poil(self, stretched, lab3) :
self.stretched2 = stretched.copy()
for self.i in range(self.stretched2.shape[0]) :
self.stretched2[self.i,:,:][np.where(lab3==1)] = 0
self.stop = round(self.stretched2.shape[2]/5)*5 - 5
self.denoised22 = np.transpose(self.stretched2,(0,2,1))
self.consec_im2 = self.denoised22[:,:self.stop,:].reshape((self.stretched2.shape[0],-1,5, self.stretched2.shape[1]))
self.zero_data2 = np.array(np.ma.average(self.consec_im2, axis=2,
weights=self.consec_im2.astype(bool)).tolist()).astype(self.stretched2.dtype)
self.zero_data2 = np.concatenate((self.zero_data2,
np.transpose((self.moyenne(self.denoised22[:,self.stop+1:,:],
self.stretched2, axis=1)[..., np.newaxis]),(0,2,1))), axis=1)
self.zero_data2[np.isnan(self.zero_data2)] = 0
return self.zero_data2
def kmeans_seg_pca(self, features) :
self.F = np.concatenate((features[:,:,11].reshape(-1,1).T, features[:,:,8].reshape(-1,1).T), axis=0).T
self.kmeans = MiniBatchKMeans(n_clusters = 3, batch_size = 5000,max_iter=500,
n_init='auto', random_state = 0).fit(self.F)
#self.kmeans.fit(self.F.get())
self.idx = np.argsort(self.kmeans.cluster_centers_.sum(axis=1))
self.L = np.zeros_like(self.idx)
self.L[self.idx] = np.arange(3)
#self.L = self.kmeans.labels_ +1
#self.label_encoder = LabelEncoder()
#self.label_encoder.fit(self.L)
#self.lab = self.label_encoder.transform(self.L) + 1
self.lab_v = self.L[self.kmeans.labels_].reshape(self.shape[1], self.shape[2]) + 1
return self.lab_v
def label_hair(self,ftr,lab) :
#ftr is label from triangle threshold
#lab is label from kmeans
self.labr1 = morphology.binary_dilation(ftr, footprint=np.ones((3, 3)))
self.lab1 = morphology.remove_small_objects(self.labr1, min_size=500, connectivity=10)
self.contours1 = measure.find_contours(self.lab1)
self.labr = morphology.remove_small_objects(lab==1, min_size=500, connectivity=10)
self.lab2 = morphology.binary_dilation(self.labr)
self.contours2 = measure.find_contours(self.lab2)
self.contours3 = []
for self.c1 in self.contours1 :
for self.c2 in self.contours2 :
if bool(set(self.c1[:,1]) & set(self.c2[:,1])) :
self.contours3.append(self.c2)
self.lab3 = np.zeros_like(self.lab2)
for self.c3 in self.contours3 :
self.rr, self.cc = ski.draw.polygon(self.c3[:, 1], self.c3[:, 0])
self.lab3[self.cc, self.rr] = 1
return self.lab3
def Mean(self, data, m) :
self.stop = round(self.shape[2]/m)*m - m
self.data1 = np.transpose(data,(0,2,1))
self.consec_im = self.data1[:,:self.stop,:].reshape((self.shape[0],-1, m,self.shape[1]))
self.mean_data = np.mean(self.consec_im, axis=2)
self.mean_data = np.concatenate((self.mean_data,
np.transpose((np.mean(self.data1[:,self.stop+1:,:], axis=1)[..., np.newaxis]),(0,2,1))),
axis=1)
self.mean_data = np.transpose(self.mean_data,(0,2,1))
return self.mean_data
def normalize(self, data) :
self.scaler = MinMaxScaler()
self.da_nor = self.scaler.fit_transform(data.reshape(-1, data.shape[2])).reshape(data.shape)
return self.da_nor
# In[ ]:
class SegBScan(object) :
"""
This class is used to segment all images in the second dimension (here in the LCOCT case this class is used
to process the 500 images.)
First, the data is preprocessed (normalization, denoising, and each batch of 5 images is averaged).
Then eigenvalues of the Hessian matrix are used in KMeans algorithm for first segmentation.
After that RandomForest is used to migrate pixels belonging to wrong classes.
Then with Parallel Compusing we segmentation all images obtained after preprocessing.
Finaly, results are postprocessed using morphology binary operation, closing, dilatation...
input :
my_data : the 3D dicom pixels array.
return :
mean of stratum thickness calcuted for each segmented image.
"""
def __init__(self, my_data,remove_num = None, disk_num = None, disk_num1 = None,
disk_num2 = None, octagon_num1=None, octagon_num2=None, **kwargs) :
self.my_data = my_data.copy()
self.shape = self.my_data.shape
#self.remove_num = remove_num
#self.disk_num = disk_num
if remove_num == None :
self.remove_num = 50
else :
self.remove_num = remove_num
if disk_num == None :
self.disk_num = 3
else :
self.disk_num = disk_num
self.disk_num1 = disk_num1
self.disk_num2 = disk_num2
self.octagon_num1 = octagon_num1
self.octagon_num2 = octagon_num2
# data normalization
self.da_norm = self.normalize(self.my_data)
self.vmin, self.vmax = np.percentile(self.da_norm, [1,99])
#stretching intensity levels.
self.stretched = exposure.rescale_intensity(
self.da_norm,
in_range=(self.vmin, self.vmax),
out_range=np.float32
)
self.sigma_min = 0.5
self.sigma_max = 20
self.features_func2_pca = partial(feature.multiscale_basic_features,
intensity=False, edges=True, texture=True,
sigma_min=self.sigma_min, sigma_max=self.sigma_max,
channel_axis=None, num_workers=20)
self.features_func2 = partial(feature.multiscale_basic_features,
intensity=False, edges=False, texture=True,
sigma_min=self.sigma_min, sigma_max=self.sigma_max,
channel_axis=None, num_workers=20)
## poil detection results
self.da_pca_red = self.pca_hair(self.stretched)
self.features2_pca = self.features_func2_pca((self.da_pca_red[:,:,0]))
self.lab_v2_pca = self.kmeans_seg_pca(self.features2_pca)
self.t1 = threshold_triangle(self.features2_pca[:,:,8])
self.ftr = self.features2_pca[:,:,8]<self.t1
self.lab_pca = self.label_hair(self.ftr, self.lab_v2_pca)
self.zero_data = self.replace_pixel_poil(self.stretched, self.lab_pca)
#####################################
#self.shape1 = self.denoised2.shape
self.mean_data2 = self.Mean(self.stretched, 5)
#self.mean_data2 = median_filter(cp.asarray(self.stretched), size=3)
self.denoised2 = median_filter((self.mean_data2), size=3)
#self.denoised2 = self.Mean(self.mean_data2, 5)
# features extraction
self.features2 = self.features_func2((self.denoised2[:,60,:]))
self.shape2 = self.denoised2.shape
self.lab_v2 = self.kmeans_seg(self.features2)
self.foret2 = RandomForestClassifier(n_estimators=100, n_jobs=-1,
max_depth=None, max_samples=2500)
self.foret2 = future.fit_segmenter(self.lab_v2,self.features2, self.foret2)
self.All_features2 = np.asarray(Parallel(n_jobs=1)(delayed(self.allfea2)((self.denoised2[:,i,:]))
for i in range(self.shape2[1])))
self.All_seg2 = np.asarray(Parallel(n_jobs=1)(delayed(self.allseg2)(self.All_features2[j])
for j in range(self.shape2[1])))
self.All_seg2 = np.transpose(self.All_seg2, (1, 0, 2))
self.zero_seg = self.All_seg2.copy()
for self.i in range(self.zero_seg.shape[1]) :
self.zero_seg[:,self.i,:][np.where(self.zero_data[:,self.i,:]==0)] = 0
self.D_v2 = np.asarray(Parallel(n_jobs=1)(delayed(self.sc_th2)(self.All_seg2[:,i,:], self.zero_seg[:,i,:],self.remove_num, self.disk_num, self.disk_num1, self.disk_num2, self.octagon_num1, self.octagon_num2)
for i in range(self.shape2[1])))
self.conts = Parallel(n_jobs=1)(delayed(self.return_contours)(self.All_seg2[:,i,:], self.zero_seg[:,i,:],self.remove_num, self.disk_num, self.disk_num1, self.disk_num2, self.octagon_num1, self.octagon_num2)
for i in range(self.shape2[1]))
self.D_v2_sc = np.nanmean(np.delete(self.D_v2[:,0], np.where((self.D_v2[:,0] > 30) | (self.D_v2[:,0] == 0) )))
self.D_v2_std = np.nanstd(np.delete(self.D_v2[:,0], np.where((self.D_v2[:,0] > 30) | (self.D_v2[:,0] == 0) )))
self.D_v2_perc = np.nanmean(self.D_v2[:,1])
def sc_shrink(self) :
# return stratum thickness value
return self.D_v2_sc
def image_percentage(self) :
# return stratum thickness value
return self.D_v2_perc
def image_std(self) :
# return stratum thickness value
return self.D_v2_std
def all_metrics(self) :
# return stratum thickness value
return self.All_seg2
def return_im_cont(self) :
return self.conts, self.denoised2, self.D_v2[:,0]
# pca for hair follicle detection
def pca_hair(self, data) :
self.da_pca = np.transpose(data[50:70,:,:].reshape((20,-1)), (1,0))
self.pca = decomposition.PCA(n_components=3, svd_solver='full')
self.pca_fit = self.pca.fit(self.da_pca)
self.da_pca_red = self.pca_fit.transform(self.da_pca).reshape(data[0,:,:].shape + (-1,))
return self.da_pca_red
def replace_pixel_poil(self, stretched, lab3) :
self.stretched2 = stretched.copy()
for self.i in range(self.stretched2.shape[0]) :
self.stretched2[self.i,:,:][np.where(lab3==1)] = 0
self.consec_im = self.stretched2.reshape((self.stretched2.shape[0],-1,5, self.stretched2.shape[2]))
self.zero_data = np.array(np.ma.average(self.consec_im, axis=2,
weights=self.consec_im.astype(bool)).tolist()).astype(self.stretched2.dtype)
self.zero_data[np.isnan(self.zero_data)] = 0
return self.zero_data
def return_contours(self, one_seg, zer, remove_num, disk_num, disk_num1, disk_num2, octagon_num1, octagon_num2) :
self.i1 = (one_seg == 1).copy()
self.i1[110:,:] = 0
"""
self.i2 = morphology.binary_closing(morphology.dilation(morphology.remove_small_objects(self.i1, 50)
, footprint = morphology.disk(2))
, footprint = morphology.disk(6))
self.i2 = morphology.binary_closing(morphology.dilation(morphology.erosion(self.i1, footprint = morphology.octagon(0,1))
, footprint = morphology.disk(4))
, footprint = morphology.disk(10))
"""
if disk_num1 is None :
self.i2 = morphology.remove_small_objects(self.i1, remove_num)
self.i2[np.where(zer==0)] = 0
self.i2 = morphology.dilation(self.i2, footprint = morphology.disk(disk_num))
else :
self.i2 = morphology.erosion(self.i1, footprint = morphology.octagon(octagon_num1,octagon_num2))
self.i2[np.where(zer==0)] = 0
self.i2 = morphology.dilation(self.i2, footprint = morphology.disk(disk_num1))
self.i2 = morphology.binary_closing(self.i2, footprint = morphology.disk(disk_num2))
"""
self.i2 = morphology.binary_closing(morphology.dilation(morphology.erosion(self.i1, footprint = morphology.octagon(octagon_num1,octagon_num2))
, footprint = morphology.disk(disk_num1))
, footprint = morphology.disk(disk_num2))
"""
self.c = measure.find_contours(self.i2)
return self.c
# segmentation according to the region of interest
def sc_th2(self, one_seg, zer, remove_num, disk_num, disk_num1, disk_num2, octagon_num1, octagon_num2) :
self.i1 = (one_seg == 1).copy()
self.i1[110:,:] = 0
"""
self.i2 = morphology.binary_closing(morphology.dilation(morphology.remove_small_objects(self.i1, 50)
, footprint = morphology.disk(2))
, footprint = morphology.disk(6))
self.i2 = morphology.binary_closing(morphology.dilation(morphology.erosion(self.i1, footprint = morphology.octagon(0,1))
, footprint = morphology.disk(4))
, footprint = morphology.disk(10))
"""
if disk_num1 is None :
self.i2 = morphology.remove_small_objects(self.i1, remove_num)
self.i2[np.where(zer==0)] = 0
self.i2 = morphology.dilation(self.i2, footprint = morphology.disk(disk_num))
else :
self.i2 = morphology.erosion(self.i1, footprint = morphology.octagon(octagon_num1,octagon_num2))
self.i2[np.where(zer==0)] = 0
self.i2 = morphology.dilation(self.i2, footprint = morphology.disk(disk_num1))
self.i2 = morphology.binary_closing(self.i2, footprint = morphology.disk(disk_num2))
"""
self.i2 = morphology.binary_closing(morphology.dilation(morphology.erosion(self.i1, footprint = morphology.octagon(octagon_num1,octagon_num2))
, footprint = morphology.disk(disk_num1))
, footprint = morphology.disk(disk_num2))
"""
self.c = measure.find_contours(self.i2)
self.c_up = []
self.n_up = []
self.l_up = []
#if (len(self.c) > 1) :
#self.ma = max(self.c, key=len)
if ((len(self.c)==2) and (self.c[0][0][1] != self.c[0][-1][1])) :
self.c_up.append(self.c)
self.n_up.append(0)
self.l_up.append(one_seg.shape[1])
else :
for ma in self.c :
if (len(ma)) > 500 :
for w in range(10, 17) :
self.n = int(np.round(ma[:,1].min() + w))
self.l = int(np.round(ma[:,1].max() - w))
if (self.i2[:,self.n:self.l].shape[0] < 2 or self.i2[:,self.n:self.l].shape[1] < 2) :
break
else :
self.c = measure.find_contours(self.i2[:,self.n:self.l])
if (len(self.c) == 2) :
break
while (len(self.c) > 2) :
self.c.remove(min(self.c, key=len))
#if (self.i2[:,self.n:self.l].shape[0] < 2 or self.i2[:,self.n:self.l].shape[1] < 2) :
# pass
#else :
if (len(self.c[0]) > 20) :
self.c_up.append(self.c)
self.n_up.append(self.n)
self.l_up.append(self.l)
else :
pass
if ((len(self.n_up)==2) and (self.n_up[0] == self.n_up[1]) and (self.l_up[0] == self.l_up[1])) :
self.c_up = [self.c_up[0]]
self.n_up = [self.n_up[0]]
self.l_up = [self.l_up[0]]
self.B = []
for self.pos in range(len(self.c_up)) :
self.base, self.p = pybaselines.whittaker.iasls(self.c_up[self.pos][0][:,0], lam=3*1e3,
p=0.9, lam_1=0.01, max_iter=100, tol=0.001)
self.B.append(self.base)
#self.split_mean = [np.mean(np.min(cdist(np.column_stack((self.B[self.pos],
# self.c_up[self.pos][0][:, 1])),self.c_up[self.pos][1]),axis=1))
# for self.pos in range(len(self.c_up))]
self.split_mean = [np.mean(np.min(cdist(self.c_up[self.pos][0],self.c_up[self.pos][1]),axis=1))
for self.pos in range(len(self.c_up))]
self.split_mean = list(filter(lambda x: x >8, self.split_mean))
self.split_mean = list(filter(lambda x: x <30, self.split_mean))
self.im_percentage = sum([one_seg[:,self.n_up[self.pos]:self.l_up[self.pos]].shape[1]/one_seg.shape[1]
for self.pos in range(len(self.l_up))])
#while (len(self.c)!=2) :
# self.c = measure.find_contours(self.i2[:,self.n:self.l])
# self.ma = max(self.c, key=len)
# self.n = int(np.round(self.ma[:,1].min() + 10))
#self.l = int(np.round(self.ma[:,1].max() - 10))
#else :
# return 0
return np.nanmean(np.array(self.split_mean)), self.im_percentage
def allfea2(self, a) :
return self.features_func2(a)
def allseg2(self, a) :
return future.predict_segmenter(a, self.foret2)
def kmeans_seg(self, features) :
self.F = np.concatenate((features[:,:,9].reshape(-1,1).T, features[:,:,7].reshape(-1,1).T), axis=0).T
self.kmeans = MiniBatchKMeans(n_clusters = 3, batch_size = 5000,max_iter=500,
n_init='auto', random_state = 0).fit(self.F)
#self.kmeans.fit(self.F.get())
self.idx = np.argsort(self.kmeans.cluster_centers_.sum(axis=1))
self.L = np.zeros_like(self.idx)
self.L[self.idx] = np.arange(3)
#self.L = self.kmeans.labels_ +1
#self.label_encoder = LabelEncoder()
#self.label_encoder.fit(self.L)
#self.lab = self.label_encoder.transform(self.L) + 1
self.lab_v = self.L[self.kmeans.labels_].reshape(self.shape[0], self.shape[2]) + 1
return self.lab_v
def kmeans_seg_pca(self, features) :
self.F = np.concatenate((features[:,:,11].reshape(-1,1).T, features[:,:,8].reshape(-1,1).T), axis=0).T
self.kmeans = MiniBatchKMeans(n_clusters = 3, batch_size = 5000,max_iter=500,
n_init='auto', random_state = 0).fit(self.F)
#self.kmeans.fit(self.F.get())
self.idx = np.argsort(self.kmeans.cluster_centers_.sum(axis=1))
self.L = np.zeros_like(self.idx)
self.L[self.idx] = np.arange(3)
#self.L = self.kmeans.labels_ +1
#self.label_encoder = LabelEncoder()
#self.label_encoder.fit(self.L)
#self.lab = self.label_encoder.transform(self.L) + 1
self.lab_v = self.L[self.kmeans.labels_].reshape(self.shape[1], self.shape[2]) + 1
return self.lab_v
def label_hair(self,ftr,lab) :
#ftr is label from triangle threshold
#lab is label from kmeans
self.labr1 = morphology.binary_dilation(ftr, footprint=np.ones((3, 3)))
self.lab1 = morphology.remove_small_objects(self.labr1, min_size=500, connectivity=10)
self.contours1 = measure.find_contours(self.lab1)
self.labr = morphology.remove_small_objects(lab==1, min_size=500, connectivity=10)
self.lab2 = morphology.binary_dilation(self.labr)
self.contours2 = measure.find_contours(self.lab2)
self.contours3 = []
for self.c1 in self.contours1 :
for self.c2 in self.contours2 :
if bool(set(self.c1[:,1]) & set(self.c2[:,1])) :
self.contours3.append(self.c2)
self.lab3 = np.zeros_like(self.lab2)
for self.c3 in self.contours3 :
self.rr, self.cc = ski.draw.polygon(self.c3[:, 1], self.c3[:, 0])
self.lab3[self.cc, self.rr] = 1
return self.lab3
def Mean(self, data, m) :
self.consec_im = data.reshape((self.shape[0],-1,m, self.shape[2]))
self.mean_data = np.mean(self.consec_im, axis=2)
return self.mean_data
def normalize(self, data) :
self.scaler = MinMaxScaler()
self.da_nor = self.scaler.fit_transform(data.reshape(-1, data.shape[2])).reshape(data.shape)
return self.da_nor
# In[ ]:
def segment(F) :
"""
this function is used to segment All stack of one patient
input :
F : files containing the stacks
return :
average of stratum corneum thickness of each stack
"""
all_sc_oneP = []
All_long_perc = []
All_short_perc = []
All_long_std = []
All_short_std = []
for i in range(len(F)) :
da = dicom.dcmread(F[i])
data = da.pixel_array
D11 = SegShrinkBScan(data)
D22 = SegBScan(data)
all_sc_oneP.append((D11.sc_shrink() + D22.sc_shrink())/2)
All_long_perc.append(D22.image_percentage())
All_short_perc.append(D11.image_percentage())
All_long_std.append(D22.image_std())
All_short_std.append(D11.image_std())
del D11
del D22
return np.asarray(all_sc_oneP), np.asarray(All_long_perc), np.asarray(All_short_perc), np.asarray(All_long_std), np.asarray(All_short_std)
def segment_long(F) :
"""
this function is used to segment All stack of one patient
input :
F : files containing the stacks
return :
average of stratum corneum thickness of each stack
"""
all_sc_oneP = []
for i in range(len(F)) :
da = dicom.dcmread(F[i])
data = da.pixel_array
D22 = SegBScan(data)
all_sc_oneP.append(D22.sc_shrink())
del D22
return np.asarray(all_sc_oneP)

307
app.py Normal file
View File

@ -0,0 +1,307 @@
#!/usr/bin/env python
# coding: utf-8
# In[2]:
import streamlit as st
from PIL import Image
import numpy as np
import os
import pydicom as dicom
import io
import matplotlib.pyplot as plt
from Segment_Stack_without_gpu import SegBScan, SegShrinkBScan
# In[7]:
st.set_page_config(layout="wide")
def main():
selected_box = st.sidebar.selectbox(
'Choose one of the following',
('Welcome','LC-OCT image analysis')
)
if selected_box == 'Welcome':
welcome()
if selected_box == 'LC-OCT image analysis':
#selected_radio = st.sidebar.radio("Select analysis:", ["Visualisation", "Segmentation"])
#if selected_radio == "Visualisation":
lcoct()
def welcome():
st.title('3D Stack LCOCT images Visualisation and Processing')
st.subheader("This app is designed to swiftly visualize and analyze LCOCT images, specifically focusing on segmentation."
+ " The application is currently in deployment, and additional tools will be incorporated in the future.")
st.image('LCOCT_Stack.png',use_column_width=True)
def lcoct():
st.title('Visualisation')
allowed_extensions = ["dcm"]
uploaded_files = st.file_uploader("Choose LCOCT files", type=allowed_extensions, accept_multiple_files=True)
#file_contents = uploaded_file.read()
# Display the contents
count = 0
if uploaded_files:
st.text("Uploaded LCOCT data:")
for i, file in enumerate(uploaded_files):
file_size = len(file.read())
#if (file_size//1024)>2000 :
st.text(f"{i}, {file.name}")
count+=1
#st.text(file_size)
if uploaded_files:
stack = st.slider('Which stack do you whant to visualize?', 0, count, 0)
st.write("Current Selected Stack: ", stack)
if uploaded_files:
selected_file = uploaded_files[stack]
data = dicom.dcmread(io.BytesIO(selected_file.getvalue())).pixel_array
if data.ndim == 3 :
data = data
else :
data = np.transpose(data[np.newaxis,...], (1,0,2))
selected_slice = st.slider("Select Slice", 0, data.shape[1] - 1, 0)
selected_image_slice = data[:, selected_slice, :]
st.image(selected_image_slice, caption=f"Slice {selected_slice}", use_column_width=True)
save_directory1 = st.text_input("Enter the download directory path:", key="file_uploader1")
# Button to save the current slice
if st.button("Save Slice", key="save_button1"):
#save_directory1 = st.file_uploader("Choose a directory to save the slice", type=["directory"], key="file_uploader1")
# Check if the directory is selected
if save_directory1 is not None:
# Get the selected directory path
directory_path = os.path.abspath(save_directory1)
# Create the directory if it doesn't exist
os.makedirs(directory_path, exist_ok=True)
# Save the slice as an image using matplotlib with a custom filename and selected directory
filename = f"dicom_slice_{selected_slice}.png"
full_path = os.path.join(directory_path, filename)
plt.imsave(full_path, selected_image_slice, cmap='gray')
st.success(f"Slice {selected_slice} saved successfully.")
else:
st.warning("Please choose a directory to save the image.")
#if st.button("Save Slice"):
# plt.imsave(f"lcoct_slice_{selected_slice}.png", selected_image_slice, cmap='gray')
# st.success(f"Slice {selected_slice} saved successfully.")
if uploaded_files:
selected_file = uploaded_files[stack]
data = dicom.dcmread(io.BytesIO(selected_file.getvalue())).pixel_array
if data.ndim == 3 :
data = data
else :
data = np.transpose(data[np.newaxis,...], (1,0,2))
selected_slice = st.slider("Select Slice", 0, data.shape[0] - 1, 0)
selected_image_slice = data[selected_slice, :, :]
st.image(selected_image_slice, caption=f"Slice {selected_slice}", use_column_width=True)
#save_directory2 = st.file_uploader("Choose a directory to save the slice", type=["directory"], key="file_uploader2")
save_directory2 = st.text_input("Enter the download directory path:", key="file_uploader2")
# Button to save the current slice
if st.button("Save Slice", key="save_button2"):
# Check if the directory is selected
if save_directory2 is not None:
# Get the selected directory path
directory_path = os.path.abspath(save_directory2)
# Create the directory if it doesn't exist
os.makedirs(directory_path, exist_ok=True)
# Save the slice as an image using matplotlib with a custom filename and selected directory
filename = f"dicom_slice_{selected_slice}.png"
full_path = os.path.join(directory_path, filename)
plt.imsave(full_path, selected_image_slice, cmap='gray')
st.success(f"Slice {selected_slice} saved successfully.")
else:
st.warning("Please choose a directory to save the image.")
if uploaded_files:
selected_file = uploaded_files[stack]
data = dicom.dcmread(io.BytesIO(selected_file.getvalue())).pixel_array
if data.ndim == 3 :
data = data
else :
data = np.transpose(data[np.newaxis,...], (1,0,2))
selected_slice = st.slider("Select Slice", 0, data.shape[2] - 1, 0)
selected_image_slice = data[:, :, selected_slice]
st.image(selected_image_slice, caption=f"Slice {selected_slice}", use_column_width=True)
#save_directory3 = st.file_uploader("Choose a directory to save the slice", type=["directory"], key="file_uploader3")
save_directory3 = st.text_input("Enter the download directory path:", key="file_uploader3")
# Button to save the current slice
if st.button("Save Slice", key="save_button3"):
# Check if the directory is selected
if save_directory3 is not None:
# Get the selected directory path
directory_path = os.path.abspath(save_directory3)
# Create the directory if it doesn't exist
os.makedirs(directory_path, exist_ok=True)
# Save the slice as an image using matplotlib with a custom filename and selected directory
filename = f"dicom_slice_{selected_slice}.png"
full_path = os.path.join(directory_path, filename)
plt.imsave(full_path, selected_image_slice, cmap='gray')
st.success(f"Slice {selected_slice} saved successfully.")
else:
st.warning("Please choose a directory to save the image.")
##################################Segmentation########################################
st.title('Segmentation of Stratum Corneum')
st.write("This will take a few minitues ")
@st.cache_data
def segmentation(data1, remove1, disk1) :
#if uploaded_files:
result = SegBScan(data1, remove_num=remove1, disk_num=disk1)
contours1, denoised_med1, epaiss1 = result.return_im_cont()
epaiss1mean = result.sc_shrink()
epaiss1std = result.image_std()
return contours1, denoised_med1, epaiss1, epaiss1mean, epaiss1std
@st.cache_data
def segmentation1(data2, remove2, disk2):
#if uploaded_files:
result2 = SegShrinkBScan(data2, remove_num=remove2, disk_num=disk2)
contours2, denoised_med2, epaiss2 = result2.return_im_cont()
epaiss2mean = result2.sc_shrink()
epaiss2std = result2.image_std()
return contours2, denoised_med2, epaiss2, epaiss2mean, epaiss2std
@st.cache_data
def segmentation2(data1, disk1, disk2, octagon1, octagon2) :
#if uploaded_files:
result = SegBScan(data1, disk_num1 = disk1, disk_num2 = disk2, octagon_num1 = octagon1, octagon_num2 = octagon2)
contours1, denoised_med1, epaiss1 = result.return_im_cont()
epaiss1mean = result.sc_shrink()
epaiss1std = result.image_std()
return contours1, denoised_med1, epaiss1, epaiss1mean, epaiss1std
@st.cache_data
def segmentation3(data2, disk1, disk2, octagon1, octagon2):
#if uploaded_files:
result2 = SegShrinkBScan(data2, disk_num1 = disk1, disk_num2 = disk2, octagon_num1 = octagon1, octagon_num2 = octagon2)
contours2, denoised_med2, epaiss2 = result2.return_im_cont()
epaiss2mean = result2.sc_shrink()
epaiss2std = result2.image_std()
return contours2, denoised_med2, epaiss2, epaiss2mean, epaiss2std
selected_box = st.sidebar.selectbox(
'Choose one of the following postprocessing',
('1st way','2nd way')
)
if selected_box == '2nd way':
st.text("This postprocessing uses morphology.remove_small_objects followed by morphology.dilatation")
if uploaded_files:
selected_remove = st.slider("Select the smallest allowable object size. Choosing a value will rerun the whole segmentation", 0, 500, 50, key='remove')
selected_disk = st.slider("Select the radius of the disk-shaped footprint. Choosing a value will rerun the whole segmentation", 0, 10, 3, key='disk')
contours, denoised_med, epaiss, epaiss_mean, epaiss_std = segmentation(data,selected_remove, selected_disk)
denoised_med_get = denoised_med.copy()
selected_slice = st.slider("Select Slice", 0, denoised_med.shape[1] - 1, 0)
selected_image_slice = denoised_med_get[:, selected_slice,:]
#st.image(selected_image_slice, caption=f"Slice {selected_slice}", use_column_width=True)
fig, ax = plt.subplots()
ax.imshow(selected_image_slice, cmap='gray')
ax.set_title(f"Slice N°{selected_slice} ; Epaisseur du Stratum Corneum : {np.round(epaiss[selected_slice],3)}µm, (Epaisseur du stack {np.round(epaiss_mean,3)}µm +/- {np.round(epaiss_std,3)})", fontsize=6)
ax.axis('off')
for i in contours[selected_slice] :
if (len(i)> 200):
ax.plot(i[:,1],i[:,0])
st.pyplot(fig)
if uploaded_files:
selected_remove = st.slider("Select the smallest allowable object size. Choosing a value will rerun the whole segmentation", 0, 500, 50, key='remove2')
selected_disk = st.slider("Select the radius of the disk-shaped footprint. Choosing a value will rerun the whole segmentation", 0, 10, 2, key='disk2')
contours1, denoised_med1, epaiss1, epaiss_mean1, epaiss_std1 = segmentation1(data, selected_remove, selected_disk)
denoised_med_get1 = denoised_med1.copy()
selected_slice = st.slider("Select Slice", 0, denoised_med1.shape[2] - 1, 0, key =6)
selected_image_slice = denoised_med_get1[:, :,selected_slice]
#st.image(selected_image_slice, caption=f"Slice {selected_slice}", use_column_width=True)
fig, ax = plt.subplots()
ax.imshow(selected_image_slice, cmap='gray')
ax.set_title(f"Slice N°{selected_slice} ; Epaisseur du Stratum Corneum : {np.round(epaiss1[selected_slice],3)}µm, (Epaisseur du stack {np.round(epaiss_mean1,3)}µm +/- {np.round(epaiss_std1,3)})", fontsize=6)
ax.axis('off')
for i in contours1[selected_slice] :
if (len(i)> 200):
ax.plot(i[:,1],i[:,0])
st.pyplot(fig)
if selected_box == '1st way':
st.text("This postprocessing uses morphology.erosion followed by dilation and binary.closing")
if uploaded_files:
selected_disk11 = st.slider("Select the radius of the disk-shaped footprint. Choosing a value will rerun the whole segmentation", 0, 10, 3, key='disk11')
selected_disk12 = st.slider("Select the radius of the disk-shaped footprint. Choosing a value will rerun the whole segmentation", 0, 20, 0, key='disk12')
st.text("Generates an octagon shaped footprint")
selected_octagon1 = st.slider("The size of the horizontal and vertical sides. Choosing a value will rerun the whole segmentation", 0, 5, 0, key='octagon1')
selected_octagon2 = st.slider("The height or width of the slanted sides. Choosing a value will rerun the whole segmentation", 0, 5, 1, key='octagon2')
contours, denoised_med, epaiss, epaiss_mean, epaiss_std = segmentation2(data,selected_disk11, selected_disk12, selected_octagon1, selected_octagon2)
denoised_med_get = denoised_med.copy()
selected_slice = st.slider("Select Slice", 0, denoised_med.shape[1] - 1, 0)
selected_image_slice = denoised_med_get[:, selected_slice,:]
#st.image(selected_image_slice, caption=f"Slice {selected_slice}", use_column_width=True)
fig, ax = plt.subplots()
ax.imshow(selected_image_slice, cmap='gray')
ax.set_title(f"Slice N°{selected_slice} ; Epaisseur du Stratum Corneum : {np.round(epaiss[selected_slice],3)}µm, (Epaisseur du stack {np.round(epaiss_mean,3)}µm +/- {np.round(epaiss_std,3)})", fontsize=6)
ax.axis('off')
for i in contours[selected_slice] :
if (len(i)> 500):
ax.plot(i[:,1],i[:,0])
st.pyplot(fig)
if uploaded_files:
selected_disk11 = st.slider("Select the radius of the disk-shaped footprint. Choosing a value will rerun the whole segmentation", 0, 10, 3, key='disk13')
selected_disk12 = st.slider("Select the radius of the disk-shaped footprint. Choosing a value will rerun the whole segmentation", 0, 20, 0, key='disk14')
st.text("Generates an octagon shaped footprint")
selected_octagon1 = st.slider("The size of the horizontal and vertical sides. Choosing a value will rerun the whole segmentation", 0, 5, 0, key='octagon3')
selected_octagon2 = st.slider("The height or width of the slanted sides. Choosing a value will rerun the whole segmentation", 0, 5, 1, key='octagon4')
contours1, denoised_med1, epaiss1, epaiss_mean1, epaiss_std1 = segmentation3(data,selected_disk11, selected_disk12, selected_octagon1, selected_octagon2)
denoised_med_get1 = denoised_med1.copy()
selected_slice = st.slider("Select Slice", 0, denoised_med1.shape[2] - 1, 0, key =6)
selected_image_slice = denoised_med_get1[:, :,selected_slice]
#st.image(selected_image_slice, caption=f"Slice {selected_slice}", use_column_width=True)
fig, ax = plt.subplots()
ax.imshow(selected_image_slice, cmap='gray')
ax.set_title(f"Slice N°{selected_slice} ; Epaisseur du Stratum Corneum : {np.round(epaiss1[selected_slice],3)}µm, (Epaisseur du stack {np.round(epaiss_mean1,3)}µm +/- {np.round(epaiss_std1,3)})", fontsize=6)
ax.axis('off')
for i in contours1[selected_slice] :
if (len(i)> 200):
ax.plot(i[:,1],i[:,0])
st.pyplot(fig)
if __name__ == "__main__":
main()

13
requirements.txt Normal file
View File

@ -0,0 +1,13 @@
numpy==1.23.5
pandas==2.0.1
imageio==2.28.1
pydicom==2.3.1
matplotlib==3.7.1
scikit-learn==1.4.1.post1
scikit-image==0.20.0
scipy==1.10.1
tqdm==4.65.0
joblib==1.2.0
pybaselines==1.0.0
Pillow==9.4.0
streamlit==1.32.0