projects:otolaryngologist:task_calculate_volume

Расчёт объёма дыхательных пазух: выбор алгоритма/подхода

Способ через отдельную 3D-программу

В качестве 3D программы для пробы того, что будет ли способ вообще рабочим, был выбран Blender, так как он поддерживает множество форматов, а также является open source приложением.

Преобразование nii в stl

Было попробовано преобразование nii в stl через данный метод:
http://wiki.osll.ru/doku.php/projects:otolaryngologist:nii_to_mesh

Вывод:

данная идея была сразу отброшена по данным причинам:

  • Сложно настраивать параметры, потому что нужно каждый раз запускать скрипты, чтобы увидеть результат, так ещё и через двойной прогон;

    Без настройки параметров можно увидеть множество шумов и дефектов.

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

Анализ библиотек плагинов 3D Slicer'а для экспорта моделей

  • Были найдены библиотеки для Python для импорта/экспорта разных 3D форматов из сохранённой сегментации, но это всё не равно не удобно, ведь присутствует дополнительный шаг, хоть и эта библиотека работает с уже сегментированном форматом файлов, поэтому позволяет подготовить сегментацию в 3D Slicer;

    Вот пример:
    https://pypi.org/project/slicerio/
  • Также было найдено, что есть такой набор библиотек, как VTK. Он встроен в 3D Slicer, что позволяет вообще нативно проводить сегментацию снимков (с множеством настроек), а также имеется так нужный нам экспорт.

Вывод:

мы нашли способ позволяющий несколькими кликами экспортировать медицинские снимки в популярные 3D форматы файлов.

Инструкция по способам:

Давайте тут подробнее разберём шаги:

  1. Через 3D Slicer открываем наши снимки (пусть для примера это будут nii файл);
  2. Далее нам нужно сегментировать нашу область, поэтому мы открываем Segment Editor, который находится в списке всех модулей по такому пути: Segmentation/Segment Editor;
  3. Создаём области с помощью инструмента Threshold: крутим нижний порог до того, чтобы на взгляд почти везде хорошо выглядело;
  4. А на тех местах, где не дорисовывается стенка — мы используем инструмент Paint и дорисовываем;
  5. Затем мы переименовываем сегмент, как нам удобно и нажимаем на выдвигающиеся меню на кнопке с зелёной стрелкой. И выбираем пункт Export to Files. Выбираем разве что только путь, куда сохранится 3D объект и его формат (будем использовать stl);
  6. Уже в Blender удаляем внешние стенки (например через выделение через лассо с включенным x-ray с видом сверху);
  7. Затем удаляем аккуратно всё лишнее посередине через тот же способ, что и выше;
  8. Пользуемся функцией выделения сетки по соседям (Select Linked) на каждой из пазухи и делаем Separate в отдельные объекты;
  9. Подчищаем геометрию, если нужно. Можно даже подчистить с дырками;
  10. Ставим плагин под названием Mesh: 3D-print Toolbox;
  11. Открываем в свойствах объекта вкладку 3D-print и пользуемся функцией Clean Up/Make Manifold, которая заделывает дырки;
  12. И нажимаем Statistics/Volume - и видим объём.

Проблемы и попытки их решить

3D объект получается большим

Из-за чего Blender'у немного тяжело работать в Edit Mode (может в среднем думать по 2-3 секунды на приблизительно среднем железе), когда мы пытаемся сделать какие-нибудь преобразования над мешем.

  • Можно уменьшить кол-во сетки с помощью модификатор Decimate в Blender'е, но тогда мы можем потерять в точности вычисления объёма, поэтому мы отказались от такой идеи;
  • Можно в 3D Slicer'е воспользоваться инструментом Scissors, и он нам помогает, но уже в Blender придётся аккуратнее тогда удалять стенки, которые появляются ещё в 3D Slicer'е из-за логики работы сегментации. Ну и ножницами тоже надо аккуратнее пользоваться, чтобы не отрезать лишнего.

Невозможно подкрутить идеально Threshold в 3D Slicer'е.

На выбор у нас есть множество пресетов: Otsu, Huang, IsoData, Kittler-Illingworth, Maximum Entropy, Moments, Renyi entropy, Shanbhag, Triangle, Yen, но они все не работали хорошо на примере, который мы рассматривали и приходилось вручную двигать ползунки threshold'а.

Но если даже двигать их вручную, то появлялись такие артефакты, как не бралась область, где на глаз должна быть стенка. Но когда мы увеличивали значение threshold'а, то брались области, где должна быть пустота.

Учитывая пункты выше можно пойти двумя путями:

  1. threshold не надо завышать и нужно будет воспользоваться инструментом Paint и провести стенки самим, там где они должны были быть. В основном это не надо делать больше, чем на нескольких снимках;
  2. Либо же чуток завышаем threshold, чтобы появлялись артефакты, но мы ими просто пренебрегаем, потому что нам нужна приблизительная оценка.

Только встроенными инструментами 3D Slicer'а

С использованием только лишь встроенного функционала

Был проанализирован функционал 3D Slicer'а и были найдены такие интересные инструменты, как:

  1. Инструмент под названием Islands в модуле Segment Editor;
  2. Модуль Segment Statistics, который находится в категории Quantification.

Сочетание этих двух инструментов открыл ещё более быстрый и удобный путь для получения объёма.

