Я новичок в ITK и C ++, и в настоящее время я нахожусь в процессе настройки программы тестирования для алгоритма регистрации изображений. В своем сообщении я расскажу о том, что я делаю при использовании ITK-терминологии. Тем не менее, я ожидаю, что любой опытный C ++ — программист должен быть в состоянии сказать, что не так с моим кодом, не понимая специфику ITK, так как я думаю, что я просто делаю что-то не так в своих ссылках / разыменованиях. Здесь нужно отслеживать (более подробно объяснено ниже) переменную типа MetaDataContainer, которая во всех функциях называется metaData. Если какая-либо из особенностей ITK смущает вас, пожалуйста, попросите более подробную информацию, но я не хотел делать это оригинальное сообщение слишком длинным. Во всяком случае, здесь идет:
Мне придется читать и писать dicom-образы довольно часто в процессе выполнения программы, поэтому вместо того, чтобы каждый раз выполнять все этапы процессов чтения / записи, я решил написать для них отдельные функции. Поскольку мне нужно использовать некоторые данные из процесса чтения, такие как MetaDataDictionaryArray, в выводе, функция readDicom (а также другая функция preregistrationOperations) возвращает кортеж:
typedef std::tuple< ImageType::Pointer, MetaDataContainer, FileNamesContainer > ImageMetaOutputTuple;
Другой важный typedef, о котором нужно знать, это MetaDataContainer, который сам по себе является указателем на вектор DictionaryRawPointer. Из ItkImageSeriesReader.h:
typedef MetaDataDictionary DictionaryType;
typedef MetaDataDictionary * DictionaryRawPointer;
typedef std::vector< DictionaryRawPointer > DictionaryArrayType;
typedef const DictionaryArrayType * DictionaryArrayRawPointer;
В моем собственном заголовочном файле feir.h:
typedef itk::ImageSeriesWriter< ImageType, Image2DType >::DictionaryArrayRawPointer MetaDataContainer;
Другие типы такие же, как в ITK-примерах для Dicom-обработки. Грубый макет моей программы показан ниже.
Однако эта программа вылетает в функции writeDicom с ошибкой «… векторный индекс находится вне диапазона». Я сузил ошибку до необходимости делать с MetaDataDictionaryArray. Его размер, когда впервые читаются в reaDicom, например, 64 (количество файлов в серии), но когда он возвращается в preRegistrationOperations, его размер внезапно становится равным 0, и именно этот контейнер нулевого размера затем передается в writeDicom, который завершается сбоем.
РЕДАКТИРОВАТЬ: сам сбой происходит на seriesWriter->Update()
-линия.
Я пытался обойти эту проблему, используя вместо этого MetaDataContainer-указатели, но проблема сохраняется. Могу добавить, что я тоже новичок в C ++ (скорее физик, чем программист). Кто-нибудь может мне помочь в этом? Должно быть достаточно просто вернуть MetaDataContainer в кортеже из readDicom, распаковать его в preRegistrationOperations и передать его в writeDicom, но как бы я ни старался, я не могу заставить его работать.
С наилучшими пожеланиями,
Микаэл
ImageMetaOutputTuple preRegistrationOperations( std::string inputDir, std::string outputDir, std::string seriesNumber, bool preparationsDone = false, bool verbose = true )
{
// No output verbose if operations have already been done
if (preparationsDone) verbose = false;
// Read input-Dicoms
ImageType::Pointer image;
MetaDataContainer metaData;
FileNamesContainer outputFilenames;
ImageMetaOutputTuple returnTuple = readDicom( inputDir, outputDir, seriesNumber, verbose );
std::tie (image, metaData, outputFilenames) = returnTuple;
// Pass image and directory and filename information to writeDicom and write into outputDir
if (!preparationsDone) {
try {
int resultCode = writeDicom( image, outputDir, outputFilenames, metaData );
}
catch (itk::ExceptionObject &ex) {
std::cout << "Exception caught in writeDICOM:" << std::endl;
std::cout << ex << std::endl;
}
}
return returnTuple;
}
—
int writeDicom ( ImageType::Pointer image, std::string inputDir, FileNamesContainer filenames, MetaDataContainer metaData )
{
…
seriesWriter->SetMetaDataDictionaryArray( metaData );
try {
seriesWriter->Update();
return 0;
}
catch {
...
}
…
}
—
ImageMetaOutputTuple readDicom ( std::string inputDir, std::string outputDir = "", std::string seriesNumber = "", bool verbose = true)
{
…
image = reader->GetOutput();
files = nameGenerator->GetOutputFileNames();
MetaDataContainer metaData = reader->GetMetaDataDictionaryArray();
…
ImageMetaOutputTuple returnTuple (image, metaData, files);
return returnTuple;
}
—
int main( int argc, char* argv[] )
{
…
ImageType::Pointer moving;
ImageType::Pointer target;
std::tie(moving, std::ignore, std::ignore) = preRegistrationOperations( inputDir, movingDir, movingSeriesNumber, preparationsDone, verbose );
std::tie(target, std::ignore, std::ignore) = preRegistrationOperations( inputDir, targetDir, targetSeriesNumber, preparationsDone, verbose );
…
}
Может случиться так, что GetMetaDataDictionaryArray просто возвращает необработанный указатель члена читателя серии, но при выходе из readDicom читатель выходит за рамки. Я бы ожидал большего, что в этом случае у вас произошел сбой, но, возможно, это направление, в которое стоит разобраться.
Вместо того, чтобы возвращать кортеж, вы можете добавить в свою функцию 4 дополнительных аргумента, где будет храниться результат (для упрощения отладки).
Прикосновение к словарю это всегда противно, вот пример http://www.itk.org/Wiki/ITK/Examples/DICOM/ResampleDICOM (но здесь они читают словарь по частям). Вы уверены, что вам нужно обновить его? Некоторые поля будут автоматически обновляться автором dicom (например, ориентация), вы не можете их принудительно установить.