Commit 6e2a6509 authored by CAMP C++ Builder's avatar CAMP C++ Builder
Browse files

extended ItkReader to read image series as slices and create a volume

parent 2b68a4f3
......@@ -29,6 +29,8 @@
#include "core/datastructures/imagedata.h"
#include "core/datastructures/genericimagerepresentationlocal.h"
#include "core/tools/stringutils.h"
#include <iomanip>
namespace campvis {
......@@ -37,10 +39,14 @@ namespace campvis {
ItkReader::ItkReader()
: AbstractProcessor()
, p_url("sourcefile", "Source File Name", "", StringProperty::OPEN_FILENAME)
, p_url("sourcefile", "Source File", "", StringProperty::OPEN_FILENAME)
, p_imageSeries("imageseries", "Image Series", false)
, p_lastUrl("lastsourcefile", "Last Series File", "", StringProperty::OPEN_FILENAME)
, p_targetImageID("OutoutImage", "Image Output Name", "itkImage", DataNameProperty::WRITE)
{
addProperty(p_url, INVALID_RESULT);
addProperty(p_imageSeries, INVALID_RESULT);
addProperty(p_lastUrl, INVALID_RESULT);
addProperty(p_targetImageID, INVALID_RESULT);
}
......@@ -51,6 +57,16 @@ namespace campvis {
void ItkReader::updateResult(DataContainer& data) {
if (p_imageSeries.getValue()) {
ReadImageSeries(data);
}
else {
ReadImageDirect(data);
}
validate(INVALID_RESULT);
}
void ItkReader::ReadImageDirect(DataContainer& data) {
typedef itk::ImageIOBase::IOComponentType ScalarPixelType;
itk::ImageIOBase::Pointer imageIO =
......@@ -116,8 +132,12 @@ namespace campvis {
wtp._baseType = WeaklyTypedPointer::INT32; break;
case ScalarPixelType::UINT:
wtp._baseType = WeaklyTypedPointer::UINT32; break;
case ScalarPixelType::DOUBLE:
LWARNING("Pixel Type is DOUBLE. Conversion to float may result in loss of precision!");
case ScalarPixelType::FLOAT:
wtp._baseType = WeaklyTypedPointer::FLOAT; break;
default:
LERROR("Error while loading ITK image: unsupported type: " << pixelType);
return;
......@@ -130,9 +150,26 @@ namespace campvis {
ioRegion.SetSize(ioSize);
imageIO->SetIORegion(ioRegion);
//Finally, allocate buffer and read the image data
wtp._pointer = new uint8_t[imageIO->GetImageSizeInBytes()];
imageIO->Read(wtp._pointer);
if (pixelType != ScalarPixelType::DOUBLE) {
//Finally, allocate buffer and read the image data
wtp._pointer = new uint8_t[imageIO->GetImageSizeInBytes()];
imageIO->Read(wtp._pointer);
}
else {
//convert float volume to double volume
double * inputBuf = new double[imageIO->GetImageSizeInComponents()];
wtp._pointer = new uint8_t[imageIO->GetImageSizeInComponents() * sizeof(float)];
imageIO->Read(inputBuf);
double * dptr = inputBuf;
float * fptr = reinterpret_cast<float*>(wtp._pointer);
for (int i = 0, s = imageIO->GetImageSizeInComponents(); i < s; ++i) {
*fptr = *dptr;
fptr++;
dptr++;
}
delete[] inputBuf;
}
ImageData* image = new ImageData(dimensionality, size, wtp._numChannels);
ImageRepresentationLocal::create(image, wtp);
......@@ -143,7 +180,236 @@ namespace campvis {
else {
LWARNING("Unable to create ImageIO Instance; No suitable reader found!");
}
validate(INVALID_RESULT);
}
void ItkReader::ReadImageSeries(DataContainer& data) {
typedef itk::ImageIOBase::IOComponentType ScalarPixelType;
std::vector<std::string> imageFileNames = GetImageFileNames();
if (!imageFileNames.size())
return;
itk::ImageIOBase::Pointer imageIO =
itk::ImageIOFactory::CreateImageIO(imageFileNames[0].c_str(), itk::ImageIOFactory::ReadMode);
const int numSlices = imageFileNames.size();
if (imageIO.IsNotNull())
{
WeaklyTypedPointer wtp;
imageIO->SetFileName(imageFileNames[0]);
imageIO->ReadImageInformation();
const ScalarPixelType pixelType = imageIO->GetComponentType();
const size_t numDimensions = imageIO->GetNumberOfDimensions();
LDEBUG("Reading Image with Reader " << imageIO->GetNameOfClass());
LDEBUG("Pixel Type is " << imageIO->GetComponentTypeAsString(pixelType));
LDEBUG("numDimensions: " << numDimensions);
if (numDimensions > 3) {
LERROR("Error: Dimensions higher than 3 not supported!");
return;
}
itk::ImageIORegion ioRegion(numDimensions);
itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex();
itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize();
tgt::vec3 imageOffset(0.f);
tgt::vec3 voxelSize(1.f);
tgt::ivec3 size_i(1);
//we assured above that numDimensions is < 3
for (int i = 0; i < numDimensions; i++) {
size_i[i] = imageIO->GetDimensions(i);
imageOffset[i] = imageIO->GetOrigin(i);
voxelSize[i] = imageIO->GetSpacing(i);
ioStart[i] = 0;
ioSize[i] = size_i[i];
}
tgt::svec3 size(size_i);
size_t dimensionality = (size_i[2] == 1) ? ((size_i[1] == 1) ? 1 : 2) : 3;
if (dimensionality > 2) {
LERROR("Error: Cannot load image series with more than two dimensions!");
return;
}
LDEBUG("Image Size is " << size);
LDEBUG("Voxel Size is " << voxelSize);
LDEBUG("Image Offset is " << imageOffset);
LDEBUG("component size: " << imageIO->GetComponentSize());
LDEBUG("components: " << imageIO->GetNumberOfComponents());
LDEBUG("pixel type (string): " << imageIO->GetPixelTypeAsString(imageIO->GetPixelType()));
LDEBUG("pixel type: " << imageIO->GetPixelType());
switch (pixelType) {
case ScalarPixelType::CHAR:
wtp._baseType = WeaklyTypedPointer::INT8; break;
case ScalarPixelType::UCHAR:
wtp._baseType = WeaklyTypedPointer::UINT8; break;
case ScalarPixelType::SHORT:
wtp._baseType = WeaklyTypedPointer::INT16; break;
case ScalarPixelType::USHORT:
wtp._baseType = WeaklyTypedPointer::UINT16; break;
case ScalarPixelType::INT:
wtp._baseType = WeaklyTypedPointer::INT32; break;
case ScalarPixelType::UINT:
wtp._baseType = WeaklyTypedPointer::UINT32; break;
case ScalarPixelType::DOUBLE:
LWARNING("Pixel Type is DOUBLE. Conversion to float may result in loss of precision!");
case ScalarPixelType::FLOAT:
wtp._baseType = WeaklyTypedPointer::FLOAT; break;
default:
LERROR("Error while loading ITK image: unsupported type: " << pixelType);
return;
}
wtp._numChannels = imageIO->GetNumberOfComponents();
//Setup the image region to read
ioRegion.SetIndex(ioStart);
ioRegion.SetSize(ioSize);
imageIO->SetIORegion(ioRegion);
//allocate a temporary buffer if necessary
double * inputBuf = (pixelType == ScalarPixelType::DOUBLE) ? new double[imageIO->GetImageSizeInComponents()] : nullptr;
size_t sliceSize = (pixelType == ScalarPixelType::DOUBLE) ? imageIO->GetImageSizeInComponents() * sizeof(float) : imageIO->GetImageSizeInBytes();
wtp._pointer = new uint8_t[numSlices * sliceSize];
for(size_t idx=0; idx < numSlices; idx++) {
itk::ImageIOBase::Pointer fileIO = imageIO;
//itk::ImageIOFactory::CreateImageIO(imageFileNames[idx].c_str(), itk::ImageIOFactory::ReadMode);
fileIO->SetFileName(imageFileNames[idx]);
fileIO->ReadImageInformation();
fileIO->SetIORegion(ioRegion);
size_t currentSliceSize = (pixelType == ScalarPixelType::DOUBLE) ? imageIO->GetImageSizeInComponents() * sizeof(float) : fileIO->GetImageSizeInBytes();
if (currentSliceSize != sliceSize) {
LERROR("Image " << imageFileNames[idx] << " has different dimensionality or data type!");
delete wtp._pointer;
delete inputBuf;
wtp._pointer = nullptr;
return;
}
uint8_t * sliceBuffer = reinterpret_cast<uint8_t*>(wtp._pointer) + idx * sliceSize;
if (pixelType != ScalarPixelType::DOUBLE) {
// directly read slice into buffer
fileIO->Read(sliceBuffer);
}
else {
//convert float volume to double volume
fileIO->Read(inputBuf);
double * dptr = inputBuf;
float * fptr = reinterpret_cast<float*>(sliceBuffer);
for (int i = 0, s = fileIO->GetImageSizeInComponents(); i < s; ++i) {
*fptr = static_cast<float>(*dptr);
fptr++;
dptr++;
}
}
}
delete[] inputBuf;
size[2] = numSlices;
//series adds one dimension
ImageData* image = new ImageData(dimensionality+1, size, wtp._numChannels);
ImageRepresentationLocal::create(image, wtp);
image->setMappingInformation(ImageMappingInformation(size, imageOffset/* + p_imageOffset.getValue()*/, voxelSize /** p_voxelSize.getValue()*/));
data.addData(p_targetImageID.getValue(), image);
}
else {
LWARNING("Unable to create ImageIO Instance; No suitable reader found!");
}
}
std::vector<std::string> ItkReader::GetImageFileNames() {
std::vector<std::string> filenames;
std::string first = p_url.getValue();
std::string last = p_lastUrl.getValue();
//either one is empty - we cant do anything
if (!first.size() || !last.size())
return filenames;
//this is a pretty naive scheme to find out the naming convention of the files
//we first scan both filenames from left to find the first position where they diverge
size_t diverge_left = 0;
for (; diverge_left < first.size(); ++diverge_left) {
//check if we are outside last string
if (diverge_left >= last.size())
break;
if (first[diverge_left] != last[diverge_left]) break;
}
std::string fileBegin = first.substr(0, diverge_left);
//strings are equal?
if (diverge_left == first.size() && diverge_left == last.size()) {
LWARNING("First and last filenames are equal!");
filenames.push_back(first);
return filenames;
}
// now we assume the diverging letters are a number, which we skip forward
int digitFirstEnd = diverge_left;
if (!isdigit(first[digitFirstEnd])) {
LERROR("Cannot find sequence between first and last filename!");
return filenames;
}
while (isdigit(first[digitFirstEnd])) digitFirstEnd++;
int digitLastEnd = diverge_left;
if (!isdigit(last[digitLastEnd])) {
LERROR("Cannot find sequence between first and last filename!");
return filenames;
}
while (isdigit(last[digitLastEnd])) digitLastEnd++;
std::string fileEnd = first.substr(digitFirstEnd, std::string::npos);
if (fileEnd != last.substr(digitLastEnd, std::string::npos)) {
LERROR("Filename Tails mismatch!");
return filenames;
}
//now, we have diverge_left and diverge_right
LINFO("Diverge Left: " << diverge_left << "; digit end position: " << digitFirstEnd);
LINFO("String begin: " << fileBegin << "; end: " << fileEnd);
//the substrings of first and last filename are converted to numbers to get the numerical range
int numFirst = StringUtils::fromString<int>(first.substr(diverge_left, digitFirstEnd - diverge_left));
int numLast = StringUtils::fromString<int>(last.substr(diverge_left, digitLastEnd - diverge_left));
LINFO("Indices from " << first.substr(diverge_left, digitFirstEnd - diverge_left) << "(" << numFirst << ") to " << numLast);
//then we step through all numbers in the range and generate filenames by replacing the diverged substring
//with the generated number
for (int imgIdx = numFirst; imgIdx <= numLast; ++imgIdx) {
std::stringstream s;
if (first.size() == last.size()) {
s << std::setfill('0');
s << std::setw(digitFirstEnd - diverge_left);
}
s.clear();
s << imgIdx;
filenames.push_back(fileBegin + s.str() + fileEnd);
//LINFO(filenames.back());
}
return filenames;
}
}
......@@ -60,6 +60,9 @@ namespace campvis {
StringProperty p_url;
BoolProperty p_imageSeries;
StringProperty p_lastUrl;
DataNameProperty p_targetImageID; ///< ID for output image
protected:
......@@ -67,6 +70,12 @@ namespace campvis {
virtual void updateResult(DataContainer& data);
static const std::string loggerCat_;
private:
void ReadImageDirect(DataContainer& data);
void ReadImageSeries(DataContainer& data);
std::vector<std::string> GetImageFileNames();
};
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment