Posted in

Introdução à imagem médica 3D para aprendizado de máquina: pré -processamento e aumento

Quando percebi que não posso aplicar pipelines comuns de processamento de imagens em imagens médicas, fiquei completamente desencorajado. Por que essa funcionalidade não existe? Então, eu inventei este post (mais um caderno) para indivíduos desencorajados que, como eu, estão interessados ​​em resolver problemas de imagem médica.

Já discutimos a segmentação de imagem médica e alguns antecedentes iniciais em Sistemas de coordenadas e arquivos dicom. Minha experiência no campo me leva a continuar com o entendimento dos dados, o pré -processamento e alguns aumentos. Como eu sempre digo, se você apenas entende seus dados e suas particularidades, provavelmente está jogando bingo. No campo da imagem médica, encontro algumas manipulações de dados, que são fortemente usadas no pré-processamento e aumento nos métodos de última geração, para serem críticos em nosso entendimento. Para esse fim, eu forneço um caderno para todos brincarem. Ele realiza transformações em imagens médicas, que são simplesmente uma grade estruturada em 3D.

Para se aprofundar na maneira como a IA é usada na medicina, você não pode errar com o Você tem para medicina Curso on -line, oferecido pela Coursera. Se você deseja se concentrar na análise de imagem médica com aprendizado profundo, eu recomendo começar a partir do Curso de Udemy, baseado em Pytorch.

Dados: Tocaremos com 2 imagens de ressonância magnética fornecidas pela Nibabel (Biblioteca Python) para fins de ilustração. As imagens são armazenadas como arquivos bacanas. Mas antes disso, vamos escrever algum código para visualizar os volumes médicos 3D.

As imagens serão mostradas em 3 aviões: sagital, coronal e axial da esquerda para a direita durante todo o post.

Visualização de planos bidimensionais

Em todo o tutorial, usaremos extensivamente uma função que visualiza as três fatias medianas no sagital, coronal e axial aviões respectivamente. Vamos escrever uma função mínima para fazê -lo:

def show_mid_slice(img_numpy, title='img'):

"""

Accepts an 3D numpy array and shows median slices in all three planes

"""

assert img_numpy.ndim == 3

n_i, n_j, n_k = img_numpy.shape

center_i1 = int((n_i - 1) / 2)

center_j1 = int((n_j - 1) / 2)

center_k1 = int((n_k - 1) / 2)

show_slices((img_numpy(center_i1, :, :),

img_numpy(:, center_j1, :),

img_numpy(:, :, center_k1)))

plt.suptitle(title)

def show_slices(slices):

"""

Function to display a row of image slices

Input is a list of numpy 2D image slices

"""

fig, axes = plt.subplots(1, len(slices))

for i, slice in enumerate(slices):

axes(i).imshow(slice.T, cmap="gray", origin="lower")

Nada mais do que o matplotlib “imshow" e manipulações de matriz de Numpy. Para o registro, as imagens médicas são um único canal e as visualizamos em cores em escala de cinza.

As duas imagens que usaremos para brincar com uma infinidade de transformações podem ser ilustradas abaixo:

show_mid_slice(epi_img_numpy, 'first image')

show_mid_slice(anatomy_img_numpy,'second image')




Cérebro-Mris-Tutorial-Nibabel


As imagens iniciais de ressonância magnética do cérebro que usaremos.

Agora estamos prontos para ir! Vamos começar com redimensionar e redimensionar imagens médicas. Sim, não é exatamente o mesmo.

Redimensionamento da imagem médica (para baixo/amostragem)

O Scipy A biblioteca fornece muitas funcionalidades para imagens multidimensionais. Como as imagens médicas são tridimensionais, muitas funcionalidades podem ser usadas. Desta vez vamos usar Scipy.nowage.tinepoinntion.zoom para redimensionar a imagem nas dimensões desejadas. Isso é semelhante à amostragem em uma imagem 2D. A mesma função pode ser usada para interpolação para aumentar as dimensões espaciais. Como ilustração, dobraremos e metade do tamanho da imagem original.

