Pyradiomics: Pourquoi une discordance de géométrie se produirait-elle lorsque le masque et l'image sont dérivés de la même série DICOM?

Créé le 23 avr. 2019  ·  13Commentaires  ·  Source: AIM-Harvard/pyradiomics

Salut,

Pour le contexte, j'ai pu exécuter avec succès PyRadiomics à partir de la ligne de commande, à la fois pour des paires image-masque uniques, ainsi que pour exécuter une extraction par lots pour des dizaines de paires image-masque simultanément avec diverses configurations YAML. Cependant, j'ai rencontré un problème que je sais comment contourner, mais je suis toujours curieux de savoir pourquoi cela se produit.

La configuration: J'ai une série DICOM pour un seul patient avec 158 coupes, chacune de dimension 512x512 pixels. J'ai chargé la série dans Slicer et profilé un retour sur investissement (nodule), puis exporté la carte d'étiquettes binaires sous forme de fichier .nrrd. Ensuite, j'ai utilisé l'outil de ligne de commande dcm2niix pour convertir la même série DICOM en un volume .nii.

Lorsque j'exécute pyradiomics à partir du terminal en utilisant l'image .nii et le masque .nrrd, j'obtiens un décalage de géométrie comme décrit ci-dessous. Pourquoi cela se produirait-il si le masque et le volume d'image étaient dérivés de la même série DICOM? Est-ce un événement attendu?

Comme mentionné, deux solutions sont: (1) enregistrer l'image en tant que volume .nii ou .nrrd directement à partir de Slicer, et cela semble fonctionner correctement; (2) Ajustez la valeur de tolérance (bien que cette solution me laisse mal à l'aise). Finalement, je souhaite effectuer une conversion par lots de centaines de séries DICOM en volumes .nii ou .nrrd, et j'ai déjà des masques pour ces centaines de séries. J'espérais donc utiliser un outil de ligne de commande pour la conversion par lots de dcm >> nii ou nrrd au lieu de Slicer.

Merci de votre aide.

[2019-04-23 16:38:19] E: radiomics.script: Feature extraction failed!
Traceback (most recent call last):
  File "/anaconda3/lib/python3.6/site-packages/pyradiomics-0+unknown-py3.6-macosx-10.7-x86_64.egg/radiomics/imageoperations.py", line 192, in checkMask
    lsif.Execute(imageNode, maskNode)
  File "/anaconda3/lib/python3.6/site-packages/SimpleITK/SimpleITK.py", line 43958, in Execute
    return _SimpleITK.LabelStatisticsImageFilter_Execute(self, *args)
RuntimeError: Exception thrown in SimpleITK LabelStatisticsImageFilter_Execute: /scratch/dashboard/SimpleITK-OSX10.6-x86_64-pkg/SimpleITK-build/ITK-prefix/include/ITK-4.13/itkImageToImageFilter.hxx:241:
itk::ERROR: LabelStatisticsImageFilter(0x7fcd1e601050): Inputs do not occupy the same physical space! 
InputImage Origin: [-1.8200000e+02, 1.6933569e+02, -3.0314999e+02], InputImage_1 Origin: [-1.8200000e+02, -1.7000000e+02, -3.0314999e+02]
    Tolerance: 6.6406202e-07
InputImage Direction: 1.0000000e+00 0.0000000e+00 0.0000000e+00
0.0000000e+00 -1.0000000e+00 0.0000000e+00
0.0000000e+00 0.0000000e+00 1.0000000e+00
, InputImage_1 Direction: 1.0000000e+00 0.0000000e+00 0.0000000e+00
0.0000000e+00 1.0000000e+00 0.0000000e+00
0.0000000e+00 0.0000000e+00 1.0000000e+00

    Tolerance: 1.0000000e-06


