Pyradiomics: Por que a incompatibilidade de geometria ocorre quando a máscara e a imagem são derivadas da mesma série DICOM?

Criado em 23 abr. 2019  ·  13Comentários  ·  Fonte: AIM-Harvard/pyradiomics

Oi,

Para contexto, consegui executar com êxito PyRadiomics a partir da linha de comando, tanto para pares de máscara de imagem única, como também executar extração em lote para dezenas de pares de máscara de imagem simultaneamente com várias configurações YAML. No entanto, encontrei um problema que sei como contornar, mas ainda estou curioso para saber por que ele ocorre.

A configuração: tenho uma série DICOM de paciente único com 158 cortes, cada um com dimensão de 512x512 pixels. Eu carreguei a série no Slicer e contornei uma ROI (nódulo) e, em seguida, exportei o mapa de rótulo binário como um arquivo .nrrd. Em seguida, usei a ferramenta de linha de comando dcm2niix para converter a mesma série DICOM em um volume .nii.

Quando executo a piradiômica do terminal usando a imagem .nii e a máscara .nrrd, obtenho uma incompatibilidade de geometria, conforme descrito abaixo. Por que isso ocorreria se a máscara e o volume da imagem fossem derivados da mesma série DICOM? Esta é uma ocorrência esperada?

Conforme mencionado, duas soluções são: (1) salvar a imagem como um volume .nii ou .nrrd diretamente do Slicer, e isso parece funcionar bem; (2) Ajuste o valor de tolerância (embora esta solução me deixe desconfortável). Eventualmente, desejo realizar a conversão em lote de centenas de séries DICOM para volumes .nii ou .nrrd, e já tenho máscaras para essas centenas de séries. Então, eu esperava usar uma ferramenta de linha de comando para conversão em lote de dcm >> nii ou nrrd em vez do Slicer.

Obrigado pela ajuda.