Lembre -se de que, nesse tipo de transformação, as proporções geralmente são importantes a serem mantidas.

Você provavelmente não quer perder a anatomia do corpo humano 🙂

import scipy

def resize_data_volume_by_scale(data, scale):

"""

Resize the data based on the provided scale

"""

scale_list = (scale,scale,scale)

return scipy.ndimage.interpolation.zoom(data, scale_list, order=0)

result = resize_data_volume_by_scale(epi_img_numpy, 0.5)

result2 = resize_data_volume_by_scale(epi_img_numpy, 2)

Esse tipo de escala é geralmente chamado de isométrica. Honestamente, eu não sou um grande fã da terminologia da Scipy para usar a palavra zoom para essa funcionalidade.




redimensionado-mri-imagens


Desmembrou a imagem e ascensão por um fator de 2

É muito comum reduzir a imagem em uma dimensão mais baixa para o aprendizado de máquina pesado.

Observe que há outro tipo de redimensionamento. Em vez de fornecer a forma de saída desejada, você especifica o tamanho da voxel desejado (por exemplo, voxel_size = (1,1,1) mm). Nibabel fornece uma função chamada reample_to_output (). Funciona com arquivos nifti e não com matrizes Numpy. Honestamente, eu não recomendaria sozinho, pois as imagens resultantes podem não ter a mesma forma. Isso pode ser um problema para o aprendizado profundo. Por exemplo, para criar lotes com Dataloaders, a dimensão deve ser consistente nas instâncias. No entanto, você pode optar por incluí -lo em uma etapa anterior em seu pipeline. Pode ser usado para trazer imagens diferentes para ter o mesmo tamanho de voxel ou similar.

Redaling de imagem médica (zoom-in/out)

O redimensionamento pode ser considerado como uma transformação afim. Vamos dar um zoom aleatoriamente dentro e fora da imagem. Esse aumento geralmente ajuda o modelo a aprender recursos invariantes em escala.

def random_zoom(matrix,min_percentage=0.7, max_percentage=1.2):

z = np.random.sample() *(max_percentage-min_percentage) + min_percentage

zoom_matrix = np.array(((z, 0, 0, 0),

(0, z, 0, 0),

(0, 0, z, 0),

(0, 0, 0, 1)))

return ndimage.interpolation.affine_transform(matrix, zoom_matrix)




Zoom-in-out-ri


Zoom aleatório e amplie o zoom

É importante ver que a área vazia está preenchida com pixels pretos (intensidade zero).

Observe aqui que o ar circundante em imagens médicas não tem intensidade zero. O preto é realmente relativo às imagens médicas.

O próximo na lista é a rotação 3D.

Rotação de imagem médica

A rotação é um dos métodos mais comuns para obter aumento de dados na visão computacional. Também tem sido considerado uma técnica auto-supervisionada com resultados notáveis ​​(Spyros Gidaris et al. ).

Na imagem médica, é uma funcionalidade de importação igual que também tem sido usada de pré-treinamento auto-supervisionado (Xinrui Zhuang et al. 2019 ). Uma rotação 3D aleatória simples em uma determinada gama de graus pode ser ilustrada com o código abaixo:

def random_rotate3D(img_numpy, min_angle, max_angle):

"""

Returns a random rotated array in the same shape

:param img_numpy: 3D numpy array

:param min_angle: in degrees

:param max_angle: in degrees

"""

assert img_numpy.ndim == 3, "provide a 3d numpy array"

assert min_angle < max_angle, "min should be less than max val"

assert min_angle > -360 or max_angle < 360

all_axes = ((1, 0), (1, 2), (0, 2))

angle = np.random.randint(low=min_angle, high=max_angle+1)

axes_random_id = np.random.randint(low=0, high=len(all_axes))

axes = all_axes(axes_random_id)

return scipy.ndimage.rotate(img_numpy, angle, axes=axes)




rotação3d


Rotação 3D aleatória