During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/anaconda3/lib/python3.6/site-packages/pyradiomics-0+unknown-py3.6-macosx-10.7-x86_64.egg/radiomics/scripts/segment.py", line 40, in extractSegment
    feature_vector.update(extractor.execute(imageFilepath, maskFilepath, label))
  File "/anaconda3/lib/python3.6/site-packages/pyradiomics-0+unknown-py3.6-macosx-10.7-x86_64.egg/radiomics/featureextractor.py", line 397, in execute
    boundingBox, correctedMask = imageoperations.checkMask(image, mask, **self.settings)
  File "/anaconda3/lib/python3.6/site-packages/pyradiomics-0+unknown-py3.6-macosx-10.7-x86_64.egg/radiomics/imageoperations.py", line 207, in checkMask
    raise ValueError('Image/Mask geometry mismatch. Potential fix: increase tolerance using geometryTolerance, '
ValueError: Image/Mask geometry mismatch. Potential fix: increase tolerance using geometryTolerance, see Documentation:Usage:Customizing the Extraction:Settings:geometryTolerance for more information
question

Tous les 13 commentaires

En effet, la tolérance par défaut est trop stricte. Le plus simple serait d'activer le rééchantillonnage.

@warkentinmatt Il peut également être possible de corriger en activant correctMask . Cela permet de rééchantillonner le masque (le plus proche voisin), sans rééchantillonner l'image. La seule exigence est que l'espace physique du masque soit contenu dans l'image.

Ce que je suppose, c'est que votre masque a le même espacement / direction que l'image, mais la taille et l'origine sont différentes, car Slicer stocke maintenant le masque en recadrant la zone et en ajustant l'origine en conséquence. Cela économise de la mémoire, mais vous oblige à dire à PyRadiomics que vous pouvez rééchantillonner les masques (ce qui, dans ce dernier cas, équivaut à un simple remplissage jusqu'à ce que la taille de l'image corresponde).

PyRadiomics ne corrige pas le masque par défaut, car cela sert d'avertissement à l'étape supplémentaire exécutée par PyRadiomics.

Merci à vous deux pour vos réponses.

@JoostJM Donc, juste pour m'assurer de bien comprendre vos commentaires, lorsque Slicer exporte / enregistre une carte d'étiquette NRRD, il recadre la taille en fonction du retour sur investissement? Il ne conserve pas la dimensionnalité d'origine du volume d'entrée? Lorsque j'efface la scène dans Slicer, réimporte la série DICOM, puis charge le masque NRRD, le masque est superposé au scanner à l'emplacement anatomique approprié. Pour moi, j'ai supposé que cela signifiait que les dimensions d'origine du scanner étaient conservées dans le masque. Slicer est-il juste assez intelligent (c'est-à-dire en utilisant les bonnes méta-données) pour aligner correctement le masque sur le CT?

Aussi, juste pour être clair sur ce qui est réalisé avec le rééchantillonnage du masque binaire: je crois comprendre que si le retour sur investissement est situé au centre de l'image (ainsi le centre du tableau contient quelques centaines / mille "1"), ce rééchantillonnage les plus proches voisins du masque à la frontière du volume remplissent essentiellement le masque avec des 0? Est-ce une interprétation correcte?

Merci de votre aide.

Donc, juste pour m'assurer de bien comprendre vos commentaires, lorsque Slicer exporte / enregistre une carte d'étiquette NRRD, il recadre la taille en fonction du retour sur investissement?

Oui, c'est possible, car les fichiers Nrrd contiennent également l'origine, c'est-à-dire l'emplacement physique du premier voxel.
Nrrd contient également des informations sur la direction et l'espacement, vous permettant de chevaucher même les segmentations faites sur des images avec différentes tailles de pixels ou même celles qui sont pivotées.
C'est aussi la raison pour laquelle les tableaux numpy ne sont pas acceptés comme entrée dans PyRadiomics, car ils ne contiennent pas cette information géométrique

Aussi, juste pour être clair sur ce qui est réalisé avec le rééchantillonnage du masque binaire: je crois comprendre que si le retour sur investissement est situé au centre de l'image (ainsi le centre du tableau contient quelques centaines / mille "1"), ce rééchantillonnage les plus proches voisins du masque à la frontière du volume remplissent essentiellement le masque avec des 0? Est-ce une interprétation correcte?

Corriger

Lorsque j'efface la scène dans Slicer, réimporte la série DICOM, puis charge le masque NRRD, le masque est superposé au scanner à l'emplacement anatomique approprié. Pour moi, j'ai supposé que cela signifiait que les dimensions d'origine du scanner étaient conservées dans le masque. Slicer est-il juste assez intelligent (c'est-à-dire en utilisant les bonnes méta-données) pour aligner correctement le masque sur le CT?

Le masque peut correspondre à une sous-région de l'image de la taille du cadre de délimitation du masque. Il n'est pas nécessaire qu'il ait les mêmes dimensions que l'image. Le fait que les dimensions correspondent ou non dépendra de la manière dont vous créez la segmentation dans Slicer et de la manière dont vous l'exportez. Si vous souhaitez plus de détails sur la manière de garantir la correspondance des dimensions, faites-le nous savoir.

En regardant à nouveau l'erreur (la première fois que je viens de jeter un coup d'œil sur le téléphone), voici les différences:

InputImage Origin: [-1.8200000e+02, 1.6933569e+02, -3.0314999e+02], 
InputImage_1 Origin: [-1.8200000e+02, -1.7000000e+02, -3.0314999e+02]
    Tolerance: 6.6406202e-07

InputImage Direction: 1.0000000e+00 0.0000000e+00 0.0000000e+00
0.0000000e+00 -1.0000000e+00 0.0000000e+00
0.0000000e+00 0.0000000e+00 1.0000000e+00
, 

InputImage_1 Direction: 1.0000000e+00 0.0000000e+00 0.0000000e+00
0.0000000e+00 1.0000000e+00 0.0000000e+00
0.0000000e+00 0.0000000e+00 1.0000000e+00

Vous avez donc la situation où la valeur absolue d'origine est légèrement décalée (et la différence est plus grande que la tolérance par défaut), mais aussi que l'orientation d'une image est inversée en Y par rapport à l'autre. Cela signifie que vous ne pouvez pas résoudre le problème en réduisant la tolérance et que vous devrez rééchantillonner le masque ou l'image. Ce rééchantillonnage ne doit cependant pas modifier les valeurs des pixels, ce sera effectivement une opération de réorientation.

@JoostJM Merci encore pour votre réponse, très appréciée.

@fedorov Comment le rééchantillonnage du masque ou de l'image résoudrait-il le problème de

Si vous souhaitez plus de détails sur la manière de garantir la correspondance des dimensions, faites-le nous savoir.

Oui, j'aimerais plus d'informations sur la manière de garantir la correspondance des dimensions. S'il te plaît et merci.

En fait, j'adorerais toute information ou ressource qui pourrait renforcer ma compréhension de certains de ces concepts dont nous avons discuté (c.-à-d. Espacement, direction, origine). Naïvement, je pourrais penser que l'origine d'une image est les coordonnées [x, y, z] de [0,0,0], en d'autres termes, l'un des "coins" (antéro-supérieur droit?) D'un tableau d'images . Les valeurs d'origine indiquées dans le journal ont-elles une interprétation tangible (par exemple des millimètres ou quelque chose)? Que représentent ces chiffres? Pardonnez mon ignorance, je suis autodidacte sur tout ce qui concerne l'imagerie / radiomique, mais je veux comprendre ces concepts plutôt que simplement mettre en œuvre des solutions pour faire fonctionner les choses.

@warkentinmatt pas de soucis pour toutes les questions, c'est une courbe raide pour un débutant!

Comment le rééchantillonnage du masque ou de l'image résoudrait-il le problème de basculement de Y l'un par rapport à l'autre?

Les directions de l'image sont essentiellement une transformation qui fait pivoter le système de coordonnées du tableau d'images (IJK) vers l'espace anatomique (XYZ).

Dans un exemple 1D simple ci-dessous, "index de tableau" est le système de coordonnées de votre tableau 1d, et Gauche-Droite est le système de coordonnées dans l'espace physique d'un monde 1d. Dans Image2, l'ordre des valeurs dans le tableau est opposé à la direction de l'axe du système de coordonnées physique, c'est-à-dire que le tableau de voxels est orienté différemment. Le rééchantillonnage prend la géométrie d'une image définie par la taille, la direction et l'origine du tableau, et échantillonne les valeurs d'une autre image au niveau des voxels de la géométrie de référence.

image

Est-ce que ça a du sens?

Si vous souhaitez plus de détails sur la manière de garantir la correspondance des dimensions lors de l'exportation de l'étiquette à partir de 3D Slicer, faites-le nous savoir.
Oui, j'aimerais plus d'informations sur la manière de garantir la correspondance des dimensions. S'il te plaît et merci.

Voir la capture d'écran ci-dessous pour savoir comment exporter un segment de 3D Slicer dans un labelmap (qui est en fait une image binaire). Notez que même si vous suivez cette procédure, il n'est pas impossible que vous obteniez toujours une discordance de géométrie, car les valeurs de tolérance par défaut sont trop strictes. Mais l'orientation du tableau d'images doit être la même.

image

En fait, j'adorerais toute information ou ressource qui pourrait renforcer ma compréhension de certains de ces concepts dont nous avons discuté (c.-à-d. Espacement, direction, origine). Naïvement, je pourrais penser que l'origine d'une image est les coordonnées [x, y, z] de [0,0,0], en d'autres termes, l'un des "coins" (antéro-supérieur droit?) D'un tableau d'images . Les valeurs d'origine indiquées dans le journal ont-elles une interprétation tangible (par exemple des millimètres ou quelque chose)? Que représentent ces chiffres? Pardonnez mon ignorance, je suis autodidacte sur tout ce qui concerne l'imagerie / radiomique, mais je veux comprendre ces concepts plutôt que simplement mettre en œuvre des solutions pour faire fonctionner les choses.

Voici quelques ressources qui pourraient être utiles:

J'espère que cela t'aides!

@fedorov Merci beaucoup d'avoir pris le temps d'expliquer ces concepts, cela a été très utile. Cela a beaucoup plus de sens et j'apprécie votre patience.

Il me semble donc que le rééchantillonnage a deux rôles importants mais distincts pour assurer l'alignement géométrique entre deux volumes:

1) Tout d'abord, si nécessaire, remplissez le volume le plus petit jusqu'à ce qu'il corresponde à la même taille / dimensionnalité du volume de référence, en utilisant quelque chose comme les plus proches voisins.

2) Ensuite, une fois que le dimensionnement est en accord, utilisez les informations d'origine, de direction et d'espacement pour échantillonner les voxels de manière à garantir que deux volumes qui sont en alignement anatomique / physique sont également indexés de la même manière (c.-à-d. Alignement d'index de tableau ).

Serait-ce un bon résumé du rôle du rééchantillonnage?

Aussi, merci de partager ces ressources, j'ai hâte de travailler à travers elles pour renforcer ma compréhension.

@warkentinmatt , presque. Les étapes 1 et 2 se produisent simultanément. Ce qui se passe, c'est que vous définissez une grille de points dans l'espace physique, puis que vous échantillonnez votre image à ces points. Si les nouveaux points de grille ne correspondent pas exactement aux points de grille existants, les nouvelles valeurs sont calculées à l'aide de l'algorithme d'interpolation, en fonction des points (pixels) environnants (d'origine) de l'image.