[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

Todos 13 comentários

Isso ocorre porque a tolerância padrão é muito restrita. O mais fácil seria habilitar a reamostragem.

@warkentinmatt Também pode ser possível corrigir ativando correctMask . Isso permite a reamostragem da máscara (vizinho mais próximo), sem reamostrar a imagem. O único requisito é que o espaço físico da máscara esteja contido na imagem.

O que eu suspeito que aconteceu é que sua máscara tem o mesmo espaçamento / direção da imagem, mas o tamanho e a origem são diferentes, já que o Slicer agora armazena a máscara cortando a área e ajustando a origem de acordo. Isso economiza memória, mas exige que você diga ao PyRadiomics que não há problema em reamostrar as máscaras (que, neste último caso, é igual a apenas preencher até que corresponda ao tamanho da imagem).

O PyRadiomics não corrige a máscara por padrão, pois isso serve como um aviso para a etapa extra que o PyRadiomics está realizando.

Obrigado a ambos por suas respostas.

@JoostJM Então, apenas para garantir que entendi seus comentários, quando o Slicer exporta / salva um mapa de rótulo NRRD, ele corta o tamanho na extensão do ROI? Não mantém a dimensionalidade original do volume de entrada? Quando eu limpo a cena no Slicer, reimporto a série DICOM e, em seguida, carrego a máscara NRRD, a máscara é sobreposta na TC no local anatômico adequado. Para mim, presumi que isso significava que as dimensões originais do TC eram mantidas na máscara. O Slicer é inteligente o suficiente (ou seja, usando os metadados corretos) para alinhar corretamente a máscara no CT?

Além disso, estou certo do que é alcançado com a reamostragem da máscara binária: meu entendimento é que se o ROI estiver localizado centralmente na imagem (portanto, o centro da matriz contém algumas centenas / mil "1s"), essa reamostragem os vizinhos mais próximos da máscara na borda do volume estão basicamente preenchendo a máscara com 0's? Esta é uma interpretação correta?

Obrigado pela ajuda.

Então, apenas para garantir que entendi seus comentários, quando o Slicer exporta / salva um mapa de rótulo NRRD, ele corta o tamanho na extensão do ROI?

Sim, isso é possível, pois os arquivos Nrrd também contêm a origem, ou seja, a localização física do primeiro voxel.
Nrrd também contém informações sobre direção e espaçamento, permitindo sobrepor até mesmo segmentações feitas em imagens com diferentes tamanhos de pixel ou mesmo aquelas que são giradas.
Esta também é a razão pela qual matrizes numpy não são aceitas como entrada para PyRadiomics, uma vez que não contêm essas informações geométricas

Além disso, estou certo do que é alcançado com a reamostragem da máscara binária: meu entendimento é que se o ROI estiver localizado centralmente na imagem (portanto, o centro da matriz contém algumas centenas / mil "1s"), essa reamostragem os vizinhos mais próximos da máscara na borda do volume estão basicamente preenchendo a máscara com 0's? Esta é uma interpretação correta?

Correto

Quando eu limpo a cena no Slicer, reimporto a série DICOM e, em seguida, carrego a máscara NRRD, a máscara é sobreposta na TC no local anatômico adequado. Para mim, presumi que isso significava que as dimensões originais do TC eram mantidas na máscara. O Slicer é inteligente o suficiente (ou seja, usando os metadados corretos) para alinhar corretamente a máscara no CT?

A máscara pode corresponder a uma sub-região da imagem do tamanho da caixa delimitadora da máscara. Não precisa ter as mesmas dimensões da imagem. Se as dimensões correspondem ou não, dependerá de como você cria a segmentação no Slicer e como você a exporta. Se você quiser mais detalhes sobre como garantir a correspondência das dimensões, entre em contato conosco.

Olhando novamente para o erro (na primeira vez, acabei de dar uma olhada no telefone), aqui estão as diferenças:

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

Portanto, você tem a situação em que o valor absoluto de origem está ligeiramente fora (e a diferença é maior do que a tolerância padrão), mas também em que a orientação de uma imagem é invertida em Y em comparação com a outra. Isso significa que você não pode solucionar o problema reduzindo a tolerância e precisará fazer uma nova amostra da máscara ou da imagem. Esta reamostragem não deve alterar os valores de pixel, porém, será efetivamente uma operação de reorientação.

@JoostJM Obrigado por sua resposta novamente, muito apreciado.

@fedorov Como reamostrar a máscara ou imagem resolveria o problema de Y ser invertido em uma relação à outra? Isso não está claro para mim. Meu entendimento foi que a reamostragem foi usada para garantir o mesmo tamanho / dimensões.

Se você quiser mais detalhes sobre como garantir a correspondência das dimensões, entre em contato conosco.

Sim, adoraria obter mais informações sobre como garantir a correspondência das dimensões. Por favor e obrigado.

Na verdade, eu adoraria qualquer informação ou recurso que pudesse fortalecer minha compreensão de alguns desses conceitos que discutimos (isto é, espaçamento, direção, origem). Ingenuamente, posso pensar que a origem de uma imagem são as coordenadas [x, y, z] de [0,0,0], ou seja, um dos "cantos" (superior anterior direito?) De uma matriz de imagem . Os valores de origem mostrados no registro têm uma interpretação tangível (por exemplo, milímetros ou algo assim)? O que esses números representam? Perdoe minha ignorância, sou autodidata em todas as coisas de imagem / radiômica, mas quero entender esses conceitos em vez de apenas implementar soluções para fazer as coisas funcionarem.

@warkentinmatt não se preocupe com todas as questões, é uma curva íngreme para um iniciante!

Como reamostrar a máscara ou imagem resolveria o problema de Y ser invertido em uma relação à outra?

As direções da imagem são essencialmente uma transformação que gira o sistema de coordenadas da matriz da imagem (IJK) para o espaço anatômico (XYZ).

Em um exemplo 1D simples abaixo, "índice de matriz" é o sistema de coordenadas de sua matriz 1d e Esquerda-Direita é o sistema de coordenadas no espaço físico de um mundo 1d. Na Image2, a ordem dos valores na matriz é oposta à direção do eixo do sistema de coordenadas físicas, ou seja, a matriz de voxels é orientada de forma diferente. A reamostragem pega a geometria de uma imagem definida pelo tamanho do array, direção e origem, e amostras de valores de outra imagem nos voxels da geometria de referência.

image

Isso faz sentido?

Se você quiser mais detalhes sobre como garantir a correspondência das dimensões ao exportar o rótulo do 3D Slicer, informe-nos.
Sim, adoraria obter mais informações sobre como garantir a correspondência das dimensões. Por favor e obrigado.

Veja a captura de tela abaixo sobre como exportar um segmento do 3D Slicer para um mapa de etiquetas (que é uma imagem binária). Observe que, mesmo se você seguir este procedimento, não é impossível que você ainda obtenha incompatibilidade de geometria, uma vez que os valores de tolerância padrão são muito restritos. Mas a orientação da matriz da imagem deve ser a mesma.

image

Na verdade, eu adoraria qualquer informação ou recurso que pudesse fortalecer minha compreensão de alguns desses conceitos que discutimos (isto é, espaçamento, direção, origem). Ingenuamente, posso pensar que a origem de uma imagem são as coordenadas [x, y, z] de [0,0,0], ou seja, um dos "cantos" (superior anterior direito?) De uma matriz de imagem . Os valores de origem mostrados no registro têm uma interpretação tangível (por exemplo, milímetros ou algo assim)? O que esses números representam? Perdoe minha ignorância, sou autodidata em todas as coisas de imagem / radiômica, mas quero entender esses conceitos em vez de apenas implementar soluções para fazer as coisas funcionarem.

Aqui estão alguns recursos que podem ser úteis:

Espero que isto ajude!

@fedorov Muito obrigado por

Portanto, parece-me que a reamostragem tem dois papéis importantes, mas distintos para garantir o alinhamento geométrico entre dois volumes:

1) Primeiro, se necessário, preencha o volume menor até que corresponda ao mesmo tamanho / dimensionalidade do volume de referência, usando algo como vizinhos mais próximos.