Simplesmente temos que definir o eixo e o ângulo de rotação. Como a escala proporcionou ao modelo mais diversidade, a fim de aprender características invariantes em escala, a rotação auxilia no aprendizado de recursos invariantes à rotação.

O próximo em nossa lista é o lançamento da imagem.

Flip de imagem médica

Semelhante às imagens RGB comuns, podemos executar o eixo lançando imagens médicas. Neste ponto, é realmente importante esclarecer uma coisa:

Quando realizamos aumentos e/ou pré -processamento em nossos dados, podemos ter que aplicar operações semelhantes nos dados da verdade no solo.

Por exemplo, se resolvermos a tarefa de Segmentação de imagem médicaé importante virar o mapa de segmentação de destino. Uma implementação simples pode ser encontrada abaixo:

def random_flip(img, label=None):

axes = (0, 1, 2)

rand = np.random.randint(0, 3)

img = flip_axis(img, axes(rand))

img = np.squeeze(img)

if label is None:

return img

else:

y = flip_axis(y, axes(rand))

y = np.squeeze(y)

return x, y

def flip_axis(x, axis):

x = np.asarray(x).swapaxes(axis, 0)

x = x(::-1, ...)

x = x.swapaxes(0, axis)

return x




flipação de imagem


A imagem inicial como referência e duas versões invertidas

Observe que, lançando um eixo, duas visualizações mudam. A primeira imagem na parte superior é a imagem inicial como referência.

Mudança de imagem médica (deslocamento)

Aqui eu gostaria de dizer outra coisa.

Rotação, mudança e escala não passam de transformações afins.

Às vezes, eu os implemento apenas definindo as transformações afins e aplico na imagem com o Scipy, e às vezes uso as funções já implementadas para o processamento de imagem multidimensional.

Para usar esta operação no meu pipeline de aumento de dados, você pode ver que incluí uma função de wrapper. Este último basicamente amostra um número aleatório, geralmente na faixa desejada, e chama a função de transformação afim. Abaixo está a implementação para mudança/deslocamento aleatório.

def transform_matrix_offset_center_3d(matrix, x, y, z):

offset_matrix = np.array(((1, 0, 0, x), (0, 1, 0, y), (0, 0, 1, z), (0, 0, 0, 1)))

return ndimage.interpolation.affine_transform(matrix, offset_matrix)

def random_shift(img_numpy, max_percentage=0.4):

dim1, dim2, dim3 = img_numpy.shape

m1,m2,m3 = int(dim1*max_percentage/2),int(dim1*max_percentage/2), int(dim1*max_percentage/2)

d1 = np.random.randint(-m1,m1)

d2 = np.random.randint(-m2,m2)

d3 = np.random.randint(-m3,m3)

return transform_matrix_offset_center_3d(img_numpy, d1, d2, d3)




Disponed-Mris


As imagens médicas deslocadas

Esse aumento não é muito comum no aumento da imagem médica, mas os incluímos aqui para completar.

A razão pela qual não incluímos é que as redes neurais convolucionais são, por definição, projetadas para aprender recursos invariantes à tradução.

Cultura 3D aleatória

O corte também não é significativamente diferente das imagens naturais. No entanto, lembre -se de que geralmente precisamos levar todas as fatias de uma dimensão e precisamos cuidar disso. O motivo é que uma dimensão pode ter menos fatias que as outras. Por exemplo, uma vez eu tive que lidar com uma imagem de 384x384x64, que é comum em Imagens de CT.

def crop_3d_volume(img_tensor, crop_dim, crop_size):

assert img_tensor.ndim == 3, '3d tensor must be provided'

full_dim1, full_dim2, full_dim3 = img_tensor.shape

slices_crop, w_crop, h_crop = crop_dim

dim1, dim2, dim3 = crop_size

if full_dim1 == dim1:

img_tensor = img_tensor(:, w_crop:w_crop + dim2,

h_crop:h_crop + dim3)

elif full_dim2 == dim2:

img_tensor = img_tensor(slices_crop:slices_crop + dim1, :,

h_crop:h_crop + dim3)