Vous pouvez également trouver une explication détaillée dans la documentation IBSI, section sur l' interpolation . Celui-ci contient également une image illustrant la grille de rééchantillonnage.

@fedorov @JoostJM Je sais que ce numéro est clos et j'apprécie profondément toute l'aide que vous nous avez apportée tous les deux. Je voulais juste revenir en arrière pour clarifier ma compréhension. Après avoir beaucoup lu les ressources que vous avez fournies et visionné quelques vidéos utiles, je suis beaucoup plus à l'aise avec ces concepts.

Ainsi, dans l'exemple qui a engendré ce problème, étant donné que le masque était dérivé de la même image que celle utilisée pour l'extraction de caractéristiques, l'espacement était le même. En d'autres termes, un changement d'une unité de l'emplacement du voxel dans l'une quelconque des directions x, y, z pour le masque ou l'image porte la même interprétation de changement physique. Dans les cas où l'espacement est le même entre un masque et une image, une interpolation ne serait pas nécessaire pour "combler les vides", n'est-ce pas? Ainsi, rééchantillonner le masque garantirait simplement une correspondance géométrique avec l'image en ce qui concerne la dimensionnalité du volume, tout en garantissant que les deux volumes qui sont en alignement physique sont également indexés sur le tableau de la même manière (en ce qui concerne la direction et l'origine ). Ai-je enfin obtenu ce correct?