2) Em seguida, uma vez que o dimensionamento esteja de acordo, use a origem, direção e informações de espaçamento para amostrar os voxels de modo a garantir que dois volumes que estão em alinhamento anatômico / físico também sejam indexados da mesma maneira (ou seja, alinhamento de índice de matriz )

Este seria um resumo justo do papel da reamostragem?

Além disso, obrigado por compartilhar esses recursos, espero trabalhar com eles para fortalecer meu entendimento.

@warkentinmatt , quase. As etapas 1 e 2 acontecem simultaneamente. O que ocorre é que você define uma grade de pontos no espaço físico e então faz uma amostra de sua imagem nesses pontos. Se os novos pontos de grade não corresponderem exatamente aos pontos de grade existentes, novos valores são calculados usando o algoritmo de interpolação, com base nos pontos circundantes (originais) (pixels) da imagem.

Você também pode encontrar uma explicação extensa nos documentos do IBSI, seção sobre interpolação . Também contém uma imagem ilustrando a grade de reamostragem.

@fedorov @JoostJM Sei que este problema está encerrado e agradeço profundamente toda a ajuda que vocês dois forneceram. Eu só queria circular de volta para esclarecer meu entendimento. Depois de ler muito os recursos que cada um de vocês forneceu e assistir a alguns vídeos úteis, estou muito mais confortável com esses conceitos.

Portanto, no exemplo que gerou esse problema, uma vez que a máscara foi derivada da mesma imagem que estava sendo usada para extração de recursos, o espaçamento era o mesmo. Em outras palavras, uma mudança de uma unidade na localização do voxel em qualquer uma das direções x, y, z para a máscara ou a imagem carrega a mesma interpretação de mudança física. Nos casos em que o espaçamento é o mesmo entre a máscara e a imagem, a interpolação não seria necessária para "preencher as lacunas", correto? Portanto, a reamostragem da máscara simplesmente garantiria uma correspondência geométrica com a imagem no que diz respeito à dimensionalidade do volume, bem como garantir que ambos os volumes que estão em alinhamento físico também sejam indexados por matriz da mesma maneira (em relação à direção e origem ) Eu entendi corretamente, finalmente?

Se o espaçamento não fosse o mesmo entre dois volumes, você precisaria interpolar voxels para obter dois volumes em alinhamento geométrico. Por exemplo, se o espaçamento da imagem era [1 mm, 1 mm, 1 mm] e a máscara era [2 mm, 2 mm, 2 mm], você precisaria interpolar voxels na máscara para "preencher as lacunas" no espaçamento físico.

Obrigado novamente por toda a sua ajuda. Isso fez toda a diferença.

@warkentinmatt sim, faz sentido, acho que acertou!

Outro recurso útil relacionado à reamostragem é esta página: https://www.slicer.org/wiki/Registration : Reamostragem

@fedorov Ótimo. Obrigado novamente por toda a sua ajuda.

Pode não ser a hora nem o lugar, mas, pelo que sei, você mora em Boston. Atualmente, sou candidato a doutorado em Toronto, mas estou me mudando para Boston dentro de duas semanas para uma bolsa de pesquisa no Departamento de Bioestatística do HSPH. Eu adoraria entrar em contato pessoalmente e talvez continuar algumas dessas conversas, se você estiver interessado e tiver tempo. De qualquer forma, sua ajuda foi muito apreciada.

Claro, prazer em conectar e aprender mais sobre como você usa a piradiômica! Basta enviar-me um e-mail (é público no meu perfil no github) e podemos nos encontrar para um café.

Esta página foi útil?
0 / 5 - 0 avaliações