Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

ITK

Глава 9 из книги "Архитектура приложений с открытым исходным кодом", том 2.

Оригинал: ITK
Авторы: Luis Ibanez, Brad King
Перевод: А.Панин

Фабрики

Одним из фундаментальных требований, предъявляемых во время проектирования к ITK, является возможность поддержки множества платформ. Это требование основывается на желании максимально расширить распространение тулкита путем реализации возможности его использования сообществом вне зависимости от предпочтительных платформ участников. В рамках проекта ITK был реализован шаблон проектирования фабрики (Factory design pattern) для решения задачи по поддержке фундаментальных различий множества аппаратных и программных платформ без ущерба совместимости решения с каждой из платформ.

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

Фабрики ввода/вывода

Механизм фабрик особенно важен при осуществлении операций ввода/вывода.

Обработка различий систем с помощью фасадов

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

Зависимости фабрик ввода/вывода
Рисунок 9.8: Зависимости фабрик ввода/вывода

Эта расширяемая архитектура для осуществления операций ввода/вывода основана на механизме фабрик, описанном в предыдущем разделе. Основное отличие заключается в том, что в случае системы для операций ввода/вывода фабрики ввода/вывода регистрируются в специализированном реестре, управляемом базовым классом ImageIOFactory, изображенным в верхнем левом углу Рисунка 9.8. Сами функции чтения и записи данных для различных форматов файлов изображений реализованы в рамках семейства классов ImageIO, изображенного справа на Рисунке 9.8. Эти служебные классы предназначены для создания экземпляров по требованию в тот момент, когда пользователь осуществляет чтение или запись изображения. Эти служебные классы не раскрываются в рамках кода приложений. Вместо прямого обращения к этим классам приложения должны взаимодействовать с классами фасадов:
  • ImageFileReader
  • ImageFileWriter
Это два класса, при использовании которых в приложении может применяться подобный код:
reader->SetFileName("../image1.png");
reader->Update();
или:
writer->SetFileName("../image2.jpg");
writer->Update();

В обоих случаях вызов метода Update() приводит к запуску конвейера, с которым соединены рассматриваемые объекты ProcessObject. И объект чтения, и объект записи данных ведут себя как дополнительный фильтр конвейера. В случае объекта для чтения данных вызов метода Update() приводит к чтению соответствующего файла изображения и размещению данных в памяти. В случае объекта записи вызов метода Update() приводит к выполнению операций конвейера, в результате чего объект записи получает входные данные, и, наконец, завершается записью изображения на диск в файл определенного формата.

Эти классы фасадов скрывают от разработчика приложений внутренние сложности, возникающие из-за особенностей каждого из форматов файлов. Они скрывают даже признаки самого существования формата. Эти фасады спроектированы таким способом, что большую часть времени разработчикам приложений не требуется знать о том, какие форматы файлов могут быть прочитаны приложением. Стандартное приложение может просто использовать подобный код:
std::string filename = this->GetFileNameFromGUI();
writer->SetFileName( filename );
writer->Update();
Эти вызовы будут работать аналогично в том случае, если значение переменной filename будет представлено одной из следующих строк:
  • image1.png
  • image1.jpeg
  • image1.tiff
  • image1.dcm
  • image1.mha
  • image1.nii
  • image1.nii.gz

причем расширения файлов указывают на различные форматы файлов в каждом случае.

Знайте тип пикселя

Несмотря на содействие со стороны фасадных классов чтения и записи, разработчику приложений следует заботиться о том, какой тип пикселей требуется обрабатывать приложению. В контексте работы с медицинскими изображениями разумно ожидать того, что разработчик приложения будет знать о том, содержит ли исходное изображение магнитно-рзеонансную томограмму, маммографию или компьютерную томограмму и, следовательно, позаботится о выборе подходящего типа пикселя и разрешения изображения для каждого из этих различных типов изображений. Эти особенности типов изображений могут доставлять неудобства в случае использования настроек приложения, при которых пользователи хотят иметь возможность чтения любого типа изображения, что часто встречается при быстром создании прототипов и обучении. В контексте развертывания приложений для работы с медицинскими изображениями для эксплуатации в клиниках, однако, ожидается, что тип пикселей и разрешение изображений будут четко заданы и смогут определяться на основе типа предназначенного для обработки изображения. Конкретный пример, в котором приложение работает с трехмерными магнитно-резонанснвми томограммами, выглядит следующим образом:
typedef itk::Image< signed short, 3 >  MRImageType;
typedef itk::ImageFileWriter< MRImageType > MRIWriterType;
MRIWriterType::Pointer writer = MRIWriterType::New();
writer->Update();

Однако, существует ограничение того, насколько особенности форматов файлов изображений могут быть скрыты от разработчика приложений. Например, при чтении изображений из файлов форматов DICOM или RAW разработчику придется использовать дополнительные вызовы для точного указания характеристик имеющегося формата. Файлы формата DICOM наиболее часто встречаются в медицинских учреждениях, а файлы формата RAW все еще являются необходимым злом, предназначенным для обмена данными в процессе исследований.

Объединенные, но разделенные

Автономный характер каждой фабрики ввода/вывода и служебный класс ImageIO также были затронуты процессом разделения на модули. Обычно класс ImageIO зависит от специализированной библиотеки, предназначенной для работы со специфическим форматом файлов. Такими форматами, например, являются PNG, JPEG, TIFF и DICOM. В этих случаях сторонняя библиотека рассматривается как независимый модуль и специализированный код класса ImageIO, являющийся интерфейсом между кодом ITK и кодом сторонней библиотеки, также размещается в модуле. Таким образом, специфические приложения могут могут ограничить использование множества форматов файлов, которые не входят в сферу их применения и работать только с теми форматами файлов, которые окажутся полезными в ожидаемых сценариях применения данного приложения.

Как и в случае со стандартными фабриками, загрузка фабрик ввода/вывода может быть осуществлена в процессе работы приложения из динамических библиотек. Этот гибкий процесс загрузки фабрик упрощает использование специализированных самостоятельно разработанных форматов фалов без необходимости включения поддержки таких форматов файлов непосредственно в состав тулкита ITK. Загружаемые фабрики ввода/вывода были одним из наиболее успешных архитектурных решений проекта ITK. Они позволили достаточно просто разрешить сложную ситуацию без усложнения или запутывания кода. Не так давно подобная архитектура ввода/вывода была реализована для управления процессом чтения и записи файлов, содержащих пространственные преобразования в рамках семейства классов Transform.


Продолжение статьи: Потоковая передача данных