Инструкция по нему

  1. Первые шаги по открытии и выбора модуля Segmentation/Segment Editor аналогичны;
  2. Выбираем один из способов выделения областей описанных после данной инструкции и применяем его;
  3. Далее выбираем инструмент Islands и далее выбираем Split Islands to segments. В Minumum Size ставим, например, 70k (данное значение позволило выделить только пазухи и оставляет всё ещё запас, потому что по итогу пазухи имели в районе 130k вокселей, поэтому можно даже взять значение больше и не бояться, что пазухи не выделяться);
  4. Возможно перед следующим шагом надо будет применить ещё какие-то инструкции из способов ниже;
  5. Тут у нас должны остаться лишь 3 сегмента: две пазухи и внешняя часть. Если это не так, то можем повысить кол-во вокселей на прошлом шаге или вручную удалить, ведь внешнюю часть в любом случае надо будет удалить;
  6. Затем заходим в Quantification/Segment Statistics и выбираем в поле Segmentation нашу сегментацию с двумя сегментами (то есть нашими пазухами);
  7. В `Advanced` по умолчанию настроено уже, как нам нужно, но можно на всякий случай убрать Scalar Volume, потому что там будут идентичные результаты с Label Map (хотя он и так работает только тогда, когда выбираешь Scalar Volume вместо None, поэтому и говорю, что и по умолчанию всё хорошо);

    Не знаю почему, но Closed Surface работает только тогда, когда в Segment Editor включено Show 3D.

  8. Нажимаем Apply и видим результаты. Их разбор будет пунктом ниже.

Алгоритмы выбора сегментов

  1. Вручную дополнять:

    Это случай с завышением Threshold'а и ручным дорисовыванием с помощью инструмента Paint или убирать лишнее с помощью Erase.

    Результаты:

    Segment Voxel count Volume mm3 (LM) Volume cm3 (LM) Surface mm2 Volume mm3 (CS) Volume cm3 (CS)
    Left 152976 19122 19.122 4232.45 19179 19.179
    Right 136020 17002.5 17.0025 4071.59 17056.4 17.0564

    Вывод: Это не удобно и очень муторно.

  2. Завысить Threshold:

    Главное, чтобы пазуха была отдельной частью.

    Результаты:

    Segment Voxel count Volume mm3 (LM) Volume cm3 (LM) Surface mm2 Volume mm3 (CS) Volume cm3 (CS)
    Left 121637 15204.6 15.2046 4759.2 15287.7 15.2877
    Right 118842 14855.2 14.8552 4451.78 14916.7 14.9167

    Вывод: имеет смысл, но результаты получаются не такими точными и красивыми, как хотелось бы.

  3. Завысить Threshold c Smoothing

    Инструкция:

    1. после применения инструмента Islands мы для каждой из пазух применяем инструмент Smoothing в режиме Closing (fill holes) (я выбрал 10.00mm);

    Результаты:

    Segment Voxel count Volume mm3 (LM) Volume cm3 (LM) Surface mm2 Volume mm3 (CS) Volume cm3 (CS)
    Left 135528 16941 16.941 3913.61 16994 16.994
    Right 132024 16503 16.503 3928.04 16556.9 16.5569

    Вывод: уже в разы лучше.

  4. С использованием Margin и опционально Smoothing

    Инструкция:

    1. Тут мы можем взять такой Threshold, который будет удовлетворять визуально и забить на то, что могут быть небольшие соединения пазух с другими элементами носа. И применяем его;
    2. Затем мы выбираем инструмент Margin и выбираем в Operation: Shrink, а в качестве размера можно выбрать: 1.00mm;
    3. Далее используем инструмент Islands по инструкции выше;
    4. И теперь применяем обратно Margin с таким же размером, но в режиме: Grow.
    5. Опционально ещё можем применить инструмент Smoothing в режиме Closing (fill holes), например, на 5.00mm.

    Результаты только с Margin:

    Segment Voxel count Volume mm3 (LM) Volume cm3 (LM) Surface mm2 Volume mm3 (CS) Volume cm3 (CS)
    Left 145906 18238.2 18.2383 4250.16 18284.2 18.2842
    Right 141524 17690.5 17.6905 4202.23 17743 17.743

    Результаты также ещё и с Closing (fill holes) (5.00mm):

    Segment Voxel count Volume mm3 (LM) Volume cm3 (LM) Surface mm2 Volume mm3 (CS) Volume cm3 (CS)
    Left 148392 18549 18.549 4114.5 18608.2 18.6082
    Right 143420 17927.5 17.9275 4130.2 17986.9 17.9869

    Выводы: По результатам можно увидеть, что Smoothing (остальные алгоритмы я тоже пробовал, но они визуально работали хуже) не особо что-то меняет, потому что и без него получается очень приближённо к настоящим результатам, но при этом он бывает сглаживает не в тех местах, из-за чего, например, Right и повысился. Поэтому данный способ лучше юзать без сглаживания (либо с небольшим сглаживанием, но результаты тогда будут на уровне +1-2%).

Выводы

Лучше всего показал себя последний способ с использованием Margin и без Smoothing. И если я правильно вручную выбрал пазухи, то отставание получается в примерно 5%.

На чём остановились

projects/otolaryngologist/task_calculate_volume.txt · Last modified: 2024/02/23 22:37 by psamsonov