Si l'espacement n'était pas le même entre deux volumes, vous auriez besoin d'interpoler les voxels afin d'obtenir deux volumes en alignement géométrique. Par exemple, si l'espacement de l'image était de [1 mm, 1 mm, 1 mm] et le masque de [2 mm, 2 mm, 2 mm], vous devrez interpoler les voxels dans le masque pour «combler les vides» de l'espacement physique.

Merci encore pour votre aide. Cela a fait toute la différence.

@warkentinmatt oui, cela a du sens, je pense que vous avez bien compris!

Une autre ressource utile liée au rééchantillonnage est cette page: https://www.slicer.org/wiki/Registration : Resampling

@fedorov Super. Merci encore pour votre aide.

Ce n'est peut-être ni le moment ni le lieu, mais je crois comprendre que vous êtes basé à Boston. Actuellement, je suis candidat au doctorat à Toronto, mais je déménage à Boston, c'est deux semaines pour une bourse de recherche au Département de biostatistique de l'HSPH. J'adorerais me connecter en personne et peut-être continuer certaines de ces conversations, si cela vous intéresse et que vous avez le temps. Quoi qu'il en soit, votre aide a été très appréciée.

Bien sûr, heureux de vous connecter et d'en savoir plus sur la façon dont vous utilisez la pyradiomique! Envoyez-moi simplement un e-mail (c'est public sur mon profil github), et nous pourrons nous rencontrer pour un café.

Cette page vous a été utile?
0 / 5 - 0 notes