projects:otolaryngologist:task_calculate_volume

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
projects:otolaryngologist:task_calculate_volume [2024/02/18 00:53] psamsonovprojects:otolaryngologist:task_calculate_volume [2024/05/06 20:48] (current) – Добавил финальный пайплайн и инструкцию по использованию psamsonov
Line 1: Line 1:
-====== Задача: расчёт объёма дыхательных пазух ======+====== Расчёт объёма дыхательных пазух: выбор алгоритма/подхода ======
  
-===== Способ через 3D =====+===== Способ через отдельную 3D-программу =====
 В качестве 3D программы для пробы того, что будет ли способ вообще рабочим, был выбран Blender, так как он поддерживает множество форматов, а также является open source приложением. В качестве 3D программы для пробы того, что будет ли способ вообще рабочим, был выбран Blender, так как он поддерживает множество форматов, а также является open source приложением.
  
-==== Что было попробовано: ==== +==== Преобразование nii в stl ====
- +
-=== Преобразование nii в stl ===+
 Было попробовано преобразование **nii** в **stl** через данный метод: \\ http://wiki.osll.ru/doku.php/projects:otolaryngologist:nii_to_mesh Было попробовано преобразование **nii** в **stl** через данный метод: \\ http://wiki.osll.ru/doku.php/projects:otolaryngologist:nii_to_mesh
  
-== Вывод: ==+=== Вывод: ===
 данная идея была сразу отброшена по данным причинам: данная идея была сразу отброшена по данным причинам:
     * Сложно настраивать параметры, потому что нужно каждый раз запускать скрипты, чтобы увидеть результат, так ещё и через двойной прогон; \\ \\ Без настройки параметров можно увидеть множество шумов и дефектов. \\  {{:projects:otolaryngologist:nii_to_stl.png?400|}} \\     * Сложно настраивать параметры, потому что нужно каждый раз запускать скрипты, чтобы увидеть результат, так ещё и через двойной прогон; \\ \\ Без настройки параметров можно увидеть множество шумов и дефектов. \\  {{:projects:otolaryngologist:nii_to_stl.png?400|}} \\
     * Также способ сам по себе не удобен и долгий, ведь предполагает использование множества шагов почти несвязанных друг с другом.     * Также способ сам по себе не удобен и долгий, ведь предполагает использование множества шагов почти несвязанных друг с другом.
  
-=== Анализ библиотеки плагинов 3D Slicer'а и его самого === +==== Анализ библиотек плагинов 3D Slicer'а для экспорта моделей ==== 
-    * Были найдены библиотеки для Python для импорта/экспорта разных 3D форматов из сохранённой сегментации, но это всё не равно не удобно, ведь присутствует дополнительный шаг, хоть и эта библиотека работает с уже сегментированном форматом файлов, поэтому позволяет подготовить сегментацию в 3D Slicer; \\ \\ Вот пример: \\ https://pypi.org/project/slicerio/+    * Были найдены библиотеки для Python для импорта/экспорта разных 3D форматов из сохранённой сегментации, но это всё не равно не удобно, ведь присутствует дополнительный шаг, хоть и эта библиотека работает с уже сегментированном форматом файлов, поэтому позволяет подготовить сегментацию в **3D Slicer**; \\ \\ Вот пример: \\ https://pypi.org/project/slicerio/
     * Также было найдено, что есть такой набор библиотек, как **VTK**. Он встроен в **3D Slicer**, что позволяет вообще нативно проводить сегментацию снимков (с множеством настроек), а также имеется так нужный нам экспорт.     * Также было найдено, что есть такой набор библиотек, как **VTK**. Он встроен в **3D Slicer**, что позволяет вообще нативно проводить сегментацию снимков (с множеством настроек), а также имеется так нужный нам экспорт.
  
-== Вывод: ==+=== Вывод: ===
 мы нашли способ позволяющий несколькими кликами экспортировать медицинские снимки в популярные 3D форматы файлов. мы нашли способ позволяющий несколькими кликами экспортировать медицинские снимки в популярные 3D форматы файлов.
  