elif full_dim3 == dim3:

img_tensor = img_tensor(slices_crop:slices_crop + dim1, w_crop:w_crop + dim2, :)

else:

img_tensor = img_tensor(slices_crop:slices_crop + dim1, w_crop:w_crop + dim2,

h_crop:h_crop + dim3)

return img_tensor

def find_random_crop_dim(full_vol_dim, crop_size):

assert full_vol_dim(0) >= crop_size(0), "crop size is too big"

assert full_vol_dim(1) >= crop_size(1), "crop size is too big"

assert full_vol_dim(2) >= crop_size(2), "crop size is too big"

if full_vol_dim(0) == crop_size(0):

slices = crop_size(0)

else:

slices = np.random.randint(full_vol_dim(0) - crop_size(0))

if full_vol_dim(1) == crop_size(1):

w_crop = crop_size(1)

else:

w_crop = np.random.randint(full_vol_dim(1) - crop_size(1))

if full_vol_dim(2) == crop_size(2):

h_crop = crop_size(2)

else:

h_crop = np.random.randint(full_vol_dim(2) - crop_size(2))

return (slices, w_crop, h_crop)




Crop aleatório


Exemplo de corte aleatório

Existem outras técnicas para o corte que se concentram na área em que estamos interessados, ou seja, o tumor, mas não entraremos nisso agora.

Até agora, brincamos com transformações geométricas. Vamos ver o que podemos fazer com a intensidade da imagem.

Valores de intensidade do clipe (outliers)

Esta etapa não é aplicável a este tutorial, mas pode ser bastante útil em geral. Especialmente para Imagens de CT. A razão pela qual não é aplicável é que as imagens de ressonância magnética estão em uma gama bastante estreita de valores.

def percentile_clip(img_numpy, min_val=5, max_val=95):

"""

Intensity normalization based on percentile

Clips the range based on the quartile values.

:param min_val: should be in the range (0,100)

:param max_val: should be in the range (0,100)

:return: intensity normalized image

"""

low = np.percentile(img_numpy, min_val)

high = np.percentile(img_numpy, max_val)

img_numpy(img_numpy < low) = low

img_numpy(img_numpy > high) = high

return img_numpy

def clip_range(img_numpy, min_intensity=10, max_intensity=240):

return np.clip(img_numpy, min_intensity, max_intensity)

Normalização de intensidade em imagens médicas

Aqui, incluo as normalizações de intensidade mais comuns: min-max e média/std. Uma coisinha a ter em mente:

Quando realizamos normalização média/padrão, geralmente omitimos os voxels de intensidade zero a partir do cálculo da média. Isso vale principalmente para imagens de ressonância magnética.

Uma maneira de olhar para isso é se tivermos uma imagem cerebral; Provavelmente não queremos normalizá -lo com a intensidade dos voxels ao seu redor.

def normalize_intensity(img_tensor, normalization="mean"):

"""

Accepts an image tensor and normalizes it

:param normalization: choices = "max", "mean" , type=str

For mean normalization we use the non zero voxels only.

"""

if normalization == "mean":

mask = img_tensor.ne(0.0)

desired = img_tensor(mask)

mean_val, std_val = desired.mean(), desired.std()

img_tensor = (img_tensor - mean_val) / std_val

elif normalization == "max":

MAX, MIN = img_tensor.max(), img_tensor.min()

img_tensor = (img_tensor - MIN) / (MAX - MIN)

return img_tensor

Não faz sentido visualizar essa transformação, pois seu objetivo é alimentar os dados pré -processados ​​no modelo de aprendizado profundo. Obviamente, qualquer outro tipo de normalização da intensidade pode ser aplicado em imagens médicas.

Deformação elástica

Quando li essa transformação pela primeira vez no artigo UNET original, não entendi uma única palavra do parágrafo:

“Quanto às nossas tarefas, existem muito poucos dados de treinamento disponíveis, usamos o aumento excessivo de dados aplicando deformações elásticas às imagens de treinamento disponíveis. Isso permite que a rede aprenda invariância a tais deformações, sem a necessidade de ver essas transformações na imagem mais comum no segmento de imagens mais importantes. Olaf Ronneberger et al. 2015 (sonhos de papel)

