paint-brush
Можете ли вы открыть медицинские данные (МР, КТ, рентген) на Python и найти опухоли с помощью ИИ?! Может бытьк@thebojda
3,808 чтения
3,808 чтения

Можете ли вы открыть медицинские данные (МР, КТ, рентген) на Python и найти опухоли с помощью ИИ?! Может быть

к Laszlo Fazekas8m2024/04/23
Read on Terminal Reader

Слишком долго; Читать

Когда программисты слышат об обработке медицинских данных, они могут подумать, что это что-то серьезное, с чем могут справиться только университеты и исследовательские институты (по крайней мере, я так думал). Как видите, мы говорим о простых изображениях в оттенках серого, которые очень легко обрабатывать и которые идеально подходят для таких задач, как обработка нейронных сетей.
featured image - Можете ли вы открыть медицинские данные (МР, КТ, рентген) на Python и найти опухоли с помощью ИИ?! Может быть
Laszlo Fazekas HackerNoon profile picture
0-item


Несколько дней назад я прошел МРТ. Меня поместили в большую трубу, и в течение 15 минут машина вокруг меня гудела, гудела и щелкала. По окончании обследования я получил компакт-диск с данными. Что делает хороший разработчик в такой ситуации? Конечно, как только они возвращаются домой, они начинают изучать данные и думать о том, как их можно извлечь с помощью Python .


Компакт-диск содержал кучу DLL, EXE-файл и несколько дополнительных файлов для программы просмотра Windows, которые явно бесполезны в Linux. Суть была в папке с названием DICOM. Он содержал ряд файлов без расширений в восьми папках. ChatGPT помог мне понять, что DICOM — это стандартный формат, обычно используемый для хранения изображений МРТ, КТ и рентгеновских снимков. По сути, это простой формат упаковки, содержащий изображения в оттенках серого и некоторые метаданные.


Следующий код позволяет легко экспортировать файлы в формате PNG:


 import os import pydicom from PIL import Image import sys def save_image(pixel_array, output_path): if pixel_array.dtype != 'uint8': pixel_array = ((pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min()) * 255).astype('uint8') img = Image.fromarray(pixel_array) img.save(output_path) print(f"Saved image to {output_path}") def process_dicom_directory(dicomdir_path, save_directory): if not os.path.exists(save_directory): os.makedirs(save_directory) dicomdir = pydicom.dcmread(dicomdir_path) for patient_record in dicomdir.patient_records: if 'PatientName' in patient_record: print(f"Patient: {patient_record.PatientName}") for study_record in patient_record.children: if 'StudyDate' in study_record: print(f"Study: {study_record.StudyDate}") for series_record in study_record.children: for image_record in series_record.children: if 'ReferencedFileID' in image_record: file_id = list(map(str, image_record.ReferencedFileID)) file_id_path = os.path.join(*file_id) print(f"Image: {file_id_path}") image_path = os.path.join(os.path.dirname(dicomdir_path), file_id_path) modified_filename = '_'.join(file_id) + '.png' image_save_path = os.path.join(save_directory, modified_filename) try: img_data = pydicom.dcmread(image_path) print("DICOM image loaded successfully.") save_image(img_data.pixel_array, image_save_path) except FileNotFoundError as e: print(f"Failed to read DICOM file at {image_path}: {e}") except Exception as e: print(f"An error occurred: {e}") if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: python dicomsave.py <path_to_DICOMDIR> <path_to_save_images>") sys.exit(1) dicomdir_path = sys.argv[1] save_directory = sys.argv[2] process_dicom_directory(dicomdir_path, save_directory)


Код ожидает два параметра. Первый — это путь к файлу DICOMDIR, а второй — каталог, в котором изображения будут сохраняться в формате PNG.


Для чтения файлов DICOM мы используем библиотеку Pydicom . Структура загружается с помощью функции pydicom.dcmread , из которой можно извлечь метаданные (например, имя пациента) и исследования, содержащие изображения. Данные изображения также можно прочитать с помощью dcmread . Необработанные данные доступны через поле пиксельного массива , которое функция save_image преобразует в PNG. Это не так уж сложно.