-== Инструкция по способам: ==+=== Инструкция по способам: ===
 Давайте тут подробнее разберём шаги: Давайте тут подробнее разберём шаги:
     - Через **3D Slicer** открываем наши снимки (пусть для примера это будут **nii** файл);     - Через **3D Slicer** открываем наши снимки (пусть для примера это будут **nii** файл);
     - Далее нам нужно сегментировать нашу область, поэтому мы открываем **Segment Editor**, который находится в списке всех модулей по такому пути: **Segmentation/Segment Editor**;     - Далее нам нужно сегментировать нашу область, поэтому мы открываем **Segment Editor**, который находится в списке всех модулей по такому пути: **Segmentation/Segment Editor**;
-    - Создаём области с помощью инструмента **Threshold**. Выбираем в нём любой алгоритм; +    - Создаём области с помощью инструмента **Threshold**: крутим нижний порог до того, чтобы на взгляд почти везде хорошо выглядело;
-    - Крутим нижний порог до того, чтобы на взгляд почти везде хорошо выглядело;+
     - А на тех местах, где не дорисовывается стенка — мы используем инструмент **Paint** и дорисовываем;     - А на тех местах, где не дорисовывается стенка — мы используем инструмент **Paint** и дорисовываем;
-    - Затем мы переименовываем сегмент, как нам удобно и нажимаем на выдвигающиеся меню на кнопке с зелёной стрелкой. И выбираем пункт `Export to Files`. Выбираем разве что только путь, куда сохранится 3D объект и его формат (будем использовать `stl`); {{:projects:otolaryngologist:export_from_3d_slicer.png?400|}} +    - Затем мы переименовываем сегмент, как нам удобно и нажимаем на выдвигающиеся меню на кнопке с зелёной стрелкой. И выбираем пункт **Export to Files**. Выбираем разве что только путь, куда сохранится 3D объект и его формат (будем использовать **stl**); {{ :projects:otolaryngologist:export_from_3d_slicer.png?400 |}} 
-    - Уже в `Blenderудаляем внешние стенки (например через выделение через лассо с включенным `x-rayс видом сверху); +    - Уже в **Blender** удаляем внешние стенки (например через выделение через лассо с включенным **x-ray** с видом сверху); 
-    - Затем удаляем аккруратно всё лишнее посередине через тот же способ, что и выше; +    - Затем удаляем аккуратно всё лишнее посередине через тот же способ, что и выше; 
-    - Пользуемся функцией выделения сетки по соседям (Select Linked) на каждой из пазухи и делаем `Separateв отдельные объекты;+    - Пользуемся функцией выделения сетки по соседям (Select Linked) на каждой из пазухи и делаем **Separate** в отдельные объекты;
     - Подчищаем геометрию, если нужно. Можно даже подчистить с дырками;     - Подчищаем геометрию, если нужно. Можно даже подчистить с дырками;
-    - Ставим плагин под названием `Mesh: 3D-print Toolbox`+    - Ставим плагин под названием **Mesh: 3D-print Toolbox**
-    - Открываем в свойствах объекта вкладку `3D-printи пользуемся функцией `Clean Up/Make Manifold`, которая заделывает дырки; +    - Открываем в свойствах объекта вкладку **3D-print** и пользуемся функцией **Clean Up/Make Manifold**, которая заделывает дырки; 
-    - И нажимаем `Statistics/Volume- и видим объём. {{:projects:otolaryngologist:calculated_volume.png?400|}}+    - И нажимаем **Statistics/Volume** - и видим объём. {{:projects:otolaryngologist:calculated_volume.png?400|}}
   
-=== Проблемы и попытки их решить ===+==== Проблемы и попытки их решить ====
  
-== 3D объект получается большим == +=== 3D объект получается большим === 
-Из-за чего `Blender`'у немного тяжело работать в `Edit Mode(может в среднем думать по 2-3 секунды), когда мы пытаемся сделать какие-нибудь преобразования над мешем. +Из-за чего **Blender**'у немного тяжело работать в **Edit Mode** (может в среднем думать по 2-3 секунды на приблизительно среднем железе), когда мы пытаемся сделать какие-нибудь преобразования над мешем. 
-    * Можно уменьшить кол-во сетки с помощью модификатор `Decimateв Blender'е, но тогда мы можем потерять в точности вычисления объёма, поэтому мы отказались от такой идеи; +    * Можно уменьшить кол-во сетки с помощью модификатор **Decimate** в **Blender**'е, но тогда мы можем потерять в точности вычисления объёма, поэтому мы отказались от такой идеи; 
-    * Можно в `3D Slicer`'е воспользоваться инструментом `Scissors`, и он нам помогает, но уже в `Blenderпридётся аккуратнее тогда удалять стенки, которые появляются ещё в `3D Slicer`'е из-за логики работы сегментации. Ну и ножницами тоже надо аккуратнее пользоваться, чтобы не отрезать лишнего.+    * Можно в **3D Slicer**'е воспользоваться инструментом **Scissors**, и он нам помогает, но уже в **Blender** придётся аккуратнее тогда удалять стенки, которые появляются ещё в **3D Slicer**'е из-за логики работы сегментации. Ну и ножницами тоже надо аккуратнее пользоваться, чтобы не отрезать лишнего.
  