Honestamente, não analisei a publicação original de 2003. E você provavelmente também não. Eu olhei para outras implementações de código e tentei torná -lo mais simples. Decidi incluí -lo no meu tutorial porque você verá muito na literatura.

def elastic_transform_3d(image, labels=None, alpha=4, sigma=35, bg_val=0.1):

"""

Elastic deformation of images as described in

Simard, Steinkraus and Platt, "Best Practices for

Convolutional Neural Networks applied to Visual

Document Analysis", in

Proc. of the International Conference on Document Analysis and

Recognition, 2003.

Modified from:

https://gist.github.com/chsasank/4d8f68caf01f041a6453e67fb30f8f5a

https://github.com/fcalvet/image_tools/blob/master/image_augmentation.py#L62

Modified to take 3D inputs

Deforms both the image and corresponding label file

image linear/trilinear interpolated

Label volumes nearest neighbour interpolated

"""

assert image.ndim == 3

shape = image.shape

dtype = image.dtype

coords = np.arange(shape(0)), np.arange(shape(1)), np.arange(shape(2))

im_intrps = RegularGridInterpolator(coords, image,

method="linear",

bounds_error=False,

fill_value=bg_val)

dx = gaussian_filter((np.random.rand(*shape) * 2 - 1), sigma,

mode="constant", cval=0.) * alpha

dy = gaussian_filter((np.random.rand(*shape) * 2 - 1), sigma,

mode="constant", cval=0.) * alpha

dz = gaussian_filter((np.random.rand(*shape) * 2 - 1), sigma,

mode="constant", cval=0.) * alpha

x, y, z = np.mgrid(0:shape(0), 0:shape(1), 0:shape(2))

indices = np.reshape(x + dx, (-1, 1)), \

np.reshape(y + dy, (-1, 1)), \

np.reshape(z + dz, (-1, 1))

image = np.empty(shape=image.shape, dtype=dtype)

image = im_intrps(indices).reshape(shape)

if labels is not None:

lab_intrp = RegularGridInterpolator(coords, labels,

method="nearest",

bounds_error=False,

fill_value=0)

labels = lab_intrp(indices).reshape(shape).astype(labels.dtype)

return image, labels

return image




Deformação elástica


Antes e depois da deformação elástica

O que você precisa ter em mente é que essa transformação altera a intensidade e aplica algum ruído gaussiano em cada dimensão. Para mais informações, você deve voltar ao original trabalhar.

Conclusão

Até agora você pode ressoar com meus pensamentos sobre as particularidades sobre o pré -processamento e os aumentos de imagens médicas. Mas não se esqueça: você pode brincar com o tutorial online E veja as transformações sozinho. Ajuda, acredite em mim. Compreender nossas imagens médicas é importante. Agora você pode escolher quais transformações para aplicar em seu projeto.

Se você gostou do nosso tutorial, sinta -se à vontade para compartilhá -lo em sua página de mídia social, como uma recompensa pelo nosso trabalho. Seria muito apreciado.

Fique ligado para mais artigos de verão da IA!

Aprendizagem profunda no livro de produção 📖

Aprenda a construir, treinar, implantar, escalar e manter modelos de aprendizado profundo. Entenda a infraestrutura de ML e os MLOPs usando exemplos práticos.

Saber mais

* Divulgação: Observe que alguns dos links acima podem ser links de afiliados e, sem nenhum custo adicional, ganharemos uma comissão se você decidir fazer uma compra depois de clicar.

Luis es un experto en Ciberseguridad, Computación en la Nube, Criptomonedas e Inteligencia Artificial. Con amplia experiencia en tecnología, su objetivo es compartir conocimientos prácticos para ayudar a los lectores a entender y aprovechar estas áreas digitales clave.

Leave a Reply

Your email address will not be published. Required fields are marked *

sabong international app