salimkanoun / dicom-to-cnn Goto Github PK
View Code? Open in Web Editor NEWThis project forked from thomast3563/library-dicom
Python Library to handle Input / Output conversion in Dicom <=> Convolutional Neural Network
License: MIT License
This project forked from thomast3563/library-dicom
Python Library to handle Input / Output conversion in Dicom <=> Convolutional Neural Network
License: MIT License
Lire la coupe au niveau instance et appliquer la fonction affine avant de retourner la matrice
Valeur pixel = (valeur row pixel * resclale slope) + rescale intercept
SK Dans series faudra surement indexer les position des coupes.
Data Patient :
Patient ID
Patient Name
"PatientBirthDate"
PatientSex"
Patient Weight
Patient Height
Data Study :
"AccessionNumber"
"InstitutionName"
"StudyDate"
"StudyDescription"
"StudyID"
"StudyInstanceUID"
"StudyTime"
Acqusition Time
DataSeries :
"ImageOrientationPatient" :
"Manufacturer" :
"Modality" (SK Ici probabkement faire une Enum et match avec sop class uid?)
"SeriesDate"
"SeriesDescription"
"SeriesInstanceUID"
"SeriesNumber"
"SeriesTime"
Si TEP => Ajout des données
=> ces info sans dans un module
https://dicom.innolitics.com/ciods/pet-image/pet-isotope (groupe 0054,0016 et 0018,0026 ou tu va trouver des séquence de tag dicoms c'est une array de tag dicom dans un tag dicom)
Nb : Attention tous ne seront pas présent, si absent probabelement afficher "undefined"
En gras les clé unique qui vont etre utilisé pour indexer les data
L'atribut ImageOrientationPatient donne deux coordonnées x,y,z du 1er pixel de l'image
Alors que Sitk attend une array 3*3 pour un vecteur d'orientation
Je ne vois pas comment passer de l'un à l'autre
Pour l'instant l'ImageOrientation est hardcodée en coupe transversale mais il faudrait généraliser
http://dicomiseasy.blogspot.com/2013/06/getting-oriented-using-image-plane.html
Lecture DICOM compressé necessite installation de GDCM python
A explorer
Prendre 10 DICOM tests de differentes modalities : PT, CT, SC, SR, RTSS, ...
et faire un test pour lecture de tags
Dans Dcmjs le rtss d'olivier le StructureSetROISequence n'est pas une séquence alors que ca devrait etre une séquence de 1 item.
Pourtant voir une sequence dans dicom parser
Physical size to assign new matrix is hardcoded, should be variable.
Calcul du SUV
Data à recupérer :
Patient Height (m)
Patient Weight (Kg)
Series Time
Acquisition Time
Modality (Voir methode de thomas avec le SOP Class UID)
Manufacturer
Half Life (sec) (0018,1075)
Total Dose (Bq) (0018,1074)
Radiopharmaceutical Start DateTime (0018,1078)
Decay Correction : 0054,1102 ; 3 valeur possible "NONE", "START", "ADMIN" => dans PET series module attribute
Unit (0054,1001) voir http://dicomlookup.com/lookup.asp?sw=Tnumber&q=(0054,1001)
Si philips : Conversion SUV 7053,1000 (tag privé)
Si Philips : conversion Bqml 7053,1009 (tag privé)
Check :
Si unit different de GML : Pas de calcul de SUV si manque un des parametre suivant
Total dose, serie acquisition date, patient weight, Half life
(SK a voir si on ajout une exception pour philips)
Pas de SUL si pas de patient Height
Si Philips le private tag 7053,1009 doit etre présent
Algo :
2)Definition de l'heure d'acquisition
Definition de l'heure d'acqusisition = Series time
Si acquisition Time présent et < à seriesTimes et si unité en BQML => Heure d'acqusition = Acquisition time
Si Philips
Multipiler l'image par 7053,1009 pour avoir l'image en BQML (meme si l'image est en unit BQML, philips donne de fausse info, ce sont des counts)
correction de decroissance
Si 0054,1102 n'est pas "ADMIN" => Faire la correction de decroissante (calculer un facteur de decroissance)
Si 0054,1102 est "ADMIN" => pas de correction de decroissance, le facteur de decroissance est de 1
Calcul du facteur de decroissance :
Difference en secondes entre InjectionTinme et l'heure d'acquisition définir dans l'étape 2)
Checker que cette difference est positive (Acquisition Time Unix - InjectionTinme Unix est positive) cad di Acquisition Time est posterieure a l'injection Time
(si ce n'est pas le cas pas de SUV, ce cas n'est pas possible)
Faire la comparaison en dateTime (tenant en compte du jour car il peut y avoir des acquisition faites le landemin, parser l'ensemble de la dateTime)
Facteur de décroissance = Math.exp(-(DeltaTempsCalculé)*ln(2)/halfLife);
Calcul du Facteur SUV
SUV = valeurVoxel(Bq/Ml) / [ (totalDose * facteur de decroissance) / poids patient ]
3 types de ROIS
Type 1 :Polygone, les coordonnées sont les "arretes"
Type 11 : Sphere ou Elipse : Les coordonnées sont Centre, Est et Nord
Num Point : Pixel inclus 1 à 1
Tag 0028,0051
Si Corrigé il y a l'attribut "ATTN"
Parser et diviser en tableau avec \ et regarder si l'attribue est dans la liste
Exemple
Corrigé
0028,0051 Corrected Image: DECY\ATTN\SCAT\DTIM\RANSNG\DCAL\SLSENS\NORM
Non Corrigé
0028,0051 Corrected Image: DECY\DTIM\RANSNG\DCAL\SLSENS\NORM
A implementer dans SeriesPT un is_corrected_attenuation
Pour reperer les dicoms compresses
=> Pour lire les matrice image des DICOM
Ici faudra surement dissocier la partie pyDICOM read qui va renvoyer une image
et la deuxieme ligne qui va ordoner les coupes par position qui doit etre faite dans Series
def __GatherSlices(self):
""" called by convert_to_NIFTI """
self.slices = [pydicom.dcmread(s) for s in self.filenames]
self.slices.sort(key=lambda x:int(x.ImagePositionPatient[2]),reverse=True)
=> Les deux methode suivante permettent d'appliquer la fonction affine pour avoir des valeurs interrpretable dans les matrice, à encapsuler dan la classe Instance
def __getPixelData(self):
""" called by convert_to_NIFTI """
pixel_data = [self.__RescaleSlope_PixelData(ds) for ds in self.slices]
return pixel_data
def __RescaleSlope_PixelData(self,ds):
""" called by __getPixelData """
return ds.pixel_array*float(ds.RescaleSlope)+float(ds.RescaleIntercept)
=> Premet de verifier que l'épaisseur de coupe est constante dans la series, à mettre dans methode Series
SK : Peut etre modifier par une methode verifiant qu'il n'y a pas de gap dans l'espace d'acquisition?
def __getZSpacing(self):
""" called by __getMetadata """
Z_positions = [ds.ImagePositionPatient[2] for ds in self.slices]
initial_z_spacing = Z_positions[0]-Z_positions[1]
for i in range(1,len(Z_positions)):
z_spacing = Z_positions[i-1]-Z_positions[i]
if (z_spacing!=initial_z_spacing):
warnings.warn("Z axis Spacing is not constant : %s / %s" % (z_spacing,initial_z_spacing))
return initial_z_spacing
=> info sur la postion et taill des voxel de la coupe, dans classe Instance
def __getMetadata(self):
""" called by convert_to_NIFTI """
Direction = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) #hard coded
Origin = (self.slices[-1].ImagePositionPatient[0],self.slices[-1].ImagePositionPatient[1],self.slices[-1].ImagePositionPatient[2])
Spacing = (self.slices[-1].PixelSpacing[0],self.slices[-1].PixelSpacing[1],self.__getZSpacing())
return (Direction,Origin,Spacing)
=> creation du Nifti, ici ca sera a adapter en fonction de la modalité (ajouter SUV etc...) mais cette partie de code permet d'assigner les voxel au repere Nifti et de generer le Nifti
def convert_to_NIFTI(self,filename=None):
""" Generates .nii files from the DICOM serie """
self.__GatherSlices()
(Direction,Origin,Spacing) = self.__getMetadata()
Array = np.stack(self.__getPixelData(),axis=0).astype(self.output_format)
sitk_img = sitk.GetImageFromArray(Array)
sitk_img.SetDirection(Direction)
sitk_img.SetOrigin(Origin)
sitk_img.SetSpacing(Spacing)
#sitk_img.SetMetaData()
sitk.WriteImage(sitk_img,filename)
Nifti sera en 16bits pour CT et 32 bits pour SUV (c'est la num py array qui doit avoir le bon format (exemple :self.output_format = np.int16)
Renommer RTSS_Reader en Instance_RTSS
Cette classe doit heriter de Instance (pour la partie commune a tous les DICOM) et ajoute les méthode spécifiques aux RTSS.
Dans le model mettre un sous repertoire "reader" pour y mettre les classe qui lisent les dicom Instance et Instance_RTSS
Le calcul du SUL part de la valeur de SUV
Possible que si le SUV est dispo avec le sexe et la taille du patient
BMI = weight/Math.pow(height, 2);
if( isFemale) {
SULFactor 9270 / (8780 + 244*BMI);
} else {
SULFactor 9270 / (6680 + 216*BMI);
}
Sk verif formul jenhmassian
L'image en SUL est l'image en SUV * SUL Factor
A checker SK
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.