- Использовал как базу Google cloud. Там при регистрации дают стартовые 300$ для демонстрации системы.
- для сборки и тестирования есть набор шелл скриптов в корне проекта. Это является отправной точкой для работы с проектом:
- debug.sh - делает сборку и запускает проект локально в режиме отладки, понятном для idea
- test.sh - делает сборку и запускает проект локально (по дефолту порт 4567)
- Далее идут 2 скрипта которые имеют одинаковую логику. делают сборку, собирают докер контейнер, публикуют в Dockerhub. Но в разных форматах:
- withDataBuild.sh - публекует вместе с данными. Публикуется с тегом withData.
- withoutDataBuild.sh - публикует без данных. Добавляет точку монтирования вольюма, куда можно приатачить внешний вольюм с данными через volume bind. Публикуется с тегом withoutData.
- Для сборки джава артефактов используется инструмент Gradle. Инструкцию по основным методом можно прочитать здесь Для него ключевые файлы, из которых он будет делать сборку:
- build.gradle - основной файл сборки. Здесь указаны все зависимости и логика сборки.
- gradle.properties - файл собержит, различные переменные, которые могут понадобится градлу для сборки.
- settings.gradle - содержит тех. информацию, например о имени пакета, версии, или логин/пароль, если артефакты куда-то публикуются
- после публикации на докерхабе, можно создать kubernates проект в google cloud. Наиболее близкую инструкцию можно найти здесь Если использовать вариант, когда данные снаружи и аттачатся через вольюм, то вот инструкция, как это можно сделать
- Пример развернутого приложения 35.239.213.226
- За основу REST-сервиса была взята библиотека Spark. Процессинг изображений реализован с помощью opencv 3.4 (opencv javadoc) Работа с файлами формата hdf5 использовалась библиотека jhdf5 (javadoc)
- Для всех запросов идет gzip архивирование
- Для всех запросов был добавлен хедер ("Access-Control-Allow-Origin": "*") чтобы можно было тестировать локалоно фронтенд отдельно от бекенда. Не был вынесен отдельно в пост шаг, а проставлен в каждом ресту, тк проставлялся не у всех запросов
- Был добавлено ограничение по количеству одновременных запросов на особо тяжелые методы, тк система сильно подвисала, от вызова одновременно нескольких рестов
- hdf5 и набор имеджей конвертируются в универсальный объект, описаный в интерфейсе IMatDatasetObject
- Реализована работа только для массивов float, тк он использовался в работе. Если нужны какие-то другие типы, нужно наследоваться от класса H5Object. Сделать как-то универсально и красиво через дженерики не получилось из-за особенности библиотки jhdf5, где для каждого типа свои классы
- Для работы с массивами, по максимуму применяются джава стримы, тк они оказались быстрее перебора циклом
- В com.rbtm.reconstruction.DataObjects.NDArray реализована обертка для работы с многомерными массивами, тк стандартной реализации для джавы нет
- Так же как и нет каких-то базовых вещей, типа нахождения минимума/максимума. Это реализовано в классе com.rbtm.reconstruction.Utils:CustomArrayUtils
- com.rbtm.reconstruction:Constants - все константы, включая различные пути. Описаны в самом файле
- com.rbtm.reconstruction.Utils - различные полезные классы
- com.rbtm.reconstruction.Utils:Timer удобно использовать, если нужно засечь время на определенном участке
- com.rbtm.reconstruction:Main - описание всех рест запросов, REST API ниже
- com.rbtm.reconstruction:WebAppHealper - вспомгательный класс для обработки рест запросов. Реализуют логику обработки запросов
- com.rbtm.reconstruction.Converters - собраны конвертеры датасетов между форматами. Реализованы
- hdf5 файл -> набор png изображений
- hdf5 файл -> набор Mat
- набор png изображений -> obj файл
- com.rbtm.reconstruction.DataProcessing - классы по обработке изображений
- com.rbtm.reconstruction.DataProcessing.Circle - классы для построения диаграммы по интегрированию колец
- com.rbtm.reconstruction.MarchingCubes - Классы по алгоритму марширующих кубов
-
GET /objects/all/
- Описание: Возращает список всех объектов доступных для исследования
- Тело запроса: -
- Возр. тип: json
- Комментарий: парсит список фалов, в дирестории OBJ_PATH, указаной в константах'
-
GET /objects/all/current/
- Описание: Возвращает имя объекта, который выбран на данный момент
- Тело запроса: -
- Возр. Тип: text
- Тело ответа: Имя объекта, выбраного последним. По дефолту возращает ""
-
POST /objects/all/current/
- Описание: Выбрать новый объект для исследования
- Тело запроса: {"objName": String }
- Возр. Тип: text
- Тело ответа: если операция выполнена успешно вернется имя объекта, если такого объекта нет, вернется “Fail”
- Комментарий: после того, как выбран объект идет проверка, есть ли конвертированная модель в png формат, Если такой нет, то запускается конвертация hdf5->png array. Это позволило очень сильно сократить выполнение некоторых операций.
-
POST /objects/all/current/forceInit/
- Описание: запускает конвертацию hdf5->png array.
- Тело запроса: -
- Возр. Тип: text
- Тело ответа: строка Success при успехе и Fail, если произошел сбой.
- Комментарий: операция нужна, если мы не уверены в консистентности данных (например повреждение буфера или искажение данных в нем) и требуется переконвертация
-
GET /objects/all/current/shape/
- Описание: возвращает размеры объекта, который выбран на данный момент
- Тело запроса: -
- Возр. Тип: json
- Тело ответа: {num: int, heigth: int, width: int}
- Комментарий:
-
POST /objects/all/current/slice/filters/
- Описание: задает фильтры в формате json массива
- Тело запроса: [{имя_филтра: значение_филтра}, ...]
- Возр. Тип: text
- Тело ответа: Возвращает Success при успехе и Fail, если произошел сбой
- Комментарий: порядок в пересылаемом массиве важен
-
GET /objects/all/current/slice/:id/
- Описание: возвращает срез с номером id и заданными фильтрами в формате png
- Тело запроса:
- Возр. Тип: binary
- Тело ответа: бинарный файл с картинкой в формате png
-
GET /objects/all/current/slice/:id/circleDiagram/
- Описание: Возвращает данные для построения диаграммы интегрирования окружностей для среза с номером id и заданными фильтрами
- Тело запроса: -
- Возр. Тип: json
- Тело ответа: {radius: int, value: float}, ...]
- Комментарий: если нет подсчитанной диаграммы для текущих значений, делается подсчет и записывается в буфер, иначе возвращается диаграмма из буфера.
-
GET /objects/all/current/objFile/
- Описание: Возвращает 3d объект в формате .obj
- Тело запроса: -
- Возр. Тип: text
- Тело ответа: Текст с описанием модели в формате obj
- Комментарий: Это оказалась достаточно тяжелая операция, тк размер файла может достигать 100-150мб. Браузер кидает ООМ
Для css-шаблонов используется библиотека bulma.io У нее достаточно понятная и простая документация и есть все необходимое для данной задачи. Интерфейс выглядит следующим образом:
Его можно разделить на следующие елементы:
- Строка выбора - здесь можно можно выбрать объект для исследования, тип визуализации.
Так же здесь отображается имя объекта, который иследуется в данный момент.
Сейчас доступны следующие вкладки:
- Welcome - где отображается данный текст
- Slice - послойная визуализация
- Surfaces - визуализция с помощью изоповерхностей
- Окно визуализации - область визулаизации, которая делится на 2 части:
- Область визуализации - область где непосредственно происходит визуализация
- Панель нстроек - настройки и елементы управления, для каждого типа визуализации свои настройки
Для визуализации сладов реализована возможность накладывания произвольного набора фильтров. Все минимальные-максимальные значения привязаны к размеру изображения и присваиваются в файле filterGenerator. Порядок фильров имеет значение, могут повторяться. Посылается на сервер в json формате, при каждом изменении слоя иди фильтра Не было реализовано блокирования опций, если не выбран объект. Поэтому при изменении параметров автоматически выбирается первый в списке
Все скрипты вынесены в одно место, в конец index.html Порядок фалов важен
- 3party-scripts/Three.js - библиотека three.js
- 3party-scripts/OBJLoader.js - загрузка obj файлов
- 3party-scripts/OrbitControl.js - управление камерой в three.js сцене
- canvasjs - для построения графиков. Был вариант с d3.js но показалось силишком громозким решением для построения одного графика
- scripts/globalVars.js - глобальные переменные
- scripts/networkHelper.js - функция для ajax запросов
-
scripts/threeJsManipulations.js - все что связано с three.js:
- создание и рендеринг сцены
- добавление управления камерой
- загрузка obj файла
- логика кнопок управления
- удаление сцены (TODO: после удаления все равно блокируется все остальное, нужно перезагружать страницу)
-
scripts/chartViz.js - создание графика (x - радиус, y - относительная сумма по кольцу)
-
scripts/imgSliceUpdater.js - Скрипт для для обновления среза.
- Отпрвлет серверу список фильтров
- Обновляет изображение среза
- Высчитывает новые параметры изображения
- Вызывает метод построения графиков из chartViz. Использует библиотеку canvasjs
-
scripts/objectListUploader.js - выгружает с сервера список доступных объектов для исследования. Запускается один раз на загрузке страницы. Полученый список генерирует в выпадающее меню в строке выбора
-
scripts/filterGenerator.js - скрипт который генерирует новый фильтр. Создает новый елемент списка со следующими полями:
- Имя
- Мин. значение
- Слайдер
- Текущее значение( в SliderUi.js описана логика как текущее значение становится интерактивной)
- Макс. значение
- Кнопка удаления фильтра
Генерируется случайный id и присваевается каждому этому элементу в виде Имя_фильтра-id. Это нужно для того чтобы:
- Находить все элементы при удалении
- Для возможности хранить не уникальные имена фильтров, если мы накладываем несколько с одинаковым именем
-
scripts/index.js - скрипт, запускающийся на старте страницы:
- Делает активными все падающие меню.
- Проставляет необходимые действия всем кнопкам
- Запускает objectListUploader.
-
scripts/sliderUi.js - скрипт необходимый чтобы все слайдеры были интерактивными