diff --git a/IO/Image/Testing/Cxx/CMakeLists.txt b/IO/Image/Testing/Cxx/CMakeLists.txt index 30d106cafc..dbc0321bd7 100644 --- a/IO/Image/Testing/Cxx/CMakeLists.txt +++ b/IO/Image/Testing/Cxx/CMakeLists.txt @@ -10,6 +10,7 @@ vtk_add_test_cxx(vtkIOImageCxxTests tests NO_DATA NO_VALID NO_OUTPUT TestDataObjectIO.cxx TestMetaIO.cxx + TestMetaImageReaderName.cxx TestImportExport.cxx TestHDRReaderInvalidFileHandling.cxx TestBMPReaderInvalidFileHandling.cxx diff --git a/IO/Image/vtkMetaImageReader.cxx b/IO/Image/vtkMetaImageReader.cxx index 19e8baf0f8..3321db733e 100644 --- a/IO/Image/vtkMetaImageReader.cxx +++ b/IO/Image/vtkMetaImageReader.cxx @@ -215,8 +215,6 @@ void vtkMetaImageReader::ExecuteDataWithInformation(vtkDataObject* output, vtkIn return; } - data->GetPointData()->GetScalars()->SetName("MetaImage"); - this->ComputeDataIncrements(); if (!this->MetaImagePtr->Read(this->FileName, true, data->GetScalarPointer())) @@ -226,6 +224,9 @@ void vtkMetaImageReader::ExecuteDataWithInformation(vtkDataObject* output, vtkIn } this->MetaImagePtr->ElementByteOrderFix(); + + const char* name = this->MetaImagePtr->Name(); + data->GetPointData()->GetScalars()->SetName(name && name[0] ? name : "MetaImage"); } int vtkMetaImageReader::RequestInformation( diff --git a/IO/Image/vtkMetaImageReader.h b/IO/Image/vtkMetaImageReader.h index 93165ef6c1..8405a151bc 100644 --- a/IO/Image/vtkMetaImageReader.h +++ b/IO/Image/vtkMetaImageReader.h @@ -37,6 +37,10 @@ * * MetaImage headers are expected to have extension: ".mha" or ".mhd" * + * The optional Name header field, specified before ElementDataFile, is used as + * the point-data scalar array name. If it is absent or empty, the scalar array + * is named "MetaImage". + * * Once you write this header text file, it should be possible to read the * image into your ITK based application using the itk::FileIOToImageFilter * class. diff --git a/IO/Image/Testing/Cxx/TestMetaImageReaderName.cxx b/IO/Image/Testing/Cxx/TestMetaImageReaderName.cxx new file mode 100644 index 0000000000..836731d71d --- /dev/null +++ b/IO/Image/Testing/Cxx/TestMetaImageReaderName.cxx @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen +// SPDX-License-Identifier: BSD-3-Clause + +#include "vtkDataArray.h" +#include "vtkImageData.h" +#include "vtkMetaImageReader.h" +#include "vtkNew.h" +#include "vtkPointData.h" +#include "vtkTestUtilities.h" + +#include "vtksys/FStream.hxx" +#include "vtksys/SystemTools.hxx" + +#include +#include +#include +#include + +namespace +{ +constexpr unsigned char Pixels[] = { 0, 1, 2, 3, 127, 128, 254, 255 }; + +bool WriteImage(const std::string& fileName, bool local, const char* nameField) +{ + vtksys::ofstream header(fileName.c_str(), std::ios::binary); + header << "ObjectType = Image\n" + << "NDims = 3\n" + << "BinaryData = True\n" + << "BinaryDataByteOrderMSB = False\n" + << nameField << "DimSize = 2 2 2\n" + << "ElementType = MET_UCHAR\n" + << "ElementDataFile = " << (local ? "LOCAL" : "TestMetaImageReaderName.raw") << '\n'; + if (local) + { + header.write(reinterpret_cast(Pixels), sizeof(Pixels)); + } + header.close(); + return !header.fail(); +} + +bool CheckImage(vtkMetaImageReader* reader, const char* expectedName) +{ + vtkImageData* image = reader->GetOutput(); + vtkDataArray* scalars = image->GetPointData()->GetScalars(); + if (!scalars || !scalars->GetName() || std::strcmp(scalars->GetName(), expectedName) != 0) + { + std::cerr << reader->GetFileName() << ": expected scalar name " << expectedName << ", got " + << (scalars && scalars->GetName() ? scalars->GetName() : "(null)") << '\n'; + return false; + } + + const int* dimensions = image->GetDimensions(); + if (dimensions[0] != 2 || dimensions[1] != 2 || dimensions[2] != 2 || + scalars->GetDataType() != VTK_UNSIGNED_CHAR || scalars->GetNumberOfComponents() != 1 || + scalars->GetNumberOfTuples() != 8) + { + std::cerr << reader->GetFileName() << ": unexpected image dimensions or scalar layout\n"; + return false; + } + for (vtkIdType i = 0; i < 8; ++i) + { + if (scalars->GetComponent(i, 0) != Pixels[i]) + { + std::cerr << reader->GetFileName() << ": unexpected value at voxel " << i << '\n'; + return false; + } + } + return true; +} +} + +int TestMetaImageReaderName(int argc, char* argv[]) +{ + char* temp = + vtkTestUtilities::GetArgOrEnvOrDefault("-T", argc, argv, "VTK_TEMP_DIR", "Testing/Temporary"); + const std::string tempDir = temp; + delete[] temp; + if (!vtksys::SystemTools::MakeDirectory(tempDir)) + { + std::cerr << "Cannot create temporary directory " << tempDir << '\n'; + return EXIT_FAILURE; + } + + const std::string rawName = tempDir + "/TestMetaImageReaderName.raw"; + vtksys::ofstream raw(rawName.c_str(), std::ios::binary); + raw.write(reinterpret_cast(Pixels), sizeof(Pixels)); + raw.close(); + if (raw.fail()) + { + std::cerr << "Cannot write " << rawName << '\n'; + vtksys::SystemTools::RemoveFile(rawName); + return EXIT_FAILURE; + } + + const struct + { + const char* Field; + const char* Expected; + } cases[] = { + { "Name = First volume\n", "First volume" }, + { "", "MetaImage" }, + { "Name = Second volume\n", "Second volume" }, + { "Name =\n", "MetaImage" }, + { "Name = Third volume\n", "Third volume" }, + { "Name = \t \n", "MetaImage" }, + }; + + // Reuse one reader to catch stale names when switching files. + vtkNew reader; + bool success = true; + for (bool local : { true, false }) + { + int index = 0; + for (const auto& test : cases) + { + const std::string fileName = tempDir + "/TestMetaImageReaderName" + std::to_string(index++) + + (local ? ".mha" : ".mhd"); + if (!WriteImage(fileName, local, test.Field)) + { + std::cerr << "Cannot write " << fileName << '\n'; + success = false; + } + else + { + reader->SetFileName(fileName.c_str()); + reader->UpdateInformation(); + reader->Update(); + success = CheckImage(reader, test.Expected) && success; + + // Check both a cached update and a forced reread. + reader->Update(); + success = CheckImage(reader, test.Expected) && success; + reader->Modified(); + reader->Update(); + success = CheckImage(reader, test.Expected) && success; + } + vtksys::SystemTools::RemoveFile(fileName); + } + } + vtksys::SystemTools::RemoveFile(rawName); + return success ? EXIT_SUCCESS : EXIT_FAILURE; +}