-== Невозможно подкрутить идеально Threshold в 3D Slicer'е. == +=== Невозможно подкрутить идеально Threshold в 3D Slicer'е. === 
-На выбор у нас есть множество алгоритмов: **Otsu**, **Huang**, **IsoData**, **Kittler-Illingworth**, **Maximum Entropy**, **Moments**, **Renyi entropy**, **Shanbhag**, **Triangle**, **Yen**. +На выбор у нас есть множество пресетов: **Otsu**, **Huang**, **IsoData**, **Kittler-Illingworth**, **Maximum Entropy**, **Moments**, **Renyi entropy**, **Shanbhag**, **Triangle**, **Yen**, но они все не работали хорошо на примере, который мы рассматривали и приходилось вручную двигать ползунки **threshold**'а.
  
-Мы попробовали их всех и пришли к таким выводам: +Но если даже двигать их вручную, то появлялись такие артефакты, как не бралась область, где на глаз должна быть стенка. Но когда мы увеличивали значение **threshold**'а, то брались области, где должна быть пустота.
-    - В целом визуально примерно все давали одинаковые результатыпоэтому сложно было выбрать какой-то конкретный; +
-    - Также во всех алгоритмах на некоторых снимках появлялись такие артефакты, как не бралась область, где на глаз должна быть стенка. Но когда мы увеличивали значение `threshold`'а, то брались области, где должна быть пустота.+
  