В результате получается несколько квадратных (обычно 512x512) изображений в оттенках серого, которые можно легко использовать в дальнейшем.


Почему я решил, что важно написать этот короткий рассказ о моем приключении в формате DICOM? Когда программисты слышат об обработке медицинских данных, они могут подумать, что это что-то серьезное, с чем могут справиться только университеты и исследовательские институты (по крайней мере, я так думал). Как видите, мы говорим о простых изображениях в оттенках серого, которые очень легко обрабатывать и которые идеально подходят для таких задач, как обработка нейронных сетей.


Подумайте, какие возможности это открывает. Для чего-то вроде простого детектора опухолей все, что вам нужно, — это относительно простая сверточная сеть и достаточное количество образцов. На самом деле задача мало чем отличается от распознавания собак или кошек на изображениях.


Чтобы проиллюстрировать, насколько это верно, позвольте мне привести пример. После непродолжительного поиска на Kaggle я нашел блокнот, в котором опухоли головного мозга классифицируются на основе данных МРТ (изображения в оттенках серого). Набор данных разделяет изображения на четыре группы: три типа опухолей головного мозга и четвертая категория с изображениями здорового мозга. Архитектура сети следующая:


 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 164, 164, 64) │ 1,664 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 54, 54, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_1 (Conv2D) │ (None, 50, 50, 64) │ 102,464 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_2 (Conv2D) │ (None, 13, 13, 128) │ 131,200 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 6, 6, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_3 (Conv2D) │ (None, 3, 3, 128) │ 262,272 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_3 (MaxPooling2D) │ (None, 1, 1, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten (Flatten) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense (Dense) │ (None, 512) │ 66,048 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_1 (Dense) │ (None, 4) │ 2,052 │ └─────────────────────────────────┴────────────────────────┴───────────────┘


Как видите, первые несколько слоев состоят из сэндвича свертки и максимального объединения, который извлекает структуры опухолей, за которыми следуют два плотных слоя, которые выполняют классификацию. Это точно такая же архитектура, которая используется в примере кода CNN TensorFlow , который использует набор данных CIFAR-10. Я хорошо это помню, так как это была одна из первых нейронных сетей, с которыми я столкнулся. Набор данных CIFAR-10 содержит 60 000 изображений, разделенных на десять классов, включая два класса для собак и кошек, как я упоминал ранее. Архитектура сети выглядит следующим образом:


 Model: “sequential” _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 30, 30, 32) 896 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 15, 15, 32) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 13, 13, 64) 18496 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 6, 6, 64) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 4, 4, 64) 36928 _________________________________________________________________ flatten (Flatten) (None, 1024) 0 _________________________________________________________________ dense (Dense) (None, 64) 65600 _________________________________________________________________ dense_1 (Dense) (None, 10) 650 ================================================================= Total params: 122,570 Trainable params: 122,570 Non-trainable params: 0 _________________________________________________________________


Понятно, что мы имеем дело с одной и той же архитектурой, только с немного разными параметрами, поэтому распознать опухоли головного мозга на МРТ-изображениях действительно не намного сложнее, чем идентифицировать кошек или собак на снимках. Более того, упомянутый блокнот Kaggle способен классифицировать опухоли с эффективностью 99%!


Я просто хочу отметить, насколько простым может быть извлечение и обработка медицинских данных в определенных случаях и как проблемы, которые кажутся сложными, например, обнаружение опухолей головного мозга, часто могут быть весьма эффективно решены с помощью простых традиционных решений.


Поэтому я бы посоветовал всем не уклоняться от медицинских данных. Как видите, мы имеем дело с относительно простыми форматами, и используя относительно простые, устоявшиеся технологии (сверточные сети, преобразователи зрения и т. д.), здесь можно добиться значительного эффекта. Если вы в свободное время присоединитесь к медицинскому проекту с открытым исходным кодом просто в качестве хобби и сможете хоть немного улучшить используемую нейронную сеть, вы потенциально сможете спасти жизни!