-Учитывая пункты выше было решено, что thresholdне надо завышать и легче воспользоваться инструментом `Paintи провести стенки самим, там где они должны были быть. В основном это не надо делать больше, чем на нескольких снимках.+Учитывая пункты выше можно пойти двумя путями: 
 +    - **threshold** не надо завышать и нужно будет воспользоваться инструментом **Paint** и провести стенки самим, там где они должны были быть. В основном это не надо делать больше, чем на нескольких снимках
 +    - Либо же чуток завышаем **threshold**, чтобы появлялись артефакты, но мы ими просто пренебрегаем, потому что нам нужна приблизительная оценка.
  
-  +===== Только встроенными инструментами 3D Slicer'а ===== 
-===== На чём остановились =====+ 
 +==== С использованием только лишь встроенного функционала ==== 
 +Был проанализирован функционал **3D Slicer**'а и были найдены такие интересные инструменты, как: 
 +    - Инструмент под названием **Islands** в модуле **Segment Editor**; 
 +    - Модуль **Segment Statistics**, который находится в категории **Quantification**. 
 + 
 +Сочетание этих двух инструментов открыл ещё более быстрый и удобный путь для получения объёма. 
 + 
 +=== Инструкция по нему === 
 +    - Первые шаги по открытии и выбора модуля **Segmentation/Segment Editor** аналогичны; 
 +    - Выбираем один из способов выделения областей описанных после данной инструкции и применяем его; 
 +    - Далее выбираем инструмент **Islands** и далее выбираем **Split Islands to segments**. В **Minumum Size** ставим, например, **70k** (данное значение позволило выделить только пазухи и оставляет всё ещё запас, потому что по итогу пазухи имели в районе **130k** вокселей, поэтому можно даже взять значение больше и не бояться, что пазухи не выделяться); 
 +    - Возможно перед следующим шагом надо будет применить ещё какие-то инструкции из способов ниже; 
 +    - Тут у нас должны остаться лишь 3 сегмента: две пазухи и внешняя часть. Если это не так, то можем повысить кол-во вокселей на прошлом шаге или вручную удалить, ведь внешнюю часть в любом случае надо будет удалить; 
 +    - Затем заходим в **Quantification/Segment Statistics** и выбираем в поле **Segmentation** нашу сегментацию с двумя сегментами (то есть нашими пазухами); 
 +    - В `Advanced` по умолчанию настроено уже, как нам нужно, но можно на всякий случай убрать **Scalar Volume**, потому что там будут идентичные результаты с **Label Map** (хотя он и так работает только тогда, когда выбираешь **Scalar Volume** вместо **None**, поэтому и говорю, что и по умолчанию всё хорошо);<WRAP center round important> 
 +Для работы **Closed Surface** нужно, чтобы в **Segment Editor** было включено **Show 3D**. 
 +</WRAP> 
 +    - Нажимаем **Apply** и видим результаты. Их разбор будет пунктом ниже. 
 + 
 +=== Алгоритмы выбора сегментов === 
 +    - Вручную дополнять: \\ <WRAP> 
 +Это случай с завышением **Threshold**'а и ручным дорисовыванием с помощью инструмента **Paint** или убирать лишнее с помощью **Erase**. 
 + 
 +**Результаты:** 
 +| Segment | Voxel count | Surface mm2 | Volume cm3 | 
 +| Left    | 152976      | 4232.45     | 19.179     | 
 +| Right   | 136020      | 4071.59     | 17.0564    | 
 +**Вывод:** Это не удобно и очень муторно. 
 +</WRAP> 
 +    - Завысить Threshold: \\ <WRAP> 
 +Главное, чтобы пазуха была отдельной частью. 
 + 
 +**Результаты:** 
 +| Segment | Voxel count | Surface mm2 | Volume cm3 | 
 +| Left    | 121637      | 4759.2      | 15.2877    | 
 +| Right   | 118842      | 4451.78     | 14.9167    | 
 +**Вывод:** имеет смысл, но результаты получаются не такими точными и красивыми, как хотелось бы. 
 +</WRAP> 
 +    - Завысить Threshold c Smoothing \\ <WRAP> 
 +Инструкция: 
 +        - после применения инструмента **Islands** мы для **каждой** из пазух применяем инструмент **Smoothing** в режиме **Closing (fill holes)** (я выбрал **10.00mm**); 
 + 
 +**Результаты:** 
 +| Segment | Voxel count | Surface mm2 | Volume cm3 | 
 +| Left    | 135528      | 3913.61     | 16.994     | 
 +| Right   | 132024      | 3928.04     | 16.5569    | 
 +**Вывод:** уже в разы лучше. 
 +</WRAP> 
 +    - С использованием Margin и опционально Smoothing \\ <WRAP> 
 +Инструкция: 
 +        - Тут мы можем взять такой **Threshold**, который будет удовлетворять визуально и забить на то, что могут быть небольшие соединения пазух с другими элементами носа. И применяем его; 
 +        - Затем мы выбираем инструмент **Margin** и выбираем в **Operation**: **Shrink**, а в качестве размера можно выбрать: **1.00mm**; 
 +        - Далее используем инструмент **Islands** по инструкции выше; 
 +        - И теперь применяем обратно **Margin** с таким же размером, но в режиме: **Grow**. 
 +        - Опционально ещё можем применить инструмент **Smoothing** в режиме **Closing (fill holes)**, например, на **5.00mm**. 
 + 
 +**Результаты только с Margin:** 
 +| Segment | Voxel count | Surface mm2 | Volume cm3 | 
 +| Left    | 145906      | 4250.16     | 18.2842    | 
 +| Right   | 141524      | 4202.23     | 17.743     | 
 + 
 +**Результаты также ещё и с Closing (fill holes) (5.00mm):** 
 +| Segment | Voxel count | Surface mm2 | Volume cm3 | 
 +| Left    | 148392      | 4114.5      | 18.6082    | 
 +| Right   | 143420      | 4130.2      | 17.9869    | 
 + 
 +**Выводы:** 
 +По результатам можно увидеть, что **Smoothing** (остальные алгоритмы я тоже пробовал, но они визуально работали хуже) не особо что-то меняет, потому что и без него получается очень приближённо к настоящим результатам, но при этом он бывает сглаживает не в тех местах, из-за чего, например, **Right** и повысился. Поэтому данный способ лучше юзать без сглаживания (либо с небольшим сглаживанием, но результаты тогда будут на уровне +1-2%). 
 +</WRAP> 
 + 
 +    - Окончательный алгоритм: \\ <WRAP> 
 +Этапы: 
 +    - Создаём сегмент для маски, которую мы будем в дальнейшем использовать и делаем её активной; 
 +    - Применяем инструмент **Threshold** (параметры для него задаёт пользователь и подробнее про них будет ниже) в качестве **MinimumThreshold**; 
 +    - Затем применяем **Smoothing** с параметрами: **Closing (fill holes)** и 1.5mm; 
 +    - Затем инвертируем сегмент маски; 
 +    - Переключаемся на первоначальный сегмент, а также выставляем в качестве маски, тот сегмент маски, с которым мы работали до этого; 
 +    - Применяем **Local Threshold** (инструмент из данного пакета: [[https://github.com/lassoan/SlicerSegmentEditorExtraEffects|ссылка]]) и затем задаём такие параметры: в качестве уже **MaximumThreshold** ставим параметр от пользователя, в качестве **SegmentationAlgorithm** выбираем **GrowCut**, а в качестве **MinimumDiameterMm** 1.0mm; 
 +    - Теперь мы выполнили всю работу с маской, тем самым можем удалить эту маску; 
 +    - Далее применяем **Islands** с режимом удаления мелких островков и с параметром **MinimumSize**=3000, чтобы отбросить лишнее, если оно будет; 
 +    - Под конец я применяем **Smoothing** с параметрами: **Closing (fill holes)** и 1.5mm, чтобы соединить те части, которые могли потенциально разъединиться от шагов выше. 
 + 
 +| Segment | Voxel count | Surface mm2 | Volume cm3 | 
 +| Left    | 156162      | 4492.03     | 19.5664    | 
 +| Right   | 141382      | 4258.5      | 17.7276    | 
 + 
 + 
 +</WRAP> 
 + 
 +=== Выводы === 
 +На основе всех вычислений выше (и на основе вычислений значений на других наборах слайсов), и так как это был наихудший пример — было принято взять последний алгоритм, так как он работал более стабильнее. 
 + 
 +===== Инструкция по использованию ===== 
 + 
 +{{ :projects:otolaryngologist:instruction_calculator_volume.png?600 |}} 
 + 
 +    - Раскрывает/закрывает работу с пазухами; 
 +    - Можно выбрать/создать/переименовать ноду сегментации; 
 +    - Можно выбрать тот объём слайсов с которым будет производится работа; 
 +    - Можно настроить отклонение порога, где базовым значение является значение выставленное алгоритмом в следующем пункте; 
 +    - Позволяет выбрать алгоритм, который будет выставлять базовое значение порога; 
 +    - Переключает отображение 3D; 
 +    - Является переключателем, показывающим примерную область, которая будет выделена при нажатии на слайс; 
 +    - Список имён, которое переключает выбранный сегмент сегментации; 
 +    - Можно выбрать таблицу, куда будут экспортированы данные; 
 +    - Подсчитывает результаты и отображает их внизу экрана.
projects/otolaryngologist/task_calculate_volume.1708206807.txt.gz · Last modified: 2024/02/18 00:53 by psamsonov