diff --git a/CMake/FindDCMTK.cmake b/CMake/FindDCMTK.cmake new file mode 100644 index 0000000000..9750db2505 --- /dev/null +++ b/CMake/FindDCMTK.cmake @@ -0,0 +1,157 @@ +# adapted version of FindDCMTK, better suited for super-builds + +# - find DCMTK libraries and applications +# + +# DCMTK_INCLUDE_DIRS - Directories to include to use DCMTK +# DCMTK_LIBRARIES - Files to link against to use DCMTK +# DCMTK_FOUND - If false, don't try to use DCMTK +# DCMTK_DIR - (optional) Source directory for DCMTK +# +# DCMTK_DIR can be used to make it simpler to find the various include +# directories and compiled libraries if you've just compiled it in the +# source tree. Just set it to the root of the tree where you extracted +# the source (default to /usr/include/dcmtk/) + +#============================================================================= +# Copyright 2004-2009 Kitware, Inc. +# Copyright 2009-2010 Mathieu Malaterre +# Copyright 2010 Thomas Sondergaard +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +# +# Written for VXL by Amitha Perera. +# Upgraded for GDCM by Mathieu Malaterre. +# Modified for EasyViz by Thomas Sondergaard. +# + +# prefer DCMTK_DIR over default system paths like /usr/lib +SET(CMAKE_PREFIX_PATH ${DCMTK_DIR}/lib ${CMAKE_PREFIX_PATH}) # this is given to FIND_LIBRARY or FIND_PATH + +if(NOT DCMTK_FOUND AND NOT DCMTK_DIR) + set(DCMTK_DIR + "/usr/include/dcmtk/" + CACHE + PATH + "Root of DCMTK source tree (optional).") + mark_as_advanced(DCMTK_DIR) +endif() + +foreach(lib + dcmdata + dcmimage + dcmimgle + dcmjpeg + dcmnet + dcmpstat + dcmqrdb + dcmsign + dcmsr + dcmtls + ijg12 + ijg16 + ijg8 + oflog + ofstd) + + find_library(DCMTK_${lib}_LIBRARY + ${lib} + PATHS + ${DCMTK_DIR}/${lib}/libsrc + ${DCMTK_DIR}/${lib}/libsrc/Release + ${DCMTK_DIR}/${lib}/libsrc/Debug + ${DCMTK_DIR}/${lib}/Release + ${DCMTK_DIR}/${lib}/Debug + ${DCMTK_DIR}/lib + NO_DEFAULT_PATH + ) + + mark_as_advanced(DCMTK_${lib}_LIBRARY) + + #message("** DCMTKs ${lib} found at ${DCMTK_${lib}_LIBRARY}") + + if(DCMTK_${lib}_LIBRARY) + list(APPEND DCMTK_LIBRARIES ${DCMTK_${lib}_LIBRARY}) + endif() + +endforeach() + + +set(DCMTK_config_TEST_HEADER osconfig.h) +set(DCMTK_dcmdata_TEST_HEADER dctypes.h) +set(DCMTK_dcmimage_TEST_HEADER dicoimg.h) +set(DCMTK_dcmimgle_TEST_HEADER dcmimage.h) +set(DCMTK_dcmjpeg_TEST_HEADER djdecode.h) +set(DCMTK_dcmnet_TEST_HEADER assoc.h) +set(DCMTK_dcmpstat_TEST_HEADER dcmpstat.h) +set(DCMTK_dcmqrdb_TEST_HEADER dcmqrdba.h) +set(DCMTK_dcmsign_TEST_HEADER sicert.h) +set(DCMTK_dcmsr_TEST_HEADER dsrtree.h) +set(DCMTK_dcmtls_TEST_HEADER tlslayer.h) +set(DCMTK_ofstd_TEST_HEADER ofstdinc.h) + +foreach(dir + config + dcmdata + dcmimage + dcmimgle + dcmjpeg + dcmnet + dcmpstat + dcmqrdb + dcmsign + dcmsr + dcmtls + ofstd) + find_path(DCMTK_${dir}_INCLUDE_DIR + ${DCMTK_${dir}_TEST_HEADER} + PATHS + ${DCMTK_DIR}/${dir}/include + ${DCMTK_DIR}/${dir} + ${DCMTK_DIR}/include/dcmtk/${dir} + ${DCMTK_DIR}/include/${dir}) + + mark_as_advanced(DCMTK_${dir}_INCLUDE_DIR) + #message("** DCMTKs ${dir} found at ${DCMTK_${dir}_INCLUDE_DIR}") + + if(DCMTK_${dir}_INCLUDE_DIR) + list(APPEND + DCMTK_INCLUDE_DIRS + ${DCMTK_${dir}_INCLUDE_DIR}) + endif() +endforeach() + +if(WIN32) + list(APPEND DCMTK_LIBRARIES netapi32 wsock32) +endif() + +if(DCMTK_ofstd_INCLUDE_DIR) + get_filename_component(DCMTK_dcmtk_INCLUDE_DIR + ${DCMTK_ofstd_INCLUDE_DIR} + PATH + CACHE) + list(APPEND DCMTK_INCLUDE_DIRS ${DCMTK_dcmtk_INCLUDE_DIR}) + mark_as_advanced(DCMTK_dcmtk_INCLUDE_DIR) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DCMTK DEFAULT_MSG + DCMTK_config_INCLUDE_DIR + DCMTK_ofstd_INCLUDE_DIR + DCMTK_ofstd_LIBRARY + DCMTK_dcmdata_INCLUDE_DIR + DCMTK_dcmdata_LIBRARY + DCMTK_dcmimgle_INCLUDE_DIR + DCMTK_dcmimgle_LIBRARY) + +# Compatibility: This variable is deprecated +set(DCMTK_INCLUDE_DIR ${DCMTK_INCLUDE_DIRS}) diff --git a/CMake/PackageDepends/MITK_DCMTK_Config.cmake b/CMake/PackageDepends/MITK_DCMTK_Config.cmake new file mode 100644 index 0000000000..4a1bd503a5 --- /dev/null +++ b/CMake/PackageDepends/MITK_DCMTK_Config.cmake @@ -0,0 +1,48 @@ +IF(MITK_USE_DCMTK) + +IF(NOT DCMTK_DIR) + SET(DCMTK_DIR "$ENV{DCMTK_DIR}" CACHE PATH "Location of DCMTK") + SET(DCMTK_DIR "$ENV{DCMTK_DIR}") + IF(NOT DCMTK_DIR) + MESSAGE(FATAL_ERROR "DCMTK_DIR not set. Cannot proceed.") + ENDIF(NOT DCMTK_DIR) +ENDIF(NOT DCMTK_DIR) + +FIND_PACKAGE(DCMTK) + +IF(NOT DCMTK_FOUND) + MESSAGE(SEND_ERROR "DCMTK development files not found.\n Please check variables (e.g. DCMTK_DIR) for include directories and libraries.\nYou may set environment variable DCMTK_DIR before pressing 'configure'") +ENDIF(NOT DCMTK_FOUND) + +IF( NOT WIN32 ) + SET(MISSING_LIBS_REQUIRED_BY_DCMTK wrap tiff) +ENDIF( NOT WIN32 ) + +SET(QT_USE_QTSQL 1) + +IF(EXISTS ${DCMTK_config_INCLUDE_DIR}/osconfig.h) + +FILE(READ ${DCMTK_config_INCLUDE_DIR}/osconfig.h _osconfig_h) + IF(NOT _osconfig_h MATCHES "PACKAGE_VERSION_NUMBER \"354\"") + MESSAGE(STATUS "Found DCMTK newer that 3.5.4 ...") + SET(MITK_USE_DCMTK_NEWER_THAN_3_5_4 TRUE) + # assume the new oflog library is located next to the others + # this can be removed if FindDCMTK is adapted for 3.5.5 + GET_FILENAME_COMPONENT(_DCMTK_lib_dir ${DCMTK_ofstd_LIBRARY} PATH) + FIND_LIBRARY(DCMTK_oflog_LIBRARY oflog ${_DCMTK_lib_dir} ) + LIST(APPEND DCMTK_LIBRARIES ${DCMTK_oflog_LIBRARY}) + ENDIF(NOT _osconfig_h MATCHES "PACKAGE_VERSION_NUMBER \"354\"") +ENDIF(EXISTS ${DCMTK_config_INCLUDE_DIR}/osconfig.h) + +# +# Usually all code should be adapted to DCMTK 3.6 +# If necessary you could configure the MITK_USE_DCMTK_NEWER_THAN_3_5_4 variable +# to configure a header file for ifdefs: +# CONFIGURE_FILE( mitkDCMTKConfig.h.in mitkDCMTKConfig.h ) + +LIST(APPEND ALL_INCLUDE_DIRECTORIES ${DCMTK_INCLUDE_DIR} ${DCMTK_DIR}/include) +LIST(APPEND ALL_LIBRARIES ${DCMTK_LIBRARIES} ${MISSING_LIBS_REQUIRED_BY_DCMTK}) +#link_directories() + +ENDIF(MITK_USE_DCMTK) + diff --git a/Core/Code/IO/mitkDicomSeriesReader.cpp b/Core/Code/IO/mitkDicomSeriesReader.cpp index c69c8626a2..de6e93caca 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.cpp +++ b/Core/Code/IO/mitkDicomSeriesReader.cpp @@ -1,926 +1,968 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { typedef itk::GDCMSeriesFileNames DcmFileNamesGeneratorType; DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, UpdateCallBackMethod callback) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, callback)) { if( filenames.empty() ) { return NULL; } return node; } else { return NULL; } } bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, UpdateCallBackMethod callback) { if( filenames.empty() ) { MITK_WARN << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(NULL); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); switch (io->GetComponentType()) { case DcmIoType::UCHAR: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::CHAR: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::USHORT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::SHORT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::UINT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::INT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::ULONG: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::LONG: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::FLOAT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; case DcmIoType::DOUBLE: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); return true; default: MITK_ERROR << "Found unsupported DICOM pixel type: (enum value) " << io->GetComponentType(); } } } catch(itk::MemoryAllocationError& e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch(std::exception& e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch(...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { //Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // transversal gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018,0x602e> spaceTagY; gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018,0x6026> physicalTagY; gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); imageItk->SetSpacing(mySpacing); imageItk->SetRegions( myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX*dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane + timeCount*numberOfSlices*planeSize; // add time offset iterator.Set( new_pixels[ adressedPixel ] ); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } DicomSeriesReader::TwoStringContainers DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer& files, const gdcm::Scanner::MappingType& tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again TwoStringContainers result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; Point3D lastOrigin; Point3D lastDifferentOrigin; bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader "; unsigned int fileIndex(0); for (StringContainer::const_iterator fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); - + std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that - std::string thisOriginString = tagValueMappings[fileIter->c_str()][tagImagePositionPatient]; - + const char* value = tagValueMappings[fileIter->c_str()][tagImagePositionPatient]; + if (value) + { + thisOriginString = value; + } + std::istringstream originReader(thisOriginString); std::string coordinate; unsigned int dim(0); while( std::getline( originReader, coordinate, '\\' ) ) thisOrigin[dim++] = atof(coordinate.c_str()); if (dim != 3) { MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << "instead of 3 values."; } - - MITK_DEBUG << " " << fileIndex << " " << *fileIter - << " at " + + MITK_DEBUG << " " << fileIndex << " " << *fileIter + << " at " << thisOriginString << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.second.push_back( *fileIter ); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Now make sure this direction is along the normal vector of the first slice // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be loaded into a single mitk::Image at the moment // Again ugly code to read tag Image Orientation into two vEctors Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point - std::string thisOrientationString = tagValueMappings[fileIter->c_str()][tagImageOrientation]; - + std::string thisOrientationString; + const char* value = tagValueMappings[fileIter->c_str()][tagImageOrientation]; + if (value) + { + thisOrientationString = value; + } std::istringstream orientationReader(thisOrientationString); std::string coordinate; unsigned int dim(0); while( std::getline( orientationReader, coordinate, '\\' ) ) if (dim<3) right[dim++] = atof(coordinate.c_str()); else up[dim++ - 3] = atof(coordinate.c_str()); if (dim != 6) { MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << "instead of 6 values."; } /* Determine if line (thisOrigin + l * normal) contains lastDifferentOrigin. Done by calculating the distance of lastDifferentOrigin from line (thisOrigin + l *normal) E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html squared distance = | (pointAlongNormal - thisOrign) x (thisOrigin - lastDifferentOrigin) | ^ 2 / |pointAlongNormal - thisOrigin| ^ 2 ( x meaning the cross product ) MITK_DEBUG << "Tilt check: right vector (" << right[0] << "," << right[1] << "," << right[2] << "), " "up vector (" << up[0] << "," << up[1] << "," << up[2] << ")"; */ Vector3D normal = itk::CrossProduct(right, up); Point3D pointAlongNormal = thisOrigin + normal; double numerator = itk::CrossProduct( pointAlongNormal - thisOrigin , thisOrigin - lastDifferentOrigin ).GetSquaredNorm(); double denominator = (pointAlongNormal - thisOrigin).GetSquaredNorm(); double distance = sqrt(numerator / denominator); if (distance > 0.001) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { MITK_DEBUG << " Series might contain a tilted geometry"; MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; /* Pessimistic approach: split block right here result.first.assign( files.begin(), fileIter ); result.second.insert( result.second.end(), fileIter, files.end() ); return result; // stop processing with first split */ /* optimistic approach: save file for later, check all further files */ result.second.push_back(*fileIter); fileFitsIntoPattern = false; } else { result.first.push_back(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Pessimistic approach: split right here: result.first.assign( files.begin(), fileIter ); result.second.insert( result.second.end(), fileIter, files.end() ); return result; // stop processing with first split */ /* Optimistic approach: check if any of the remaining slices fits in */ result.second.push_back( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.first.push_back(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.first.push_back(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // recored current origin for reference in later iterations if ( !lastOriginInitialized || fileFitsIntoPattern && (thisOrigin != lastOrigin) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } return result; } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const StringContainer& files, const StringContainer &restrictions) { return GetSeries(files, true, restrictions); } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, const StringContainer &restrictions) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ UidFileNamesMap groupsOfSimilarImages; // preliminary result, refined into the final result mapOf3DPlusTBlocks // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID scanner.AddTag( tagSeriesInstanceUID ); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag( tagImageOrientation ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag( tagSliceThickness ); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag( tagNumberOfRows ); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag( tagNumberOfColumns ); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) scanner.AddTag( tagImagePositionPatient ); // TODO add further restrictions from arguments // let GDCM scan files if ( !scanner.Scan( files ) ) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return groupsOfSimilarImages; } // assign files IDs that will separate them for loading into image blocks for (gdcm::Scanner::ConstIterator fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { //MITK_DEBUG << "Scan file " << fileIter->first << std::endl; if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) ); groupsOfSimilarImages [ moreUniqueSeriesId ].push_back( fileIter->first ); } // PART III: sort slices spatially for ( UidFileNamesMap::const_iterator groupIter = groupsOfSimilarImages.begin(); groupIter != groupsOfSimilarImages.end(); ++groupIter ) { + try + { groupsOfSimilarImages[ groupIter->first ] = SortSeriesSlices( groupIter->second ); // sort each slice group spatially + } catch(...) + { + MITK_ERROR << "Catched something."; + } } // PART II: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks UidFileNamesMap mapOf3DPlusTBlocks; // final result of this function for ( UidFileNamesMap::const_iterator groupIter = groupsOfSimilarImages.begin(); groupIter != groupsOfSimilarImages.end(); ++groupIter ) { UidFileNamesMap mapOf3DBlocks; // intermediate result for only this group(!) StringContainer filesStillToAnalyze = groupIter->second; std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { TwoStringContainers analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, scanner.GetMappings() ); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; mapOf3DBlocks[ newGroupUID.str() ] = analysisResult.first; MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << mapOf3DBlocks[ newGroupUID.str() ].size() << " files"; ++subgroup; filesStillToAnalyze = analysisResult.second; // remember what needs further analysis } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of mapOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if ( !sortTo3DPlust ) { // copy 3D blocks to output // TODO avoid collisions (or prove impossibility) mapOf3DPlusTBlocks.insert( mapOf3DBlocks.begin(), mapOf3DBlocks.end() ); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for ( UidFileNamesMap::const_iterator block3DIter = mapOf3DBlocks.begin(); block3DIter != mapOf3DBlocks.end(); ++block3DIter ) { unsigned int numberOfFilesInThisBlock = block3DIter->second.size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; mapOf3DPlusTBlocks[thisBlockKey].insert( mapOf3DPlusTBlocks[thisBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; previousBlockKey = thisBlockKey; } else { - // check whether this and the previous block share a comon origin - // TODO should be safe, but a little try/catch or other error handling wouldn't hurt - std::string thisOriginString = scanner.GetValue( mapOf3DBlocks[thisBlockKey].front().c_str(), tagImagePositionPatient ); - std::string previousOriginString = scanner.GetValue( mapOf3DBlocks[previousBlockKey].front().c_str(), tagImagePositionPatient ); - - // also compare last origin, because this might differ if z-spacing is different - std::string thisDestinationString = scanner.GetValue( mapOf3DBlocks[thisBlockKey].back().c_str(), tagImagePositionPatient ); - std::string previousDestinationString = scanner.GetValue( mapOf3DBlocks[previousBlockKey].back().c_str(), tagImagePositionPatient ); - - bool identicalOrigins( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); + bool identicalOrigins; + try { + // check whether this and the previous block share a comon origin + // TODO should be safe, but a little try/catch or other error handling wouldn't hurt + std::string thisOriginString = scanner.GetValue( mapOf3DBlocks[thisBlockKey].front().c_str(), tagImagePositionPatient ); + std::string previousOriginString = scanner.GetValue( mapOf3DBlocks[previousBlockKey].front().c_str(), tagImagePositionPatient ); + + // also compare last origin, because this might differ if z-spacing is different + std::string thisDestinationString = scanner.GetValue( mapOf3DBlocks[thisBlockKey].back().c_str(), tagImagePositionPatient ); + std::string previousDestinationString = scanner.GetValue( mapOf3DBlocks[previousBlockKey].back().c_str(), tagImagePositionPatient ); + + identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); + + } catch(...) + { + identicalOrigins = false; + } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block mapOf3DPlusTBlocks[previousBlockKey].insert( mapOf3DPlusTBlocks[previousBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block mapOf3DPlusTBlocks[thisBlockKey].insert( mapOf3DPlusTBlocks[thisBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " ==> group closed with " << mapOf3DPlusTBlocks[previousBlockKey].size() / numberOfFilesInPreviousBlock << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for ( UidFileNamesMap::const_iterator groupIter = mapOf3DPlusTBlocks.begin(); groupIter != mapOf3DPlusTBlocks.end(); ++groupIter ) { MITK_DEBUG << " Image volume " << groupIter->first << " with " << groupIter->second.size() << " files"; } MITK_DEBUG << "Done. "; MITK_DEBUG << "================================================================================"; return mapOf3DPlusTBlocks; } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const std::string &dir, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load( dir.c_str(), false ); // non-recursive return GetSeries(directoryLister.GetFilenames(), restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ) { std::string result; try { result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") ); } catch (std::exception& e) { MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ) { const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols std::string constructedID; try { constructedID = tagValueMap[ tagSeriesInstanceUID ]; } catch (std::exception& e) { MITK_ERROR << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image."; MITK_ERROR << "Error from exception: " << e.what(); } constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string& value) { std::string IDifiedValue( value ); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, const StringContainer &restrictions) { UidFileNamesMap allSeries = GetSeries(dir, restrictions); StringContainer resultingFileList; for ( UidFileNamesMap::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter ) { if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid { resultingFileList.insert( resultingFileList.end(), idIter->second.begin(), idIter->second.end() ); // append } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); - sorter.Sort(unsortedFilenames); - return sorter.GetFilenames(); + try + { + sorter.Sort(unsortedFilenames); + return sorter.GetFilenames(); + } + catch(std::logic_error& e) + { + MITK_WARN << "Sorting error. Leaving series unsorted."; + return unsortedFilenames; + } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { + // make sure we habe Image Position and Orientation + if ( ! ( + ds1.FindDataElement(gdcm::Tag(0x0020,0x0032)) && + ds1.FindDataElement(gdcm::Tag(0x0020,0x0037)) && + ds2.FindDataElement(gdcm::Tag(0x0020,0x0032)) && + ds2.FindDataElement(gdcm::Tag(0x0020,0x0037)) + ) + ) + { + MITK_WARN << "Dicom images are missing attributes for a meaningful sorting."; + throw std::logic_error("Dicom images are missing attributes for a meaningful sorting."); + } + gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020,0x0032> image_pos2; gdcm::Attribute<0x0020,0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); if (image_orientation1 != image_orientation2) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } if ( fabs(dist1 - dist2) < mitk::eps) { gdcm::Attribute<0x0008,0x0032> acq_time1; // Acquisition time (may be missing, so we check existence first) gdcm::Attribute<0x0008,0x0032> acq_time2; if (ds1.FindDataElement(gdcm::Tag(0x0008,0x0032))) acq_time1.Set(ds1); if (ds2.FindDataElement(gdcm::Tag(0x0008,0x0032))) acq_time2.Set(ds2); // TODO this could lead to comparison of unset times (does Attribute initialize to good defaults?) // exception: same position: compare by acquisition time return acq_time1 < acq_time2; } else { // default: compare position return dist1 < dist2; } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif //configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties( const StringContainer& files, DcmIoType* io, Image* image ) { if (!io || !image) return; StringLookupTable filesForSlices; unsigned int slice(0); for ( StringContainer::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter, ++slice ) { filesForSlices.SetTableValue( slice, *fIter ); } image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) ); /* TODO DICOM tags for patient, study, series level can easily be copied from io->GetMetaDataDictionary to appropriate mitk::Image::m_PropertyList entries. Keys should follow the format "dicom.patient.gggg.eeee", values the raw strings from ITK initially, no type specific handling. Image level attributes will cause an additional run of the files through gdcm::Scanner probably. Tags of interest would be: (0020,1041) Slice Location (for verification of loading) (0020,0013) Instance Number (for display and visual reference against PACS for users) (0008,0018) SOP Instance UID (for real reference to PACS in applications) */ } } // end namespace mitk #include diff --git a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp index d656a6889a..5d42144101 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp +++ b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.cpp @@ -1,810 +1,713 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkGeometry2DDataVtkMapper3D.h" #include "mitkImageMapperGL2D.h" #include "mitkLookupTableProperty.h" #include "mitkSmartPointerProperty.h" #include "mitkSurface.h" #include "mitkVtkRepresentationProperty.h" #include "mitkWeakPointerProperty.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { -Geometry2DDataVtkMapper3D::Geometry2DDataVtkMapper3D() - : m_DisplayNormals(false), - m_ColorTwoSides(false), - m_InvertNormals(true), - m_NormalsActorAdded(false), - m_DataStorage(NULL) -{ - m_EdgeTuber = vtkTubeFilter::New(); - m_EdgeMapper = vtkPolyDataMapper::New(); - - // Disable OGL Displaylist for the "Edge". Workaround for Bug #1787 - // m_EdgeMapper->ImmediateModeRenderingOn(); - - m_SurfaceCreator = Geometry2DDataToSurfaceFilter::New(); - m_SurfaceCreatorBoundingBox = BoundingBox::New(); - m_SurfaceCreatorPointsContainer = BoundingBox::PointsContainer::New(); - m_Edges = vtkFeatureEdges::New(); - - m_Edges->BoundaryEdgesOn(); - m_Edges->FeatureEdgesOff(); - m_Edges->NonManifoldEdgesOff(); - m_Edges->ManifoldEdgesOff(); - - m_EdgeTransformer = vtkTransformPolyDataFilter::New(); - m_NormalsTransformer = vtkTransformPolyDataFilter::New(); - m_EdgeActor = vtkActor::New(); - m_BackgroundMapper = vtkPolyDataMapper::New(); - m_BackgroundActor = vtkActor::New(); - m_Prop3DAssembly = vtkAssembly::New(); - m_ImageAssembly = vtkAssembly::New(); - - m_SurfaceCreatorBoundingBox->SetPoints( m_SurfaceCreatorPointsContainer ); - - m_Cleaner = vtkCleanPolyData::New(); - - m_Cleaner->PieceInvariantOn(); - m_Cleaner->ConvertLinesToPointsOn(); - m_Cleaner->ConvertPolysToLinesOn(); - m_Cleaner->ConvertStripsToPolysOn(); - m_Cleaner->PointMergingOn(); - - // Make sure that the FeatureEdge algorithm is initialized with a "valid" - // (though empty) input - vtkPolyData *emptyPolyData = vtkPolyData::New(); - m_Cleaner->SetInput( emptyPolyData ); - emptyPolyData->Delete(); - - m_Edges->SetInput(m_Cleaner->GetOutput()); - m_EdgeTransformer->SetInput( m_Edges->GetOutput() ); - - m_EdgeTuber->SetInput( m_EdgeTransformer->GetOutput() ); - m_EdgeTuber->SetVaryRadiusToVaryRadiusOff(); - m_EdgeTuber->SetNumberOfSides( 12 ); - m_EdgeTuber->CappingOn(); - - m_EdgeMapper->SetInput( m_EdgeTuber->GetOutput() ); - m_EdgeMapper->ScalarVisibilityOff(); - - m_BackgroundMapper->SetInput(emptyPolyData); - // m_BackgroundMapper->ImmediateModeRenderingOn(); - m_EdgeActor->SetMapper( m_EdgeMapper ); - - - m_BackgroundActor->GetProperty()->SetAmbient( 0.5 ); - m_BackgroundActor->GetProperty()->SetColor( 0.0, 0.0, 0.0 ); - m_BackgroundActor->GetProperty()->SetOpacity( 1.0 ); - m_BackgroundActor->SetMapper( m_BackgroundMapper ); - - vtkProperty * backfaceProperty = m_BackgroundActor->MakeProperty(); - backfaceProperty->SetColor( 0.0, 0.0, 0.0 ); - m_BackgroundActor->SetBackfaceProperty( backfaceProperty ); - backfaceProperty->Delete(); - - m_FrontHedgeHog = vtkHedgeHog::New(); - m_BackHedgeHog = vtkHedgeHog::New(); - - m_FrontNormalsMapper = vtkPolyDataMapper::New(); - m_FrontNormalsMapper->SetInput( m_FrontHedgeHog->GetOutput() ); - m_BackNormalsMapper = vtkPolyDataMapper::New(); - - - m_Prop3DAssembly->AddPart( m_EdgeActor ); - m_Prop3DAssembly->AddPart( m_ImageAssembly ); - m_FrontNormalsActor = vtkActor::New(); - m_FrontNormalsActor->SetMapper(m_FrontNormalsMapper); - m_BackNormalsActor = vtkActor::New(); - m_BackNormalsActor->SetMapper(m_BackNormalsMapper); - - m_DefaultLookupTable = vtkLookupTable::New(); - m_DefaultLookupTable->SetTableRange( -1024.0, 4096.0 ); - m_DefaultLookupTable->SetSaturationRange( 0.0, 0.0 ); - m_DefaultLookupTable->SetHueRange( 0.0, 0.0 ); - m_DefaultLookupTable->SetValueRange( 0.0, 1.0 ); - m_DefaultLookupTable->Build(); - m_DefaultLookupTable->SetTableValue( 0, 0.0, 0.0, 0.0, 0.0 ); - - - - - - m_ImageMapperDeletedCommand = MemberCommandType::New(); - - m_ImageMapperDeletedCommand->SetCallbackFunction( - this, &Geometry2DDataVtkMapper3D::ImageMapperDeletedCallback ); -} - + Geometry2DDataVtkMapper3D::Geometry2DDataVtkMapper3D() + : m_NormalsActorAdded(false), + m_DataStorage(NULL) + { + m_EdgeTuber = vtkTubeFilter::New(); + m_EdgeMapper = vtkPolyDataMapper::New(); + + m_SurfaceCreator = Geometry2DDataToSurfaceFilter::New(); + m_SurfaceCreatorBoundingBox = BoundingBox::New(); + m_SurfaceCreatorPointsContainer = BoundingBox::PointsContainer::New(); + m_Edges = vtkFeatureEdges::New(); + + m_Edges->BoundaryEdgesOn(); + m_Edges->FeatureEdgesOff(); + m_Edges->NonManifoldEdgesOff(); + m_Edges->ManifoldEdgesOff(); + + m_EdgeTransformer = vtkTransformPolyDataFilter::New(); + m_NormalsTransformer = vtkTransformPolyDataFilter::New(); + m_EdgeActor = vtkActor::New(); + m_BackgroundMapper = vtkPolyDataMapper::New(); + m_BackgroundActor = vtkActor::New(); + m_Prop3DAssembly = vtkAssembly::New(); + m_ImageAssembly = vtkAssembly::New(); + + m_SurfaceCreatorBoundingBox->SetPoints( m_SurfaceCreatorPointsContainer ); + + m_Cleaner = vtkCleanPolyData::New(); + + m_Cleaner->PieceInvariantOn(); + m_Cleaner->ConvertLinesToPointsOn(); + m_Cleaner->ConvertPolysToLinesOn(); + m_Cleaner->ConvertStripsToPolysOn(); + m_Cleaner->PointMergingOn(); + + // Make sure that the FeatureEdge algorithm is initialized with a "valid" + // (though empty) input + vtkPolyData *emptyPolyData = vtkPolyData::New(); + m_Cleaner->SetInput( emptyPolyData ); + emptyPolyData->Delete(); + + m_Edges->SetInput(m_Cleaner->GetOutput()); + m_EdgeTransformer->SetInput( m_Edges->GetOutput() ); + + m_EdgeTuber->SetInput( m_EdgeTransformer->GetOutput() ); + m_EdgeTuber->SetVaryRadiusToVaryRadiusOff(); + m_EdgeTuber->SetNumberOfSides( 12 ); + m_EdgeTuber->CappingOn(); + + m_EdgeMapper->SetInput( m_EdgeTuber->GetOutput() ); + m_EdgeMapper->ScalarVisibilityOff(); + + m_BackgroundMapper->SetInput(emptyPolyData); + + m_EdgeActor->SetMapper( m_EdgeMapper ); + + m_BackgroundActor->GetProperty()->SetAmbient( 0.5 ); + m_BackgroundActor->GetProperty()->SetColor( 0.0, 0.0, 0.0 ); + m_BackgroundActor->GetProperty()->SetOpacity( 1.0 ); + m_BackgroundActor->SetMapper( m_BackgroundMapper ); + + vtkProperty * backfaceProperty = m_BackgroundActor->MakeProperty(); + backfaceProperty->SetColor( 0.0, 0.0, 0.0 ); + m_BackgroundActor->SetBackfaceProperty( backfaceProperty ); + backfaceProperty->Delete(); + + m_FrontHedgeHog = vtkHedgeHog::New(); + m_BackHedgeHog = vtkHedgeHog::New(); + + m_FrontNormalsMapper = vtkPolyDataMapper::New(); + m_FrontNormalsMapper->SetInput( m_FrontHedgeHog->GetOutput() ); + m_BackNormalsMapper = vtkPolyDataMapper::New(); + + m_Prop3DAssembly->AddPart( m_EdgeActor ); + m_Prop3DAssembly->AddPart( m_ImageAssembly ); + m_FrontNormalsActor = vtkActor::New(); + m_FrontNormalsActor->SetMapper(m_FrontNormalsMapper); + m_BackNormalsActor = vtkActor::New(); + m_BackNormalsActor->SetMapper(m_BackNormalsMapper); + + m_DefaultLookupTable = vtkLookupTable::New(); + m_DefaultLookupTable->SetTableRange( -1024.0, 4096.0 ); + m_DefaultLookupTable->SetSaturationRange( 0.0, 0.0 ); + m_DefaultLookupTable->SetHueRange( 0.0, 0.0 ); + m_DefaultLookupTable->SetValueRange( 0.0, 1.0 ); + m_DefaultLookupTable->Build(); + m_DefaultLookupTable->SetTableValue( 0, 0.0, 0.0, 0.0, 0.0 ); + + m_ImageMapperDeletedCommand = MemberCommandType::New(); + m_ImageMapperDeletedCommand->SetCallbackFunction( + this, &Geometry2DDataVtkMapper3D::ImageMapperDeletedCallback ); + } -Geometry2DDataVtkMapper3D::~Geometry2DDataVtkMapper3D() -{ - m_ImageAssembly->Delete(); - m_Prop3DAssembly->Delete(); - m_EdgeTuber->Delete(); - m_EdgeMapper->Delete(); - m_EdgeTransformer->Delete(); - m_Cleaner->Delete(); - m_Edges->Delete(); - m_NormalsTransformer->Delete(); - m_EdgeActor->Delete(); - m_BackgroundMapper->Delete(); - m_BackgroundActor->Delete(); - m_DefaultLookupTable->Delete(); - m_FrontNormalsMapper->Delete(); - m_FrontNormalsActor->Delete(); - m_FrontHedgeHog->Delete(); - m_BackNormalsMapper->Delete(); - m_BackNormalsActor->Delete(); - m_BackHedgeHog->Delete(); - - // Delete entries in m_ImageActors list one by one - m_ImageActors.clear(); - - LookupTablePropertiesList::iterator it; - for(it = m_LookupTableProperties.begin(); it != m_LookupTableProperties.end();++it) + Geometry2DDataVtkMapper3D::~Geometry2DDataVtkMapper3D() { - if ( it->second.LookupTableSource != NULL ) + m_ImageAssembly->Delete(); + m_Prop3DAssembly->Delete(); + m_EdgeTuber->Delete(); + m_EdgeMapper->Delete(); + m_EdgeTransformer->Delete(); + m_Cleaner->Delete(); + m_Edges->Delete(); + m_NormalsTransformer->Delete(); + m_EdgeActor->Delete(); + m_BackgroundMapper->Delete(); + m_BackgroundActor->Delete(); + m_DefaultLookupTable->Delete(); + m_FrontNormalsMapper->Delete(); + m_FrontNormalsActor->Delete(); + m_FrontHedgeHog->Delete(); + m_BackNormalsMapper->Delete(); + m_BackNormalsActor->Delete(); + m_BackHedgeHog->Delete(); + + // Delete entries in m_ImageActors list one by one + m_ImageActors.clear(); + + LookupTablePropertiesList::iterator it; + for(it = m_LookupTableProperties.begin(); it != m_LookupTableProperties.end();++it) { - it->second.LookupTableSource->Delete(); - it->second.LookupTableSource = NULL; + if ( it->second.LookupTableSource != NULL ) + { + it->second.LookupTableSource->Delete(); + it->second.LookupTableSource = NULL; + } } + m_DataStorage = NULL; } - m_DataStorage = NULL; -} - -vtkProp* Geometry2DDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) -{ - if ( (this->GetDataNode() != NULL ) - && (m_ImageAssembly != NULL) ) + vtkProp* Geometry2DDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { - // Do not transform the entire Prop3D assembly, but only the image part - // here. The colored frame is transformed elsewhere (via m_EdgeTransformer), - // since only vertices should be transformed there, not the poly data - // itself, to avoid distortion for anisotropic datasets. - m_ImageAssembly->SetUserTransform( this->GetDataNode()->GetVtkTransform() ); + if ( (this->GetDataNode() != NULL ) + && (m_ImageAssembly != NULL) ) + { + // Do not transform the entire Prop3D assembly, but only the image part + // here. The colored frame is transformed elsewhere (via m_EdgeTransformer), + // since only vertices should be transformed there, not the poly data + // itself, to avoid distortion for anisotropic datasets. + m_ImageAssembly->SetUserTransform( this->GetDataNode()->GetVtkTransform() ); + } + return m_Prop3DAssembly; } - return m_Prop3DAssembly; -} - - -void Geometry2DDataVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) -{ - m_ImageAssembly->SetUserTransform( - this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); -} -const Geometry2DData * - Geometry2DDataVtkMapper3D::GetInput() -{ - return static_cast ( GetData() ); -} + void Geometry2DDataVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) + { + m_ImageAssembly->SetUserTransform( + this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); + } + const Geometry2DData* Geometry2DDataVtkMapper3D::GetInput() + { + return static_cast ( GetData() ); + } -void Geometry2DDataVtkMapper3D::SetDataStorageForTexture(mitk::DataStorage* storage) -{ - if (storage != NULL) + void Geometry2DDataVtkMapper3D::SetDataStorageForTexture(mitk::DataStorage* storage) { - if( m_DataStorage != storage ) + if(storage != NULL && m_DataStorage != storage ) { m_DataStorage = storage; this->Modified(); } } -} - -// -// METHOD COMMENTED OUT SINCE IT IS CURRENTLY UNUSED -// -//void -//Geometry2DDataVtkMapper3D::BuildPaddedLookupTable( -// vtkLookupTable *inputLookupTable, vtkLookupTable *outputLookupTable, -// vtkFloatingPointType min, vtkFloatingPointType max ) -//{ -// // Copy the table values from the input lookup table -// vtkUnsignedCharArray *inputTable = vtkUnsignedCharArray::New(); -// inputTable->DeepCopy( inputLookupTable->GetTable() ); -// -// vtkUnsignedCharArray *outputTable = outputLookupTable->GetTable(); -// -// // Calculate the size of one lookup table "bin" -// vtkFloatingPointType binSize = (max - min) / 256.0; -// -// // Calculate the extended table size, assuming the range [-32767, max], -// // increased by 1. -// int tableSize = (int) ((max + 32767) / binSize) + 1; -// outputLookupTable->SetNumberOfTableValues( tableSize ); -// -// unsigned char *inputPtr = inputTable->WritePointer( 0, 0 ); -// unsigned char *outputPtr = outputTable->WritePointer( 0, 0 ); -// -// // Initialize the first (translucent) bin. -// *outputPtr++ = 0; *outputPtr++ = 0; *outputPtr++ = 0; *outputPtr++ = 0; -// -// int i; -// for ( i = 1; i < tableSize; ++i ) -// { -// *outputPtr++ = *inputPtr++; -// *outputPtr++ = *inputPtr++; -// *outputPtr++ = *inputPtr++; -// *outputPtr++ = *inputPtr++; -// -// // While filling the padded part of the table, use the default value -// // (the first value of the input table) -// if ( i < (tableSize - 256) ) -// { -// inputPtr -= 4; -// } -// } -// -// // Apply the new table range; the lower boundary is decreased by binSize -// // since we have one additional translucent bin. -// outputLookupTable->SetTableRange( -32767.0 - binSize, max ); -// -// inputTable->Delete(); -//} - - -int - Geometry2DDataVtkMapper3D::FindPowerOfTwo( int i ) -{ - int size; - for ( --i, size = 1; i > 0; size *= 2 ) + void Geometry2DDataVtkMapper3D::ImageMapperDeletedCallback( + itk::Object *caller, const itk::EventObject& /*event*/ ) { - i /= 2; - } - return size; -} - -void - Geometry2DDataVtkMapper3D::ImageMapperDeletedCallback( - itk::Object *caller, const itk::EventObject& /*event*/ ) -{ - ImageMapperGL2D *imageMapper = dynamic_cast< ImageMapperGL2D * >( caller ); - if ( (imageMapper != NULL) ) - { - if ( m_ImageActors.count( imageMapper ) > 0) - { - m_ImageActors[imageMapper].m_Sender = NULL; // sender is already destroying itself - m_ImageActors.erase( imageMapper ); - } - if ( m_LookupTableProperties.count( imageMapper ) > 0 ) + ImageMapperGL2D *imageMapper = dynamic_cast< ImageMapperGL2D * >( caller ); + if ( (imageMapper != NULL) ) { - m_LookupTableProperties[imageMapper].LookupTableSource->Delete(); - m_LookupTableProperties.erase( imageMapper ); + if ( m_ImageActors.count( imageMapper ) > 0) + { + m_ImageActors[imageMapper].m_Sender = NULL; // sender is already destroying itself + m_ImageActors.erase( imageMapper ); + } + if ( m_LookupTableProperties.count( imageMapper ) > 0 ) + { + m_LookupTableProperties[imageMapper].LookupTableSource->Delete(); + m_LookupTableProperties.erase( imageMapper ); + } } } -} + void Geometry2DDataVtkMapper3D::GenerateData(BaseRenderer* renderer) + { + SetVtkMapperImmediateModeRendering(m_EdgeMapper); + SetVtkMapperImmediateModeRendering(m_BackgroundMapper); -void Geometry2DDataVtkMapper3D::GenerateData(BaseRenderer* renderer) -{ - SetVtkMapperImmediateModeRendering(m_EdgeMapper); - SetVtkMapperImmediateModeRendering(m_BackgroundMapper); + // Remove all actors from the assembly, and re-initialize it with the + // edge actor + m_ImageAssembly->GetParts()->RemoveAllItems(); - // Remove all actors from the assembly, and re-initialize it with the - // edge actor - m_ImageAssembly->GetParts()->RemoveAllItems(); + if ( !this->IsVisible(renderer) ) + { + // visibility has explicitly to be set in the single actors + // due to problems when using cell picking: + // even if the assembly is invisible, the renderer contains + // references to the assemblies parts. During picking the + // visibility of each part is checked, and not only for the + // whole assembly. + m_ImageAssembly->VisibilityOff(); + m_EdgeActor->VisibilityOff(); + return; + } - if ( !this->IsVisible(renderer) ) - { // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. - m_ImageAssembly->VisibilityOff(); - m_EdgeActor->VisibilityOff(); - return; - } - - // visibility has explicitly to be set in the single actors - // due to problems when using cell picking: - // even if the assembly is invisible, the renderer contains - // references to the assemblies parts. During picking the - // visibility of each part is checked, and not only for the - // whole assembly. - m_ImageAssembly->VisibilityOn(); - m_EdgeActor->VisibilityOn(); + m_ImageAssembly->VisibilityOn(); + m_EdgeActor->VisibilityOn(); - Geometry2DData::Pointer input = const_cast< Geometry2DData * >(this->GetInput()); + Geometry2DData::Pointer input = const_cast< Geometry2DData * >(this->GetInput()); - if (input.IsNotNull() && (input->GetGeometry2D() != NULL)) - { - SmartPointerProperty::Pointer surfacecreatorprop; - surfacecreatorprop = dynamic_cast< SmartPointerProperty * >(GetDataNode()->GetProperty("surfacegeometry", renderer)); - - if ( (surfacecreatorprop.IsNull()) - || (surfacecreatorprop->GetSmartPointer().IsNull()) - || ((m_SurfaceCreator = dynamic_cast( - surfacecreatorprop->GetSmartPointer().GetPointer())).IsNull() ) - ) + if (input.IsNotNull() && (input->GetGeometry2D() != NULL)) { - //m_SurfaceCreator = Geometry2DDataToSurfaceFilter::New(); - m_SurfaceCreator->PlaceByGeometryOn(); - surfacecreatorprop = SmartPointerProperty::New( m_SurfaceCreator ); - GetDataNode()->SetProperty("surfacegeometry", surfacecreatorprop); - } + SmartPointerProperty::Pointer surfacecreatorprop; + surfacecreatorprop = dynamic_cast< SmartPointerProperty * >(GetDataNode()->GetProperty("surfacegeometry", renderer)); - m_SurfaceCreator->SetInput(input); - - int res; - if (GetDataNode()->GetIntProperty("xresolution", res, renderer)) - { - m_SurfaceCreator->SetXResolution(res); - } - if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) - { - m_SurfaceCreator->SetYResolution(res); - } + if ( (surfacecreatorprop.IsNull()) + || (surfacecreatorprop->GetSmartPointer().IsNull()) + || ((m_SurfaceCreator = dynamic_cast + (surfacecreatorprop->GetSmartPointer().GetPointer())).IsNull() ) ) + { + m_SurfaceCreator->PlaceByGeometryOn(); + surfacecreatorprop = SmartPointerProperty::New( m_SurfaceCreator ); + GetDataNode()->SetProperty("surfacegeometry", surfacecreatorprop); + } - double tubeRadius = 1.0; // Radius of tubular edge surrounding plane + m_SurfaceCreator->SetInput(input); - // Clip the Geometry2D with the reference geometry bounds (if available) - if ( input->GetGeometry2D()->HasReferenceGeometry() ) - { - Geometry3D *referenceGeometry = - input->GetGeometry2D()->GetReferenceGeometry(); + int res; + if (GetDataNode()->GetIntProperty("xresolution", res, renderer)) + { + m_SurfaceCreator->SetXResolution(res); + } + if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) + { + m_SurfaceCreator->SetYResolution(res); + } - BoundingBox::PointType boundingBoxMin, boundingBoxMax; - boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); - boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); + double tubeRadius = 1.0; // Radius of tubular edge surrounding plane - if ( referenceGeometry->GetImageGeometry() ) + // Clip the Geometry2D with the reference geometry bounds (if available) + if ( input->GetGeometry2D()->HasReferenceGeometry() ) { - for ( unsigned int i = 0; i < 3; ++i ) + Geometry3D *referenceGeometry = + input->GetGeometry2D()->GetReferenceGeometry(); + + BoundingBox::PointType boundingBoxMin, boundingBoxMax; + boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); + boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); + + if ( referenceGeometry->GetImageGeometry() ) { - boundingBoxMin[i] -= 0.5; - boundingBoxMax[i] -= 0.5; + for ( unsigned int i = 0; i < 3; ++i ) + { + boundingBoxMin[i] -= 0.5; + boundingBoxMax[i] -= 0.5; + } } - } - m_SurfaceCreatorPointsContainer->CreateElementAt( 0 ) = boundingBoxMin; - m_SurfaceCreatorPointsContainer->CreateElementAt( 1 ) = boundingBoxMax; + m_SurfaceCreatorPointsContainer->CreateElementAt( 0 ) = boundingBoxMin; + m_SurfaceCreatorPointsContainer->CreateElementAt( 1 ) = boundingBoxMax; - m_SurfaceCreatorBoundingBox->ComputeBoundingBox(); + m_SurfaceCreatorBoundingBox->ComputeBoundingBox(); - m_SurfaceCreator->SetBoundingBox( m_SurfaceCreatorBoundingBox ); + m_SurfaceCreator->SetBoundingBox( m_SurfaceCreatorBoundingBox ); + + tubeRadius = referenceGeometry->GetDiagonalLength() / 450.0; + } - tubeRadius = referenceGeometry->GetDiagonalLength() / 450.0; - } - else - { // If no reference geometry is available, clip with the current global // bounds - if (m_DataStorage.IsNotNull()) + else if (m_DataStorage.IsNotNull()) { m_SurfaceCreator->SetBoundingBox(m_DataStorage->ComputeVisibleBoundingBox(NULL, "includeInBoundingBox")); - tubeRadius = sqrt( m_SurfaceCreator->GetBoundingBox()->GetDiagonalLength2() ) / 450.0; } - } - - // Calculate the surface of the Geometry2D - m_SurfaceCreator->Update(); - Surface *surface = m_SurfaceCreator->GetOutput(); + // Calculate the surface of the Geometry2D + m_SurfaceCreator->Update(); + Surface *surface = m_SurfaceCreator->GetOutput(); - // Check if there's something to display, otherwise return - if ( (surface->GetVtkPolyData() == 0 ) - || (surface->GetVtkPolyData()->GetNumberOfCells() == 0) ) - { - m_ImageAssembly->VisibilityOff(); - return; - } - - // add a graphical representation of the surface normals if requested - DataNode* node = this->GetDataNode(); - node->GetBoolProperty("draw normals 3D", m_DisplayNormals, renderer); - node->GetBoolProperty("color two sides", m_ColorTwoSides, renderer); - node->GetBoolProperty("invert normals", m_InvertNormals, renderer); - - if ( m_DisplayNormals || m_ColorTwoSides ) - { - float frontColor[3] = { 0.0, 0.0, 1.0 }; - node->GetColor( frontColor, renderer, "front color" ); - float backColor[3] = { 1.0, 0.0, 0.0 }; - node->GetColor( backColor, renderer, "back color" ); + // Check if there's something to display, otherwise return + if ( (surface->GetVtkPolyData() == 0 ) + || (surface->GetVtkPolyData()->GetNumberOfCells() == 0) ) + { + m_ImageAssembly->VisibilityOff(); + return; + } - if ( m_DisplayNormals ) + // add a graphical representation of the surface normals if requested + DataNode* node = this->GetDataNode(); + bool displayNormals = false; + bool colorTwoSides = false; + bool invertNormals = false; + node->GetBoolProperty("draw normals 3D", displayNormals, renderer); + node->GetBoolProperty("color two sides", colorTwoSides, renderer); + node->GetBoolProperty("invert normals", invertNormals, renderer); + + //if we want to draw the display normals or render two sides we have to get the colors + if( displayNormals || colorTwoSides ) { - m_NormalsTransformer->SetInput( surface->GetVtkPolyData() ); - m_NormalsTransformer->SetTransform(node->GetVtkTransform(this->GetTimestep()) ); + //get colors + float frontColor[3] = { 0.0, 0.0, 1.0 }; + node->GetColor( frontColor, renderer, "front color" ); + float backColor[3] = { 1.0, 0.0, 0.0 }; + node->GetColor( backColor, renderer, "back color" ); - m_FrontHedgeHog->SetInput( m_NormalsTransformer->GetOutput() ); - m_FrontHedgeHog->SetVectorModeToUseNormal(); - m_FrontHedgeHog->SetScaleFactor( m_InvertNormals ? 1.0 : -1.0 ); + if ( displayNormals ) + { + m_NormalsTransformer->SetInput( surface->GetVtkPolyData() ); + m_NormalsTransformer->SetTransform(node->GetVtkTransform(this->GetTimestep()) ); - m_FrontNormalsActor->GetProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); + m_FrontHedgeHog->SetInput( m_NormalsTransformer->GetOutput() ); + m_FrontHedgeHog->SetVectorModeToUseNormal(); + m_FrontHedgeHog->SetScaleFactor( invertNormals ? 1.0 : -1.0 ); - m_BackHedgeHog->SetInput( m_NormalsTransformer->GetOutput() ); - m_BackHedgeHog->SetVectorModeToUseNormal(); - m_BackHedgeHog->SetScaleFactor( m_InvertNormals ? -1.0 : 1.0 ); + m_FrontNormalsActor->GetProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); - m_BackNormalsActor->GetProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); + m_BackHedgeHog->SetInput( m_NormalsTransformer->GetOutput() ); + m_BackHedgeHog->SetVectorModeToUseNormal(); + m_BackHedgeHog->SetScaleFactor( invertNormals ? -1.0 : 1.0 ); - if ( !m_NormalsActorAdded ) - { - m_Prop3DAssembly->AddPart( m_FrontNormalsActor ); - m_Prop3DAssembly->AddPart( m_BackNormalsActor ); - m_NormalsActorAdded = true; - } - } + m_BackNormalsActor->GetProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); - if ( m_ColorTwoSides ) - { - if ( !m_InvertNormals ) - { - m_BackgroundActor->GetBackfaceProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); - m_BackgroundActor->GetProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); + //if there is no actor added yet, add one + if ( !m_NormalsActorAdded ) + { + m_Prop3DAssembly->AddPart( m_FrontNormalsActor ); + m_Prop3DAssembly->AddPart( m_BackNormalsActor ); + m_NormalsActorAdded = true; + } } - else + //if we don't want to display normals AND there is an actor added remove the actor + else if ( m_NormalsActorAdded ) { - m_BackgroundActor->GetProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); - m_BackgroundActor->GetBackfaceProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); + m_Prop3DAssembly->RemovePart( m_FrontNormalsActor ); + m_Prop3DAssembly->RemovePart( m_BackNormalsActor ); + m_NormalsActorAdded = false; } - } - } - else if ( !m_DisplayNormals ) - { - if ( m_NormalsActorAdded ) - { - m_Prop3DAssembly->RemovePart( m_FrontNormalsActor ); - m_Prop3DAssembly->RemovePart( m_BackNormalsActor ); - m_NormalsActorAdded = false; + if ( colorTwoSides ) + { + if ( !invertNormals ) + { + m_BackgroundActor->GetProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); + m_BackgroundActor->GetBackfaceProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); + } + else + { + m_BackgroundActor->GetProperty()->SetColor( frontColor[0], frontColor[1], frontColor[2] ); + m_BackgroundActor->GetBackfaceProperty()->SetColor( backColor[0], backColor[1], backColor[2] ); + } + } } - } - - // Add black background for all images (which may be transparent) - m_BackgroundMapper->SetInput( surface->GetVtkPolyData() ); - m_ImageAssembly->AddPart( m_BackgroundActor ); - LayerSortedActorList layerSortedActors; + // Add black background for all images (which may be transparent) + m_BackgroundMapper->SetInput( surface->GetVtkPolyData() ); + m_ImageAssembly->AddPart( m_BackgroundActor ); + LayerSortedActorList layerSortedActors; - // Traverse the data tree to find nodes resliced by ImageMapperGL2D - mitk::NodePredicateOr::Pointer p = mitk::NodePredicateOr::New(); - p->AddPredicate(mitk::NodePredicateDataType::New("Image")); - p->AddPredicate(mitk::NodePredicateDataType::New("DiffusionImage")); - p->AddPredicate(mitk::NodePredicateDataType::New("TensorImage")); - p->AddPredicate(mitk::NodePredicateDataType::New("QBallImage")); - mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetSubset(p); - for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) - { - DataNode *node = it->Value(); - if (node != NULL) - this->ProcessNode(node, renderer, surface, layerSortedActors); - } - - - // Add all image actors to the assembly, sorted according to - // layer property - LayerSortedActorList::iterator actorIt; - for ( actorIt = layerSortedActors.begin(); actorIt != layerSortedActors.end(); ++actorIt ) - m_ImageAssembly->AddPart( actorIt->second ); + // Traverse the data tree to find nodes resliced by ImageMapperGL2D + mitk::NodePredicateOr::Pointer p = mitk::NodePredicateOr::New(); + //use a predicate to get all data nodes which are "images" or inherit from mitk::Image + mitk::TNodePredicateDataType< mitk::Image >::Pointer predicateAllImages = mitk::TNodePredicateDataType< mitk::Image >::New(); + mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetSubset(predicateAllImages); + //process all found images + for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) + { - // Configurate the tube-shaped frame: size according to the surface - // bounds, color as specified in the plane's properties - vtkPolyData *surfacePolyData = surface->GetVtkPolyData(); - m_Cleaner->SetInput(surfacePolyData); - m_EdgeTransformer->SetTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); + DataNode *node = it->Value(); + if (node != NULL) + this->ProcessNode(node, renderer, surface, layerSortedActors); + } - // Determine maximum extent - vtkFloatingPointType* surfaceBounds = surfacePolyData->GetBounds(); - vtkFloatingPointType extent = surfaceBounds[1] - surfaceBounds[0]; - vtkFloatingPointType extentY = surfaceBounds[3] - surfaceBounds[2]; - vtkFloatingPointType extentZ = surfaceBounds[5] - surfaceBounds[4]; + // Add all image actors to the assembly, sorted according to + // layer property + LayerSortedActorList::iterator actorIt; + for ( actorIt = layerSortedActors.begin(); actorIt != layerSortedActors.end(); ++actorIt ) + { + m_ImageAssembly->AddPart( actorIt->second ); + } - if ( extent < extentY ) - extent = extentY; - if ( extent < extentZ ) - extent = extentZ; + // Configurate the tube-shaped frame: size according to the surface + // bounds, color as specified in the plane's properties + vtkPolyData *surfacePolyData = surface->GetVtkPolyData(); + m_Cleaner->SetInput(surfacePolyData); + m_EdgeTransformer->SetTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); + // Adjust the radius according to extent + m_EdgeTuber->SetRadius( tubeRadius ); - // Adjust the radius according to extent - m_EdgeTuber->SetRadius( tubeRadius ); + // Get the plane's color and set the tube properties accordingly + ColorProperty::Pointer colorProperty; + colorProperty = dynamic_cast(this->GetDataNode()->GetProperty( "color" )); + if ( colorProperty.IsNotNull() ) + { + const Color& color = colorProperty->GetColor(); + m_EdgeActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); + } + else + { + m_EdgeActor->GetProperty()->SetColor( 1.0, 1.0, 1.0 ); + } - // Get the plane's color and set the tube properties accordingly - ColorProperty::Pointer colorProperty; - colorProperty = dynamic_cast(this->GetDataNode()->GetProperty( "color" )); - if ( colorProperty.IsNotNull() ) - { - const Color& color = colorProperty->GetColor(); - m_EdgeActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); - } - else - { - m_EdgeActor->GetProperty()->SetColor( 1.0, 1.0, 1.0 ); + m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); } - m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep()) ); + VtkRepresentationProperty* representationProperty; + this->GetDataNode()->GetProperty(representationProperty, "material.representation", renderer); + if ( representationProperty != NULL ) + m_BackgroundActor->GetProperty()->SetRepresentation( representationProperty->GetVtkRepresentation() ); } - VtkRepresentationProperty* representationProperty; - this->GetDataNode()->GetProperty(representationProperty, "material.representation", renderer); - if ( representationProperty != NULL ) - m_BackgroundActor->GetProperty()->SetRepresentation( representationProperty->GetVtkRepresentation() ); -} - - -void Geometry2DDataVtkMapper3D::ProcessNode( DataNode * node, BaseRenderer* renderer, Surface * surface, LayerSortedActorList &layerSortedActors ) -{ - if ( node != NULL ) + void Geometry2DDataVtkMapper3D::ProcessNode( DataNode * node, BaseRenderer* renderer, + Surface * surface, LayerSortedActorList &layerSortedActors ) { - ImageMapperGL2D *imageMapper = - dynamic_cast< ImageMapperGL2D * >( node->GetMapper(1) ); - - if(!imageMapper) + if ( node != NULL ) { - if(node->GetMapper(1)) - { + //we need to get the information from the 2D mapper to render the texture on the 3D plane + ImageMapperGL2D *imageMapper = dynamic_cast< ImageMapperGL2D * >( node->GetMapper(1) ); //GetMapper(1) provides the 2D mapper for the data node + + //if there is a 2D mapper, which is not the standard image mapper... + if(!imageMapper && node->GetMapper(1)) + { //... check if it is the composite mapper std::string cname(node->GetMapper(1)->GetNameOfClass()); - if(!cname.compare("CompositeMapper")) + if(!cname.compare("CompositeMapper")) //string.compare returns 0 if the two strings are equal. { + //get the standard image mapper. + //This is a special case in MITK and does only work for the CompositeMapper. imageMapper = dynamic_cast( node->GetMapper(3) ); } } - } - if ( (node->IsVisible(renderer)) && imageMapper ) - { - WeakPointerProperty::Pointer rendererProp = - dynamic_cast< WeakPointerProperty * >(GetDataNode()->GetPropertyList()->GetProperty("renderer")); - - if ( rendererProp.IsNotNull() ) + if ( (node->IsVisible(renderer)) && imageMapper ) { - BaseRenderer::Pointer planeRenderer = dynamic_cast< BaseRenderer * >(rendererProp->GetWeakPointer().GetPointer()); - if ( planeRenderer.IsNotNull() ) + WeakPointerProperty::Pointer rendererProp = + dynamic_cast< WeakPointerProperty * >(GetDataNode()->GetPropertyList()->GetProperty("renderer")); + + if ( rendererProp.IsNotNull() ) { - // If it has not been initialized already in a previous pass, - // generate an actor, a lookup table and a texture object to - // render the image associated with the ImageMapperGL2D. - vtkActor *imageActor; - vtkDataSetMapper *dataSetMapper = NULL; - vtkLookupTable *lookupTable; - vtkTexture *texture; - if ( m_ImageActors.count( imageMapper ) == 0 ) - { - dataSetMapper = vtkDataSetMapper::New(); - dataSetMapper->ImmediateModeRenderingOn(); - - lookupTable = vtkLookupTable::New(); - lookupTable->DeepCopy( m_DefaultLookupTable ); - lookupTable->SetRange( -1024.0, 4095.0 ); - - texture = vtkTexture::New(); - texture->InterpolateOn(); - texture->SetLookupTable( lookupTable ); - texture->RepeatOff(); - - imageActor = vtkActor::New(); - imageActor->GetProperty()->SetAmbient( 0.5 ); - imageActor->SetMapper( dataSetMapper ); - imageActor->SetTexture( texture ); - - // Make imageActor the sole owner of the mapper and texture - // objects - lookupTable->UnRegister( NULL ); - dataSetMapper->UnRegister( NULL ); - texture->UnRegister( NULL ); - - // Store the actor so that it may be accessed in following - // passes. - m_ImageActors[imageMapper].Initialize(imageActor, imageMapper, m_ImageMapperDeletedCommand); - } - else + BaseRenderer::Pointer planeRenderer = dynamic_cast< BaseRenderer * >(rendererProp->GetWeakPointer().GetPointer()); + + if ( planeRenderer.IsNotNull() ) { - // Else, retrieve the actor and associated objects from the - // previous pass. - imageActor = m_ImageActors[imageMapper].m_Actor; - dataSetMapper = (vtkDataSetMapper *)imageActor->GetMapper(); - texture = imageActor->GetTexture(); + // If it has not been initialized already in a previous pass, + // generate an actor, a lookup table and a texture object to + // render the image associated with the ImageMapperGL2D. + vtkActor *imageActor; + vtkDataSetMapper *dataSetMapper = NULL; + vtkLookupTable *lookupTable; + vtkTexture *texture; + if ( m_ImageActors.count( imageMapper ) == 0 ) + { + dataSetMapper = vtkDataSetMapper::New(); + //Enable rendering without copying the image. + dataSetMapper->ImmediateModeRenderingOn(); + + lookupTable = vtkLookupTable::New(); + lookupTable->DeepCopy( m_DefaultLookupTable ); + + texture = vtkTexture::New(); + texture->SetLookupTable( lookupTable ); + texture->RepeatOff(); + + imageActor = vtkActor::New(); + imageActor->GetProperty()->SetAmbient( 0.5 ); + imageActor->SetMapper( dataSetMapper ); + imageActor->SetTexture( texture ); + + // Make imageActor the sole owner of the mapper and texture + // objects + lookupTable->UnRegister( NULL ); + dataSetMapper->UnRegister( NULL ); + texture->UnRegister( NULL ); + + // Store the actor so that it may be accessed in following + // passes. + m_ImageActors[imageMapper].Initialize(imageActor, imageMapper, m_ImageMapperDeletedCommand); + } + else + { + // Else, retrieve the actor and associated objects from the + // previous pass. + imageActor = m_ImageActors[imageMapper].m_Actor; + dataSetMapper = (vtkDataSetMapper *)imageActor->GetMapper(); + texture = imageActor->GetTexture(); - //BUG (#1551) added dynamic cast for VTK5.2 support + //BUG (#1551) added dynamic cast for VTK5.2 support #if ( ( VTK_MAJOR_VERSION >= 5 ) && ( VTK_MINOR_VERSION>=2) ) - lookupTable = dynamic_cast(texture->GetLookupTable()); + lookupTable = dynamic_cast(texture->GetLookupTable()); #else - lookupTable = texture->GetLookupTable(); + lookupTable = texture->GetLookupTable(); #endif - } + } - // Set poly data new each time its object changes (e.g. when - // switching between planar and curved geometries) - if ( dataSetMapper != NULL ) - { - if ( dataSetMapper->GetInput() != surface->GetVtkPolyData() ) + // Set poly data new each time its object changes (e.g. when + // switching between planar and curved geometries) + if ( (dataSetMapper != NULL) && (dataSetMapper->GetInput() != surface->GetVtkPolyData()) ) { dataSetMapper->SetInput( surface->GetVtkPolyData() ); } - } - imageActor->GetMapper()->GetInput()->Update(); - imageActor->GetMapper()->Update(); + imageActor->GetMapper()->GetInput()->Update(); + imageActor->GetMapper()->Update(); - // We have to do this before GenerateAllData() is called - // since there may be no RendererInfo for renderer yet, - // thus GenerateAllData won't update the (non-existing) - // RendererInfo for renderer. By calling GetRendererInfo - // a RendererInfo will be created for renderer (if it does not - // exist yet). - imageMapper->GetRendererInfo( planeRenderer ); + // We have to do this before GenerateAllData() is called + // since there may be no RendererInfo for renderer yet, + // thus GenerateAllData won't update the (non-existing) + // RendererInfo for renderer. By calling GetRendererInfo + // a RendererInfo will be created for renderer (if it does not + // exist yet). + imageMapper->GetRendererInfo( planeRenderer ); + imageMapper->GenerateAllData(); - imageMapper->GenerateAllData(); + // ensure the right openGL context, as 3D widgets may render and take their plane texture from 2D image mappers + renderer->GetRenderWindow()->MakeCurrent(); - // ensure the right openGL context, as 3D widgets may render and take their plane texture from 2D image mappers - renderer->GetRenderWindow()->MakeCurrent(); + // Retrieve and update image to be mapped + const ImageMapperGL2D::RendererInfo *rit = imageMapper->GetRendererInfo( planeRenderer ); + if(rit->m_Image != NULL) + { + rit->m_Image->Update(); + //set the 2D image as texture for the 3D plane + texture->SetInput( rit->m_Image ); - // Retrieve and update image to be mapped - const ImageMapperGL2D::RendererInfo *rit = - imageMapper->GetRendererInfo( planeRenderer ); - if(rit->m_Image != NULL) - { - rit->m_Image->Update(); - texture->SetInput( rit->m_Image ); - // check for level-window-prop and use it if it exists - ScalarType windowMin = 0.0; - ScalarType windowMax = 255.0; - LevelWindow levelWindow; - - bool binary = false; - node->GetBoolProperty( "binary", binary, renderer ); - - // VTK (mis-)interprets unsigned char (binary) images as color images; - // So, we must manually turn on their mapping through a (gray scale) lookup table; - if( binary ) - texture->MapColorScalarsThroughLookupTableOn(); - else - texture->MapColorScalarsThroughLookupTableOff(); + //default level window + ScalarType windowMin = 0.0; + ScalarType windowMax = 255.0; + LevelWindow levelWindow; - if( binary ) - { - windowMin = 0; - windowMax = 1; - } - else if( node->GetLevelWindow( levelWindow, planeRenderer, "levelWindow" ) - || node->GetLevelWindow( levelWindow, planeRenderer ) ) - { - windowMin = levelWindow.GetLowerWindowBound(); - windowMax = levelWindow.GetUpperWindowBound(); - } + bool binary = false; + node->GetBoolProperty( "binary", binary, renderer ); - vtkLookupTable *lookupTableSource; - // check for "use color" - bool useColor; - if ( !node->GetBoolProperty( "use color", useColor, planeRenderer ) ) - useColor = false; - if ( binary ) - useColor = true; - - // check for LookupTable - LookupTableProperty::Pointer lookupTableProp; - lookupTableProp = dynamic_cast< LookupTableProperty * >(node->GetPropertyList()->GetProperty( "LookupTable" )); - - // If there is a lookup table supplied, use it; otherwise, - // use the default grayscale table - if ( lookupTableProp.IsNotNull() && !useColor ) - { - lookupTableSource = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); - } - else - { - lookupTableSource = m_DefaultLookupTable; - } + // check for "use color" + bool useColor = false; + node->GetBoolProperty( "use color", useColor, planeRenderer ); - LookupTableProperties &lutProperties = - m_LookupTableProperties[imageMapper]; + // VTK (mis-)interprets unsigned char (binary) images as color images; + // So, we must manually turn on their mapping through a (gray scale) lookup table; + texture->SetMapColorScalarsThroughLookupTable(binary); - // If there has been some change since the last pass which - // makes it necessary to re-build the lookup table, do it. - if ( (lutProperties.LookupTableSource != lookupTableSource) - || (lutProperties.windowMin != windowMin) - || (lutProperties.windowMax != windowMax) ) - { - // Note the values for the next pass (lutProperties is a - // reference to the list entry!) - if ( lutProperties.LookupTableSource != NULL ) + //if we have a binary image, the range is just 0 to 1 + if( binary ) { - lutProperties.LookupTableSource->Delete(); + windowMin = 0; + windowMax = 1; + useColor = true; } - lutProperties.LookupTableSource = lookupTableSource; - lutProperties.LookupTableSource->Register( NULL ); - lutProperties.windowMin = windowMin; - lutProperties.windowMax = windowMax; - lookupTable->DeepCopy( lookupTableSource ); - lookupTable->SetRange( windowMin, windowMax ); - } + // check for level-window-prop and use it if it exists + if( !binary && + ( node->GetLevelWindow( levelWindow, planeRenderer, "levelWindow" ) + || node->GetLevelWindow( levelWindow, planeRenderer ) ) ) + { + windowMin = levelWindow.GetLowerWindowBound(); + windowMax = levelWindow.GetUpperWindowBound(); + } + + vtkLookupTable *lookupTableSource; + + + // check for LookupTable + LookupTableProperty::Pointer lookupTableProp; + lookupTableProp = dynamic_cast< LookupTableProperty * >(node->GetPropertyList()->GetProperty( "LookupTable" )); + + // If there is a lookup table supplied and we don't + // want to use the color property, use it; + //otherwise, use the default grayscale table + if ( lookupTableProp.IsNotNull() && !useColor ) + { + lookupTableSource = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); + } + else + { + lookupTableSource = m_DefaultLookupTable; + } + + LookupTableProperties &lutProperties = + m_LookupTableProperties[imageMapper]; + + // If there has been some change since the last pass which + // makes it necessary to re-build the lookup table, do it. + if ( (lutProperties.LookupTableSource != lookupTableSource) + || (lutProperties.windowMin != windowMin) + || (lutProperties.windowMax != windowMax) ) + { + // Note the values for the next pass (lutProperties is a + // reference to the list entry!) + if ( lutProperties.LookupTableSource != NULL ) + { + lutProperties.LookupTableSource->Delete(); + } + lutProperties.LookupTableSource = lookupTableSource; + lutProperties.LookupTableSource->Register( NULL ); + lutProperties.windowMin = windowMin; + lutProperties.windowMax = windowMax; + + lookupTable->DeepCopy( lookupTableSource ); + lookupTable->SetRange( windowMin, windowMax ); + } + + //get the color + float rgb[3] = { 1.0, 1.0, 1.0 }; + node->GetColor( rgb, renderer ); - // Apply color property (of the node, not of the plane) - float rgb[3] = { 1.0, 1.0, 1.0 }; - node->GetColor( rgb, renderer ); - imageActor->GetProperty()->SetColor( rgb[0], rgb[1], rgb[2] ); - //m_BackgroundActor->GetProperty()->SetColor(1,1,1); - - // Apply opacity property (of the node, not of the plane) - float opacity = 0.999; - node->GetOpacity( opacity, renderer ); - imageActor->GetProperty()->SetOpacity( opacity ); - - // Set texture interpolation on/off - bool textureInterpolation = node->IsOn( "texture interpolation", renderer ); - texture->SetInterpolate( textureInterpolation ); - - // Store this actor to be added to the actor assembly, sort - // by layer - int layer = 1; - node->GetIntProperty( "layer", layer ); - layerSortedActors.insert(std::pair< int, vtkActor * >( layer, imageActor ) ); + // Apply color property (of the node, not of the plane) + // if we want to use the color + if(useColor) + { + imageActor->GetProperty()->SetColor( rgb[0], rgb[1], rgb[2] ); + } + else //else default color = white to avoid site effects from the lookuptable + { + imageActor->GetProperty()->SetColor( 1, 1, 1 ); + } + + // Apply opacity property (of the node, not of the plane) + float opacity = 0.999; + node->GetOpacity( opacity, renderer ); + imageActor->GetProperty()->SetOpacity( opacity ); + + // Set texture interpolation on/off + bool textureInterpolation = node->IsOn( "texture interpolation", renderer ); + texture->SetInterpolate( textureInterpolation ); + + // Store this actor to be added to the actor assembly, sort + // by layer + int layer = 1; + node->GetIntProperty( "layer", layer ); + layerSortedActors.insert(std::pair< int, vtkActor * >( layer, imageActor ) ); + } } } } } } -} - - -void Geometry2DDataVtkMapper3D::ActorInfo::Initialize(vtkActor* actor, itk::Object* sender, itk::Command* command) -{ - m_Actor = actor; - m_Sender = sender; - // Get informed when ImageMapper object is deleted, so that - // the data structures built here can be deleted as well - m_ObserverID = sender->AddObserver( itk::DeleteEvent(), command ); -} - - -Geometry2DDataVtkMapper3D::ActorInfo::ActorInfo() : m_Actor(NULL), m_Sender(NULL), m_ObserverID(0) -{ -} + void Geometry2DDataVtkMapper3D::ActorInfo::Initialize(vtkActor* actor, itk::Object* sender, itk::Command* command) + { + m_Actor = actor; + m_Sender = sender; + // Get informed when ImageMapper object is deleted, so that + // the data structures built here can be deleted as well + m_ObserverID = sender->AddObserver( itk::DeleteEvent(), command ); + } -Geometry2DDataVtkMapper3D::ActorInfo::~ActorInfo() -{ - if(m_Sender != NULL) + Geometry2DDataVtkMapper3D::ActorInfo::ActorInfo() : m_Actor(NULL), m_Sender(NULL), m_ObserverID(0) { - m_Sender->RemoveObserver(m_ObserverID); } - if(m_Actor != NULL) + + Geometry2DDataVtkMapper3D::ActorInfo::~ActorInfo() { - m_Actor->Delete(); + if(m_Sender != NULL) + { + m_Sender->RemoveObserver(m_ObserverID); + } + if(m_Actor != NULL) + { + m_Actor->Delete(); + } } -} } // namespace mitk diff --git a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h index 19a0efb044..a19efee794 100644 --- a/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h +++ b/Core/Code/Rendering/mitkGeometry2DDataVtkMapper3D.h @@ -1,256 +1,234 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F #define MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F #include "mitkCommon.h" #include "mitkVtkMapper3D.h" #include "mitkDataStorage.h" #include "mitkGeometry2DDataToSurfaceFilter.h" #include "mitkWeakPointer.h" #include #include class vtkActor; class vtkPolyDataMapper; class vtkDataSetMapper; class vtkLookupTable; class vtkAssembly; class vtkFeatureEdges; class vtkTubeFilter; class vtkTransformPolyDataFilter; class vtkHedgeHog; namespace mitk { class Geometry2DData; class BaseRenderer; class ImageMapperGL2D; class DataStorage; /** * \brief Vtk-based mapper to display a Geometry2D in a 3D window * \ingroup Mapper * * Uses a Geometry2DDataToSurfaceFilter object to create a vtkPolyData representation of a given Geometry2D instance. * Geometry2D may either contain a common flat plane or a curved plane (ThinPlateSplineCurvedGeometry). * * The vtkPolyData object is then decorated by a colored tube on the edges and by image textures if possible * (currently this requires that there is a 2D render window rendering the same geometry as this mapper). * * Properties that influence rendering are: * * - \b "color": (ColorProperty) Color of the tubed frame. * - \b "xresolution": (FloatProperty) Resolution (=number of tiles) in x direction. Only relevant for ThinPlateSplineCurvedGeometry * - \b "yresolution": (FloatProperty) Resolution (=number of tiles) in y direction. Only relevant for ThinPlateSplineCurvedGeometry * - \b "draw normals 3D": (BoolProperty) If true, a vtkHedgeHog is used to display normals for the generated surface object. Useful to distinguish front and back of a plane. Hedgehogs are colored according to "front color" and "back color" * - \b "color two sides": (BoolProperty) If true, front and back side of the plane are colored differently ("front color" and "back color") * - \b "invert normals": (BoolProperty) Inverts front/back for display. * - \b "front color": (ColorProperty) Color for front side of the plane * - \b "back color": (ColorProperty) Color for back side of the plane + * - \b "material.representation": (BoolProperty) Choose the representation to draw the mesh in (Surface, Wireframe, Point Cloud) + * - \b "surfacegeometry": TODO: Add documentation + * - \b "LookupTable": (LookupTableProperty) Set the lookuptable to render with. + * + * Note: The following properties are set for each image individually, and thus, also influence the rendering of this mapper: + * + * - \b "texture interpolation": (BoolProperty) Turn on/off the texture interpolation of each image + * - \b "use color": (BoolProperty) Decide whether we want to use the color property or a lookuptable. + * - \b "binary": (BoolProperty) Binary image handling: Color the value=1.0 with the color property and make the background (value=0.0) of the image translucent. + * - \b "layer": (IntProperty) Controls what image is considered "on top" of another. In the case that two should inhabit the same space, higher layer occludes lower layer. + * - \b "opacity": (FloatProperty) Set the opacity for each rendered image. + * - \b "color": (FloatProperty) Set the color for each rendered image. * * The internal filter pipeline which combines a (sometimes deformed) 2D surface * with a nice frame and image textures is illustrated in the following sketch: * * \image html mitkGeometry2DDataVtkMapper3D.png "Internal filter pipeline" * */ class MITK_CORE_EXPORT Geometry2DDataVtkMapper3D : public VtkMapper3D { public: mitkClassMacro(Geometry2DDataVtkMapper3D, VtkMapper3D); itkNewMacro(Geometry2DDataVtkMapper3D); /** * Overloaded since the displayed color-frame of the image mustn't be * transformed after generation of poly data, but before (vertex coordinates * only) */ virtual vtkProp *GetVtkProp(mitk::BaseRenderer *renderer); virtual void UpdateVtkTransform(mitk::BaseRenderer *renderer); /** * \brief Get the Geometry2DData to map */ virtual const Geometry2DData *GetInput(); /** * \brief All images found when traversing the (sub-) tree starting at * \a iterator which are resliced by an ImageMapperGL2D will be mapped. + * This method is used to set the data storage to traverse. This offers + * the possibility to use this mapper for other data storages (not only + * the default data storage). */ virtual void SetDataStorageForTexture(mitk::DataStorage* storage); protected: + typedef std::multimap< int, vtkActor * > LayerSortedActorList; + Geometry2DDataVtkMapper3D(); virtual ~Geometry2DDataVtkMapper3D(); virtual void GenerateData(BaseRenderer* renderer); - typedef std::multimap< int, vtkActor * > LayerSortedActorList; - void ProcessNode( DataNode * node, BaseRenderer* renderer, Surface * surface, LayerSortedActorList &layerSortedActors ); - /* - * \brief Construct an extended lookup table from the given one. - * - * In order to overlay differently sized images over each other, it is - * necessary to have a special translucent value, so that the empty - * surroundings of the smaller image do not occlude the bigger image. - * - * The smallest possible short (-32768) is used for this. Due to the - * implementation of vtkLookupTable, the lookup table needs to be extended - * so that this value is included. Entries between -32768 and the actual - * table minimum will be set to the lowest entry in the input lookup table. - * From this point onward, the input lookup is just copied into the output - * lookup table. - * - * See also mitk::ImageMapperGL2D, where -32768 is defined as BackgroundLevel - * for resampling. - * - * NOTE: This method is currently not used; to make sure that the plane is - * not rendered with translucent "holes", a black background plane is first - * rendered under all other planes. - */ - //virtual void BuildPaddedLookupTable( - // vtkLookupTable *inputLookupTable, vtkLookupTable *outputLookupTable, - // vtkFloatingPointType min, vtkFloatingPointType max ); - - int FindPowerOfTwo( int i ); - void ImageMapperDeletedCallback( itk::Object *caller, const itk::EventObject &event ); /** \brief general PropAssembly to hold the entire scene */ vtkAssembly *m_Prop3DAssembly; /** \brief PropAssembly to hold the planes */ vtkAssembly *m_ImageAssembly; Geometry2DDataToSurfaceFilter::Pointer m_SurfaceCreator; BoundingBox::Pointer m_SurfaceCreatorBoundingBox; BoundingBox::PointsContainer::Pointer m_SurfaceCreatorPointsContainer; /** \brief Edge extractor for tube-shaped frame */ vtkFeatureEdges *m_Edges; /** \brief Filter to apply object transform to the extracted edges */ vtkTransformPolyDataFilter *m_EdgeTransformer; /** \brief Source to create the tube-shaped frame */ vtkTubeFilter *m_EdgeTuber; /** \brief Mapper for the tube-shaped frame */ vtkPolyDataMapper *m_EdgeMapper; /** \brief Actor for the tube-shaped frame */ vtkActor *m_EdgeActor; /** \brief Mapper for black plane background */ vtkPolyDataMapper *m_BackgroundMapper; /** \brief Actor for black plane background */ vtkActor *m_BackgroundActor; /** \brief Transforms the suface before applying the glyph filter */ vtkTransformPolyDataFilter* m_NormalsTransformer; /** \brief Mapper for normals representation (thin lines) */ vtkPolyDataMapper* m_FrontNormalsMapper; vtkPolyDataMapper* m_BackNormalsMapper; /** \brief Generates lines for surface normals */ vtkHedgeHog* m_FrontHedgeHog; vtkHedgeHog* m_BackHedgeHog; /** \brief Actor to hold the normals arrows */ vtkActor* m_FrontNormalsActor; vtkActor* m_BackNormalsActor; /** Cleans the polyline in order to avoid phantom boundaries */ vtkCleanPolyData *m_Cleaner; - /** \brief Whether or not to display normals */ - bool m_DisplayNormals; - - /** \brief Whether to color front and back */ - bool m_ColorTwoSides; - - /** \brief Whether or not to invert normals */ - bool m_InvertNormals; - /** Internal flag, if actors for normals are already added to m_Prop3DAssembly*/ bool m_NormalsActorAdded; /** \brief The DataStorage defines which part of the data tree is traversed for renderering. */ mitk::WeakPointer m_DataStorage; /** A default grayscale lookup-table, used for reference */ vtkLookupTable *m_DefaultLookupTable; class MITK_CORE_EXPORT ActorInfo { public: vtkActor * m_Actor; // we do not need a smart-pointer, because we delete our // connection, when the referenced mapper is destroyed itk::Object* m_Sender; unsigned long m_ObserverID; void Initialize(vtkActor* actor, itk::Object* sender, itk::Command* command); ActorInfo(); ~ActorInfo(); }; /** \brief List holding the vtkActor to map the image into 3D for each * ImageMapper */ typedef std::map< ImageMapperGL2D *, ActorInfo > ActorList; ActorList m_ImageActors; struct LookupTableProperties { LookupTableProperties() : LookupTableSource( NULL ), windowMin( 0.0 ), windowMax( 4096.0 ) {} vtkLookupTable *LookupTableSource; vtkFloatingPointType windowMin; vtkFloatingPointType windowMax; }; typedef std::map< ImageMapperGL2D *, LookupTableProperties > LookupTablePropertiesList; /** \brief List holding some lookup table properties of the previous pass */ LookupTablePropertiesList m_LookupTableProperties; - // responsiblity to remove the observer upon its destruction typedef itk::MemberCommand< Geometry2DDataVtkMapper3D > MemberCommandType; MemberCommandType::Pointer m_ImageMapperDeletedCommand; }; } // namespace mitk #endif /* MITKGEOMETRY2DDATAVTKMAPPER3D_H_HEADER_INCLUDED_C196C71F */ diff --git a/Documentation/Doxygen/Interaction.dox b/Documentation/Doxygen/Interaction.dox index 3343d50766..dac6c11999 100644 --- a/Documentation/Doxygen/Interaction.dox +++ b/Documentation/Doxygen/Interaction.dox @@ -1,122 +1,122 @@ /** \page InteractionPage Interaction and Undo/Redo Concepts \section InteractionIntroduction Interaction in MITK \b Interaction is one of the most important tasks in clinically useful image processing software. Due to that, MITK has a special interaction concept, with which the developer can map the desired interaction. For a simple change in interaction he doesn't have to change the code. All information about the sequence of the interaction is stored in an XML-file that is loaded by the application during startup procedure at runtime. That even allows the storage of different interaction patterns, e.g. an interaction behaviour like in MS PowerPoint, in Adobe Photoshop or like the interaction behaviour on a medical image retrieval system. \section Statemachines Statemachines to realize Interaction The interaction in MITK is realized with the concept of state machines (by Mealy). This concept allows to build the steps of interaction with different states, which each have different conditions, very alike the different interactions that may have to be build to develop medical imaging applications. Furthermore state machines can be implemented using object oriented programming (OOP). Due to that we can abstract from the section of code, that implements the interaction and focus on the sequence of interaction. What steps must the user do first before the program can compute a result? For example he has to declare three points in space first and these points are the input of a filter so only after the definition of the points, the filter can produce a result. The according interaction sequence will inform the filter after the third point is set and not before that. Now the filter after an adaption only needs two points as an input. The sequence of the interaction can be easily changed if it is build up as a sequence of objects and not hard implemented in a e.g. switch/case block. Or the user wants to add a point in the scene with the right mouse button instead of the left. Wouldn't it be nice to only change the definition of an interaction sequence rather than having to search through the code and changing every single if/else condition? \subsection Statemachine State Machine So a separation of the definition of a sequence in interaction and its implementation is a useful step in the development of an interactive application. To be able to do that, we implemented the concept of state machines with several classes: States, Transitions and Actions define the interaction pattern. The state machine itself adds the handling of events, that are send to it. \image html statemachine.jpg \subsubsection ExampleA Example A: A deterministic Mealy state machine has always one current state (here state 1). If an event 1 is sent to the state machine, it searches in its current state for a transition that waits for event 1 (here transition 1). The state machine finds transition 1, changes the current state to state2, cause the transition points to it and executes actions 1 and 2. Now state 2 is the current state. The state machine receives an event 2 and searches for an according transition. Transition 2 waits for event 2, and since the transition leads to state 2 the current state is not changed. Action 3 and 4 are executed. Now Event 3 gets send to the state machine but the state machine can't find an according transition in state 2. Only transition 2 , that waits for event 2 and transition 4, that waits for event 4 are defined in that state. So the state machine ignores the event and doesn't change the state or execute an action. Now the state machine receives an event 4 and finds transition 3. So now the current state changes from state 2 to state 1 and actions 5 and 1 are executed. Several actions can be defined in one transition. The execution of an action is the active part of the state machine. Here is where the state machine can make changes in data, e.g. add a Point into a list. See mitk::StateMachine, mitk::State, mitk::Event, mitk::Action, mitk::Transition, mitk::Interactor \subsection GuardState Guard States Guard States are a special kind of states. The action, that is executed after the state is set as current state, sends a new event to the state machine, which leads out of the guard state. So the state machine will only stay in a guard state for a short time. This kind of state is used to check different conditions, e.g. is an Object is picked of a set of points will be full after the addition of one point. \image html statemachine_guard.jpg \subsubsection ExampleB Example B: Event 1 is send to the state machine. This leads the current state from state 1 into state check. The action 1 is executed. This action checks a condition and puts the result into a new event, that is send an handled by the same (this) state machine. E.g. is the the object, the state machine handles the interaction, picked with the received mouse-coordinate? The event, that is generated, will be Yes or No. In case of event No, the state machine sets the current state back to state 1 and executes action 2. In case of event Yes, the state machine changes the state from state check into state 2 and executes action 3, which e.g. can select the object taken care of. \subsection XMLDefinitionStatemachine Definition of a State machine Due to the separation of the definition of an interaction sequence and its implementation, the definition has to be archived somewhere, where the application can reach it during startup and build up all the objects (states, transitions and actions) that represent the sequence of a special interaction. In MITK, these informations are defined in an XML-file (usually in Interaction/mitkBaseInteraction/StateMachine.xml). The structure is the following (from \ref ExampleA) : \code \endcode The identification numbers (ID) inside a state machine has to be unique. Each state machine has to have one state, that is defined as the start-state of that state machine. This means, initially, the current state of the state machine is the start-state. -The Event-Ids seen above are also defined in the statemachine.xml file. They specify a unique number for a combination of input-conditions (key, mouse and so on). See \ref Events for further informations. +The Event-Ids seen above are also defined in the statemachine.xml file. They specify a unique number for a combination of input-conditions (key, mouse and so on). See \ref InteractionEvents for further informations. So a state machine is defined through a few lines of xml-code, which is loaded at runtime. This allows us to change the sequence in interaction in the xml-file and restart the application, where the changes are applied right away. The definition of one single state machine is called the \a statemachine-pattern. Since this pattern is build up during startup with objects (states, transitions and actions) and these objects only hold information about what interaction may be done at the current state, we can also reuse the pattern. Note, that you as a developer don't necessarily have to implement your own XML-File! We already have defined some interaction-patterns (e.g. for setting Points in 2D or 3D) which you can use and adapt. \subsubsection ReusePattern Reuse of Interaction Patterns If we for example have a pattern called "pointset", which defines how the user can set different points into the scene and there is an instance of a state machine called "PointSetInteractor". This state machine has a pointer pointing to the current state in its assigned state machine pattern. Several events are send to the state machine, which moves the pointer from one state to the next, according to the transitions, and executes the actions, referenced in the transitions. But now a new instance of the class "PointSetInteractor" has to be build. So we reuse the pattern and let the current state pointer of the new object point to the start state of the pattern "pointset". The implementation of the actions is \b not done inside a class of the pattern (\a state, \a transition, \a action), it is done inside a state machine class (see the reference for mitkStatemachine). \subsection StartupInteraction Loading Statemachines at Runtime During startup all state machine-patterns are checked for not having states, that have no transitions leading out of it (dead states), or states, that have no transitions leading into it (magic states) [Bin99 p. 183]. If an error is found, an error-message will be displayed \b and the state machine will be loaded. See mitk::StateMachineFactory -\subsection Events Events +\subsection InteractionEvents Events During runtime, events are thrown from e.g. the mouse to the operation system, are then send to your graphical user interface and from there it has to be send to the MITK-object called \a mitkEventMapper. This class maps the events received with an internal list of all events that can be understood in MITK. The definition of all understandable events is also located in the XML-File the state machines are defined in. If the received event can be found in the list, an internal mitk-eventnumber is added to the event and send to the object \a mitkGlobalInteraction. See mitk::Event, mitk::GlobalInteraction \subsection GlobalInteraction GlobalInteraction This object administers the transmit of events to registered state machines. There can be two kinds of state machines, the ones that are only listening and ones that also change data. Listening state machines are here called Listeners and state machines, that also change data, are called Interactors. \b Note that the discrimination between \a Listener and \a Interactor is only made in mitkGlobalInteraction. As Listener an object derived from class StateMachine can be added and removed from GlobalInteraction and as Interactor an object derived from class Interactor can be added and removed. See the interaction class diagram for further information. To add or remove a state machine to the list of registered interactors, call \a AddInteractor or \a RemoveInteractor of \a GlobalInteraction or to add or remove a listener call \a AddListener of \a RemoveListener. Listeners are always provided with the events. Interactors shall only be provided with an event, if they can handle the event. Because of that, the method CanHandleEvent, implemented in each Interactor, is called. This method analyses the event and returns a value between 0 (can't handle event) and 1 (can handle the event). Information, that can help to calculate this jurisdiction can be the bounding box of the interacted data and the picked mouse-position stored in the event. So after the object \a GlobalInteraction has received an event, it sends this event to all registered Listeners and then asks all registered Interactors through the method \a CanHandleEvent how good each Interactor can handle this event. The Interactor which can handle the event the best receives the event. Also see the documented code in \a mitkGlobalInteraction. To not ask all registered interactors on a new event, the class \a Interactor also has a mode, which can be one of the following: deselected, subselected (deprecated since HierarchicalInteraction has been removed), selected. These modes are also used for the event mechanism. If an interactor is in a state, where the user builds up a graphical object, it is likely, that the following events are also for the build of the object. Here the interactor is in mode selected as long as the interactor couldn't handle an event. Then it changes to mode deselected. The mode changes are done in the actions through operations (described further down) and so declared inside the interaction pattern. See mitk::GlobalInteraction \subsection Interactors Interactors The class \a Interactor is the superclass for all state machines, that solve the interaction for a single data-object. An example is the class \a mitkPointSetInteractor which handles the interaction of the data \a mitkPointSet. Inside the class \a mitkPointSetInteractor all actions, defined in the interaction-pattern "pointsetinteractor", are implemented. Inside the implementation of these actions (\a ExecuteAction(...) ), so called \a mitkOperations are created, filled with information and send to the \a mitkUndoController and to \a mitkOperactionActor (the data, the interaction is handled for). See mitk::Interactor \subsection ExecOperations Executing Operations The class mitkOperation and its subclasses basically holds all information needed to execute a certain change of data. This change of data is only done inside the data-class itself, which is derived from the interface \a mitkOperationActor. Interactors handle the interaction through state-differentiation and combine all informations about the change in a \a mitkOperation and send this operation-object to the method ExecuteOperation (of data-class). Here the necessary data is extracted and then the change of data is performed. When the operation-object, here called do-operation, is created inside the method \a ExecuteAction (in class \a mitkInteractor), an undo-operation is also created and together with the do-operation stored in an object called \a OperationEvent. After the Interactor has sent the do-operation to the data, the operation-event-object then is sent to the instance of class \a mitkUndoController, which administrates the undo-mechanism. See mitk::Operation, mitk::OperationActor \subsection UndoController UndoController The instance of class \a mitkUndoController administrates different Undo-Models. Currently implemented is a limited linear Undo. Only one Undo-Model can be activated at a time. The UndoController sends the received operation events further to the current Undo-Model, which then stores it according to the model. If the method \a Undo() of UndoController is called (e.g. Undo-Button pressed from ) the call is send to the current Undo-Model. Here the undo-operation from the last operation event in list is taken and send to the data, referenced in a pointer which is also stored in the operation-event. A call of the method \a Redo() is handled accordingly. See mitk::UndoController, mitk::LimitedLinearUndo \subsection references References [Bin99] Robert V. Binder. Testing Object-Oriented Systems: Models, Patterns, and Tools. Addison-Wesley, 1999 */ diff --git a/Modules/Bundles/org.mitk.gui.qt.igtnavigationtoolmanager/documentation/Manual/Manual.dox b/Modules/Bundles/org.mitk.gui.qt.igtnavigationtoolmanager/documentation/Manual/Manual.dox index fc21427c36..8053d614d8 100644 --- a/Modules/Bundles/org.mitk.gui.qt.igtnavigationtoolmanager/documentation/Manual/Manual.dox +++ b/Modules/Bundles/org.mitk.gui.qt.igtnavigationtoolmanager/documentation/Manual/Manual.dox @@ -1,13 +1,13 @@ /** \bundlemainpage{org.mitk.gui.qt.igtnavigationtoolmanager} MITK-IGT Navigation Tool Manager \image html icon.png "Icon of MITK-IGT Navigation Tool Manager" Available sections: - - \ref org.mitk.gui.qt.igtnavigationtoolmanagerOverview + - \ref IGTNavigationToolManagerOverview -\section org.mitk.gui.qt.igtnavigationtoolmanagerOverview +\section IGTNavigationToolManagerOverview This is the description for the MITK-IGT Navigation Tool Manager. */ diff --git a/Modules/Bundles/org.mitk.gui.qt.igttrackingtoolbox/documentation/Manual/Manual.dox b/Modules/Bundles/org.mitk.gui.qt.igttrackingtoolbox/documentation/Manual/Manual.dox index 3969f459ab..2acc072fd0 100644 --- a/Modules/Bundles/org.mitk.gui.qt.igttrackingtoolbox/documentation/Manual/Manual.dox +++ b/Modules/Bundles/org.mitk.gui.qt.igttrackingtoolbox/documentation/Manual/Manual.dox @@ -1,13 +1,13 @@ /** \bundlemainpage{org.mitk.gui.qt.igttrackingtoolbox} MITK-IGT Tracking Toolbox \image html icon.png "Icon of MITK-IGT Tracking Toolbox" Available sections: - - \ref org.mitk.gui.qt.igttrackingtoolboxOverview + - \ref IGTTrackingToolboxOverview -\section org.mitk.gui.qt.igttrackingtoolboxOverview +\section IGTTrackingToolboxOverview This is the description for the MITK-IGT Tracking Toolbox. */ diff --git a/Modules/Bundles/org.mitk.gui.qt.navigationdataplayer/documentation/Manual/Manual.dox b/Modules/Bundles/org.mitk.gui.qt.navigationdataplayer/documentation/Manual/Manual.dox index 07e803a7e2..f1e02994cf 100644 --- a/Modules/Bundles/org.mitk.gui.qt.navigationdataplayer/documentation/Manual/Manual.dox +++ b/Modules/Bundles/org.mitk.gui.qt.navigationdataplayer/documentation/Manual/Manual.dox @@ -1,13 +1,13 @@ /** \bundlemainpage{org.mitk.gui.qt.navigationdataplayer} NavigationData Player \image html icon.png "Icon of NavigationData Player" Available sections: - - \ref org.mitk.gui.qt.navigationdataplayerOverview + - \ref NavigationDataPlayerOverview -\section org.mitk.gui.qt.navigationdataplayerOverview +\section NavigationDataPlayerOverview This is the description for the NavigationData Player. */ diff --git a/Modules/Bundles/org.mitk.gui.qt.toftutorial/documentation/Manual/Manual.dox b/Modules/Bundles/org.mitk.gui.qt.toftutorial/documentation/Manual/Manual.dox index 2c5659b1a0..cb5130ba5a 100644 --- a/Modules/Bundles/org.mitk.gui.qt.toftutorial/documentation/Manual/Manual.dox +++ b/Modules/Bundles/org.mitk.gui.qt.toftutorial/documentation/Manual/Manual.dox @@ -1,13 +1,13 @@ /** \bundlemainpage{org.mitk.gui.qt.toftutorial} ToFTutorial \image html icon.png "Icon of ToFTutorial" Available sections: - - \ref org.mitk.gui.qt.toftutorialOverview + - \ref ToFTutorialOverview -\section org.mitk.gui.qt.toftutorialOverview +\section ToFTutorialOverview This is the description for the ToFTutorial. */ diff --git a/Modules/Bundles/org.mitk.gui.qt.toftutorial/src/internal/QmitkToFTutorialView.cpp b/Modules/Bundles/org.mitk.gui.qt.toftutorial/src/internal/QmitkToFTutorialView.cpp index d0093cb9bb..b7db784681 100644 --- a/Modules/Bundles/org.mitk.gui.qt.toftutorial/src/internal/QmitkToFTutorialView.cpp +++ b/Modules/Bundles/org.mitk.gui.qt.toftutorial/src/internal/QmitkToFTutorialView.cpp @@ -1,193 +1,193 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkToFTutorialView.h" #include "QmitkStdMultiWidget.h" // Qt #include // mitk includes +#include // class holding the intrinsic parameters of the according camera #include // MITK-ToF related includes #include // configuration file holding e.g. plugin paths or path to test file directory #include // filter from module ToFProcessing that calculates a surface from the given range image #include // creator class that provides pre-configured ToFCameraDevices #include // allows access to images provided by the ToF camera -#include // class representing a pinhole camera and holding the intrinsic parameters of the according camera const std::string QmitkToFTutorialView::VIEW_ID = "org.mitk.views.toftutorial"; QmitkToFTutorialView::QmitkToFTutorialView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) { } QmitkToFTutorialView::~QmitkToFTutorialView() { } void QmitkToFTutorialView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkToFTutorialViewControls; m_Controls->setupUi( parent ); connect( m_Controls->step1Button, SIGNAL(clicked()), this, SLOT(OnStep1()) ); connect( m_Controls->step2Button, SIGNAL(clicked()), this, SLOT(OnStep2()) ); } } void QmitkToFTutorialView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkToFTutorialView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkToFTutorialView::OnStep1() { // clean up data storage RemoveAllNodesFromDataStorage(); // use ToFImageGrabber to create instance of ToFImageGrabber that holds a ToFCameraMITKPlayerDevice for playing ToF data mitk::ToFImageGrabber::Pointer tofImageGrabber = mitk::ToFImageGrabberCreator::GetInstance()->GetMITKPlayerImageGrabber(); // set paths to test data std::string distanceFileName = MITK_TOF_DATA_DIR; distanceFileName.append("/PMDCamCube2_MF0_IT0_20Images_DistanceImage.pic"); std::string amplitudeFileName = MITK_TOF_DATA_DIR; amplitudeFileName.append("/PMDCamCube2_MF0_IT0_20Images_AmplitudeImage.pic"); std::string intensityFileName = MITK_TOF_DATA_DIR; intensityFileName.append("/PMDCamCube2_MF0_IT0_20Images_IntensityImage.pic"); // set file name property in image grabber. This will be propagated to the corresponding device and controller class tofImageGrabber->SetProperty("DistanceImageFileName",mitk::StringProperty::New(distanceFileName)); tofImageGrabber->SetProperty("AmplitudeImageFileName",mitk::StringProperty::New(amplitudeFileName)); tofImageGrabber->SetProperty("IntensityImageFileName",mitk::StringProperty::New(intensityFileName)); // connect to device if (tofImageGrabber->ConnectCamera()) { //// start camera (internally starts thread that continuously grabs images from the camera) tofImageGrabber->StartCamera(); // update image grabber which itself represents the source of a MITK filter pipeline tofImageGrabber->Update(); // grab distance image mitk::Image::Pointer distanceImage = tofImageGrabber->GetOutput(0); // grab amplitude image mitk::Image::Pointer amplitudeImage = tofImageGrabber->GetOutput(1); // grab intensity image mitk::Image::Pointer intensityImage = tofImageGrabber->GetOutput(2); //add distance image to data storage mitk::DataNode::Pointer distanceNode = mitk::DataNode::New(); distanceNode->SetName("Distance Image"); distanceNode->SetData(distanceImage); this->GetDefaultDataStorage()->Add(distanceNode); //add amplitude image to data storage mitk::DataNode::Pointer amplitudeNode = mitk::DataNode::New(); amplitudeNode->SetName("Amplitude Image"); amplitudeNode->SetData(amplitudeImage); this->GetDefaultDataStorage()->Add(amplitudeNode); //add intensity image to data storage mitk::DataNode::Pointer intensityNode = mitk::DataNode::New(); intensityNode->SetName("Intensity Image"); intensityNode->SetData(intensityImage); this->GetDefaultDataStorage()->Add(intensityNode); // stop camera (terminate internally used thread) tofImageGrabber->StopCamera(); // disconnect from camera tofImageGrabber->DisconnectCamera(); // adjust views to new data in DataStorage mitk::RenderingManager::GetInstance()->InitializeViews(distanceImage->GetGeometry()); } else { MITK_ERROR<<"Connection to ToF camera could not be established"; } } void QmitkToFTutorialView::OnStep2() { // Check if distance image is available mitk::DataNode::Pointer distanceNode = this->GetDefaultDataStorage()->GetNamedNode("Distance Image"); if (distanceNode.IsNotNull()) { // get distance image from node and check if node contains image mitk::Image::Pointer distanceImage = dynamic_cast(distanceNode->GetData()); if (distanceImage.IsNotNull()) { - // create pinhole camera model that holds intrinsic parameters of the ToF camera - mitk::PinholeCameraModel::Pointer cameraIntrinsics = mitk::PinholeCameraModel::New(); + // create object of CameraIntrinsics that holds intrinsic parameters of the ToF camera + mitk::CameraIntrinsics::Pointer cameraIntrinsics = mitk::CameraIntrinsics::New(); // change focal length and use defaults for other parameters such as inter pixel distance or principal point - cameraIntrinsics->SetFocalLength(13.3); + cameraIntrinsics->SetFocalLength(13.3,13.3); // set up filter for surface calculation mitk::ToFDistanceImageToSurfaceFilter::Pointer surfaceFilter = mitk::ToFDistanceImageToSurfaceFilter::New(); // apply intrinsic parameters to filter - surfaceFilter->SetCameraModel(cameraIntrinsics); + surfaceFilter->SetCameraIntrinsics(cameraIntrinsics); // set distance image as input surfaceFilter->SetInput(distanceImage); // update the filter surfaceFilter->Update(); // get surface from filter mitk::Surface::Pointer surface = surfaceFilter->GetOutput(); // add surface to data storage mitk::DataNode::Pointer surfaceNode = mitk::DataNode::New(); surfaceNode->SetName("ToF surface"); surfaceNode->SetData(surface); this->GetDefaultDataStorage()->Add(surfaceNode); // adjust views to new data in DataStorage mitk::RenderingManager::GetInstance()->InitializeViews(surface->GetGeometry()); mitk::RenderingManager::GetInstance()->InitializeViews(surface->GetGeometry()); } else { QMessageBox::warning(NULL,"ToF Tutorial","Node 'Distance Image' contains no image"); } } else { QMessageBox::warning(NULL,"ToF Tutorial","Perform Step 1 first to acquire a distance image"); } } void QmitkToFTutorialView::RemoveAllNodesFromDataStorage() { mitk::DataStorage::SetOfObjects::ConstPointer allNodes = this->GetDefaultDataStorage()->GetAll(); this->GetDefaultDataStorage()->Remove(allNodes); } diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt index 001a20e23f..04747cd7e9 100644 --- a/Modules/CMakeLists.txt +++ b/Modules/CMakeLists.txt @@ -1,33 +1,34 @@ SET(LIBPOSTFIX "Ext") SET(module_dirs SceneSerializationBase PlanarFigure MitkExt SceneSerialization QmitkExt DiffusionImaging GPGPU IGT + CameraCalibration IGTUI RigidRegistration RigidRegistrationUI DeformableRegistration DeformableRegistrationUI OpenCVVideoSupport Overlays InputDevices ToFHardware ToFProcessing ToFUI ) SET(MITK_DEFAULT_SUBPROJECTS MITK-Modules) FOREACH(module_dir ${module_dirs}) ADD_SUBDIRECTORY(${module_dir}) ENDFOREACH() IF(MITK_USE_BLUEBERRY) ADD_SUBDIRECTORY(Bundles) ENDIF() diff --git a/Modules/CameraCalibration/CMakeLists.txt b/Modules/CameraCalibration/CMakeLists.txt new file mode 100644 index 0000000000..825c7cbcc7 --- /dev/null +++ b/Modules/CameraCalibration/CMakeLists.txt @@ -0,0 +1,5 @@ +MITK_CREATE_MODULE(mitkCameraCalibration + DEPENDS MitkIGT + PACKAGE_DEPENDS OpenCV + PROVIDES mitkCameraCalibration +) diff --git a/Modules/CameraCalibration/files.cmake b/Modules/CameraCalibration/files.cmake new file mode 100644 index 0000000000..e87d7ca979 --- /dev/null +++ b/Modules/CameraCalibration/files.cmake @@ -0,0 +1,7 @@ +SET(CPP_FILES + mitkEndoDebug.cpp + mitkTransform.cpp + mitkCameraIntrinsics.cpp + mitkXMLSerializable.cpp +) + diff --git a/Modules/CameraCalibration/mitkAlgorithm.h b/Modules/CameraCalibration/mitkAlgorithm.h new file mode 100644 index 0000000000..3724c1cfbf --- /dev/null +++ b/Modules/CameraCalibration/mitkAlgorithm.h @@ -0,0 +1,22 @@ +#ifndef mitkAlgorithm_h +#define mitkAlgorithm_h + +namespace mitk +{ + /// + /// filter "Algorithm" interface = a class which does + /// its work when calling this function + /// this is a general polling mechansim + /// + class Algorithm + { + public: + /// + /// do update steps here + /// + virtual void Update() = 0; + }; + +} // namespace mitk + +#endif // mitkAlgorithm_h diff --git a/Modules/CameraCalibration/mitkCameraIntrinsics.cpp b/Modules/CameraCalibration/mitkCameraIntrinsics.cpp new file mode 100644 index 0000000000..2698f0a6fe --- /dev/null +++ b/Modules/CameraCalibration/mitkCameraIntrinsics.cpp @@ -0,0 +1,482 @@ +#include "mitkCameraIntrinsics.h" +#include +#include +#include + +mitk::CameraIntrinsics::CameraIntrinsics() + : m_Valid(false), m_Mutex(itk::FastMutexLock::New()) +{ + m_CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); + m_CameraMatrix.at(2,2) = 1.0; + m_DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType::type); +} + +mitk::CameraIntrinsics::~CameraIntrinsics() +{ + +} + +bool mitk::CameraIntrinsics::Equals( const CameraIntrinsics* other ) const +{ + return other->GetDistorsionCoeffsAsPoint4D()== + this->GetDistorsionCoeffsAsPoint4D() && + other->GetFocalPoint()== + this->GetFocalPoint() && + other->GetPrincipalPoint() + == this->GetPrincipalPoint(); +} + +void mitk::CameraIntrinsics::Copy(const CameraIntrinsics* other) +{ + this->SetIntrinsics( other->GetCameraMatrix().clone() + , other->GetDistorsionCoeffs().clone() ); +} + +bool mitk::CameraIntrinsics::IsValid() const +{ + itk::MutexLockHolder lock(*m_Mutex); + return m_Valid; +} + +mitk::CameraIntrinsics::Pointer mitk::CameraIntrinsics::Clone() const +{ + mitk::CameraIntrinsics::Pointer copy = mitk::CameraIntrinsics::New(); + copy->SetIntrinsics( this->GetCameraMatrix(), this->GetDistorsionCoeffs() ); + return copy; +} + +vnl_matrix_fixed + mitk::CameraIntrinsics::GetVnlCameraMatrix() const +{ + vnl_matrix_fixed mat; + mat.set_identity(); + + { + itk::MutexLockHolder lock(*m_Mutex); + mat(0,0) = m_CameraMatrix.at(0,0); + mat(1,1) = m_CameraMatrix.at(1,1); + + mat(0,2) = m_CameraMatrix.at(0,2); + mat(1,2) = m_CameraMatrix.at(1,2); + } + + return mat; +} + +void mitk::CameraIntrinsics::SetCameraMatrix( + const vnl_matrix_fixed& _CameraMatrix ) +{ + itk::MutexLockHolder lock(*m_Mutex); + m_CameraMatrix.at(0,0) = _CameraMatrix(0,0); + m_CameraMatrix.at(1,1) = _CameraMatrix(1,1); + + m_CameraMatrix.at(0,2) = _CameraMatrix(0,2); + m_CameraMatrix.at(1,2) = _CameraMatrix(1,2); +} + +vnl_matrix_fixed + mitk::CameraIntrinsics::GetVnlCameraMatrix3x4() const +{ + vnl_matrix_fixed mat; + mat.fill(0); + mat.update( this->GetVnlCameraMatrix().as_matrix() ); + + return mat; +} + +void mitk::CameraIntrinsics::SetIntrinsics( const cv::Mat& _CameraMatrix + , const cv::Mat& _DistorsionCoeffs) +{ + { + itk::MutexLockHolder lock(*m_Mutex); + if( _CameraMatrix.cols != 3 || _CameraMatrix.rows != 3) + throw std::invalid_argument("Wrong format of camera matrix. Should be 3x3" + " double."); + + endoAssertMsg( (_DistorsionCoeffs.cols == 5) && + _DistorsionCoeffs.rows == 1, "Wrong format of distorsion coefficients" + " vector. Should be 5x1 double."); + + m_CameraMatrix = _CameraMatrix; + m_DistorsionCoeffs = _DistorsionCoeffs; + + m_Valid = true; + } + this->Modified(); +} + +void mitk::CameraIntrinsics::SetIntrinsics( const mitk::Point3D& focalPoint, + const mitk::Point3D& principalPoint, + const mitk::Point4D& distortionCoefficients) + +{ + { + itk::MutexLockHolder lock(*m_Mutex); + m_CameraMatrix.at(0,0) = focalPoint[0]; + m_CameraMatrix.at(1,1) = focalPoint[1]; + + m_CameraMatrix.at(0,2) = principalPoint[0]; + m_CameraMatrix.at(1,2) = principalPoint[1]; + + m_DistorsionCoeffs.at(0,0) = distortionCoefficients[0]; + m_DistorsionCoeffs.at(0,1) = distortionCoefficients[1]; + m_DistorsionCoeffs.at(0,2) = distortionCoefficients[2]; + m_DistorsionCoeffs.at(0,3) = distortionCoefficients[3]; + } + this->Modified(); +} + +void mitk::CameraIntrinsics::SetFocalLength( double x, double y ) +{ + { + itk::MutexLockHolder lock(*m_Mutex); + m_CameraMatrix.at(0,0) = x; + m_CameraMatrix.at(1,1) = y; + } + this->Modified(); +} + +void mitk::CameraIntrinsics::SetPrincipalPoint( double x, double y ) +{ + { + itk::MutexLockHolder lock(*m_Mutex); + m_CameraMatrix.at(0,2) = x; + m_CameraMatrix.at(1,2) = y; + } + this->Modified(); +} + +void mitk::CameraIntrinsics::SetDistorsionCoeffs( double k1, double k2, + double p1, double p2 ) +{ + + { + itk::MutexLockHolder lock(*m_Mutex); + + m_DistorsionCoeffs.at(0,0) = k1; + m_DistorsionCoeffs.at(0,1) = k2; + m_DistorsionCoeffs.at(0,2) = p1; + m_DistorsionCoeffs.at(0,3) = p2; + } + this->Modified(); +} + +cv::Mat mitk::CameraIntrinsics::GetCameraMatrix() const +{ + itk::MutexLockHolder lock(*m_Mutex); + return m_CameraMatrix.clone(); // return a copy of this small matrix +} + +cv::Mat mitk::CameraIntrinsics::GetDistorsionCoeffs() const +{ + itk::MutexLockHolder lock(*m_Mutex); + return m_DistorsionCoeffs.clone(); // return a copy of this small matrix +} + +cv::Mat mitk::CameraIntrinsics::GetDistorsionCoeffs() +{ + const CameraIntrinsics* intrinsics = this; + return intrinsics->GetDistorsionCoeffs(); +} + +std::string mitk::CameraIntrinsics::ToString() const +{ + itk::MutexLockHolder lock(*m_Mutex); + std::ostringstream s; s.precision(12); + const cv::Mat& CameraMatrix = m_CameraMatrix; + const cv::Mat& DistorsionCoeffs = m_DistorsionCoeffs; + + s.str(""); s << this->GetNameOfClass() << ": "; + s << "fx = " << CameraMatrix.at(0,0); + s << ", fy = " << CameraMatrix.at(1,1); + s << ", cx = " << CameraMatrix.at(0,2); + s << ", cy = " << CameraMatrix.at(1,2); + + s << ", k1 = " << DistorsionCoeffs.at(0,0); + s << ", k2 = " << DistorsionCoeffs.at(0,1); + s << ", p1 = " << DistorsionCoeffs.at(0,2); + s << ", p2 = " << DistorsionCoeffs.at(0,3); + //s << ", k3 = " << DistorsionCoeffs.at(0,4); + + return s.str(); +} + +void mitk::CameraIntrinsics::ToXML(TiXmlElement* elem) const +{ + itk::MutexLockHolder lock(*m_Mutex); + elem->SetValue(this->GetNameOfClass()); + std::ostringstream s; s.precision(12); + const cv::Mat& CameraMatrix = m_CameraMatrix; + s.str(""); s << CameraMatrix.at(0,0); + elem->SetAttribute( "fx", s.str() ); + s.str(""); s << CameraMatrix.at(1,1); + elem->SetAttribute( "fy", s.str() ); + s.str(""); s << CameraMatrix.at(0,2); + elem->SetAttribute( "cx", s.str() ); + s.str(""); s << CameraMatrix.at(1,2); + elem->SetAttribute( "cy", s.str() ); + + const cv::Mat& DistorsionCoeffs = m_DistorsionCoeffs; + s.str(""); s << DistorsionCoeffs.at(0,0); + elem->SetAttribute( "k1", s.str() ); + s.str(""); s << DistorsionCoeffs.at(0,1); + elem->SetAttribute( "k2", s.str() ); + s.str(""); s << DistorsionCoeffs.at(0,2); + elem->SetAttribute( "p1", s.str() ); + s.str(""); s << DistorsionCoeffs.at(0,3); + elem->SetAttribute( "p2", s.str() ); + elem->SetAttribute("Valid", m_Valid); + //s.str(""); s << DistorsionCoeffs.at(4,0); + //elem->SetAttribute( "k3", s.str() ); +} + +void mitk::CameraIntrinsics::FromGMLCalibrationXML(TiXmlElement* elem) +{ + assert( elem ); + assert( elem->ValueStr() == "results" ); + cv::Mat CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); + CameraMatrix.at(2,2) = 1.0; + cv::Mat DistorsionCoeffs = cv::Mat::zeros(1, 4, cv::DataType::type); + + TiXmlElement* focus_lenXElem = elem->FirstChildElement("focus_lenX"); + endoAssert( focus_lenXElem != 0 ); + CameraMatrix.at(0,0) = atof( focus_lenXElem->GetText() ); + + TiXmlElement* focus_lenYElem = elem->FirstChildElement("focus_lenY"); + endoAssert( focus_lenYElem != 0 ); + CameraMatrix.at(1,1) = atof( focus_lenYElem->GetText() ); + + TiXmlElement* PrincipalXElem = elem->FirstChildElement("PrincipalX"); + endoAssert( PrincipalXElem != 0 ); + CameraMatrix.at(0,2) = atof( PrincipalXElem->GetText() ); + + TiXmlElement* PrincipalYElem = elem->FirstChildElement("PrincipalY"); + endoAssert( PrincipalYElem != 0 ); + CameraMatrix.at(1,2) = atof( PrincipalYElem->GetText() ); + + // DISTORSION COEFFS + + TiXmlElement* Dist1Elem = elem->FirstChildElement("Dist1"); + endoAssert( Dist1Elem != 0 ); + DistorsionCoeffs.at(0,0) = atof( Dist1Elem->GetText() ); + + TiXmlElement* Dist2Elem = elem->FirstChildElement("Dist2"); + endoAssert( Dist2Elem != 0 ); + DistorsionCoeffs.at(0,1) = atof( Dist2Elem->GetText() ); + + TiXmlElement* Dist3Elem = elem->FirstChildElement("Dist3"); + endoAssert( Dist3Elem != 0 ); + DistorsionCoeffs.at(0,2) = atof( Dist3Elem->GetText() ); + + TiXmlElement* Dist4Elem = elem->FirstChildElement("Dist4"); + endoAssert( Dist4Elem != 0 ); + DistorsionCoeffs.at(0,3) = atof( Dist4Elem->GetText() ); + + int valid = 0; + elem->QueryIntAttribute("Valid", &valid); + + { + itk::MutexLockHolder lock(*m_Mutex); + m_Valid = static_cast(valid); + m_CameraMatrix = CameraMatrix; + m_DistorsionCoeffs = DistorsionCoeffs; + } + + this->Modified(); +} + +void mitk::CameraIntrinsics::FromXML(TiXmlElement* elem) +{ + endoAssert ( elem ); + MITK_DEBUG << elem->Value(); + std::string filename; + if(elem->QueryStringAttribute("file", &filename) == TIXML_SUCCESS) + { + this->FromXMLFile(filename); + return; + } + else if(strcmp(elem->Value(), "CalibrationProject") == 0) + { + this->FromGMLCalibrationXML(elem->FirstChildElement("results")); + return; + } + + assert ( elem ); + if(strcmp(elem->Value(), this->GetNameOfClass()) != 0) + elem = elem->FirstChildElement(this->GetNameOfClass()); + + std::ostringstream err; + // CAMERA MATRIX + cv::Mat CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); + CameraMatrix.at(2,2) = 1.0; + float val = 0.0f; + if(elem->QueryFloatAttribute("fx", &val) == TIXML_SUCCESS) + CameraMatrix.at(0,0) = val; + else + err << "fx, "; + + if(elem->QueryFloatAttribute("fy", &val) == TIXML_SUCCESS) + CameraMatrix.at(1,1) = val; + else + err << "fy, "; + + if(elem->QueryFloatAttribute("cx", &val) == TIXML_SUCCESS) + CameraMatrix.at(0,2) = val; + else + err << "cx, "; + + if(elem->QueryFloatAttribute("cy", &val) == TIXML_SUCCESS) + CameraMatrix.at(1,2) = val; + else + err << "cy, "; + + // DISTORSION COEFFS + cv::Mat DistorsionCoeffs = cv::Mat::zeros(1, 4, cv::DataType::type); + if(elem->QueryFloatAttribute("k1", &val) == TIXML_SUCCESS) + DistorsionCoeffs.at(0,0) = val; + else + err << "k1, "; + + if(elem->QueryFloatAttribute("k2", &val) == TIXML_SUCCESS) + DistorsionCoeffs.at(0,1) = val; + else + err << "k2, "; + + if(elem->QueryFloatAttribute("p1", &val) == TIXML_SUCCESS) + DistorsionCoeffs.at(0,2) = val; + else + err << "p1, "; + + if(elem->QueryFloatAttribute("p2", &val) == TIXML_SUCCESS) + DistorsionCoeffs.at(0,3) = val; + else + err << "p2, "; + + /*if(elem->QueryFloatAttribute("k3", &val) == TIXML_SUCCESS) + DistorsionCoeffs.at(4,0) = val; + else + err << "k3, ";*/ + + std::string errorStr = err.str(); + int errLength = errorStr.length(); + if(errLength > 0) + { + errorStr = errorStr.substr(0, errLength-2); + errorStr.append(" not found"); + throw std::invalid_argument(err.str()); + } + + int valid = 0; + elem->QueryIntAttribute("Valid", &valid); + + { + itk::MutexLockHolder lock(*m_Mutex); + m_Valid = static_cast(valid); + m_CameraMatrix = CameraMatrix; + m_DistorsionCoeffs = DistorsionCoeffs; + } + + this->Modified(); +} + +double mitk::CameraIntrinsics::GetFocalLengthX() const +{ + itk::MutexLockHolder lock(*m_Mutex); + double FocalLengthX = m_CameraMatrix.at(0,0); + + return FocalLengthX; +} +double mitk::CameraIntrinsics::GetFocalLengthY() const +{ + itk::MutexLockHolder lock(*m_Mutex); + double FocalLengthY = m_CameraMatrix.at(1,1);; + return FocalLengthY; +} +double mitk::CameraIntrinsics::GetPrincipalPointX() const +{ + itk::MutexLockHolder lock(*m_Mutex); + double PrincipalPointX = m_CameraMatrix.at(0,2); + return PrincipalPointX; +} +double mitk::CameraIntrinsics::GetPrincipalPointY() const +{ + itk::MutexLockHolder lock(*m_Mutex); + double PrincipalPointY = m_CameraMatrix.at(1,2); + return PrincipalPointY; +} +mitk::Point4D mitk::CameraIntrinsics::GetDistorsionCoeffsAsPoint4D() const +{ + itk::MutexLockHolder lock(*m_Mutex); + mitk::Point4D coeffs; + + coeffs[0] = m_DistorsionCoeffs.at(0,0); + coeffs[1] = m_DistorsionCoeffs.at(0,1); + coeffs[2] = m_DistorsionCoeffs.at(0,2); + coeffs[3] = m_DistorsionCoeffs.at(0,3); + + return coeffs; +} + +mitk::Point3D mitk::CameraIntrinsics::GetFocalPoint() const +{ + mitk::Point3D p; + p[0] = this->GetFocalLengthX(); + p[1] = this->GetFocalLengthY(); + p[2] = 0; + return p; +} + +mitk::Point3D mitk::CameraIntrinsics::GetPrincipalPoint() const +{ + mitk::Point3D p; + p[0] = this->GetPrincipalPointX(); + p[1] = this->GetPrincipalPointY(); + p[2] = 0; + return p; +} + +vnl_vector_fixed + mitk::CameraIntrinsics::GetFocalPointAsVnlVector() const +{ + vnl_vector_fixed vec; + vec[0] = this->GetFocalLengthX(); + vec[1] = this->GetFocalLengthY(); + return vec; +} + +vnl_vector_fixed + mitk::CameraIntrinsics::GetPrincipalPointAsVnlVector() const +{ + vnl_vector_fixed vec; + vec[0] = this->GetPrincipalPointX(); + vec[1] = this->GetPrincipalPointY(); + return vec; +} + +std::ostream& operator<< (std::ostream& os, mitk::CameraIntrinsics::Pointer p) +{ + os << p->ToString(); + return os; +} + +std::string mitk::CameraIntrinsics::GetString() +{ + return this->ToString(); +} + +std::string mitk::CameraIntrinsics::ToOctaveString( + const std::string& varName) +{ + std::ostringstream s; + s << varName << " = [" << this->GetFocalLengthX() << " 0 " + << this->GetPrincipalPointX() << "; 0 " << + this->GetFocalLengthY() << " " << this->GetPrincipalPointY() << ";" + << " 0 0 1 ];"; + return s.str(); +} + +void mitk::CameraIntrinsics::SetValid( bool valid ) +{ + itk::MutexLockHolder lock(*m_Mutex); + m_Valid = valid; +} diff --git a/Modules/CameraCalibration/mitkCameraIntrinsics.h b/Modules/CameraCalibration/mitkCameraIntrinsics.h new file mode 100644 index 0000000000..bb5c3fe803 --- /dev/null +++ b/Modules/CameraCalibration/mitkCameraIntrinsics.h @@ -0,0 +1,140 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Module: $RCSfile$ +Language: C++ +Date: $Date: 2010-03-15 11:12:36 +0100 (Mo, 15 Mrz 2010) $ +Version: $Revision: 21745 $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + + +#ifndef mitkCameraIntrinsics_h +#define mitkCameraIntrinsics_h + +#include +#include +#include +#include +#include +#include +#include "mitkXMLSerializable.h" +#include + +int mitkCameraIntrinsicsTest(int argc, char* argv[]); + +namespace mitk +{ + /// + /// \brief class representing camera intrinsics and related functions + /// + class mitkCameraCalibration_EXPORT CameraIntrinsics: virtual public itk::Object, + virtual public mitk::XMLSerializable + { + public: + /// + /// for testing purposes + /// + friend int mitkCameraIntrinsicsTest(int argc, char* argv[]); + /// + /// smartpointer typedefs + /// + mitkClassMacro(CameraIntrinsics, itk::Object); + /// + /// the static new function + /// + itkFactorylessNewMacro(Self); + + /// + /// make a clone of this intrinsics + /// + Pointer Clone() const; + + /// + /// copy information from other to this + /// + void Copy(const CameraIntrinsics* other); + + /// + /// checks two intrinsics for equality + /// + bool Equals( const CameraIntrinsics* other ) const; + + /// + /// \return the intrinsic parameter matrix as a 3x3 vnl matrix + /// + vnl_matrix_fixed GetVnlCameraMatrix() const; + + /// + /// \return the intrinsic parameter matrix as a 3x4 vnl matrix + /// (the last column only containing zeros) + /// + vnl_matrix_fixed GetVnlCameraMatrix3x4() const; + + /// + /// \return true if the intrinsics are set (some plausibility checks + /// may be done here) + /// + bool IsValid() const; + void SetValid(bool valid); + cv::Mat GetCameraMatrix() const; + cv::Mat GetDistorsionCoeffs(); + cv::Mat GetDistorsionCoeffs() const; + void ToXML(TiXmlElement* elem) const; + std::string ToString() const; + std::string GetString(); + double GetFocalLengthX() const; + double GetFocalLengthY() const; + double GetPrincipalPointX() const; + double GetPrincipalPointY() const; + mitk::Point4D GetDistorsionCoeffsAsPoint4D() const; + mitk::Point3D GetFocalPoint() const; + mitk::Point3D GetPrincipalPoint() const; + vnl_vector_fixed GetFocalPointAsVnlVector() const; + vnl_vector_fixed GetPrincipalPointAsVnlVector() const; + + /// + /// set a new camera matrix utilizing a vnl matrix + /// + void SetCameraMatrix( const vnl_matrix_fixed& + _CameraMatrix ); + + void SetIntrinsics( const cv::Mat& _CameraMatrix + , const cv::Mat& _DistorsionCoeffs); + + void SetFocalLength( double x, double y ); + void SetPrincipalPoint( double x, double y ); + void SetDistorsionCoeffs( double k1, double k2, double p1, double p2 ); + + void SetIntrinsics( const mitk::Point3D& focalPoint, + const mitk::Point3D& principalPoint, + const mitk::Point4D& distortionCoefficients); + + void FromXML(TiXmlElement* elem); + void FromGMLCalibrationXML(TiXmlElement* elem); + std::string ToOctaveString(const std::string& varName="CameraIntrinsics"); + + virtual ~CameraIntrinsics(); + + protected: + CameraIntrinsics(); + cv::Mat m_CameraMatrix; + cv::Mat m_DistorsionCoeffs; + bool m_Valid; + itk::FastMutexLock::Pointer m_Mutex; + }; + +} // namespace mitk + +mitkCameraCalibration_EXPORT std::ostream& operator<< + (std::ostream& os, mitk::CameraIntrinsics::Pointer p); + +#endif // mitkCameraIntrinsics_h diff --git a/Modules/CameraCalibration/mitkCvMatFromVnlMatrix.h b/Modules/CameraCalibration/mitkCvMatFromVnlMatrix.h new file mode 100644 index 0000000000..c918a142e8 --- /dev/null +++ b/Modules/CameraCalibration/mitkCvMatFromVnlMatrix.h @@ -0,0 +1,73 @@ +#ifndef mitkCvMatFromVnlMatrix_h +#define mitkCvMatFromVnlMatrix_h + +#include +#include +#include +#include +#include + +namespace mitk +{ + /// + /// casts a random cv::Mat to a vnl Matrix + /// if the columns of the first row are > 1 + /// then the Matrix is filled with the values from this row + /// otherwise the first elements of each row are used + /// to fill the Matrix + /// + template + class CvMatFromVnlMatrix: + virtual public Algorithm + { + public: + /// + /// init default values and save references + /// + CvMatFromVnlMatrix( + const vnl_matrix* _VnlMatrix, + cv::Mat* _CvMat): + m_VnlMatrix(_VnlMatrix), + m_CvMat(_CvMat) + { + } + + /// + /// templated function for multiplexed cv::Mat + /// + template + static void Cast( const vnl_matrix& vnlMat, cv::Mat& cvMat ) + { + endodebugvar( vnlMat ) + cvMat = cv::Mat(vnlMat.rows(), vnlMat.cols(), cv::DataType::type); + for(unsigned int i=0; i(i,j) = static_cast( vnlMat(i,j) ); +// endodebugvar( vnlMat(i,j) ) +// endodebugvar( cvMat.at(i,j) ) + } + } + } + + /// + /// executes the Algorithm + /// + void Update() + { + Cast( *m_VnlMatrix, *m_CvMat ); + } + private: + /// + /// CvMatFromVnlMatrix input member variable + /// + const vnl_matrix* m_VnlMatrix; + /// + /// CvMatFromVnlMatrix output member variable + /// + cv::Mat* m_CvMat; + }; +} // namespace mitk + +#endif // mitkCvMatFromVnlMatrix_h diff --git a/Modules/CameraCalibration/mitkCvMatFromVnlVector.h b/Modules/CameraCalibration/mitkCvMatFromVnlVector.h new file mode 100644 index 0000000000..cca34d1739 --- /dev/null +++ b/Modules/CameraCalibration/mitkCvMatFromVnlVector.h @@ -0,0 +1,58 @@ +#ifndef mitkCvMatFromVnlVector_h +#define mitkCvMatFromVnlVector_h + +#include +#include +#include + +namespace mitk +{ + /// + /// casts a random cv::Mat to a vnl vector + /// if the columns of the first row are > 1 + /// then the vector is filled with the values from this row + /// otherwise the first elements of each row are used + /// to fill the vector + /// + template + class CvMatFromVnlVector: + virtual public Algorithm + { + public: + /// + /// init default values and save references + /// + CvMatFromVnlVector( + const vnl_vector* _VnlVector, + cv::Mat* _CvMat): + m_CvMatFromVnlMatrix(&m_VnlMatrix, _CvMat), + m_VnlVector(_VnlVector) + { + } + + /// + /// executes the Algorithm + /// + void Update() + { + m_VnlMatrix.set_size( m_VnlVector->size(), 1 ); + m_VnlMatrix.set_column(0, *m_VnlVector); + m_CvMatFromVnlMatrix.Update(); + } + private: + /// + /// CvMatFromVnlVector input member variable + /// + vnl_matrix m_VnlMatrix; + /// + /// internal algorithm to create a vnl matrix from a random cv mat + /// + mitk::CvMatFromVnlMatrix m_CvMatFromVnlMatrix; + /// + /// CvMatFromVnlVector input member variable + /// + const vnl_vector* m_VnlVector; + }; +} // namespace mitk + +#endif // mitkCvMatFromVnlVector_h diff --git a/Modules/CameraCalibration/mitkEndoDebug.cpp b/Modules/CameraCalibration/mitkEndoDebug.cpp new file mode 100644 index 0000000000..2b5654ced5 --- /dev/null +++ b/Modules/CameraCalibration/mitkEndoDebug.cpp @@ -0,0 +1,173 @@ +#include "mitkEndoDebug.h" +#include +#include +#include + +namespace mitk +{ + struct EndoDebugData + { + EndoDebugData() + : m_DebugEnabled(false) + , m_ShowImagesInDebug(false) + , m_ShowImagesTimeOut(false) + , m_Mutex(itk::FastMutexLock::New()) + { + + } + + std::set m_FilesToDebug; + std::set m_SymbolsToDebug; + bool m_DebugEnabled; + bool m_ShowImagesInDebug; + size_t m_ShowImagesTimeOut; + itk::FastMutexLock::Pointer m_Mutex; + }; + + EndoDebug::EndoDebug() + : d ( new EndoDebugData ) + { + + } + + EndoDebug::~EndoDebug() + { + delete d; + } + + EndoDebug& EndoDebug::GetInstance() + { + static EndoDebug instance; + return instance; + } + + std::string EndoDebug::GetFilenameWithoutExtension(const std::string& s) + { + return itksys::SystemTools::GetFilenameWithoutExtension( s ); + } + + void EndoDebug::AddFileToDebug(const std::string& s) + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + d->m_FilesToDebug.insert( s ); + } + } + + void EndoDebug::AddSymbolToDebug(const std::string& symbolToDebug) + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + d->m_SymbolsToDebug.insert( symbolToDebug ); + } + } + + bool EndoDebug::DebugSymbol(const std::string& s) + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + return d->m_SymbolsToDebug.find(s) + != d->m_SymbolsToDebug.end(); + } + } + + bool EndoDebug::DebugFile(const std::string& s) + { + std::string filename = GetFilenameWithoutExtension(s); + + { + itk::MutexLockHolder lock(*d->m_Mutex); + return d->m_FilesToDebug.find(filename) + != d->m_FilesToDebug.end(); + } + } + + bool EndoDebug::Debug( const std::string& fileToDebug, const std::string& symbol ) + { + bool debug = false; + + { + bool debugEnabled = false; + size_t filesSize = 0; + size_t symbolsSize = 0; + bool symbolFound = false; + { + itk::MutexLockHolder lock(*d->m_Mutex); + debugEnabled = d->m_DebugEnabled; + filesSize = d->m_FilesToDebug.size(); + symbolsSize = d->m_SymbolsToDebug.size(); + symbolFound = d->m_SymbolsToDebug.find(symbol) != d->m_SymbolsToDebug.end(); + } + + if( debugEnabled ) + { + if( filesSize == 0 ) + debug = true; + else + debug = DebugFile(fileToDebug); + + // ok debug is determined so far, now check if symbol set + if( symbolsSize > 0 ) + { + debug = symbolFound; + } + else + { + // do not show symbol debug output if no symbols are set at all + if( !symbol.empty() ) + debug = false; + } + } + } + + return debug; + } + + void EndoDebug::SetDebugEnabled(bool _DebugEnabled) + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + d->m_DebugEnabled = _DebugEnabled; + } + } + + bool EndoDebug::GetDebugEnabled() + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + return d->m_DebugEnabled; + } + } + + void EndoDebug::SetShowImagesInDebug(bool _ShowImagesInDebug) + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + d->m_ShowImagesInDebug = _ShowImagesInDebug; + } + } + + bool EndoDebug::GetShowImagesInDebug() + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + return d->m_ShowImagesInDebug; + } + } + + void EndoDebug::SetShowImagesTimeOut(size_t _ShowImagesTimeOut) + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + d->m_ShowImagesTimeOut = _ShowImagesTimeOut; + } + } + + bool EndoDebug::GetShowImagesTimeOut() + { + { + itk::MutexLockHolder lock(*d->m_Mutex); + return d->m_ShowImagesTimeOut; + } + } +} diff --git a/Modules/CameraCalibration/mitkEndoDebug.h b/Modules/CameraCalibration/mitkEndoDebug.h new file mode 100644 index 0000000000..d05723ad1a --- /dev/null +++ b/Modules/CameraCalibration/mitkEndoDebug.h @@ -0,0 +1,166 @@ +#ifndef mitkEndoDebug_h +#define mitkEndoDebug_h + +#include +#include +#include +#include + +namespace mitk +{ + /// + /// again d pointer impl + /// + struct EndoDebugData; + + /// + /// class responsible for handling debug matters + /// in endotracking + /// + struct mitkCameraCalibration_EXPORT EndoDebug + { + /// + /// singleton class + /// + static EndoDebug& GetInstance(); + + /// + /// set if debug is enabled at all + /// + void SetDebugEnabled(bool _DebugEnabled); + + /// + /// \return true if debug should be enabled + /// + bool GetDebugEnabled(); + + /// + /// set if debug is enabled at all + /// + void SetShowImagesInDebug(bool _ShowImagesInDebug); + + /// + /// \return true if debug should be enabled + /// + bool GetShowImagesInDebug(); + + /// + /// set if debug is enabled at all + /// + void SetShowImagesTimeOut(size_t _ShowImagesTimeOut); + + /// + /// \return true if debug should be enabled + /// + bool GetShowImagesTimeOut(); + + /// + /// \return the basename of a file without path + /// + std::string GetFilenameWithoutExtension(const std::string& s); + + /// + /// add a file to debug ( if one or more files are set ) + /// only those files will be debugged when using the macros + /// below. e.g. call AddFileToDebug("MyClass.cpp"), then + /// statements like endodebug(...) will be evaluated in + /// MyClass.cpp and nowhere else + /// + void AddFileToDebug(const std::string& fileToDebug); + + /// + /// same as files to debug, but the user can provide + /// any symbol string. if one or more symbols + /// are set only for these symbols Debug() will return true + /// + void AddSymbolToDebug(const std::string& symbolToDebug); + + /// + /// \return true if file should be debugged + /// + bool DebugFile( const std::string& fileToDebug ); + + /// + /// \return true if symbol should be debugged + /// + bool DebugSymbol( const std::string& symbolToDebug ); + + /// + /// \return the all in all status if debug output + /// should be generated + /// + bool Debug( const std::string& fileToDebug, const std::string& symbol="" ); + /// + /// init defaults + /// + EndoDebug(); + /// + /// delete d pointer + /// + virtual ~EndoDebug(); + + private: + /// + /// d pointer + /// + EndoDebugData* d; + }; +} + +/// +/// macro for debugging purposes +/// +#define endodebugmarker\ + if( mitk::EndoDebug::GetInstance().Debug(__FILE__) ) \ + { \ + std::cout << mitk::EndoDebug::GetInstance().GetFilenameWithoutExtension(__FILE__) << ", " << __LINE__\ + << ": " << __FUNCTION__ << std::endl;\ + } + +/// +/// macro for debugging purposes +/// +#define endodebug(msg)\ + if( mitk::EndoDebug::GetInstance().Debug(__FILE__) ) \ + { \ + std::cout << mitk::EndoDebug::GetInstance().GetFilenameWithoutExtension(__FILE__) << ", " << __LINE__\ + << ": " << msg << std::endl;\ + } + +/// +/// macro for debugging variables +/// +#define endodebugvar(var)\ + if( mitk::EndoDebug::GetInstance().Debug(__FILE__) ) \ + { \ + std::cout << mitk::EndoDebug::GetInstance().GetFilenameWithoutExtension(__FILE__) << ", " << __LINE__\ + << ": " #var " = " << var << std::endl;\ + } + +/// +/// macro for debugging a variable as symbol +/// +#define endodebugsymbol(var, mSymbol)\ + if( mitk::EndoDebug::GetInstance().Debug(__FILE__, mSymbol) ) \ + { \ + std::cout << mitk::EndoDebug::GetInstance().GetFilenameWithoutExtension(__FILE__) << ", " << __LINE__\ + << ": " #var " = " << var << std::endl;\ + } + +/// +/// macro for showing cv image if in debug mode +/// highgui.h must be included before +/// +#define endodebugimg(imgVariableName)\ + if( mitk::EndoDebug::GetInstance().Debug(__FILE__) \ + && mitk::EndoDebug::GetInstance().GetShowImagesInDebug()) \ + { \ + std::cout << mitk::EndoDebug::GetInstance().GetFilenameWithoutExtension(__FILE__) << ", " << __LINE__\ + << ": Showing " #imgVariableName << std::endl; \ + cv::imshow( "Debug", imgVariableName ); \ + cv::waitKey( mitk::EndoDebug::GetInstance().GetShowImagesTimeOut() ); \ + } + +#endif // mitkEndoDebug_h + + diff --git a/Modules/CameraCalibration/mitkEndoMacros.h b/Modules/CameraCalibration/mitkEndoMacros.h new file mode 100644 index 0000000000..ea450aec64 --- /dev/null +++ b/Modules/CameraCalibration/mitkEndoMacros.h @@ -0,0 +1,81 @@ +#ifndef mitkEndoMacros_h +#define mitkEndoMacros_h + +/// +/// COLLECTION OF MACROS FOR THE ENDOTRACKING MODULE +/// + +/// +/// multiplexing for cv mats +/// +#define endoAccessCvMat(function, T, arg1, arg2) \ +if( arg2.type() == cv::DataType::type ) \ + function( arg1, arg2 ); \ +else if( arg2.type() == cv::DataType::type ) \ + function( arg1, arg2 ); \ +else if( arg2.type() == cv::DataType::type ) \ + function( arg1, arg2 ); \ +else if( arg2.type() == cv::DataType::type ) \ + function( arg1, arg2 ); \ +else if( arg2.type() == cv::DataType::type ) \ + function( arg1, arg2 ); \ +else if( arg2.type() == cv::DataType::type ) \ + function( arg1, arg2 ); \ +else if( arg2.type() == cv::DataType::type ) \ + function( arg1, arg2 ); \ +else if( arg2.type() == cv::DataType::type ) \ + function( arg1, arg2 ); \ +else \ + throw std::invalid_argument("Unknown type for cv::Mat"); + +/// +/// exec an algorithm with 1 output argument +/// +#define endoExec(macroAlgName, macroOutputType, macroOutputVarName, ...)\ + macroOutputType macroOutputVarName;\ + { \ + macroAlgName _macroAlgName(__VA_ARGS__, ¯oOutputVarName);\ + _macroAlgName.Update();\ + } + +/// +/// exec an algorithm with 2 output argument +/// +#define endoExec2(macroAlgName, macroOutputType1, macroOutputVarName1, macroOutputType2, macroOutputVarName2, ...)\ + macroOutputType1 macroOutputVarName1;\ + macroOutputType1 macroOutputVarName1;\ + { \ + macroAlgName _macroAlgName(__VA_ARGS__, ¯oOutputVarName1, ¯oOutputVarName2);\ + _macroAlgName.Update();\ + } + +/// +/// an assert macro for throwing exceptions from an assert +/// +#define endoAssert(a) if(!(a)) { \ +std::ostringstream s; \ +s << mitk::EndoDebug::GetInstance().GetFilenameWithoutExtension(__FILE__) << ", " \ + << __LINE__ << ", failed: " << #a; \ +throw std::invalid_argument(s.str()); } + +/// +/// same as above but with an output error stream +/// use it like this: endoAssertMsg( file.read() == true, file << "could not be read" ); +/// +#define endoAssertMsg(a, msg) if(!(a)) { \ + std::ostringstream s; \ + s << mitk::EndoDebug::GetInstance().GetFilenameWithoutExtension(__FILE__) << ", " \ + << __LINE__ << ": " << msg; \ + throw std::invalid_argument(s.str()); \ + } + +/// +/// definition of the corresponding directory separator +/// +#ifdef WIN32 + static const std::string DIR_SEPARATOR = "\\"; +#else + static const std::string DIR_SEPARATOR = "/"; +#endif + +#endif // mitkEndoMacros_h diff --git a/Modules/CameraCalibration/mitkObservable.h b/Modules/CameraCalibration/mitkObservable.h new file mode 100644 index 0000000000..f1722f0486 --- /dev/null +++ b/Modules/CameraCalibration/mitkObservable.h @@ -0,0 +1,25 @@ +#ifndef mitkObservable_h +#define mitkObservable_h + +#include + +namespace mitk +{ + /// + /// filter "Observable" interface + /// + class Observable + { + public: + /// + /// this class garantuees to call Update() on + /// all observer if it changes + /// if you need more then one observer bundle + /// the algorithms in a BatchAlgorithm + /// + virtual void SetObserver( Algorithm* _Algorithm ) = 0; + }; + +} // namespace mitk + +#endif // mitkObservable_h diff --git a/Modules/CameraCalibration/mitkTransform.cpp b/Modules/CameraCalibration/mitkTransform.cpp new file mode 100644 index 0000000000..e3a8e9ffad --- /dev/null +++ b/Modules/CameraCalibration/mitkTransform.cpp @@ -0,0 +1,688 @@ +#include "mitkTransform.h" +#include +#include +#include +#include +#include +#include +namespace mitk +{ + + Transform::Transform() + : m_NavData(mitk::NavigationData::New()) + { + vnl_matrix_fixed rot; + rot.set_identity(); + this->SetRotation( rot ); + } + + Transform::Transform(const mitk::NavigationData* nd) + : m_NavData(mitk::NavigationData::New()) + { + m_NavData->Graft(nd); + } + + void Transform::Copy(const mitk::NavigationData* nd) + { + (const_cast(m_NavData.GetPointer()))->Graft(nd); + } + + void Transform::Concatenate( mitk::Transform* transform ) + { + vnl_matrix_fixed mat = transform->GetMatrix(); + mat = mat * this->GetMatrix(); // + this->SetMatrix( mat ); + } + + void Transform::Concatenate( const vnl_matrix_fixed& + transform ) + { + Transform::Pointer t = Transform::New(); + t->SetMatrix( transform ); + this->Concatenate( t ); + } + + void Transform::Concatenate( const vtkMatrix4x4* transform ) + { + Transform::Pointer t = Transform::New(); + t->SetMatrix( transform ); + this->Concatenate( t ); + } + + void Transform::SetOrientation( + const vnl_quaternion& orientation) + { + m_NavData->SetOrientation(orientation); + this->Modified(); + } + + void Transform::SetTranslation( const vnl_vector_fixed& + transl) + { + mitk::Point3D p; + for(unsigned int i=0; i<3; ++i) + p[i] = transl[i]; + + m_NavData->SetPosition(p); + this->Modified(); + } + + void Transform::SetTranslation( float* array ) + { + vnl_vector_fixed vec; + for(unsigned int i=0; iSetTranslation( vec ); + } + + void Transform::SetRotation( float* array ) + { + vnl_matrix_fixed mat; + + unsigned int row = 0; + unsigned int col = 0; + for(unsigned int i=0; i 0 && i % 3 == 0 ) + { + ++row; + col = 0; + } + + mat(row,col) = array[i]; + ++col; + } + + this->SetRotation( mat ); + } + + void Transform::SetOrientation( const vnl_quaternion& orientation) + { + vnl_vector_fixed qvec; + VnlVectorCaster caster( &orientation, &qvec ); + caster.Update(); + + mitk::Quaternion p( qvec ); + + this->SetOrientation( p ); + } + + vnl_vector_fixed Transform::GetVnlDoubleTranslation() const + { + vnl_vector_fixed vecFloat = this->GetVnlTranslation(); + vnl_vector_fixed vecDouble; + VnlVectorCaster caster( &vecFloat, &vecDouble ); + caster.Update(); + + return vecDouble; + } + + void Transform::SetTranslation( const vnl_vector& transl) + { + vnl_vector_fixed dTransl(transl); + vnl_vector_fixed fTransl; + VnlVectorCaster caster( &dTransl, &fTransl ); + caster.Update(); + + this->SetTranslation( fTransl ); + } + + vnl_quaternion Transform::GetVnlDoubleQuaternion() const + { + mitk::Quaternion fOrientation = this->GetOrientation(); + vnl_quaternion dOrientation; + VnlVectorCaster caster( &fOrientation, &dOrientation ); + caster.Update(); + + return dOrientation; + } + + void Transform::FromCSVFile(const std::string& file) + { + std::ifstream csvFile (file.c_str()); + endoAssert ( csvFile.fail() == false ); + + mitk::Transform::Pointer transform = mitk::Transform::New(); + vnl_matrix_fixed mat; + std::string line; + mitk::ScalarType d = 0.0f; + int row=0,column = 0; + + while (std::getline (csvFile, line)) + { + std::istringstream linestream(line); + std::string item; + column = 0; + while (std::getline (linestream, item, ',')) + { + std::istringstream number; + number.str(item); + number >> d; + mat(row, column) = d; + ++column; + } + ++row; + } + endoAssert( row == 4 && column == 4 ); + transform->SetMatrix( mat ); + + this->SetNavigationData( transform->GetNavigationData() ); + // modified is called in SetNavigationData + } + + std::string Transform::ToCSVString() const + { + std::ostringstream s; s.precision(12); + + vnl_matrix_fixed mat + = this->GetMatrix(); + + for( unsigned int j=0; j mat + = this->GetMatrix(); + + s << varname << " = ["; + for( unsigned int j=0; jGraft(transform->GetNavigationData()); + } + + mitk::Transform::Pointer Transform::Clone() const + { + Transform::Pointer copy = Transform::New(); + copy->Copy( this ); + return copy; + } + + void Transform::SetMatrix( const vtkMatrix4x4* mat) + { + vnl_matrix_fixed vnlMat; + for(unsigned int i=0; i<4; ++i) + for(unsigned int j=0; j<4; ++j) + vnlMat(i,j) = mat->GetElement(i, j); + + this->SetMatrix( vnlMat ); + } + + void Transform::ToCSVFile(const std::string& file) const + { + std::ofstream csvFile; + csvFile.open(file.c_str()); + endoAssert ( csvFile.fail() == false ); + csvFile << this->ToCSVString(); + csvFile.close(); + } + + void Transform::ToMatlabFile(const std::string& file + , const std::string& varname) const + { + std::ofstream csvFile; + csvFile.open(file.c_str()); + endoAssert ( csvFile.fail() == false ); + csvFile << this->ToMatlabString(varname); + csvFile.close(); + } + + void Transform::SetNavigationData( const mitk::NavigationData* naviData ) + { + endoAssert( naviData != 0 ); + m_NavData->Graft( naviData ); + this->Modified(); + } + + void Transform::SetRotation( vnl_matrix_fixed& mat) + { + this->m_NavData->SetOrientation( mitk::Quaternion(mat) ); + this->Modified(); + } + + void Transform::SetRotation( vnl_matrix& mat) + { + vnl_matrix_fixed tmp(mat); + this->SetRotation( tmp ); + } + + void Transform::SetPosition( const mitk::Point3D& transl) + { + this->SetTranslation( transl.GetVnlVector() ); + } + + void Transform::SetTranslation( double array[3] ) + { + mitk::Point3D p; + for(unsigned int i = 0; i < 3; ++i) + p.SetElement(i, array[i]); + this->SetTranslation( p.GetVnlVector() ); + } + + + void Transform::SetRotation( double array[3][3] ) + { + vnl_matrix_fixed mat; + + for(unsigned int i = 0; i < 3; ++i) + for(unsigned int j = 0; j < 3; ++j) + mat(i, j) = array[i][j]; + this->SetRotation( mat ); + } + + void Transform::Invert() + { + vnl_matrix_fixed tmp(this->GetMatrix()); + this->SetMatrix( vnl_inverse( tmp ) ); + } + + void Transform::SetMatrix( + const vnl_matrix_fixed& mat) + { + // set translation first + vnl_vector transl = mat.get_column(3); + mitk::Point3D p; + for(unsigned int i=0; i<3; ++i) + p[i] = transl[i]; + + m_NavData->SetPosition(p); + + // set rotation + vnl_matrix_fixed rotMatFixed( + mat.extract(3,3)); + this->SetRotation(rotMatFixed); + } + + bool Transform::IsValid() const + { + return m_NavData->IsDataValid(); + } + + void Transform::SetTranslation( const cv::Mat& transl) + { + vnl_vector vec(3); + VnlVectorFromCvMat _VnlVectorFromCvMat( &transl, &vec ); + _VnlVectorFromCvMat.Update(); + this->SetTranslation( vnl_vector_fixed( vec ) ); + } + + void Transform::SetRotation( const cv::Mat& mat ) + { + vnl_matrix vnlMat(3, 3); + VnlMatrixFromCvMat _VnlMatrixFromCvMat( &mat, &vnlMat ); + _VnlMatrixFromCvMat.Update(); + vnl_matrix_fixed vnlMatFixed(vnlMat); + + this->SetRotation(vnlMatFixed); + } + + void Transform::SetRotationVector( const cv::Mat& rotVec ) + { + cv::Mat rotMat; + cv::Rodrigues( rotVec, rotMat ); + + vnl_matrix vnlMat(3, 3); + VnlMatrixFromCvMat _VnlMatrixFromCvMat( &rotMat, &vnlMat ); + _VnlMatrixFromCvMat.Update(); + + + vnl_matrix_fixed vnlMatFixed(vnlMat); + + this->SetRotation( vnlMatFixed ); + } + + //# getter + mitk::NavigationData::Pointer Transform::GetNavigationData() const + { + return m_NavData; + } + + mitk::Point3D Transform::GetTranslation() const + { + return m_NavData->GetPosition(); + } + + mitk::Point3D Transform::GetPosition() const + { + return m_NavData->GetPosition(); + } + + mitk::Quaternion Transform::GetOrientation() const + { + return m_NavData->GetOrientation(); + } + + void Transform::GetMatrix(vtkMatrix4x4* matrix) const + { + vnl_matrix_fixed vnlMat = this->GetMatrix(); + for(unsigned int i=0; iSetElement(i,j, vnlMat(i,j)); + } + + void Transform::GetVtkOpenGlMatrix(vtkMatrix4x4* matrix) const + { + vnl_matrix vnlRotation + = this->GetVnlRotationMatrix().as_matrix(); + + // normalize rows of rotation matrix + vnlRotation.normalize_rows(); + + vnl_matrix vnlInverseRotation(3,3); + // invert rotation + vnlInverseRotation = vnl_matrix_inverse(vnlRotation); + + vnl_vector vnlTranslation + = this->GetPosition().GetVnlVector(); + // rotate translation vector by inverse rotation P = P' + vnlTranslation = vnlInverseRotation * vnlTranslation; + vnlTranslation *= -1; // save -P' + + // set position + mitk::Transform::Pointer tmp = mitk::Transform::New(); + + tmp->SetTranslation( vnlTranslation ); + tmp->SetRotation( vnlRotation ); + tmp->GetMatrix(matrix); + } + + //# cv getter + cv::Mat Transform::GetCvTranslation() const + { + cv::Mat mat; + vnl_vector vec = this->GetVnlTranslation().as_vector(); + endodebugvar( vec ) + CvMatFromVnlVector _CvMatFromVnlVector(&vec, &mat); + _CvMatFromVnlVector.Update(); + return mat; + } + + cv::Mat Transform::GetCvRotationMatrix() const + { + cv::Mat mat; + vnl_matrix vec = this->GetVnlRotationMatrix().as_matrix(); + endodebugvar( vec ) + CvMatFromVnlMatrix _CvMatFromVnlMatrix(&vec, &mat); + _CvMatFromVnlMatrix.Update(); + return mat; + } + + cv::Mat Transform::GetCvMatrix() const + { + cv::Mat mat; + vnl_matrix vec = this->GetVnlRotationMatrix().as_matrix(); + CvMatFromVnlMatrix _CvMatFromVnlMatrix(&vec, &mat); + _CvMatFromVnlMatrix.Update(); + return mat; + } + + cv::Mat Transform::GetCvRotationVector() const + { + cv::Mat rotVec(3,1,cv::DataType::type); + cv::Rodrigues( this->GetCvRotationMatrix(), rotVec ); + return rotVec; + } + + //# vnl getter + vnl_vector_fixed Transform::GetVnlTranslation() const + { + vnl_vector_fixed vec(m_NavData->GetPosition() + .GetVnlVector()); + return vec; + } + + vnl_matrix_fixed Transform::GetVnlRotationMatrix() const + { + return m_NavData->GetOrientation().rotation_matrix_transpose(); + } + + vnl_matrix_fixed Transform::GetVnlDoubleMatrix() const + { + vnl_matrix_fixed mat = this->GetMatrix(); + + vnl_matrix_fixed doubleMat; + + for(unsigned int i=0; i( mat(i,j) ); + + return doubleMat; + } + + vnl_matrix_fixed Transform::GetMatrix() + const + { + vnl_vector_fixed transl = this->GetVnlTranslation(); + vnl_matrix_fixed rot = this->GetVnlRotationMatrix(); + + vnl_matrix_fixed homMat; + homMat.set_identity(); + //std::cout << homMat << std::endl; + for(unsigned int i=0; i rotMat = this->GetVnlRotationMatrix().transpose(); + this->SetRotation( rotMat ); + } + + void Transform::SetValid( bool valid ) + { + if( m_NavData->IsDataValid() == valid ) + return; + + m_NavData->SetDataValid( valid ); + this->Modified(); + } + + std::string mitk::Transform::ToString() const + { + std::ostringstream s; s.precision(12); + + mitk::NavigationData::PositionType position; + position.Fill(0.0); + position = m_NavData->GetPosition(); + + mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0); + orientation = m_NavData->GetOrientation(); + + s << "Translation: [" << position[0] << ", " << position[1] << ", " + << position[2] << "]"; + s << ", orientation: [" << orientation[0] << ", " << orientation[1] << ", " + << orientation[2] << ", " << orientation[3] << "]"; + s << ", valid: [" << (this->IsValid()? "true": "false") << "]"; + + return s.str(); + } + + void mitk::Transform::ToXML(TiXmlElement* elem) const + { + std::string value = elem->ValueStr(); + if(value.empty()) + elem->SetValue(this->GetNameOfClass()); + + mitk::NavigationData::PositionType position; + position.Fill(0.0); + position = m_NavData->GetPosition(); + + mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0); + orientation = m_NavData->GetOrientation(); + + mitk::NavigationData::CovarianceMatrixType matrix; + matrix.SetIdentity(); + matrix = m_NavData->GetCovErrorMatrix(); + + bool hasPosition = true; + hasPosition = m_NavData->GetHasPosition(); + bool hasOrientation = true; + hasOrientation = m_NavData->GetHasOrientation(); + bool dataValid = false; + dataValid = m_NavData->IsDataValid(); + mitk::NavigationData::TimeStampType timestamp=0.0; + + elem->SetDoubleAttribute("Time", timestamp); + elem->SetDoubleAttribute("X", position[0]); + elem->SetDoubleAttribute("Y", position[1]); + elem->SetDoubleAttribute("Z", position[2]); + + elem->SetDoubleAttribute("QX", orientation[0]); + elem->SetDoubleAttribute("QY", orientation[1]); + elem->SetDoubleAttribute("QZ", orientation[2]); + elem->SetDoubleAttribute("QR", orientation[3]); + + elem->SetDoubleAttribute("C00", matrix[0][0]); + elem->SetDoubleAttribute("C01", matrix[0][1]); + elem->SetDoubleAttribute("C02", matrix[0][2]); + elem->SetDoubleAttribute("C03", matrix[0][3]); + elem->SetDoubleAttribute("C04", matrix[0][4]); + elem->SetDoubleAttribute("C05", matrix[0][5]); + elem->SetDoubleAttribute("C10", matrix[1][0]); + elem->SetDoubleAttribute("C11", matrix[1][1]); + elem->SetDoubleAttribute("C12", matrix[1][2]); + elem->SetDoubleAttribute("C13", matrix[1][3]); + elem->SetDoubleAttribute("C14", matrix[1][4]); + elem->SetDoubleAttribute("C15", matrix[1][5]); + + if (dataValid) + elem->SetAttribute("Valid",1); + else + elem->SetAttribute("Valid",0); + + if (hasOrientation) + elem->SetAttribute("hO",1); + else + elem->SetAttribute("hO",0); + + if (hasPosition) + elem->SetAttribute("hP",1); + else + elem->SetAttribute("hP",0); + } + + void mitk::Transform::FromXML(TiXmlElement* elem) + { + assert(elem); + + mitk::NavigationData::Pointer nd = mitk::NavigationData::New(); + mitk::NavigationData::PositionType position; + mitk::NavigationData::OrientationType orientation(0.0,0.0,0.0,0.0); + mitk::NavigationData::TimeStampType timestamp = -1; + mitk::NavigationData::CovarianceMatrixType matrix; + + bool hasPosition = true; + bool hasOrientation = true; + bool dataValid = false; + + position.Fill(0.0); + matrix.SetIdentity(); + + elem->QueryDoubleAttribute("Time",×tamp); + + // position and orientation is mandatory! + if(elem->QueryFloatAttribute("X", &position[0]) != TIXML_SUCCESS) + throw std::invalid_argument("No X position found in xml"); + if(elem->QueryFloatAttribute("Y", &position[1]) != TIXML_SUCCESS) + throw std::invalid_argument("No Y position found in xml"); + if(elem->QueryFloatAttribute("Z", &position[2]) != TIXML_SUCCESS) + throw std::invalid_argument("No Z position found in xml"); + + if(elem->QueryFloatAttribute("QX", &orientation[0]) != TIXML_SUCCESS) + throw std::invalid_argument("No QX orientation found in xml"); + if(elem->QueryFloatAttribute("QY", &orientation[1]) != TIXML_SUCCESS) + throw std::invalid_argument("No QY orientation found in xml"); + if(elem->QueryFloatAttribute("QZ", &orientation[2]) != TIXML_SUCCESS) + throw std::invalid_argument("No QZ orientation found in xml"); + if(elem->QueryFloatAttribute("QR", &orientation[3]) != TIXML_SUCCESS) + throw std::invalid_argument("No QR orientation found in xml"); + + elem->QueryFloatAttribute("C00", &matrix[0][0]); + elem->QueryFloatAttribute("C01", &matrix[0][1]); + elem->QueryFloatAttribute("C02", &matrix[0][2]); + elem->QueryFloatAttribute("C03", &matrix[0][3]); + elem->QueryFloatAttribute("C04", &matrix[0][4]); + elem->QueryFloatAttribute("C05", &matrix[0][5]); + elem->QueryFloatAttribute("C10", &matrix[1][0]); + elem->QueryFloatAttribute("C11", &matrix[1][1]); + elem->QueryFloatAttribute("C12", &matrix[1][2]); + elem->QueryFloatAttribute("C13", &matrix[1][3]); + elem->QueryFloatAttribute("C14", &matrix[1][4]); + elem->QueryFloatAttribute("C15", &matrix[1][5]); + + int tmpval = 0; + elem->QueryIntAttribute("Valid", &tmpval); + if (tmpval == 0) + dataValid = false; + else + dataValid = true; + + tmpval = 0; + elem->QueryIntAttribute("hO", &tmpval); + if (tmpval == 0) + hasOrientation = false; + else + hasOrientation = true; + + tmpval = 0; + elem->QueryIntAttribute("hP", &tmpval); + if (tmpval == 0) + hasPosition = false; + else + hasPosition = true; + + nd->SetTimeStamp(timestamp); + nd->SetPosition(position); + nd->SetOrientation(orientation); + nd->SetCovErrorMatrix(matrix); + nd->SetDataValid(dataValid); + nd->SetHasOrientation(hasOrientation); + nd->SetHasPosition(hasPosition); + + m_NavData = nd; + this->Modified(); + } + +} // namespace mitk + +std::ostream& operator<< (std::ostream& os, mitk::Transform::Pointer p) +{ + os << p->ToString(); + return os; +} diff --git a/Modules/CameraCalibration/mitkTransform.h b/Modules/CameraCalibration/mitkTransform.h new file mode 100644 index 0000000000..798d53ebc7 --- /dev/null +++ b/Modules/CameraCalibration/mitkTransform.h @@ -0,0 +1,257 @@ +#ifndef MITKTRANSFORM_H +#define MITKTRANSFORM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mitk { + + /// + /// \brief class representing a transfrom in 3D + /// + /// internally it stores a mitk navigation data. this is more + /// or less a wrapper for navigation data for easy casting + /// between opencv/vnl/mitk/xml representations of transform + /// data + /// + class mitkCameraCalibration_EXPORT Transform: public itk::Object, + public XMLSerializable + { + public: + mitkClassMacro(Transform, itk::Object); + itkFactorylessNewMacro(Transform); + mitkNewMacro1Param(Transform, const mitk::NavigationData*); + + /// + /// Copies the content of transform to this + /// instance + /// + void Copy( const mitk::Transform* transform ); + + /// + /// Copies the content of transform to this + /// instance + /// + void Copy( const mitk::NavigationData* transform ); + + /// + /// Inverts the rotation of this transform + /// (Polaris navigation Data have inverted rotation + /// so you may want to call this function when using + /// polaris data) + /// + void TransposeRotation(); + + /// + /// get a copy of this transform + /// + mitk::Transform::Pointer Clone() const; + + /// + /// concatenate this transform with the given one, + /// i.e. this transform is done first, then transform + /// ( if x is this transform, y is transform, then this will be y*x) + /// post multiply semantics! + /// \see vtkTransform + /// + void Concatenate( mitk::Transform* transform ); + /// + /// same as above with vnl mat argument + /// + void Concatenate( const vnl_matrix_fixed& transform ); + + /// + /// same as above with vtk mat argument + /// + void Concatenate( const vtkMatrix4x4* transform ); + + /// + /// invert this transform + /// + void Invert(); + + /// + /// read from xml + /// + void FromXML(TiXmlElement* elem); + /// + /// read csv file + /// + void FromCSVFile(const std::string& file); + /// + /// grafts the data from naviData to this transform + /// + void SetNavigationData( const mitk::NavigationData* naviData ); + /// + /// method to set orientation quat + /// + void SetOrientation( const vnl_quaternion& orientation); + /// + /// method to set double valued orientation quat + /// + void SetOrientation( const vnl_quaternion& orientation); + /// + /// method to set translation + /// + void SetTranslation( const vnl_vector_fixed& transl); + /// + /// method to set a vector of doubles as translation + /// + void SetTranslation( const vnl_vector& transl); + /// + /// method to set a mitk::Point3D as position + /// + void SetPosition( const mitk::Point3D& transl); + /// + /// sets rotation with a rotation matrix + /// + void SetRotation( vnl_matrix_fixed& mat); + /// + /// sets rotation with a non fixed rotation matrix + /// + void SetRotation( vnl_matrix& mat); + /// + /// sets rotation and translation with a transformation matrix + /// + void SetMatrix( const vnl_matrix_fixed& mat); + /// + /// sets rotation and translation with a vtk transformation matrix + /// + void SetMatrix( const vtkMatrix4x4* mat); + /// + /// sets translation from a POD vector + /// + void SetTranslation( float* array ); + /// + /// sets translation from a POD vector. this must be a + /// 3x3=9 sized vector in row major format (first row = first + /// three elements) + /// + void SetRotation( float* array ); + /// + /// sets translation from a POD vector + /// + void SetTranslation( double array[3] ); + /// + /// sets translation from a POD vector + /// + void SetRotation( double array[3][3] ); + + /// + /// method to set translation by cv vector + /// + void SetTranslation( const cv::Mat& transl); + /// + /// sets rotation with a rotation matrix + /// + void SetRotation( const cv::Mat& mat ); + /// + /// sets rotation with a rodrigues rotation vector + /// + void SetRotationVector( const cv::Mat& rotVec); + + /// + /// \return the navigation data that stores all information + /// + mitk::NavigationData::Pointer GetNavigationData() const; + /// + /// calls navigationdata::GetPosition() + /// + mitk::Point3D GetPosition() const; + /// + /// same as GetPosition + /// + mitk::Point3D GetTranslation() const; + /// + /// calls navigationdata::IsValid() + /// + bool IsValid() const; + /// + /// calls navigationdata::SetValid() + /// + void SetValid(bool valid); + + /// + /// calls navigationdata::GetOrientation() + /// + mitk::Quaternion GetOrientation() const; + + /// + /// \return the homogeneous matrix representing this transform + /// + vnl_matrix_fixed GetMatrix() const; + /// + /// \return the homogeneous vtk matrix representing this transform + /// + void GetMatrix(vtkMatrix4x4* matrix) const; + /// + /// \return the homogeneous vtk matrix representing this transform + /// in !OpenGL! left handed coordinate system + /// + void GetVtkOpenGlMatrix(vtkMatrix4x4* matrix) const; + + /// + /// create xml representation + /// + void ToXML(TiXmlElement* elem) const; + /// + /// create string representation + /// + std::string ToString() const; + /// + /// create string csv representation (only the transformation values!!!!) + /// + std::string ToCSVString() const; + /// + /// create matlab representation + /// + std::string ToMatlabString(const std::string& varname="transform", + bool printLastRow=true) const; + /// + /// write csv representation to file (only the transformation values!!!!) + /// + void ToCSVFile(const std::string& file) const; + /// + /// write matlab representation to file + /// + void ToMatlabFile(const std::string& file + , const std::string& varname="transform") const; + /// + /// conversion to cv types + /// + cv::Mat GetCvTranslation() const; + cv::Mat GetCvRotationVector() const; + cv::Mat GetCvRotationMatrix() const; + cv::Mat GetCvMatrix() const; + + /// + /// conversion to vnl types + /// + vnl_vector_fixed GetVnlTranslation() const; + vnl_vector_fixed GetVnlDoubleTranslation() const; + vnl_quaternion GetVnlDoubleQuaternion() const; + vnl_matrix_fixed GetVnlRotationMatrix() const; + vnl_matrix_fixed GetVnlDoubleMatrix() const; + + protected: + Transform(); + Transform(const mitk::NavigationData* nd); + + // everything is stored here + mitk::NavigationData::Pointer m_NavData; +}; + +} // namespace mitk + +mitkCameraCalibration_EXPORT std::ostream& operator<< + (std::ostream& os, mitk::Transform::Pointer p); + +#endif // MITKTRANSFORM_H diff --git a/Modules/CameraCalibration/mitkVnlMatrixFromCvMat.h b/Modules/CameraCalibration/mitkVnlMatrixFromCvMat.h new file mode 100644 index 0000000000..a2893c1d78 --- /dev/null +++ b/Modules/CameraCalibration/mitkVnlMatrixFromCvMat.h @@ -0,0 +1,63 @@ +#ifndef mitkVnlMatrixFromCvMat_h +#define mitkVnlMatrixFromCvMat_h + +#include +#include +#include +#include +#include + +namespace mitk +{ + /// + /// create a vnl_matrix from a cv mat + /// + template + class VnlMatrixFromCvMat: + virtual public Algorithm + { + public: + /// + /// init default values and save references + /// + VnlMatrixFromCvMat( + const cv::Mat* _CvMat, + vnl_matrix* _VnlMatrix): + m_CvMat(_CvMat), + m_VnlMatrix(_VnlMatrix) + { + } + + + /// + /// cv mat to vnl matrix with known cv type + /// + template + void ToVnlMatrix( vnl_matrix& vnlMat, const cv::Mat& mat ) + { + vnlMat.set_size( mat.rows, mat.cols ); + for(int i=0; i( mat.at(i,j) ); + } + + /// + /// executes the Algorithm + /// + void Update() + { + endoAccessCvMat( ToVnlMatrix, T, (*m_VnlMatrix), (*m_CvMat) ); + } + private: + /// + /// VnlMatrixFromCvMat input member variable + /// + const cv::Mat* m_CvMat; + /// + /// VnlMatrixFromCvMat output member variable + /// + vnl_matrix* m_VnlMatrix; + }; +} // namespace mitk + +#endif // mitkVnlMatrixFromCvMat_h diff --git a/Modules/CameraCalibration/mitkVnlVectorCaster.h b/Modules/CameraCalibration/mitkVnlVectorCaster.h new file mode 100644 index 0000000000..2e620ec000 --- /dev/null +++ b/Modules/CameraCalibration/mitkVnlVectorCaster.h @@ -0,0 +1,50 @@ +#ifndef mitkVnlVectorCaster_h +#define mitkVnlVectorCaster_h + +#include +#include +#include + +namespace mitk +{ + /// + /// class for casting vnl vectors to another basic type + /// by means of static cast + /// + template + class VnlVectorCaster: + virtual public Algorithm + { + public: + /// + /// init default values and save references + /// + VnlVectorCaster( + const vnl_vector_fixed* _InputVector, + vnl_vector_fixed* _OutputVector): + m_InputVector(_InputVector), + m_OutputVector(_OutputVector) + { + } + /// + /// executes the Algorithm + /// + void Update() + { + for( size_t i=0; isize(); ++i ) + (*m_OutputVector)[i] = static_cast( (*m_InputVector)[i] ); + + } + private: + /// + /// VnlVectorCaster input member variable + /// + const vnl_vector_fixed* m_InputVector; + /// + /// VnlVectorCaster output member variable + /// + vnl_vector_fixed* m_OutputVector; + }; +} // namespace mitk + +#endif // mitkVnlVectorCaster_h diff --git a/Modules/CameraCalibration/mitkVnlVectorFromCvMat.h b/Modules/CameraCalibration/mitkVnlVectorFromCvMat.h new file mode 100644 index 0000000000..f556b9782f --- /dev/null +++ b/Modules/CameraCalibration/mitkVnlVectorFromCvMat.h @@ -0,0 +1,57 @@ +#ifndef mitkVnlVectorFromCvMat_h +#define mitkVnlVectorFromCvMat_h + +#include +#include + +namespace mitk +{ + /// + /// create a vnl_vector from a cv mat + /// + template + class VnlVectorFromCvMat: + virtual public Algorithm + { + public: + /// + /// init default values and save references + /// + VnlVectorFromCvMat( + const cv::Mat* _CvMat, + vnl_vector* _VnlVector): + m_VnlMatrixFromCvMat( _CvMat, &m_VnlMatrix ), + m_VnlVector(_VnlVector) + { + } + + /// + /// executes the Algorithm + /// + void Update() + { + m_VnlMatrixFromCvMat.Update(); + + if( m_VnlMatrix.rows() == 1 ) + *m_VnlVector = m_VnlMatrix.get_row(0); + else if( m_VnlMatrix.cols() == 1 ) + *m_VnlVector = m_VnlMatrix.get_column(0); + + } + private: + /// + /// VnlVectorFromCvMat input member variable + /// + vnl_matrix m_VnlMatrix; + /// + /// internal algorithm to get a vnl matrix from a cv mat + /// + VnlMatrixFromCvMat m_VnlMatrixFromCvMat; + /// + /// VnlVectorFromCvMat output member variable + /// + vnl_vector* m_VnlVector; + }; +} // namespace mitk + +#endif // mitkVnlVectorFromCvMat_h diff --git a/Modules/CameraCalibration/mitkXMLSerializable.cpp b/Modules/CameraCalibration/mitkXMLSerializable.cpp new file mode 100644 index 0000000000..85b7dd5513 --- /dev/null +++ b/Modules/CameraCalibration/mitkXMLSerializable.cpp @@ -0,0 +1,126 @@ +#include +#include "mitkEndoDebug.h" +#include "mitkEndoMacros.h" +#include + +namespace mitk +{ + const std::string XMLSerializable::FILE_REFERENCE_ATTRIBUTE_NAME = "fileRef"; + const std::string XMLSerializable::ROOT_NAME = "data"; + + void mitk::XMLSerializable::ToXMLFile(const std::string& file + , const std::string& elemName) + { + TiXmlElement * rootElem=0; + TiXmlElement * element=0; + + // determine element to write to + std::string elementName = elemName; + if(elementName.empty()) + elementName = this->GetNameOfClass(); + + TiXmlDocument doc( file.c_str() ); + bool loadOkay = doc.LoadFile(); + // if the document already exists ... + if(loadOkay) + { + // try to identify the XML element of this class as the root + // or as the child of the root + rootElem = doc.RootElement(); + endoAssertMsg( rootElem, "No root element found in " << file ); + + // if root element represents this element remove the root + if( rootElem->ValueStr() == elementName ) + { + doc.RemoveChild(rootElem); + rootElem = 0; + } + else + { + // if it is a child of the root remove it too + element = rootElem->FirstChildElement(elementName); + if(element) + rootElem->RemoveChild(element); + } + } + else + { + // document did not exist, create new one with declration + TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); + doc.LinkEndChild( decl ); + } + + m_XMLFileName = file; + + // create element (if the document already exists this element was removed) + element = new TiXmlElement( elementName ); + this->ToXML( element ); + + // if we do not have a root element create a new one + if(!rootElem) + rootElem = new TiXmlElement( ROOT_NAME ); + // add the element node as child + rootElem->LinkEndChild(element); + + // if no root element exists, add it now + if(doc.RootElement() == 0) + doc.LinkEndChild( rootElem ); + + if(!doc.SaveFile( file )) + { + std::ostringstream s; s << "File " << file + << " could not be written. Please check permissions."; + throw std::logic_error(s.str()); + } + } + + std::string mitk::XMLSerializable::GetXMLFileName() const + { + return m_XMLFileName; + } + + void mitk::XMLSerializable::FromXMLFile(const std::string& file + , const std::string& elemName) + { + endodebug( "Trying to read from " << file ) + + TiXmlDocument doc( file.c_str() ); + bool loadOkay = doc.LoadFile(); + if(!loadOkay) + { + std::ostringstream s; s << "File " << file + << " could not be loaded!"; + throw std::logic_error(s.str().c_str()); + } + + m_XMLFileName = file; + + TiXmlElement* elem = doc.FirstChildElement(); + endoAssertMsg( elem, "No root element found" ); + + // determine element to read from + std::string elementName = elemName; + if(elementName.empty()) + elementName = this->GetNameOfClass(); + // try again with the first element + if(strcmp(elem->Value(), elementName.c_str()) != 0) + elem = elem->FirstChildElement(elementName.c_str()); + + endoAssertMsg( elem, "No child element \"" << elementName << + "\" found in " << file ); + + // if theres an attribute as file reference try to load the class + // from that file + std::string filename; + if(elem->QueryStringAttribute(FILE_REFERENCE_ATTRIBUTE_NAME.c_str(), &filename) + == TIXML_SUCCESS) + { + if( !itksys::SystemTools::FileIsFullPath(filename.c_str()) ) + filename = itksys::SystemTools::GetFilenamePath(file) + "/" + filename; + this->FromXMLFile(filename); + return; // exit! + } + + this->FromXML( elem ); + } +} diff --git a/Modules/CameraCalibration/mitkXMLSerializable.h b/Modules/CameraCalibration/mitkXMLSerializable.h new file mode 100644 index 0000000000..9f5ad5b356 --- /dev/null +++ b/Modules/CameraCalibration/mitkXMLSerializable.h @@ -0,0 +1,77 @@ +#ifndef MITKXMLSerializable_H +#define MITKXMLSerializable_H + +#include +#include +#include +#include +#include + +namespace mitk +{ + /// + /// \brief interface for all classes able to write themselves to XML files + /// + class mitkCameraCalibration_EXPORT XMLSerializable + { + public: + /// + /// value of the special tag for file references + /// if this is attribute is found the class + /// will be loaded from the file in the attributes value + /// + static const std::string FILE_REFERENCE_ATTRIBUTE_NAME; + /// + /// the name of the root node that is created when the element is saved + /// + static const std::string ROOT_NAME; + /// + /// the tag value will be equals to the class name (function + /// implemented by the itkTypeMacro) + /// + virtual const char* GetNameOfClass() const = 0; + + /// + /// write your values here to elem + /// + virtual void ToXML(TiXmlElement* elem) const = 0; + /// + /// read your values here from elem + /// + virtual void FromXML(TiXmlElement* elem) = 0; + + /// + /// tries to write the xml data obtained in ToXML() to file + /// + virtual void ToXMLFile(const std::string& file + , const std::string& elemName=""); + + /// + /// loads the XML file and calls FromXML() + /// takes the first child of the document + /// if this root node value is not equal to GetNameOfClass() + /// the method will try to find the first children of the root + /// node with the value of GetNameOfClass() + /// if elemName is not empty then this value will be used instead + /// of GetNameOfClass() + /// if this node is found it will check if an attribute named + /// FILE_REFERENCE_ATTRIBUTE_NAME is found: in this case + /// the method calls itself with this attributes value as parameter + /// + virtual void FromXMLFile(const std::string& file + , const std::string& elemName=""); + + /// + /// \see m_XMLFileName + /// + std::string GetXMLFileName() const; + + private: + /// + /// saves the xmlfile name set for this serializable ( in FromXMLFile() ) + /// + std::string m_XMLFileName; + }; +} + +#endif diff --git a/Modules/DiffusionImaging/IODataStructures/TensorImages/mitkNrrdTensorImageReader.cpp b/Modules/DiffusionImaging/IODataStructures/TensorImages/mitkNrrdTensorImageReader.cpp index 37fdc53bed..4aed036ab7 100644 --- a/Modules/DiffusionImaging/IODataStructures/TensorImages/mitkNrrdTensorImageReader.cpp +++ b/Modules/DiffusionImaging/IODataStructures/TensorImages/mitkNrrdTensorImageReader.cpp @@ -1,161 +1,188 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2009-07-14 19:11:20 +0200 (Tue, 14 Jul 2009) $ Version: $Revision: 18127 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkNrrdTensorImageReader.h" #include "itkImageFileReader.h" #include "itkImageRegionIterator.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkDiffusionTensor3D.h" #include "mitkITKImageImport.h" #include "mitkImageDataItem.h" namespace mitk { void NrrdTensorImageReader ::GenerateData() - { - if ( m_FileName == "") + { + if ( m_FileName == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename is empty!"); } else { try { typedef itk::VectorImage ImageType; itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(this->m_FileName); reader->Update(); ImageType::Pointer img = reader->GetOutput(); typedef itk::Image,3> VecImgType; VecImgType::Pointer vecImg = VecImgType::New(); vecImg->SetSpacing( img->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( img->GetOrigin() ); // Set the image origin vecImg->SetDirection( img->GetDirection() ); // Set the image direction vecImg->SetRegions( img->GetLargestPossibleRegion()); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot = ot.Begin(); itk::ImageRegionIterator it (img, img->GetLargestPossibleRegion() ); it = it.Begin(); typedef ImageType::PixelType VarPixType; typedef VecImgType::PixelType FixPixType; + int numComponents = img->GetNumberOfComponentsPerPixel(); - while (!it.IsAtEnd()) + if (numComponents==6) + { + while (!it.IsAtEnd()) + { + VarPixType vec = it.Get(); + FixPixType fixVec(vec.GetDataPointer()); + ot.Set(fixVec); + ++ot; + ++it; + } + } + else if(numComponents==9) { - VarPixType vec = it.Get(); - FixPixType fixVec(vec.GetDataPointer()); - ot.Set(fixVec); - ++ot; - ++it; + while (!it.IsAtEnd()) + { + VarPixType vec = it.Get(); + itk::DiffusionTensor3D tensor; + tensor.SetElement(0, vec.GetElement(0)); + tensor.SetElement(1, vec.GetElement(1)); + tensor.SetElement(2, vec.GetElement(2)); + tensor.SetElement(3, vec.GetElement(4)); + tensor.SetElement(4, vec.GetElement(5)); + tensor.SetElement(5, vec.GetElement(8)); + + FixPixType fixVec(tensor); + ot.Set(fixVec); + ++ot; + ++it; + } + } + else + { + throw itk::ImageFileReaderException(__FILE__, __LINE__, "Image has wrong number of pixel components!"); } this->GetOutput()->InitializeByItk(vecImg.GetPointer()); this->GetOutput()->SetVolume(vecImg->GetBufferPointer()); } catch(std::exception& e) { - throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); + throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested DTI file!"); } } } void NrrdTensorImageReader::GenerateOutputInformation() { } - + const char* NrrdTensorImageReader ::GetFileName() const { return m_FileName.c_str(); } - + void NrrdTensorImageReader ::SetFileName(const char* aFileName) { m_FileName = aFileName; } - + const char* NrrdTensorImageReader ::GetFilePrefix() const { return m_FilePrefix.c_str(); } - + void NrrdTensorImageReader ::SetFilePrefix(const char* aFilePrefix) { m_FilePrefix = aFilePrefix; } - + const char* NrrdTensorImageReader ::GetFilePattern() const { return m_FilePattern.c_str(); } - + void NrrdTensorImageReader ::SetFilePattern(const char* aFilePattern) { m_FilePattern = aFilePattern; } - + bool NrrdTensorImageReader - ::CanReadFile(const std::string filename, const std::string /*filePrefix*/, const std::string /*filePattern*/) + ::CanReadFile(const std::string filename, const std::string /*filePrefix*/, const std::string /*filePattern*/) { // First check the extension if( filename == "" ) { return false; } std::string ext = itksys::SystemTools::GetFilenameLastExtension(filename); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".hdti" || ext == ".dti") { return true; } return false; } } //namespace MITK diff --git a/Modules/IGT/IGTFilters/mitkTrackingDeviceSourceConfigurator.cpp b/Modules/IGT/IGTFilters/mitkTrackingDeviceSourceConfigurator.cpp index 09a95e7426..bf43af8333 100644 --- a/Modules/IGT/IGTFilters/mitkTrackingDeviceSourceConfigurator.cpp +++ b/Modules/IGT/IGTFilters/mitkTrackingDeviceSourceConfigurator.cpp @@ -1,247 +1,250 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2011-01-18 13:22:38 +0100 (Di, 18 Jan 2011) $ Version: $Revision: 28959 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkTrackingDeviceSourceConfigurator.h" #include "mitkNDITrackingDevice.h" #include "mitkClaronTrackingDevice.h" mitk::TrackingDeviceSourceConfigurator::TrackingDeviceSourceConfigurator(mitk::NavigationToolStorage::Pointer NavigationTools, mitk::TrackingDevice::Pointer TrackingDevice) { //make a copy of the navigation tool storage because we will modify the storage -m_NavigationTools = mitk::NavigationToolStorage::New(); -for (int i=0; iGetToolCount(); i++) +if (NavigationTools.IsNotNull()) + { + m_NavigationTools = mitk::NavigationToolStorage::New(); + for (int i=0; iGetToolCount(); i++) { m_NavigationTools->AddTool(NavigationTools->GetTool(i)); } + } m_TrackingDevice = TrackingDevice; m_ToolCorrespondencesInToolStorage = std::vector(); m_ErrorMessage = ""; } mitk::NavigationToolStorage::Pointer mitk::TrackingDeviceSourceConfigurator::GetUpdatedNavigationToolStorage() { return m_NavigationTools; } mitk::TrackingDeviceSourceConfigurator::~TrackingDeviceSourceConfigurator() { } bool mitk::TrackingDeviceSourceConfigurator::IsCreateTrackingDeviceSourcePossible() { if (m_NavigationTools.IsNull()) { m_ErrorMessage = "NavigationToolStorage is NULL!"; return false; } else if (m_TrackingDevice.IsNull()) { m_ErrorMessage = "TrackingDevice is NULL!"; return false; } else { for (int i=0; iGetToolCount(); i++) { if (m_NavigationTools->GetTool(i)->GetTrackingDeviceType() != m_TrackingDevice->GetType()) { m_ErrorMessage = "At least one tool is not of the same type like the tracking device."; return false; } } //TODO in case of Aurora: check if the tools are automatically detected by comparing the serial number return true; } } mitk::TrackingDeviceSource::Pointer mitk::TrackingDeviceSourceConfigurator::CreateTrackingDeviceSource() { mitk::NavigationDataObjectVisualizationFilter::Pointer dummy; return this->CreateTrackingDeviceSource(dummy); } mitk::TrackingDeviceSource::Pointer mitk::TrackingDeviceSourceConfigurator::CreateTrackingDeviceSource(mitk::NavigationDataObjectVisualizationFilter::Pointer &visualizationFilter) { if (!this->IsCreateTrackingDeviceSourcePossible()) return NULL; mitk::TrackingDeviceSource::Pointer returnValue; //create tracking device source if (m_TrackingDevice->GetType()==mitk::NDIAurora) {returnValue = CreateNDIAuroraTrackingDeviceSource(m_TrackingDevice,m_NavigationTools);} else if (m_TrackingDevice->GetType()==mitk::NDIPolaris) {returnValue = CreateNDIPolarisTrackingDeviceSource(m_TrackingDevice,m_NavigationTools);} else if (m_TrackingDevice->GetType()==mitk::ClaronMicron) {returnValue = CreateMicronTrackerTrackingDeviceSource(m_TrackingDevice,m_NavigationTools);} //TODO: insert other tracking systems? if (returnValue.IsNull()) return NULL; //create visualization filter visualizationFilter = CreateNavigationDataObjectVisualizationFilter(returnValue,m_NavigationTools); if (visualizationFilter.IsNull()) return NULL; return returnValue; } std::string mitk::TrackingDeviceSourceConfigurator::GetErrorMessage() { return this->m_ErrorMessage; } //############################ internal help methods ######################################## mitk::TrackingDeviceSource::Pointer mitk::TrackingDeviceSourceConfigurator::CreateNDIPolarisTrackingDeviceSource(mitk::TrackingDevice::Pointer trackingDevice, mitk::NavigationToolStorage::Pointer navigationTools) { mitk::TrackingDeviceSource::Pointer returnValue = mitk::TrackingDeviceSource::New(); mitk::NDITrackingDevice::Pointer thisDevice = dynamic_cast(trackingDevice.GetPointer()); m_ToolCorrespondencesInToolStorage = std::vector(); //add the tools to the tracking device for (int i=0; iGetToolCount(); i++) { mitk::NavigationTool::Pointer thisNavigationTool = m_NavigationTools->GetTool(i); m_ToolCorrespondencesInToolStorage.push_back(i); bool toolAddSuccess = thisDevice->AddTool(thisNavigationTool->GetToolName().c_str(),thisNavigationTool->GetCalibrationFile().c_str()); if (!toolAddSuccess) { this->m_ErrorMessage = "Can't add tool, is the SROM-file valid?"; return NULL; } } returnValue->SetTrackingDevice(thisDevice); return returnValue; } mitk::TrackingDeviceSource::Pointer mitk::TrackingDeviceSourceConfigurator::CreateNDIAuroraTrackingDeviceSource(mitk::TrackingDevice::Pointer trackingDevice, mitk::NavigationToolStorage::Pointer navigationTools) { mitk::TrackingDeviceSource::Pointer returnValue = mitk::TrackingDeviceSource::New(); mitk::NDITrackingDevice::Pointer thisDevice = dynamic_cast(trackingDevice.GetPointer()); //connect to aurora to dectect tools automatically thisDevice->OpenConnection(); thisDevice->StartTracking(); //now search for automatically detected tools in the tool storage and save them mitk::NavigationToolStorage::Pointer newToolStorageInRightOrder = mitk::NavigationToolStorage::New(); std::vector alreadyFoundTools = std::vector(); m_ToolCorrespondencesInToolStorage = std::vector(); for (int i=0; iGetToolCount(); i++) { bool toolFound = false; for (int j=0; jGetToolCount(); j++) { //check if the serial number is the same to identify the tool if ((dynamic_cast(thisDevice->GetTool(i)))->GetSerialNumber() == navigationTools->GetTool(j)->GetSerialNumber()) { //check if this tool was already added to make sure that every tool is only added once (in case of same serial numbers) bool toolAlreadyAdded = false; for(int k=0; kAddTool(navigationTools->GetTool(j)); //adapt name of tool dynamic_cast(thisDevice->GetTool(i))->SetToolName(navigationTools->GetTool(j)->GetToolName()); //rember that this tool was already found alreadyFoundTools.push_back(j); toolFound = true; break; } } } if (!toolFound) { this->m_ErrorMessage = "Error: did not find every automatically detected tool in the loaded tool storage: aborting initialization."; return NULL; } } //delete all tools from the tool storage navigationTools->DeleteAllTools(); //and add only the detected tools in the right order for (int i=0; iGetToolCount(); i++) { navigationTools->AddTool(newToolStorageInRightOrder->GetTool(i)); } returnValue->SetTrackingDevice(thisDevice); return returnValue; } mitk::TrackingDeviceSource::Pointer mitk::TrackingDeviceSourceConfigurator::CreateMicronTrackerTrackingDeviceSource(mitk::TrackingDevice::Pointer trackingDevice, mitk::NavigationToolStorage::Pointer navigationTools) { mitk::TrackingDeviceSource::Pointer returnValue = mitk::TrackingDeviceSource::New(); mitk::ClaronTrackingDevice::Pointer thisDevice = dynamic_cast(trackingDevice.GetPointer()); m_ToolCorrespondencesInToolStorage = std::vector(); //add the tools to the tracking device for (int i=0; iGetToolCount(); i++) { mitk::NavigationTool::Pointer thisNavigationTool = m_NavigationTools->GetTool(i); m_ToolCorrespondencesInToolStorage.push_back(i); bool toolAddSuccess = thisDevice->AddTool(thisNavigationTool->GetToolName().c_str(),thisNavigationTool->GetCalibrationFile().c_str()); if (!toolAddSuccess) { this->m_ErrorMessage = "Can't add tool, is the toolfile valid?"; return NULL; } } returnValue->SetTrackingDevice(thisDevice); return returnValue; } mitk::NavigationDataObjectVisualizationFilter::Pointer mitk::TrackingDeviceSourceConfigurator::CreateNavigationDataObjectVisualizationFilter(mitk::TrackingDeviceSource::Pointer trackingDeviceSource, mitk::NavigationToolStorage::Pointer navigationTools) { mitk::NavigationDataObjectVisualizationFilter::Pointer returnValue = mitk::NavigationDataObjectVisualizationFilter::New(); for (int i=0; iGetNumberOfOutputs(); i++) { mitk::NavigationTool::Pointer currentTool = navigationTools->GetToolByName(trackingDeviceSource->GetOutput(i)->GetName()); if (currentTool.IsNull()) { this->m_ErrorMessage = "Error: did not find correspondig tool in tracking device after initialization."; return NULL; } returnValue->SetInput(i,trackingDeviceSource->GetOutput(i)); returnValue->SetRepresentationObject(i,currentTool->GetDataNode()->GetData()); } return returnValue; } int mitk::TrackingDeviceSourceConfigurator::GetToolNumberInToolStorage(int outputID) { if (outputID < m_ToolCorrespondencesInToolStorage.size()) return m_ToolCorrespondencesInToolStorage.at(outputID); else return -1; } std::string mitk::TrackingDeviceSourceConfigurator::GetToolIdentifierInToolStorage(int outputID) { if (outputID < m_ToolCorrespondencesInToolStorage.size()) return m_NavigationTools->GetTool(m_ToolCorrespondencesInToolStorage.at(outputID))->GetIdentifier(); else return ""; } std::vector mitk::TrackingDeviceSourceConfigurator::GetToolNumbersInToolStorage() { return m_ToolCorrespondencesInToolStorage; } std::vector mitk::TrackingDeviceSourceConfigurator::GetToolIdentifiersInToolStorage() { std::vector returnValue = std::vector(); for (int i=0; iGetTool(m_ToolCorrespondencesInToolStorage.at(i))->GetIdentifier());} return returnValue; } \ No newline at end of file diff --git a/Modules/IGT/IGTFilters/mitkTrackingVolumeGenerator.cpp b/Modules/IGT/IGTFilters/mitkTrackingVolumeGenerator.cpp index 06b3376e5d..1c53090768 100644 --- a/Modules/IGT/IGTFilters/mitkTrackingVolumeGenerator.cpp +++ b/Modules/IGT/IGTFilters/mitkTrackingVolumeGenerator.cpp @@ -1,105 +1,106 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2011-01-18 13:22:38 +0100 (Di, 18 Jan 2011) $ Version: $Revision: 28959 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkTrackingVolumeGenerator.h" #include "mitkSTLFileReader.h" #include "mitkStandardFileLocations.h" #include "mitkConfig.h" #include #include #include #include #include #include mitk::TrackingVolumeGenerator::TrackingVolumeGenerator() { std::string volumeDir = MITK_ROOT; volumeDir += "Modules/IGT/IGTTrackingDevices/TrackingVolumeData"; //folder which contains the trackingdevices configs mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( volumeDir.c_str(), false ); //add this directory to StdFileLocations for the search m_TrackingDeviceType = mitk::TrackingSystemNotSpecified; } void mitk::TrackingVolumeGenerator::SetTrackingDevice (mitk::TrackingDevice::Pointer tracker) { this->m_TrackingDeviceType = tracker->GetType(); } void mitk::TrackingVolumeGenerator::GenerateData() { mitk::Surface::Pointer output = this->GetOutput();//the surface wich represents the tracking volume std::string filename = ""; switch(m_TrackingDeviceType) { case mitk::ClaronMicron: filename = mitk::StandardFileLocations::GetInstance()->FindFile("ClaronMicron.stl"); break; case mitk::IntuitiveDaVinci: filename = mitk::StandardFileLocations::GetInstance()->FindFile("IntuitiveDaVinci.stl"); break; case mitk::NDIPolaris: filename = mitk::StandardFileLocations::GetInstance()->FindFile("NDIPolaris.stl"); break; case mitk::NDIAurora: filename = mitk::StandardFileLocations::GetInstance()->FindFile("NDIAurora.stl"); break; case mitk::TrackingSystemNotSpecified: case mitk::VirtualTracker: { vtkSmartPointer cubeSource = vtkSmartPointer::New(); double bounds[6]; bounds[0] = bounds[2] = bounds[4] = -400.0; // initialize bounds to -400 ... +400 cube. This is the default value of the bounds[1] = bounds[3] = bounds[5] = 400.0; // virtual tracking device, but it can be changed. In that case, // the tracking volume polydata has be updated manually cubeSource->SetBounds(bounds); cubeSource->GetOutput()->Update(); output->SetVtkPolyData(cubeSource->GetOutput()); //set the vtkCubeSource as polyData of the surface return; } default: { + output->SetVtkPolyData(vtkPolyData::New()); //initialize with empty poly data (otherwise old surfaces may be returned) => so an empty surface is returned MITK_ERROR<< "No STL to given TrackingDevice found"; return; } } if (filename.empty()) { MITK_ERROR << "Filename is empty"; return; } mitk::STLFileReader::Pointer stlReader = mitk::STLFileReader::New(); stlReader->SetFileName( filename.c_str() ); stlReader->Update(); if ( stlReader->GetOutput() == NULL) { MITK_ERROR << "Error while reading file"; return ; } output->SetVtkPolyData( stlReader->GetOutput()->GetVtkPolyData());//set the visible trackingvolume } diff --git a/Modules/IGT/Testing/files.cmake b/Modules/IGT/Testing/files.cmake index 5495c8a60c..25f0f80f39 100644 --- a/Modules/IGT/Testing/files.cmake +++ b/Modules/IGT/Testing/files.cmake @@ -1,45 +1,46 @@ SET(MODULE_TESTS mitkCameraVisualizationTest.cpp mitkClaronInterfaceTest.cpp mitkClaronToolTest.cpp mitkClaronTrackingDeviceTest.cpp mitkInternalTrackingToolTest.cpp mitkNavigationDataDisplacementFilterTest.cpp mitkNavigationDataLandmarkTransformFilterTest.cpp mitkNavigationDataObjectVisualizationFilterTest.cpp mitkNavigationDataTest.cpp mitkNavigationDataRecorderTest.cpp mitkNavigationDataReferenceTransformFilterTest.cpp mitkNavigationDataSequentialPlayerTest.cpp mitkNavigationDataToMessageFilterTest.cpp mitkNavigationDataToNavigationDataFilterTest.cpp mitkNavigationDataToPointSetFilterTest.cpp mitkNavigationDataTransformFilterTest.cpp mitkNDIPassiveToolTest.cpp mitkNDIProtocolTest.cpp mitkNDITrackingDeviceTest.cpp mitkTimeStampTest.cpp mitkTrackingVolumeGeneratorTest.cpp mitkTrackingDeviceTest.cpp mitkTrackingToolTest.cpp mitkVirtualTrackingDeviceTest.cpp mitkNavigationDataPlayerTest.cpp mitkTrackingDeviceSourceTest.cpp + mitkTrackingDeviceSourceConfiguratorTest.cpp # ------------------ Navigation Tool Management Tests ------------------- mitkNavigationToolStorageTest.cpp mitkNavigationToolStorageSerializerAndDeserializerTest.cpp mitkNavigationToolTest.cpp mitkNavigationToolReaderAndWriterTest.cpp #deactivated, see bug #3461 # ----------------------------------------------------------------------- # ------------------ Deavtivated Tests ---------------------------------- - + # ----------------------------------------------------------------------- ) SET(MODULE_CUSTOM_TESTS mitkNDIAuroraHardwareTest.cpp mitkNDIPolarisHardwareTest.cpp mitkClaronTrackingDeviceHardwareTest.cpp ) \ No newline at end of file diff --git a/Modules/IGT/Testing/mitkTrackingDeviceSourceConfiguratorTest.cpp b/Modules/IGT/Testing/mitkTrackingDeviceSourceConfiguratorTest.cpp new file mode 100644 index 0000000000..4c4c320aba --- /dev/null +++ b/Modules/IGT/Testing/mitkTrackingDeviceSourceConfiguratorTest.cpp @@ -0,0 +1,187 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date: 2008-02-25 17:27:17 +0100 (Mo, 25 Feb 2008) $ +Version: $Revision: 7837 $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include "mitkTrackingDeviceSourceConfigurator.h" +#include +#include +#include +#include +#include + +class mitkTrackingDeviceSourceConfiguratorTestClass +{ +public: + + static void TestInstantiation() + { + // let's create an object of our class + mitk::TrackingDeviceSourceConfigurator::Pointer testInstance; + mitk::NavigationToolStorage::Pointer emptyStorage = mitk::NavigationToolStorage::New(); + mitk::TrackingDevice::Pointer dummyDevice = dynamic_cast(mitk::ClaronTrackingDevice::New().GetPointer()); + testInstance = mitk::TrackingDeviceSourceConfigurator::New(emptyStorage,dummyDevice); + MITK_TEST_CONDITION_REQUIRED(testInstance.IsNotNull(),"Testing instantiation:"); + } + + static void TestInvalidClaronTrackingDevice() + { + MITK_TEST_OUTPUT(<<"Testing simple claron tracking device with 2 invalid tools"); + + mitk::TrackingDeviceSourceConfigurator::Pointer testInstance; + + mitk::NavigationToolStorage::Pointer claronStorage = mitk::NavigationToolStorage::New(); + + //create invalid tool 1 + mitk::NavigationTool::Pointer firstTool = mitk::NavigationTool::New(); + firstTool->SetTrackingDeviceType(mitk::ClaronMicron); + mitk::DataNode::Pointer firstNode = mitk::DataNode::New(); + firstNode->SetName("Tool1"); + firstTool->SetDataNode(firstNode); + claronStorage->AddTool(firstTool); + + //create invalid tool 2 + mitk::NavigationTool::Pointer secondTool = mitk::NavigationTool::New(); + secondTool->SetTrackingDeviceType(mitk::ClaronMicron); + mitk::DataNode::Pointer secondNode = mitk::DataNode::New(); + secondNode->SetName("Tool2"); + secondTool->SetDataNode(secondNode); + claronStorage->AddTool(secondTool); + + mitk::TrackingDevice::Pointer testDevice = dynamic_cast(mitk::ClaronTrackingDevice::New().GetPointer()); + + testInstance = mitk::TrackingDeviceSourceConfigurator::New(claronStorage,testDevice); + + mitk::TrackingDeviceSource::Pointer testSource = testInstance->CreateTrackingDeviceSource(); + + MITK_TEST_CONDITION_REQUIRED(testSource.IsNull(),"..testing return value"); + MITK_TEST_CONDITION_REQUIRED(testInstance->GetErrorMessage().size()>1,"..testing if there is an error message"); + + MITK_TEST_OUTPUT(<<"Testing simple claron tracking device with another 2 invalid tools"); + + secondTool->SetTrackingDeviceType(mitk::NDIAurora); + claronStorage = mitk::NavigationToolStorage::New(); + claronStorage->AddTool(secondTool); + + testInstance = mitk::TrackingDeviceSourceConfigurator::New(claronStorage,testDevice); + + MITK_TEST_CONDITION_REQUIRED(!testInstance->IsCreateTrackingDeviceSourcePossible(),"..testing if factory class detects the invalid data"); + + testSource = testInstance->CreateTrackingDeviceSource(); + + MITK_TEST_CONDITION_REQUIRED(testSource.IsNull(),"..testing return value"); + MITK_TEST_CONDITION_REQUIRED(testInstance->GetErrorMessage().size()>1,"..testing if there is an error message"); + MITK_TEST_CONDITION_REQUIRED(testInstance->GetUpdatedNavigationToolStorage()->GetToolCount()==1,"..testing if navigation tool storage is still there"); + + MITK_TEST_OUTPUT(<<"Testing other invalid test cases"); + testInstance = mitk::TrackingDeviceSourceConfigurator::New(claronStorage,NULL); + MITK_TEST_CONDITION_REQUIRED(!testInstance->IsCreateTrackingDeviceSourcePossible(),"..(1) testing if factory class detects the invalid data"); + testInstance = mitk::TrackingDeviceSourceConfigurator::New(NULL,testDevice); + MITK_TEST_CONDITION_REQUIRED(!testInstance->IsCreateTrackingDeviceSourcePossible(),"..(2) testing if factory class detects the invalid data"); + + + + + } + static void TestValidClaronTrackingDevice() + { + MITK_TEST_OUTPUT(<<"Testing simple claron tracking device with 2 valid tools"); + + std::string toolFileName = mitk::StandardFileLocations::GetInstance()->FindFile("ClaronTool", "Modules/IGT/Testing/Data"); + MITK_TEST_CONDITION(toolFileName.empty() == false, "Check if tool calibration of claron tool file exists"); + + mitk::TrackingDeviceSourceConfigurator::Pointer testInstance; + + mitk::NavigationToolStorage::Pointer claronStorage = mitk::NavigationToolStorage::New(); + + //create valid tool 1 + mitk::NavigationTool::Pointer firstTool = mitk::NavigationTool::New(); + firstTool->SetTrackingDeviceType(mitk::ClaronMicron); + mitk::DataNode::Pointer firstNode = mitk::DataNode::New(); + firstNode->SetName("Tool1"); + firstTool->SetDataNode(firstNode); + firstTool->SetCalibrationFile(toolFileName); + firstTool->SetIdentifier("Tool#1"); + claronStorage->AddTool(firstTool); + + //create valid tool 2 + mitk::NavigationTool::Pointer secondTool = mitk::NavigationTool::New(); + secondTool->SetTrackingDeviceType(mitk::ClaronMicron); + mitk::DataNode::Pointer secondNode = mitk::DataNode::New(); + secondNode->SetName("Tool2"); + secondTool->SetDataNode(secondNode); + secondTool->SetCalibrationFile(toolFileName); + secondTool->SetIdentifier("Tool#2"); + claronStorage->AddTool(secondTool); + + mitk::TrackingDevice::Pointer testDevice = dynamic_cast(mitk::ClaronTrackingDevice::New().GetPointer()); + + testInstance = mitk::TrackingDeviceSourceConfigurator::New(claronStorage,testDevice); + + mitk::TrackingDeviceSource::Pointer testSource = testInstance->CreateTrackingDeviceSource(); + + MITK_TEST_CONDITION_REQUIRED(testSource->GetNumberOfOutputs()==2,"testing number of outputs"); + MITK_TEST_CONDITION_REQUIRED(testSource->GetTrackingDevice()->GetToolCount()==2,"testing tracking device"); + } + + static void TestNDIAuroraTrackingDevice() + { + + } + + static void TestNDIPolarisTrackingDevice() + { + MITK_TEST_OUTPUT(<<"Testing simple NDI Polaris tracking device with 1 valid tool"); + + std::string toolFileName = mitk::StandardFileLocations::GetInstance()->FindFile("SROMFile.rom", "Modules/IGT/Testing/Data"); + MITK_TEST_CONDITION(toolFileName.empty() == false, "..check if tool calibration of claron tool file exists"); + + mitk::TrackingDeviceSourceConfigurator::Pointer testInstance; + + mitk::NavigationToolStorage::Pointer polarisStorage = mitk::NavigationToolStorage::New(); + + //create valid tool 1 + mitk::NavigationTool::Pointer firstTool = mitk::NavigationTool::New(); + firstTool->SetTrackingDeviceType(mitk::NDIPolaris); + mitk::DataNode::Pointer firstNode = mitk::DataNode::New(); + firstNode->SetName("Tool1"); + firstTool->SetDataNode(firstNode); + firstTool->SetCalibrationFile(toolFileName); + firstTool->SetIdentifier("Tool#1"); + polarisStorage->AddTool(firstTool); + + mitk::NDITrackingDevice::Pointer ndiDevice = mitk::NDITrackingDevice::New(); + ndiDevice->SetType(mitk::NDIPolaris); + mitk::TrackingDevice::Pointer testDevice = dynamic_cast(ndiDevice.GetPointer()); + + testInstance = mitk::TrackingDeviceSourceConfigurator::New(polarisStorage,testDevice); + + mitk::TrackingDeviceSource::Pointer testSource = testInstance->CreateTrackingDeviceSource(); + + MITK_TEST_CONDITION_REQUIRED(testSource->GetNumberOfOutputs()==1,"..testing number of outputs"); + MITK_TEST_CONDITION_REQUIRED(testSource->GetTrackingDevice()->GetToolCount()==1,"..testing tracking device"); + } +}; + +int mitkTrackingDeviceSourceConfiguratorTest(int /* argc */, char* /*argv*/[]) +{ + MITK_TEST_BEGIN("TrackingDeviceConfigurator"); + + mitkTrackingDeviceSourceConfiguratorTestClass::TestInstantiation(); + mitkTrackingDeviceSourceConfiguratorTestClass::TestInvalidClaronTrackingDevice(); + mitkTrackingDeviceSourceConfiguratorTestClass::TestValidClaronTrackingDevice(); + + MITK_TEST_END(); +} \ No newline at end of file diff --git a/Modules/IGT/Testing/mitkTrackingVolumeGeneratorTest.cpp b/Modules/IGT/Testing/mitkTrackingVolumeGeneratorTest.cpp index fb7b67ca43..4d33373fb0 100644 --- a/Modules/IGT/Testing/mitkTrackingVolumeGeneratorTest.cpp +++ b/Modules/IGT/Testing/mitkTrackingVolumeGeneratorTest.cpp @@ -1,119 +1,142 @@ /*==================================================================== Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include +#include #include "mitkCommon.h" #include "mitkTestingMacros.h" #include "vtkPolyData.h" #include #include class mitkTrackingVolumeGeneratorTestClass { public: static void TestInstantiation() { // let's create an object of our class mitk::TrackingVolumeGenerator::Pointer myTVGenerator = mitk::TrackingVolumeGenerator::New(); MITK_TEST_CONDITION_REQUIRED(myTVGenerator.IsNotNull(),"Testing instantiation"); } static void TestTrackingSystemNotSpecified() { MITK_TEST_OUTPUT(<<"---- Testing Trackingsystem not specified ----"); mitk::TrackingVolumeGenerator::Pointer myTVGenerator = mitk::TrackingVolumeGenerator::New(); MITK_TEST_CONDITION((myTVGenerator->GetTrackingDeviceType() == mitk::TrackingSystemNotSpecified), "Tracking System not specified:"); myTVGenerator->Update(); mitk::Surface::Pointer volume = myTVGenerator->GetOutput(); MITK_TEST_CONDITION((volume->IsEmpty(0) == false),"Output contains data"); } static void TestClaronTrackingVolume() { MITK_TEST_OUTPUT(<< "---- Testing MicronTracker 2 Tracking Volume ----"); mitk::TrackingVolumeGenerator::Pointer myTVGenerator = mitk::TrackingVolumeGenerator::New (); myTVGenerator->SetTrackingDeviceType(mitk::ClaronMicron); MITK_TEST_CONDITION((myTVGenerator->GetTrackingDeviceType() == mitk::ClaronMicron),"loading MicronTracker Volume data:"); myTVGenerator->Update(); mitk::Surface::Pointer volume = myTVGenerator->GetOutput(); MITK_TEST_CONDITION((volume->IsEmpty(0) == false),"Output contains data"); } static void TestNDIAuroraTrackingVolume() { MITK_TEST_OUTPUT(<< "---- Testing NDI Aurora Tracking Volume ----"); mitk::TrackingVolumeGenerator::Pointer myTVGenerator = mitk::TrackingVolumeGenerator::New (); myTVGenerator->SetTrackingDeviceType(mitk::NDIAurora); MITK_TEST_CONDITION((myTVGenerator->GetTrackingDeviceType() == mitk::NDIAurora),"loading Aurora Volume data:"); myTVGenerator->Update(); mitk::Surface::Pointer volume = myTVGenerator->GetOutput(); MITK_TEST_CONDITION((volume->IsEmpty(0) == false),"Output contains data"); } static void TestNDIPolarisTrackingVolume() { MITK_TEST_OUTPUT(<< "---- Testing NDI Polaris Tracking Volume ----"); mitk::TrackingVolumeGenerator::Pointer myTVGenerator = mitk::TrackingVolumeGenerator::New (); myTVGenerator->SetTrackingDeviceType(mitk::NDIPolaris); MITK_TEST_CONDITION((myTVGenerator->GetTrackingDeviceType() == mitk::NDIPolaris),"loading Polaris Volume data:"); myTVGenerator->Update(); mitk::Surface::Pointer volume = myTVGenerator->GetOutput(); MITK_TEST_CONDITION((volume->IsEmpty(0) == false),"Output contains data"); } static void TestIntuitiveDaVinciTrackingVolume() { MITK_TEST_OUTPUT(<< "---- Testing Intuitive Da Vinci Tracking Volume ----"); mitk::TrackingVolumeGenerator::Pointer myTVGenerator = mitk::TrackingVolumeGenerator::New (); myTVGenerator->SetTrackingDeviceType(mitk::IntuitiveDaVinci); MITK_TEST_CONDITION((myTVGenerator->GetTrackingDeviceType() == mitk::IntuitiveDaVinci),"loading Da Vinci Volume data:"); myTVGenerator->Update(); mitk::Surface::Pointer volume = myTVGenerator->GetOutput(); MITK_TEST_CONDITION((volume->IsEmpty(0) == false),"Output contains data"); } + static void TestInvalidInputBehaviour() + { + MITK_TEST_OUTPUT(<< "---- Testing Invalid Inputs (errors should occure) ----"); + mitk::TrackingVolumeGenerator::Pointer myTVGenerator = mitk::TrackingVolumeGenerator::New (); + myTVGenerator->SetTrackingDeviceType(mitk::AscensionMicroBird); //MicroBird not implemented yet, so using as test dummy + MITK_TEST_CONDITION((myTVGenerator->GetTrackingDeviceType() == mitk::AscensionMicroBird),"testing device type"); + myTVGenerator->Update(); + MITK_TEST_CONDITION(myTVGenerator->GetOutput()->GetVtkPolyData()->GetNumberOfVerts()==0,"testing (invalid) output"); + } + + static void TestSetTrackingDevice() + { + MITK_TEST_OUTPUT(<< "---- Testing method SetTrackingDevice() ----"); + mitk::ClaronTrackingDevice::Pointer testTrackingDevice = mitk::ClaronTrackingDevice::New(); + mitk::TrackingVolumeGenerator::Pointer myTVGenerator = mitk::TrackingVolumeGenerator::New (); + myTVGenerator->SetTrackingDevice(dynamic_cast(testTrackingDevice.GetPointer())); + MITK_TEST_CONDITION((myTVGenerator->GetTrackingDeviceType() == mitk::ClaronMicron),"testing SetTrackingDevice()"); + + } + /* The isInside() method is not implemented so far. So please activate is as soon as this is done. Then we could load * the different Trackingvolumens (Polaris, MicronTracker, etc) and test different points inside and outside in this method. static void TestIsInside() { MITK_TEST_OUTPUT(<< "---- Testing IsInside-Method ----") mitk::TrackingVolume::Pointer myTrackingVolume = mitk::TrackingVolume::New(); mitk::Point3D p1; mitk::FillVector3D(p1,(float)0,(float)0,(float)0); MITK_TEST_CONDITION(myTrackingVolume->IsInside(p1)==false,"... successfull") } */ }; /** This function is testing the TrackingVolume class. */ int mitkTrackingVolumeGeneratorTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("TrackingVolumeGenerator"); mitkTrackingVolumeGeneratorTestClass::TestInstantiation(); mitkTrackingVolumeGeneratorTestClass::TestTrackingSystemNotSpecified (); mitkTrackingVolumeGeneratorTestClass::TestClaronTrackingVolume(); mitkTrackingVolumeGeneratorTestClass::TestNDIAuroraTrackingVolume(); mitkTrackingVolumeGeneratorTestClass::TestNDIPolarisTrackingVolume(); mitkTrackingVolumeGeneratorTestClass::TestIntuitiveDaVinciTrackingVolume(); + mitkTrackingVolumeGeneratorTestClass::TestInvalidInputBehaviour(); + mitkTrackingVolumeGeneratorTestClass::TestSetTrackingDevice(); //mitkTrackingVolumeTestClass::TestIsInside(); Activate this code when method isInside() is implemented! MITK_TEST_END() ; } diff --git a/Modules/ToFProcessing/CMakeLists.txt b/Modules/ToFProcessing/CMakeLists.txt index bc2090729c..d1df90e238 100644 --- a/Modules/ToFProcessing/CMakeLists.txt +++ b/Modules/ToFProcessing/CMakeLists.txt @@ -1,13 +1,13 @@ MITK_CREATE_MODULE(mitkToFProcessing SUBPROJECTS MITK-ToF - DEPENDS Mitk MitkExt + DEPENDS Mitk MitkExt mitkCameraCalibration PACKAGE_DEPENDS OpenCV PROVIDES mitkToFProcessing ) IF(BUILD_TESTING) ADD_SUBDIRECTORY(Testing) ENDIF(BUILD_TESTING) diff --git a/Modules/ToFProcessing/Testing/mitkToFDistanceImageToPointSetFilterTest.cpp b/Modules/ToFProcessing/Testing/mitkToFDistanceImageToPointSetFilterTest.cpp index 6225b3652b..ec880a2ed3 100644 --- a/Modules/ToFProcessing/Testing/mitkToFDistanceImageToPointSetFilterTest.cpp +++ b/Modules/ToFProcessing/Testing/mitkToFDistanceImageToPointSetFilterTest.cpp @@ -1,248 +1,231 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: $ Version: $Revision: $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include #include #include #include #include #include -#include #include #include /**Documentation * test for the class "ToFDistanceImageToPointSetFilter". */ mitk::PointSet::Pointer CreateTestPointSet() { mitk::PointSet::Pointer subSet = mitk::PointSet::New(); mitk::Point3D point; point[0] = 10; point[1] = 20; point[2] = 0; subSet->InsertPoint(0,point); point[0] = 100; point[1] = 150; point[2] = 0; subSet->InsertPoint(1,point); point[0] = 110; point[1] = 30; point[2] = 0; subSet->InsertPoint(2,point); point[0] = 40; point[1] = 200; point[2] = 0; subSet->InsertPoint(3,point); return subSet; } inline static mitk::Image::Pointer CreateTestImageWithPointSet(mitk::ScalarType pixelValue, unsigned int dimX, unsigned int dimY, mitk::PointSet::Pointer subSet) { typedef itk::Image ItkImageType2D; typedef itk::ImageRegionIterator ItkImageRegionIteratorType2D; ItkImageType2D::Pointer image = ItkImageType2D::New(); ItkImageType2D::IndexType start; start[0] = 0; start[1] = 0; ItkImageType2D::SizeType size; size[0] = dimX; size[1] = dimY; ItkImageType2D::RegionType region; region.SetSize(size); region.SetIndex( start); ItkImageType2D::SpacingType spacing; spacing[0] = 1.0; spacing[1] = 1.0; image->SetRegions( region ); image->SetSpacing ( spacing ); image->Allocate(); //Obtaining image data from ToF camera// //Correlate inten values to PixelIndex// ItkImageRegionIteratorType2D imageIterator(image,image->GetLargestPossibleRegion()); imageIterator.GoToBegin(); while (!imageIterator.IsAtEnd()) { imageIterator.Set(pixelValue); ++imageIterator; } // distances varying from pixelValue std::vector distances; distances.push_back(50); distances.push_back(500); distances.push_back(2050); distances.push_back(300); // set the pixel values for the subset for (unsigned int i=0; iGetSize(); i++) { mitk::Point3D point = subSet->GetPoint(i); ItkImageType2D::IndexType index; index[0] = point[0]; index[1] = point[1]; mitk::ScalarType distance = distances.at(i); image->SetPixel(index,distance); } mitk::Image::Pointer mitkImage = mitk::Image::New(); mitk::CastToMitkImage(image,mitkImage); return mitkImage; } int mitkToFDistanceImageToPointSetFilterTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("ToFDistanceImageToPointSetFilter"); mitk::ToFDistanceImageToPointSetFilter::Pointer filter = mitk::ToFDistanceImageToPointSetFilter::New(); //create test sub set MITK_INFO<<"Create test pointset"; mitk::PointSet::Pointer subSet = CreateTestPointSet(); //create test image unsigned int dimX = 204; unsigned int dimY = 204; MITK_INFO<<"Create test image"; mitk::Image::Pointer image = CreateTestImageWithPointSet(1000.0f,dimX,dimY,subSet); //initialize intrinsic parameters - mitk::ScalarType focalLength = 10; - mitk::Point2D interPixelDistance; - interPixelDistance[0] = 0.05; - interPixelDistance[1] = 0.05; - mitk::Point2D principalPoint; - principalPoint[0] = 100; - principalPoint[1] = 100; - - mitk::PinholeCameraModel::Pointer camera = mitk::PinholeCameraModel::New(); - camera->SetFocalLength(focalLength); - camera->SetPrincipalPoint(principalPoint); - camera->SetInterPixelDistance(interPixelDistance); - - - // test setter/getter of intrinsic parameters - filter->SetCameraModel(camera); - MITK_TEST_CONDITION_REQUIRED((focalLength==filter->GetCameraModel()->GetFocalLength()),"Testing Set/GetFocalLength()"); -// filter->SetInterPixelDistance(interPixelDistance); - MITK_TEST_CONDITION_REQUIRED((interPixelDistance==filter->GetCameraModel()->GetInterPixelDistance()),"Testing Set/GetInterPixelDistance()"); -// filter->SetPrincipalPoint(principalPoint); - mitk::Point2D pp = filter->GetCameraModel()->GetPrincipalPoint(); - MITK_TEST_CONDITION_REQUIRED(mitk::Equal(principalPoint,pp),"Testing Set/GetPrincipalPoint()"); - // test SetIntrinsicParameters() - mitk::PinholeCameraModel::Pointer cameraIntrinics = mitk::PinholeCameraModel::New(); - mitk::Point2D imageArea; - imageArea[0] = dimX*interPixelDistance[0]; - imageArea[1] = dimY*interPixelDistance[1]; - cameraIntrinics->SetImageArea(imageArea); - cameraIntrinics->SetFocalLength(10); - mitk::Point2D pixelSize; - pixelSize[0] = interPixelDistance[0]; - pixelSize[1] = interPixelDistance[1]; - cameraIntrinics->SetInterPixelDistance(pixelSize); - mitk::Point2D imageShift; - imageShift[0] = (principalPoint[0] - dimX/2)*interPixelDistance[0]; - imageShift[1] = (principalPoint[1] - dimX/2)*interPixelDistance[1]; - cameraIntrinics->SetImageShift(imageShift); - filter->SetCameraModel(cameraIntrinics); - MITK_TEST_CONDITION_REQUIRED((focalLength==filter->GetCameraModel()->GetFocalLength()),"Testing SetIntrinsicParameters() - focal length"); - MITK_TEST_CONDITION_REQUIRED((interPixelDistance==filter->GetCameraModel()->GetInterPixelDistance()),"Testing SetIntrinsicParameters() - inter pixel distance"); - pp = camera->GetPrincipalPoint(); - MITK_TEST_CONDITION_REQUIRED(mitk::Equal(principalPoint,pp),"Testing SetIntrinsicParameters() - principal point"); + //initialize intrinsic parameters with some arbitrary values + mitk::ToFProcessingCommon::ToFPoint2D interPixelDistance; + interPixelDistance[0] = 0.04564; + interPixelDistance[1] = 0.0451564; + mitk::ToFProcessingCommon::ToFScalarType focalLengthX = 295.78960; + mitk::ToFProcessingCommon::ToFScalarType focalLengthY = 296.348535; + mitk::ToFProcessingCommon::ToFScalarType focalLength = (focalLengthX*interPixelDistance[0]+focalLengthY*interPixelDistance[1])/2.0; + mitk::ToFProcessingCommon::ToFScalarType k1=-0.36,k2=-0.14,p1=0.001,p2=-0.00; + mitk::ToFProcessingCommon::ToFPoint2D principalPoint; + principalPoint[0] = 103.576546; + principalPoint[1] = 100.1532; + mitk::CameraIntrinsics::Pointer cameraIntrinsics = mitk::CameraIntrinsics::New(); + cameraIntrinsics->SetFocalLength(focalLengthX,focalLengthY); + cameraIntrinsics->SetPrincipalPoint(principalPoint[0],principalPoint[1]); + cameraIntrinsics->SetDistorsionCoeffs(k1,k2,p1,p2); + // test SetCameraIntrinsics() + filter->SetCameraIntrinsics(cameraIntrinsics); + MITK_TEST_CONDITION_REQUIRED((focalLengthX==filter->GetCameraIntrinsics()->GetFocalLengthX()),"Testing SetCameraIntrinsics with focalLength"); + mitk::ToFProcessingCommon::ToFPoint2D pp; + pp[0] = filter->GetCameraIntrinsics()->GetPrincipalPointX(); + pp[1] = filter->GetCameraIntrinsics()->GetPrincipalPointY(); + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(principalPoint,pp),"Testing SetCameraIntrinsics with principalPoint()"); + // test SetInterPixelDistance() + + filter->SetInterPixelDistance(interPixelDistance); + mitk::ToFProcessingCommon::ToFPoint2D ipD = filter->GetInterPixelDistance(); + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(ipD,interPixelDistance),"Testing Set/GetInterPixelDistance()"); // test Set/GetInput() filter->SetInput(image); MITK_TEST_CONDITION_REQUIRED((image==filter->GetInput()),"Testing Set/GetInput()"); // test filter without subset MITK_INFO<<"Test filter without subset"; mitk::PointSet::Pointer expectedResult = mitk::PointSet::New(); unsigned int counter = 0; for (unsigned int i=0; iGetPixelValueByIndex(index); - mitk::Point3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,cameraIntrinics->GetFocalLength(),cameraIntrinics->GetInterPixelDistance(), - cameraIntrinics->GetPrincipalPoint()); + mitk::Point3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLength,interPixelDistance,principalPoint); expectedResult->InsertPoint(counter,coordinate); counter++; } } filter->Update(); mitk::PointSet::Pointer result = filter->GetOutput(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetSize()==result->GetSize()),"Test if point set size is equal"); bool pointSetsEqual = true; for (unsigned int i=0; iGetSize(); i++) { mitk::Point3D expectedPoint = expectedResult->GetPoint(i); mitk::Point3D resultPoint = result->GetPoint(i); if (!mitk::Equal(expectedPoint,resultPoint)) { - MITK_INFO << "erwartet " << expectedPoint; - MITK_INFO << "result " << resultPoint; + //MITK_INFO << "erwartet " << expectedPoint; + //MITK_INFO << "result " << resultPoint; pointSetsEqual = false; } } MITK_TEST_CONDITION_REQUIRED(pointSetsEqual,"Testing filter without subset"); // test filter with subset MITK_INFO<<"Test filter with subset"; filter = mitk::ToFDistanceImageToPointSetFilter::New(); filter->SetInput(image); - filter->SetCameraModel(cameraIntrinics); + filter->SetInterPixelDistance(interPixelDistance); + filter->SetCameraIntrinsics(cameraIntrinsics); expectedResult = mitk::PointSet::New(); counter = 0; for (unsigned int i=0; iGetSize(); i++) { mitk::Point3D point = subSet->GetPoint(i); mitk::Index3D index; index[0] = point[0]; index[1] = point[1]; index[2] = 0; mitk::ScalarType distance = image->GetPixelValueByIndex(index); mitk::Point3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(point[0],point[1], - distance,cameraIntrinics->GetFocalLength(),cameraIntrinics->GetInterPixelDistance(), - cameraIntrinics->GetPrincipalPoint()); + distance,focalLength,interPixelDistance,principalPoint); expectedResult->InsertPoint(counter,coordinate); counter++; } filter->SetSubset(subSet); filter->Modified(); filter->Update(); result = filter->GetOutput(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetSize()==result->GetSize()),"Test if point set size is equal"); pointSetsEqual = true; for (unsigned int i=0; iGetSize(); i++) { mitk::Point3D expectedPoint = expectedResult->GetPoint(i); mitk::Point3D resultPoint = result->GetPoint(i); if (!mitk::Equal(expectedPoint,resultPoint)) { pointSetsEqual = false; } } MITK_TEST_CONDITION_REQUIRED(pointSetsEqual,"Testing filter without subset"); MITK_TEST_END(); } diff --git a/Modules/ToFProcessing/Testing/mitkToFDistanceImageToSurfaceFilterTest.cpp b/Modules/ToFProcessing/Testing/mitkToFDistanceImageToSurfaceFilterTest.cpp index 0c2e118ce9..7a1a4845c2 100644 --- a/Modules/ToFProcessing/Testing/mitkToFDistanceImageToSurfaceFilterTest.cpp +++ b/Modules/ToFProcessing/Testing/mitkToFDistanceImageToSurfaceFilterTest.cpp @@ -1,246 +1,221 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: $ Version: $Revision: $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include #include //#include #include #include #include #include #include //#include //#include //#include #include #include #include /**Documentation * test for the class "ToFDistanceImageToSurfaceFilter". */ typedef mitk::ToFProcessingCommon::ToFPoint2D ToFPoint2D; typedef mitk::ToFProcessingCommon::ToFPoint3D ToFPoint3D; typedef mitk::ToFProcessingCommon::ToFScalarType ToFScalarType; int mitkToFDistanceImageToSurfaceFilterTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("ToFDistanceImageToSurfaceFilter"); mitk::ToFDistanceImageToSurfaceFilter::Pointer filter = mitk::ToFDistanceImageToSurfaceFilter::New(); // create test image unsigned int dimX =204; unsigned int dimY =204; mitk::Image::Pointer image = mitk::ToFTestingCommon::CreateTestImage(dimX,dimY); //initialize intrinsic parameters with some arbitrary values - ToFScalarType focalLength = 13.654368; - ToFPoint2D interPixelDistance; - interPixelDistance[0] = 0.04564; - interPixelDistance[1] = 0.0451564; + ToFScalarType focalLengthX = 295.78960; + ToFScalarType focalLengthY = 296.348535; + ToFScalarType k1=-0.36,k2=-0.14,p1=0.001,p2=-0.00; ToFPoint2D principalPoint; principalPoint[0] = 103.576546; principalPoint[1] = 100.1532; - - mitk::PinholeCameraModel::Pointer camera = mitk::PinholeCameraModel::New(); - camera->SetFocalLength(focalLength); - camera->SetPrincipalPoint(principalPoint); - camera->SetInterPixelDistance(interPixelDistance); - - filter->SetCameraModel(camera); - - MITK_TEST_CONDITION_REQUIRED((focalLength==filter->GetCameraModel()->GetFocalLength()),"Testing Set/GetFocalLength()"); - filter->GetCameraModel()->SetInterPixelDistance(interPixelDistance); - MITK_TEST_CONDITION_REQUIRED((interPixelDistance==filter->GetCameraModel()->GetInterPixelDistance()),"Testing Set/GetInterPixelDistance()"); - filter->GetCameraModel()->SetPrincipalPoint(principalPoint); - ToFPoint2D pp = filter->GetCameraModel()->GetPrincipalPoint(); - MITK_TEST_CONDITION_REQUIRED(mitk::Equal(principalPoint,pp),"Testing Set/GetPrincipalPoint()"); - // test SetIntrinsicParameters() - mitk::PinholeCameraModel::Pointer cameraIntrinics = mitk::PinholeCameraModel::New(); - ToFPoint2D imageArea; - imageArea[0] = dimX*interPixelDistance[0]; - imageArea[1] = dimY*interPixelDistance[1]; - cameraIntrinics->SetImageArea(imageArea); - cameraIntrinics->SetFocalLength(focalLength); - ToFPoint2D pixelSize; - pixelSize[0] = interPixelDistance[0]; - pixelSize[1] = interPixelDistance[1]; - cameraIntrinics->SetInterPixelDistance(pixelSize); - ToFPoint2D imageShift; - imageShift[0] = (principalPoint[0] - dimX/2)*interPixelDistance[0]; - imageShift[1] = (principalPoint[1] - dimX/2)*interPixelDistance[1]; - cameraIntrinics->SetImageShift(imageShift); - cameraIntrinics->SetPrincipalPoint(principalPoint); - filter->SetCameraModel(cameraIntrinics); - MITK_TEST_CONDITION_REQUIRED((focalLength==filter->GetCameraModel()->GetFocalLength()),"Testing SetIntrinsicParameters() - focal length"); - MITK_TEST_CONDITION_REQUIRED((interPixelDistance==filter->GetCameraModel()->GetInterPixelDistance()),"Testing SetIntrinsicParameters() - inter pixel distance"); - pp = filter->GetCameraModel()->GetPrincipalPoint(); - MITK_TEST_CONDITION_REQUIRED(mitk::Equal(principalPoint,pp),"Testing SetIntrinsicParameters() - principal point"); + mitk::CameraIntrinsics::Pointer cameraIntrinsics = mitk::CameraIntrinsics::New(); + cameraIntrinsics->SetFocalLength(focalLengthX,focalLengthY); + cameraIntrinsics->SetPrincipalPoint(principalPoint[0],principalPoint[1]); + cameraIntrinsics->SetDistorsionCoeffs(k1,k2,p1,p2); + // test SetCameraIntrinsics() + filter->SetCameraIntrinsics(cameraIntrinsics); + MITK_TEST_CONDITION_REQUIRED((focalLengthX==filter->GetCameraIntrinsics()->GetFocalLengthX()),"Testing SetCameraIntrinsics with focalLength"); + ToFPoint2D pp; + pp[0] = filter->GetCameraIntrinsics()->GetPrincipalPointX(); + pp[1] = filter->GetCameraIntrinsics()->GetPrincipalPointY(); + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(principalPoint,pp),"Testing SetCameraIntrinsics with principalPoint()"); + // test SetInterPixelDistance() + ToFPoint2D interPixelDistance; + interPixelDistance[0] = 0.04564; + interPixelDistance[1] = 0.0451564; + filter->SetInterPixelDistance(interPixelDistance); + ToFPoint2D ipD = filter->GetInterPixelDistance(); + MITK_TEST_CONDITION_REQUIRED(mitk::Equal(ipD,interPixelDistance),"Testing Set/GetInterPixelDistance()"); // test Set/GetInput() filter->SetInput(image); MITK_TEST_CONDITION_REQUIRED((image==filter->GetInput()),"Testing Set/GetInput()"); // test filter without subset MITK_INFO<<"Test filter "; + // calculate focal length considering inter pixel distance + ToFScalarType focalLength = (focalLengthX*interPixelDistance[0]+focalLengthY*interPixelDistance[1])/2.0; vtkSmartPointer expectedResult = vtkSmartPointer::New(); expectedResult->SetDataTypeToDouble(); unsigned int counter = 0; double* point = new double[3]; // MITK_INFO<<"Test"; // MITK_INFO<<"focal: "<GetPixelValueByIndex(index); ToFPoint3D coordinate = mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLength,interPixelDistance,principalPoint); // if ((i==0)&&(j==0)) // { // MITK_INFO<<"Distance test: "<InsertPoint(pointID,point); } counter++; } } filter->Update(); mitk::Surface::Pointer resultSurface = filter->GetOutput(); vtkSmartPointer result = vtkSmartPointer::New(); result->SetDataTypeToDouble(); result = resultSurface->GetVtkPolyData()->GetPoints(); MITK_TEST_CONDITION_REQUIRED((expectedResult->GetNumberOfPoints()==result->GetNumberOfPoints()),"Test if number of points in surface is equal"); bool pointSetsEqual = true; for (unsigned int i=0; iGetNumberOfPoints(); i++) { double* expected = expectedResult->GetPoint(i); double* res = result->GetPoint(i); ToFPoint3D expectedPoint; expectedPoint[0] = expected[0]; expectedPoint[1] = expected[1]; expectedPoint[2] = expected[2]; ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; if (!mitk::Equal(expectedPoint,resultPoint)) { // MITK_INFO << i; MITK_INFO<<"expected: "<GetNumberOfPoints(); i++) { double* expected = expectedResult->GetPoint(i); double* res = result->GetPoint(i); ToFPoint3D expectedPoint; expectedPoint[0] = expected[0]; expectedPoint[1] = expected[1]; expectedPoint[2] = expected[2]; ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; ToFPoint3D expectedPointBackward = - mitk::ToFProcessingCommon::CartesianToIndexCoordinates(expectedPoint, - filter->GetCameraModel()->GetFocalLength(), - filter->GetCameraModel()->GetInterPixelDistance(), - filter->GetCameraModel()->GetPrincipalPoint()); + mitk::ToFProcessingCommon::CartesianToIndexCoordinates(expectedPoint,focalLength,interPixelDistance,principalPoint); ToFPoint3D resultPointBackward = - mitk::ToFProcessingCommon::CartesianToIndexCoordinates(resultPoint, - filter->GetCameraModel()->GetFocalLength(), - filter->GetCameraModel()->GetInterPixelDistance(), - filter->GetCameraModel()->GetPrincipalPoint()); + mitk::ToFProcessingCommon::CartesianToIndexCoordinates(resultPoint,focalLength,interPixelDistance,principalPoint); if (!mitk::Equal(expectedPointBackward,resultPointBackward)) { // MITK_INFO << i; // MITK_INFO<<"expected: "<GetNumberOfPoints(); i++) { double* res = result->GetPoint(i); ToFPoint3D resultPoint; resultPoint[0] = res[0]; resultPoint[1] = res[1]; resultPoint[2] = res[2]; ToFPoint3D resultPointBackward = - mitk::ToFProcessingCommon::CartesianToIndexCoordinates(resultPoint, - filter->GetCameraModel()->GetFocalLength(), - filter->GetCameraModel()->GetInterPixelDistance(), - filter->GetCameraModel()->GetPrincipalPoint()); + mitk::ToFProcessingCommon::CartesianToIndexCoordinates(resultPoint,focalLength,interPixelDistance,principalPoint); mitk::Index3D pixelIndex; pixelIndex[0] = (int) (resultPointBackward[0]+0.5); pixelIndex[1] = (int) (resultPointBackward[1]+0.5); pixelIndex[2] = 0; if (!mitk::Equal(image->GetPixelValueByIndex(pixelIndex),resultPointBackward[2])) { // MITK_INFO<<"expected: "<< image->GetPixelValueByIndex(pixelIndex); // MITK_INFO<<"result: "<< resultPoint; compareToInput = false; } } MITK_TEST_CONDITION_REQUIRED(compareToInput,"Testing backward transformation compared to original image"); //clean up delete point; // expectedResult->Delete(); MITK_TEST_END(); } diff --git a/Modules/ToFProcessing/files.cmake b/Modules/ToFProcessing/files.cmake index 752c693643..7b47264987 100644 --- a/Modules/ToFProcessing/files.cmake +++ b/Modules/ToFProcessing/files.cmake @@ -1,10 +1,9 @@ SET(CPP_FILES - mitkPinholeCameraModel.cpp mitkToFCompositeFilter.cpp mitkToFDistanceImageToPointSetFilter.cpp mitkToFDistanceImageToSurfaceFilter.cpp mitkToFProcessingCommon.cpp mitkToFSurfaceVtkMapper3D.cpp mitkToFVisualizationFilter.cpp mitkToFTestingCommon.cpp ) diff --git a/Modules/ToFProcessing/mitkPinholeCameraModel.cpp b/Modules/ToFProcessing/mitkPinholeCameraModel.cpp deleted file mode 100644 index f89d9ac843..0000000000 --- a/Modules/ToFProcessing/mitkPinholeCameraModel.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/*========================================================================= -Program: Medical Imaging & Interaction Toolkit -Module: $RCSfile$ -Language: C++ -Date: $Date: 2009-07-17 15:44:24 +0200 (Fr, 17 Jul 2009) $ -Version: $Revision: 18261 $ - -Copyright (c) German Cancer Research Center, Division of Medical and -Biological Informatics. All rights reserved. -See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. - -This software is distributed WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the above copyright notices for more information. - -=========================================================================*/ - -#include "mitkPinholeCameraModel.h" - -mitk::PinholeCameraModel::PinholeCameraModel() - : m_FocalLength(12.8), m_MaxDistance(2000.0), m_PrincipalPoint(), m_CameraToWorldTransform_R(), m_ImageArea(), - m_InterPixelDistance(), m_DistanceImageDimension(2), m_DistanceImageDimensions(), m_CameraToWorldTransform_T(), m_ImageShift() -{ - this->InitializeWithDefaultSettings(); -} - -mitk::PinholeCameraModel::~PinholeCameraModel() -{} - -void mitk::PinholeCameraModel::GetCameraToWorldTransform(itk::Matrix& TransformR,itk::Vector& TransformT) -{ - TransformR = m_CameraToWorldTransform_R; - TransformT = m_CameraToWorldTransform_T; -} - -void mitk::PinholeCameraModel::SetCameraToWorldTransform(itk::Matrix TransformR,itk::Vector TransformT) -{ - m_CameraToWorldTransform_R = TransformR; - m_CameraToWorldTransform_T = TransformT; -} - -void mitk::PinholeCameraModel::CalculateImageArea() -{ - m_ImageArea[0] = m_DistanceImageDimensions[0]*m_InterPixelDistance[0]; - m_ImageArea[1] = m_DistanceImageDimensions[1]*m_InterPixelDistance[1]; -} - -void mitk::PinholeCameraModel::SetDistanceImageDimensions(unsigned int* dimensions) -{ - m_DistanceImageDimensions = dimensions; -} - -void mitk::PinholeCameraModel::CalculateImageDimensions() -{ - m_DistanceImageDimensions[0] = (unsigned int)(m_ImageArea[0]/m_InterPixelDistance[0] + 0.5); - m_DistanceImageDimensions[1] = (unsigned int)(m_ImageArea[1]/m_InterPixelDistance[1] + 0.5); -} - -mitk::ToFProcessingCommon::ToFPoint3D mitk::PinholeCameraModel::GetPixelMidPointInWorldCoordinates(unsigned int indexX, unsigned int indexY) -{ - ToFPoint3D returnValue; - //calculate the mid-point of a pixel in world coordinates - //[1] pinhole is in the middle, so we subtract half of the size of the image area - //[2] then we multiplicate with the pixel size to get the coordinates in mm - //[3] next we take account of the image shift (if the pinhole is NOT exactly in the middle) - //[4] last we add half of the size of a pixel to get the middle of each pixel - // [1] [2] [3] [4] - returnValue[0] = ((indexX - m_PrincipalPoint[0])*m_InterPixelDistance[0] + (m_InterPixelDistance[0]/2)); - returnValue[1] = ((indexY - m_PrincipalPoint[1])*m_InterPixelDistance[1] + (m_InterPixelDistance[1]/2)); - returnValue[2] = -m_FocalLength; - return returnValue; -} - -mitk::ToFProcessingCommon::ToFPoint3D mitk::PinholeCameraModel::GetCameraPositionInWorldCoordinates() -{ - ToFPoint3D returnValue; - ToFPoint3D origin; origin[0] = 0; origin[1] = 0; origin[2] = 0; - returnValue = (this->m_CameraToWorldTransform_R * origin) + this->m_CameraToWorldTransform_T; - return returnValue; -} - -void mitk::PinholeCameraModel::InitializeWithDefaultSettings() -{ - ToFPoint2D PrincipalPoint; - PrincipalPoint[0] = 101.5f; - PrincipalPoint[1] = 101.5f; - - ToFPoint2D ImageArea; - ImageArea[0] = 9.18f; - ImageArea[1] = 9.18f; - - ToFPoint2D InterPixelDistance; - InterPixelDistance[0] = 0.045f; - InterPixelDistance[1] = 0.045f; - - ToFPoint2D ImageShift; - ImageShift[0] = 0.0; - ImageShift[1] = 0.0; - - unsigned int* dimensions = new unsigned int[3]; - dimensions[0] = 204; - dimensions[1] = 204; -// dimensions[2] = 1; - - itk::Matrix transR; - transR.Fill(0); transR[0][0] = 1; transR[1][1] = 1; transR[2][2] = 1; - - itk::Vector transT; - transT.Fill(0); - - SetDistanceImageDimensions(dimensions); - SetPrincipalPoint(PrincipalPoint); - SetImageArea(ImageArea); - SetInterPixelDistance(InterPixelDistance); - SetImageShift(ImageShift); - SetCameraToWorldTransform(transR,transT); -} diff --git a/Modules/ToFProcessing/mitkPinholeCameraModel.h b/Modules/ToFProcessing/mitkPinholeCameraModel.h deleted file mode 100644 index d19bd7f1f5..0000000000 --- a/Modules/ToFProcessing/mitkPinholeCameraModel.h +++ /dev/null @@ -1,121 +0,0 @@ -/*========================================================================= - -Program: Medical Imaging & Interaction Toolkit -Module: $RCSfile$ -Language: C++ -Date: $Date: 2009-07-17 15:44:24 +0200 (Fr, 17 Jul 2009) $ -Version: $Revision: 18261 $ - -Copyright (c) German Cancer Research Center, Division of Medical and -Biological Informatics. All rights reserved. -See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. - -This software is distributed WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the above copyright notices for more information. - -=========================================================================*/ - -#ifndef MITKPINHOLECAMERAMODEL_H_HEADER_INCLUDED_ -#define MITKPINHOLECAMERAMODEL_H_HEADER_INCLUDED_ - -//mitk headers -#include "mitkToFProcessingExports.h" -#include "mitkToFProcessingCommon.h" -#include -#include - - -//itk headers -#include -#include -#include -#include -#include - -namespace mitk { - //##Documentation - //## \brief An object of this class represents an ideal pinhole camera. - - class mitkToFProcessing_EXPORT PinholeCameraModel : public itk::DataObject - { - public: - typedef mitk::ToFProcessingCommon::ToFScalarType ToFScalarType; - typedef mitk::ToFProcessingCommon::ToFPoint2D ToFPoint2D; - typedef mitk::ToFProcessingCommon::ToFPoint3D ToFPoint3D; - - mitkClassMacro(PinholeCameraModel, itk::DataObject); - itkNewMacro(Self); - - //getter - itkGetMacro(FocalLength,ToFScalarType); - /** @return Returns the image area in mm x mm */ - itkGetMacro(ImageArea,ToFPoint2D); - /** @return Returns the size of one pixel in mm x mm */ - itkGetMacro(InterPixelDistance,ToFPoint2D); - /** @return Returns the image shift in relation to the pinhole in mm x mm */ - itkGetMacro(ImageShift,ToFPoint2D); - /** @return Returns the maximum view distance of the camera. (in mm) */ - itkGetMacro(MaxDistance,ToFScalarType); - void GetCameraToWorldTransform(itk::Matrix& TransformR,itk::Vector& TransformT); - /** @return Returns the point of pixel (x|y) on the image area in camera coordinates (mm). */ - ToFPoint3D GetPixelMidPointInWorldCoordinates(unsigned int indexX, unsigned int indexY); - /** @return Returns the pixel position (x|y) principal point of this camera. */ - itkGetMacro(PrincipalPoint,ToFPoint2D); - itkGetMacro(DistanceImageDimension,unsigned int); - /** @brief Sets the image dimension */ - itkGetMacro(DistanceImageDimensions,unsigned int*); - /** @brief Sets the image dimensions */ - - //setter - /** @brief Sets the focal distance in pixels */ - itkSetMacro(FocalLength,ToFScalarType); - /** @brief Sets the size of one pixel in mm x mm */ - itkSetMacro(InterPixelDistance,ToFPoint2D); - /** @brief Sets the image shift in relation to the pinhole in mm x mm */ - itkSetMacro(ImageShift,ToFPoint2D); - /** @brief Sets the maximum view distance of the camera. (in mm) */ - itkSetMacro(MaxDistance,ToFScalarType); - /** @return Returns the camera positon in world coordinates */ - ToFPoint3D GetCameraPositionInWorldCoordinates(); - itkSetMacro(PrincipalPoint,ToFPoint2D); - - itkSetMacro(DistanceImageDimension,unsigned int); - itkSetMacro(ImageArea,ToFPoint2D); - - void SetDistanceImageDimensions(unsigned int* dimensions); - - void SetCameraToWorldTransform(itk::Matrix TransformR,itk::Vector TransformT); - - /** @brief Initializes the camera model with default settings which are needed often. - * (focal length: 12.8 mm; pixel size: 0.045 x 0.045; image area: 9.18 x 9.18; max distance: 2000) - */ - void InitializeWithDefaultSettings(); - /*! - \brief calculate image area in mm from m_DistanceImageDimensions and m_InterPixelDistance - */ - void CalculateImageArea(); - /*! - \brief calculate m_DistanceImageDimensions from m_ImageArea and m_InterPixelDistance - */ - void CalculateImageDimensions(); - - protected: - PinholeCameraModel(); - ~PinholeCameraModel(); - - //########### member variables #################### - ToFScalarType m_FocalLength; ///< focal length in mm - ToFScalarType m_MaxDistance; ///< range of the virtual camera - unsigned int m_DistanceImageDimension; ///< dimension of the distance image - unsigned int* m_DistanceImageDimensions; ///< x-, y-, z- (optional) dimension - ToFPoint2D m_PrincipalPoint; ///< principal point in pixel coordinates - ToFPoint2D m_ImageArea; ///< image area (mm x mm) - ToFPoint2D m_InterPixelDistance; ///< size of one pixel (mm x mm) - ToFPoint2D m_ImageShift; ///< image shift of pinhole in relation to the image center - itk::Matrix m_CameraToWorldTransform_R; ///< rotation matrix of camera to world transform - itk::Vector m_CameraToWorldTransform_T; ///< translation vector of camera to world transform - }; - -} // namespace mitk -#endif /* MITKPINHOLECAMERAMODEL_H_HEADER_INCLUDED_ */ diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp index a8ea197028..89e00f14ee 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp +++ b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.cpp @@ -1,167 +1,176 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkToFDistanceImageToPointSetFilter.h" #include "mitkPointSet.h" #include "mitkToFProcessingCommon.h" mitk::ToFDistanceImageToPointSetFilter::ToFDistanceImageToPointSetFilter() -: m_CameraModel(), m_Subset(NULL) +: m_CameraIntrinsics(), m_Subset(NULL), m_InterPixelDistance() { - m_CameraModel = mitk::PinholeCameraModel::New(); + m_InterPixelDistance.Fill(0.045); + m_CameraIntrinsics = mitk::CameraIntrinsics::New(); + m_CameraIntrinsics->SetFocalLength(295.78960196187319,296.1255427948447); + m_CameraIntrinsics->SetPrincipalPoint(113.29063841714108,97.243216122015184); + m_CameraIntrinsics->SetDistorsionCoeffs(-0.36874385358645773f,-0.14339503290129013,0.0033210108720361795,-0.004277703352074105); } mitk::ToFDistanceImageToPointSetFilter::~ToFDistanceImageToPointSetFilter() { } void mitk::ToFDistanceImageToPointSetFilter::SetInput(const mitk::Image* distanceImage ) { this->SetInput(0,distanceImage); } void mitk::ToFDistanceImageToPointSetFilter::SetInput( unsigned int idx,const mitk::Image* distanceImage ) { if ((distanceImage == NULL) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to NULL, reduce the number of inputs by one { this->SetNumberOfInputs(this->GetNumberOfInputs() - 1); } else { this->ProcessObject::SetNthInput(idx, const_cast(distanceImage)); // Process object is not const-correct so the const_cast is required here this->CreateOutputsForAllInputs(); } } mitk::Image* mitk::ToFDistanceImageToPointSetFilter::GetInput() { return this->GetInput(0); } mitk::Image* mitk::ToFDistanceImageToPointSetFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return NULL; return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx)); } void mitk::ToFDistanceImageToPointSetFilter::SetSubset(std::vector subset) { // check if points of PointSet are inside the input image mitk::Image::Pointer input = this->GetInput(); unsigned int xDim = input->GetDimension(0); unsigned int yDim = input->GetDimension(1); bool pointSetValid = true; for (unsigned int i=0; ixDim||currentIndex[1]<0||currentIndex[1]>yDim) { pointSetValid = false; } } if (pointSetValid) { m_Subset = subset; } else { MITK_ERROR<<"One or more indizes are located outside the image domain"; } } void mitk::ToFDistanceImageToPointSetFilter::SetSubset( mitk::PointSet::Pointer pointSet) { std::vector subset; for (unsigned int i=0; iGetSize(); i++) { mitk::Point3D currentPoint = pointSet->GetPoint(i); mitk::Index3D currentIndex; currentIndex[0] = currentPoint[0]; currentIndex[1] = currentPoint[1]; currentIndex[2] = currentPoint[2]; subset.push_back(currentIndex); } this->SetSubset(subset); } void mitk::ToFDistanceImageToPointSetFilter::GenerateData() { + mitk::ToFProcessingCommon::ToFScalarType focalLength = (m_CameraIntrinsics->GetFocalLengthX()*m_InterPixelDistance[0]+m_CameraIntrinsics->GetFocalLengthY()*m_InterPixelDistance[1])/2.0; + mitk::ToFProcessingCommon::ToFPoint2D principalPoint; + principalPoint[0] = m_CameraIntrinsics->GetPrincipalPointX(); + principalPoint[1] = m_CameraIntrinsics->GetPrincipalPointY(); + mitk::PointSet::Pointer output = this->GetOutput(); assert(output); mitk::Image::Pointer input = this->GetInput(); assert(input); //compute subset of points if input PointSet is defined if (m_Subset.size()!=0) { for (unsigned int i=0; iGetPixelValueByIndex(currentIndex); + mitk::ToFProcessingCommon::ToFScalarType distance = (double)input->GetPixelValueByIndex(currentIndex); + mitk::Point3D currentPoint = - mitk::ToFProcessingCommon::IndexToCartesianCoordinates(currentIndex,distance,m_CameraModel->GetFocalLength(), - m_CameraModel->GetInterPixelDistance(),m_CameraModel->GetPrincipalPoint()); + mitk::ToFProcessingCommon::IndexToCartesianCoordinates(currentIndex,distance,focalLength,m_InterPixelDistance,principalPoint); output->InsertPoint(i,currentPoint); } } else //compute PointSet holding cartesian coordinates for every image point { unsigned int xDimension = input->GetDimension(0); unsigned int yDimension = input->GetDimension(1); int pointCount = 0; for (int i=0; iGetPixelValueByIndex(pixel); + mitk::ToFProcessingCommon::ToFScalarType distance = (double)input->GetPixelValueByIndex(pixel); mitk::Point3D currentPoint = - mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,m_CameraModel->GetFocalLength(),m_CameraModel->GetInterPixelDistance(),m_CameraModel->GetPrincipalPoint()); + mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLength,m_InterPixelDistance,principalPoint); if (distance!=0) { output->InsertPoint( pointCount, currentPoint ); pointCount++; } } } } } void mitk::ToFDistanceImageToPointSetFilter::CreateOutputsForAllInputs() { this->SetNumberOfOutputs(this->GetNumberOfInputs()); // create outputs for all inputs for (unsigned int idx = 0; idx < this->GetNumberOfOutputs(); ++idx) if (this->GetOutput(idx) == NULL) { DataObjectPointer newOutput = this->MakeOutput(idx); this->SetNthOutput(idx, newOutput); } this->Modified(); } void mitk::ToFDistanceImageToPointSetFilter::GenerateOutputInformation() { this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); } diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.h b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.h index d39a1698ff..f78be3ea64 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.h +++ b/Modules/ToFProcessing/mitkToFDistanceImageToPointSetFilter.h @@ -1,122 +1,125 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef __mitkToFDistanceImageToPointSetFilter_h #define __mitkToFDistanceImageToPointSetFilter_h +#include #include "mitkImage.h" #include "mitkPointSet.h" #include #include "mitkImageSource.h" -#include +#include #include "mitkToFProcessingExports.h" namespace mitk { /** * @brief Converts a Time-of-Flight (ToF) distance image to a PointSet using the pinhole camera model for coordinate computation. * The intrinsic parameters of the camera (FocalLength, PrincipalPoint, InterPixelDistance) are set via SetIntrinsicParameters(). The * measured distance for each pixel corresponds to the distance between the object point and the corresponding image point on the * image plane. * If a subset of indizes of the image is defined via SetSubset(), the output PointSet will only contain the cartesian coordinates * of the corresponding 3D points. * * Pinhole camera model * Image plane * * @ingroup SurfaceFilters * @ingroup ToFProcessing */ class mitkToFProcessing_EXPORT ToFDistanceImageToPointSetFilter : public PointSetSource { public: - typedef mitk::PinholeCameraModel::ToFScalarType ToFScalarType; mitkClassMacro( ToFDistanceImageToPointSetFilter , PointSetSource ); itkNewMacro( Self ); - itkSetMacro(CameraModel,mitk::PinholeCameraModel::Pointer); + itkSetMacro(CameraIntrinsics,mitk::CameraIntrinsics::Pointer); - itkGetMacro(CameraModel,mitk::PinholeCameraModel::Pointer); + itkGetMacro(CameraIntrinsics,mitk::CameraIntrinsics::Pointer); + itkSetMacro(InterPixelDistance,mitk::ToFProcessingCommon::ToFPoint2D); + itkGetMacro(InterPixelDistance,mitk::ToFProcessingCommon::ToFPoint2D); /*! \brief Sets the input of this filter \param distanceImage input is the distance image of e.g. a ToF camera */ virtual void SetInput(const Image* distanceImage); /*! \brief Sets the input of this filter at idx \param idx number of the current input \param distanceImage input is the distance image of e.g. a ToF camera */ virtual void SetInput(unsigned int idx,const Image* distanceImage); /*! \brief Returns the input of this filter */ Image* GetInput(); /*! \brief Returns the input with id idx of this filter */ Image* GetInput(unsigned int idx); /*! \brief If this subset is defined, the cartesian coordinates are only computed for the contained indizes. Make sure the indizes are contained in the input image \param subset index subset specified in index coordinates. */ void SetSubset( std::vector subset); /*! \brief Sets the subset of indizes used for caluclation of output PointSet as a PointSet. Warning: make sure the points in your PointSet are index coordinates. \param PointSet specified in index coordinates. */ void SetSubset( mitk::PointSet::Pointer pointSet); protected: /*! \brief Standard constructor */ ToFDistanceImageToPointSetFilter(); /*! \brief Standard destructor */ ~ToFDistanceImageToPointSetFilter(); virtual void GenerateOutputInformation(); /*! \brief Method generating the output of this filter. Called in the updated process of the pipeline. This method generates the output of the ToFSurfaceSource: The generated surface of the 3d points */ virtual void GenerateData(); /** * \brief Create an output for each input * * This Method sets the number of outputs to the number of inputs * and creates missing outputs objects. * \warning any additional outputs that exist before the method is called are deleted */ void CreateOutputsForAllInputs(); std::vector m_Subset; ///< If this subset is specified only the contained indizes are converted to cartesian coordinates - mitk::PinholeCameraModel::Pointer m_CameraModel; ///< Pinhole camera model holding the intrinsic parameters needed for PointSet calculation + mitk::CameraIntrinsics::Pointer m_CameraIntrinsics; ///< Member holding the intrinsic parameters needed for PointSet calculation + ToFProcessingCommon::ToFPoint2D m_InterPixelDistance; ///< distance in mm between two adjacent pixels on the ToF camera chip }; } //END mitk namespace #endif diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.cpp b/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.cpp index f15471756a..c72c759a3c 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.cpp +++ b/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.cpp @@ -1,247 +1,253 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include #include #include -#include -#include #include #include #include #include #include #include #include #include #include #include -mitk::ToFDistanceImageToSurfaceFilter::ToFDistanceImageToSurfaceFilter(): m_CameraModel(), -m_TextureImageWidth(0), m_TextureImageHeight(0), m_IplScalarImage(NULL) +mitk::ToFDistanceImageToSurfaceFilter::ToFDistanceImageToSurfaceFilter(): m_CameraIntrinsics(), +m_TextureImageWidth(0), m_TextureImageHeight(0), m_IplScalarImage(NULL), m_InterPixelDistance() { - m_CameraModel = mitk::PinholeCameraModel::New(); + m_InterPixelDistance.Fill(0.045); + m_CameraIntrinsics = mitk::CameraIntrinsics::New(); + m_CameraIntrinsics->SetFocalLength(295.78960196187319,296.1255427948447); + m_CameraIntrinsics->SetPrincipalPoint(113.29063841714108,97.243216122015184); + m_CameraIntrinsics->SetDistorsionCoeffs(-0.36874385358645773f,-0.14339503290129013,0.0033210108720361795,-0.004277703352074105); } mitk::ToFDistanceImageToSurfaceFilter::~ToFDistanceImageToSurfaceFilter() { } -void mitk::ToFDistanceImageToSurfaceFilter::SetInput( Image* distanceImage, mitk::PinholeCameraModel::Pointer CameraModel ) +void mitk::ToFDistanceImageToSurfaceFilter::SetInput( Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics ) { - this->SetCameraModel(CameraModel); + this->SetCameraIntrinsics(cameraIntrinsics); this->SetInput(0,distanceImage); } -void mitk::ToFDistanceImageToSurfaceFilter::SetInput( unsigned int idx, Image* distanceImage, mitk::PinholeCameraModel::Pointer CameraModel ) +void mitk::ToFDistanceImageToSurfaceFilter::SetInput( unsigned int idx, Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics ) { - this->SetCameraModel(CameraModel); + this->SetCameraIntrinsics(cameraIntrinsics); this->SetInput(idx,distanceImage); } void mitk::ToFDistanceImageToSurfaceFilter::SetInput( mitk::Image* distanceImage ) { this->SetInput(0,distanceImage); } //TODO: braucht man diese Methode? void mitk::ToFDistanceImageToSurfaceFilter::SetInput( unsigned int idx, mitk::Image* distanceImage ) { if ((distanceImage == NULL) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to NULL, reduce the number of inputs by one this->SetNumberOfInputs(this->GetNumberOfInputs() - 1); else this->ProcessObject::SetNthInput(idx, distanceImage); // Process object is not const-correct so the const_cast is required here this->CreateOutputsForAllInputs(); } mitk::Image* mitk::ToFDistanceImageToSurfaceFilter::GetInput() { return this->GetInput(0); } mitk::Image* mitk::ToFDistanceImageToSurfaceFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return NULL; //TODO: geeignete exception werfen return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx)); } void mitk::ToFDistanceImageToSurfaceFilter::GenerateData() { mitk::Surface::Pointer output = this->GetOutput(); assert(output); mitk::Image::Pointer input = this->GetInput(); assert(input); // mesh points unsigned int xDimension = input->GetDimension(0); unsigned int yDimension = input->GetDimension(1); unsigned int size = xDimension*yDimension; //size of the image-array std::vector isPointValid; isPointValid.resize(size); int pointCount = 0; vtkSmartPointer points = vtkSmartPointer::New(); points->SetDataTypeToDouble(); vtkSmartPointer polys = vtkSmartPointer::New(); vtkSmartPointer scalarArray = vtkSmartPointer::New(); vtkSmartPointer textureCoords = vtkSmartPointer::New(); textureCoords->SetNumberOfComponents(2); float textureScaleCorrection1 = 0.0; float textureScaleCorrection2 = 0.0; if (this->m_TextureImageHeight > 0.0 && this->m_TextureImageWidth > 0.0) { textureScaleCorrection1 = float(this->m_TextureImageHeight) / float(this->m_TextureImageWidth); textureScaleCorrection2 = ((float(this->m_TextureImageWidth) - float(this->m_TextureImageHeight))/2) / float(this->m_TextureImageWidth); } float* scalarFloatData = NULL; if (this->m_IplScalarImage) // if scalar image is defined use it for texturing { scalarFloatData = (float*)this->m_IplScalarImage->imageData; } else if ((this->GetNumberOfInputs()>2)&&this->GetInput(2)) // otherwise use intensity image (input(2)) { scalarFloatData = (float*)this->GetInput(2)->GetData(); } float* inputFloatData = (float*)(input->GetSliceData(0, 0, 0)->GetData()); //calculate world coordinates for (int j=0; jGetPixelValueByIndex(pixel); //TODO: float array zugriff + mitk::ToFProcessingCommon::ToFScalarType distance = (double)inputFloatData[pixelID]; + + mitk::ToFProcessingCommon::ToFScalarType focalLength = (m_CameraIntrinsics->GetFocalLengthX()*m_InterPixelDistance[0]+m_CameraIntrinsics->GetFocalLengthY()*m_InterPixelDistance[1])/2.0; + mitk::ToFProcessingCommon::ToFPoint2D principalPoint; + principalPoint[0] = m_CameraIntrinsics->GetPrincipalPointX(); + principalPoint[1] = m_CameraIntrinsics->GetPrincipalPointY(); mitk::ToFProcessingCommon::ToFPoint3D cartesianCoordinates = - mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,m_CameraModel->GetFocalLength(),m_CameraModel->GetInterPixelDistance(),m_CameraModel->GetPrincipalPoint()); + mitk::ToFProcessingCommon::IndexToCartesianCoordinates(i,j,distance,focalLength,m_InterPixelDistance,principalPoint); //TODO: why epsilon here and what value should it have? // if (cartesianCoordinates[2] == 0) if (distance<=mitk::eps) { isPointValid[pointCount] = false; } else { isPointValid[pointCount] = true; points->InsertPoint(pixelID, cartesianCoordinates.GetDataPointer()); if((i >= 1) && (j >= 1)) { vtkIdType xy = i+j*xDimension; vtkIdType x_1y = i-1+j*xDimension; vtkIdType xy_1 = i+(j-1)*xDimension; vtkIdType x_1y_1 = (i-1)+(j-1)*xDimension; if (isPointValid[xy]&&isPointValid[x_1y]&&isPointValid[x_1y_1]&&isPointValid[xy_1]) // check if points of cell are valid { polys->InsertNextCell(3); polys->InsertCellPoint(xy); polys->InsertCellPoint(x_1y); polys->InsertCellPoint(x_1y_1); polys->InsertNextCell(3); polys->InsertCellPoint(xy); polys->InsertCellPoint(x_1y_1); polys->InsertCellPoint(xy_1); } } if (scalarFloatData) { scalarArray->InsertTuple1(pixelID, scalarFloatData[pixel[0]+pixel[1]*xDimension]); } if (this->m_TextureImageHeight > 0.0 && this->m_TextureImageWidth > 0.0) { float xNorm = (((float)pixel[0])/xDimension)*textureScaleCorrection1 + textureScaleCorrection2 ; // correct video texture scale 640 * 480!! float yNorm = 1.0 - ((float)pixel[1])/yDimension; //flip y-axis textureCoords->InsertTuple2(pixelID, xNorm, yNorm); } } pointCount++; } } vtkSmartPointer mesh = vtkSmartPointer::New(); mesh->SetPoints(points); mesh->SetPolys(polys); if (scalarArray->GetNumberOfTuples()>0) { mesh->GetPointData()->SetScalars(scalarArray); if (this->m_TextureImageHeight > 0.0 && this->m_TextureImageWidth > 0.0) { mesh->GetPointData()->SetTCoords(textureCoords); } } output->SetVtkPolyData(mesh); } void mitk::ToFDistanceImageToSurfaceFilter::CreateOutputsForAllInputs() { this->SetNumberOfOutputs(this->GetNumberOfInputs()); // create outputs for all inputs for (unsigned int idx = 0; idx < this->GetNumberOfOutputs(); ++idx) if (this->GetOutput(idx) == NULL) { DataObjectPointer newOutput = this->MakeOutput(idx); this->SetNthOutput(idx, newOutput); } this->Modified(); } void mitk::ToFDistanceImageToSurfaceFilter::GenerateOutputInformation() { this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); } void mitk::ToFDistanceImageToSurfaceFilter::SetScalarImage(IplImage* iplScalarImage) { this->m_IplScalarImage = iplScalarImage; this->Modified(); } IplImage* mitk::ToFDistanceImageToSurfaceFilter::GetScalarImage() { return this->m_IplScalarImage; } void mitk::ToFDistanceImageToSurfaceFilter::SetTextureImageWidth(int width) { this->m_TextureImageWidth = width; } void mitk::ToFDistanceImageToSurfaceFilter::SetTextureImageHeight(int height) { this->m_TextureImageHeight = height; } diff --git a/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.h b/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.h index eb044ba596..88768a5a19 100644 --- a/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.h +++ b/Modules/ToFProcessing/mitkToFDistanceImageToSurfaceFilter.h @@ -1,144 +1,149 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef __mitkToFDistanceImageToSurfaceFilter_h #define __mitkToFDistanceImageToSurfaceFilter_h #include #include #include -#include "mitkPinholeCameraModel.h" +#include +#include +#include "mitkCameraIntrinsics.h" #include #include namespace mitk { /** * @brief Converts a Time-of-Flight (ToF) distance image to a 3D surface using the pinhole camera model for coordinate computation. * The intrinsic parameters of the camera (FocalLength, PrincipalPoint, InterPixelDistance) are set via SetCameraIntrinsics(). The * measured distance for each pixel corresponds to the distance between the object point and the corresponding image point on the * image plane. * - * Pinhole camera model + * Pinhole camera model * Image plane * * @ingroup SurfaceFilters * @ingroup ToFProcessing */ class mitkToFProcessing_EXPORT ToFDistanceImageToSurfaceFilter : public SurfaceSource { public: - typedef mitk::PinholeCameraModel::ToFScalarType ToFScalarType; mitkClassMacro( ToFDistanceImageToSurfaceFilter , SurfaceSource ); itkNewMacro( Self ); - itkSetMacro(CameraModel, mitk::PinholeCameraModel::Pointer); - itkGetMacro(CameraModel, mitk::PinholeCameraModel::Pointer); + itkSetMacro(CameraIntrinsics, mitk::CameraIntrinsics::Pointer); + itkGetMacro(CameraIntrinsics, mitk::CameraIntrinsics::Pointer); + itkSetMacro(InterPixelDistance,ToFProcessingCommon::ToFPoint2D); + itkGetMacro(InterPixelDistance,ToFProcessingCommon::ToFPoint2D); /*! \brief Set scalar image used as texture of the surface. \param iplScalarImage OpenCV image for texturing */ void SetScalarImage(IplImage* iplScalarImage); /*! \brief Set scalar image used as texture of the surface. \return OpenCV image for texturing */ IplImage* GetScalarImage(); /*! \brief Set width of the scalar image used for texturing the surface \param width width (x-dimension) of the texture image */ void SetTextureImageWidth(int width); /*! \brief Set height of the scalar image used for texturing the surface \param height height (y-dimension) of the texture image */ void SetTextureImageHeight(int height); /*! \brief Sets the input of this filter \param distanceImage input is the distance image of e.g. a ToF camera */ virtual void SetInput( Image* distanceImage); /*! \brief Sets the input of this filter and the intrinsic parameters \param distanceImage input is the distance image of e.g. a ToF camera */ - virtual void SetInput( Image* distanceImage, mitk::PinholeCameraModel::Pointer CameraModel ); + virtual void SetInput( Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics ); /*! \brief Sets the input of this filter at idx \param idx number of the current input \param distanceImage input is the distance image of e.g. a ToF camera */ virtual void SetInput(unsigned int idx, Image* distanceImage); /*! \brief Sets the input of this filter at idx and the intrinsic parameters \param idx number of the current input \param distanceImage input is the distance image of e.g. a ToF camera - \param CameraModel This is the camera model which holds parameters like focal length, pixel size, etc. which are needed for the reconstruction of the surface. + \param cameraIntrinsics This is the camera model which holds parameters like focal length, pixel size, etc. which are needed for the reconstruction of the surface. */ - virtual void SetInput( unsigned int idx, Image* distanceImage, mitk::PinholeCameraModel::Pointer CameraModel ); + virtual void SetInput( unsigned int idx, Image* distanceImage, mitk::CameraIntrinsics::Pointer cameraIntrinsics ); /*! \brief Returns the input of this filter */ Image* GetInput(); /*! \brief Returns the input with id idx of this filter */ Image* GetInput(unsigned int idx); protected: /*! \brief Standard constructor */ ToFDistanceImageToSurfaceFilter(); /*! \brief Standard destructor */ ~ToFDistanceImageToSurfaceFilter(); virtual void GenerateOutputInformation(); /*! \brief Method generating the output of this filter. Called in the updated process of the pipeline. This method generates the output of the ToFSurfaceSource: The generated surface of the 3d points */ virtual void GenerateData(); /** * \brief Create an output for each input * * This Method sets the number of outputs to the number of inputs * and creates missing outputs objects. * \warning any additional outputs that exist before the method is called are deleted */ void CreateOutputsForAllInputs(); IplImage* m_IplScalarImage; ///< Scalar image used for surface texturing - mitk::PinholeCameraModel::Pointer m_CameraModel; ///< Specifies the intrinsic parameters + mitk::CameraIntrinsics::Pointer m_CameraIntrinsics; ///< Specifies the intrinsic parameters + //mitk::CameraIntrinsics::Pointer m_CameraModel; ///< Specifies the intrinsic parameters int m_TextureImageWidth; ///< Width (x-dimension) of the texture image int m_TextureImageHeight; ///< Height (y-dimension) of the texture image + ToFProcessingCommon::ToFPoint2D m_InterPixelDistance; ///< distance in mm between two adjacent pixels on the ToF camera chip }; } //END mitk namespace #endif diff --git a/Utilities/tinyxml/tinyxml.cpp b/Utilities/tinyxml/tinyxml.cpp index 9be6c6a66c..efbf3cc472 100644 --- a/Utilities/tinyxml/tinyxml.cpp +++ b/Utilities/tinyxml/tinyxml.cpp @@ -1,1839 +1,1864 @@ /* www.sourceforge.net/projects/tinyxml Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include #ifdef TIXML_USE_STL #include #include #endif #include "tinyxml.h" FILE* TiXmlFOpen( const char* filename, const char* mode ); bool TiXmlBase::condenseWhiteSpace = true; // Microsoft compiler security FILE* TiXmlFOpen( const char* filename, const char* mode ) { #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) FILE* fp = 0; errno_t err = fopen_s( &fp, filename, mode ); if ( !err && fp ) return fp; return 0; #else return fopen( filename, mode ); #endif } void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) { int i=0; while( i<(int)str.length() ) { unsigned char c = (unsigned char) str[i]; if ( c == '&' && i < ( (int)str.length() - 2 ) && str[i+1] == '#' && str[i+2] == 'x' ) { // Hexadecimal character reference. // Pass through unchanged. // © -- copyright symbol, for example. // // The -1 is a bug fix from Rob Laveaux. It keeps // an overflow from happening if there is no ';'. // There are actually 2 ways to exit this loop - // while fails (error case) and break (semicolon found). // However, there is no mechanism (currently) for // this function to return an error. while ( i<(int)str.length()-1 ) { outString->append( str.c_str() + i, 1 ); ++i; if ( str[i] == ';' ) break; } } else if ( c == '&' ) { outString->append( entity[0].str, entity[0].strLength ); ++i; } else if ( c == '<' ) { outString->append( entity[1].str, entity[1].strLength ); ++i; } else if ( c == '>' ) { outString->append( entity[2].str, entity[2].strLength ); ++i; } else if ( c == '\"' ) { outString->append( entity[3].str, entity[3].strLength ); ++i; } else if ( c == '\'' ) { outString->append( entity[4].str, entity[4].strLength ); ++i; } else if ( c < 32 ) { // Easy pass at non-alpha/numeric/symbol // Below 32 is symbolic. char buf[ 32 ]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); #else sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); #endif //*ME: warning C4267: convert 'size_t' to 'int' //*ME: Int-Cast to make compiler happy ... outString->append( buf, (int)strlen( buf ) ); ++i; } else { //char realc = (char) c; //outString->append( &realc, 1 ); *outString += (char) c; // somewhat more efficient function call. ++i; } } } TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() { parent = 0; type = _type; firstChild = 0; lastChild = 0; prev = 0; next = 0; } TiXmlNode::~TiXmlNode() { TiXmlNode* node = firstChild; TiXmlNode* temp = 0; while ( node ) { temp = node; node = node->next; delete temp; } } void TiXmlNode::CopyTo( TiXmlNode* target ) const { target->SetValue (value.c_str() ); target->userData = userData; target->location = location; } void TiXmlNode::Clear() { TiXmlNode* node = firstChild; TiXmlNode* temp = 0; while ( node ) { temp = node; node = node->next; delete temp; } firstChild = 0; lastChild = 0; } TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) { assert( node->parent == 0 || node->parent == this ); assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) { delete node; if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } node->parent = this; node->prev = lastChild; node->next = 0; if ( lastChild ) lastChild->next = node; else firstChild = node; // it was an empty list. lastChild = node; return node; } TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) { if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; return LinkEndChild( node ); } TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) { if ( !beforeThis || beforeThis->parent != this ) { return 0; } if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; node->parent = this; node->next = beforeThis; node->prev = beforeThis->prev; if ( beforeThis->prev ) { beforeThis->prev->next = node; } else { assert( firstChild == beforeThis ); firstChild = node; } beforeThis->prev = node; return node; } TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) { if ( !afterThis || afterThis->parent != this ) { return 0; } if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; node->parent = this; node->prev = afterThis; node->next = afterThis->next; if ( afterThis->next ) { afterThis->next->prev = node; } else { assert( lastChild == afterThis ); lastChild = node; } afterThis->next = node; return node; } TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) { if ( !replaceThis ) return 0; if ( replaceThis->parent != this ) return 0; if ( withThis.ToDocument() ) { // A document can never be a child. Thanks to Noam. TiXmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = withThis.Clone(); if ( !node ) return 0; node->next = replaceThis->next; node->prev = replaceThis->prev; if ( replaceThis->next ) replaceThis->next->prev = node; else lastChild = node; if ( replaceThis->prev ) replaceThis->prev->next = node; else firstChild = node; delete replaceThis; node->parent = this; return node; } bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) { if ( !removeThis ) { return false; } if ( removeThis->parent != this ) { assert( 0 ); return false; } if ( removeThis->next ) removeThis->next->prev = removeThis->prev; else lastChild = removeThis->prev; if ( removeThis->prev ) removeThis->prev->next = removeThis->next; else firstChild = removeThis->next; delete removeThis; return true; } const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const { const TiXmlNode* node; for ( node = firstChild; node; node = node->next ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const { const TiXmlNode* node; for ( node = lastChild; node; node = node->prev ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const { if ( !previous ) { return FirstChild(); } else { assert( previous->parent == this ); return previous->NextSibling(); } } const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const { if ( !previous ) { return FirstChild( val ); } else { assert( previous->parent == this ); return previous->NextSibling( val ); } } const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const { const TiXmlNode* node; for ( node = next; node; node = node->next ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const { const TiXmlNode* node; for ( node = prev; node; node = node->prev ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } void TiXmlElement::RemoveAttribute( const char * name ) { #ifdef TIXML_USE_STL TIXML_STRING str( name ); TiXmlAttribute* node = attributeSet.Find( str ); #else TiXmlAttribute* node = attributeSet.Find( name ); #endif if ( node ) { attributeSet.Remove( node ); delete node; } } const TiXmlElement* TiXmlNode::FirstChildElement() const { const TiXmlNode* node; for ( node = FirstChild(); node; node = node->NextSibling() ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const { const TiXmlNode* node; for ( node = FirstChild( _value ); node; node = node->NextSibling( _value ) ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::NextSiblingElement() const { const TiXmlNode* node; for ( node = NextSibling(); node; node = node->NextSibling() ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const { const TiXmlNode* node; for ( node = NextSibling( _value ); node; node = node->NextSibling( _value ) ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlDocument* TiXmlNode::GetDocument() const { const TiXmlNode* node; for( node = this; node; node = node->parent ) { if ( node->ToDocument() ) return node->ToDocument(); } return 0; } TiXmlElement::TiXmlElement (const char * _value) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; value = _value; } #ifdef TIXML_USE_STL TiXmlElement::TiXmlElement( const std::string& _value ) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; value = _value; } #endif TiXmlElement::TiXmlElement( const TiXmlElement& copy) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; copy.CopyTo( this ); } void TiXmlElement::operator=( const TiXmlElement& base ) { ClearThis(); base.CopyTo( this ); } TiXmlElement::~TiXmlElement() { ClearThis(); } void TiXmlElement::ClearThis() { Clear(); while( attributeSet.First() ) { TiXmlAttribute* node = attributeSet.First(); attributeSet.Remove( node ); delete node; } } const char* TiXmlElement::Attribute( const char* name ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( node ) return node->Value(); return 0; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( attrib ) return &attrib->ValueStr(); return 0; } #endif const char* TiXmlElement::Attribute( const char* name, int* i ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const char* result = 0; if ( attrib ) { result = attrib->Value(); if ( i ) { attrib->QueryIntValue( i ); } } return result; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const std::string* result = 0; if ( attrib ) { result = &attrib->ValueStr(); if ( i ) { attrib->QueryIntValue( i ); } } return result; } #endif const char* TiXmlElement::Attribute( const char* name, double* d ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const char* result = 0; if ( attrib ) { result = attrib->Value(); if ( d ) { attrib->QueryDoubleValue( d ); } } return result; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const std::string* result = 0; if ( attrib ) { result = &attrib->ValueStr(); if ( d ) { attrib->QueryDoubleValue( d ); } } return result; } #endif int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryIntValue( ival ); } #ifdef TIXML_USE_STL int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryIntValue( ival ); } #endif int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryDoubleValue( dval ); } #ifdef TIXML_USE_STL int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryDoubleValue( dval ); } #endif void TiXmlElement::SetAttribute( const char * name, int val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetIntValue( val ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetAttribute( const std::string& name, int val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetIntValue( val ); } } #endif void TiXmlElement::SetDoubleAttribute( const char * name, double val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetDoubleValue( val ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetDoubleValue( val ); } } #endif void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); if ( attrib ) { attrib->SetValue( cvalue ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); if ( attrib ) { attrib->SetValue( _value ); } } #endif void TiXmlElement::Print( FILE* cfile, int depth ) const { int i; assert( cfile ); for ( i=0; iNext() ) { fprintf( cfile, " " ); attrib->Print( cfile, depth ); } // There are 3 different formatting approaches: // 1) An element without children is printed as a node // 2) An element with only a text child is printed as text // 3) An element with children is printed on multiple lines. TiXmlNode* node; if ( !firstChild ) { fprintf( cfile, " />" ); } else if ( firstChild == lastChild && firstChild->ToText() ) { fprintf( cfile, ">" ); firstChild->Print( cfile, depth + 1 ); fprintf( cfile, "", value.c_str() ); } else { fprintf( cfile, ">" ); for ( node = firstChild; node; node=node->NextSibling() ) { if ( !node->ToText() ) { fprintf( cfile, "\n" ); } node->Print( cfile, depth+1 ); } fprintf( cfile, "\n" ); for( i=0; i", value.c_str() ); } } void TiXmlElement::CopyTo( TiXmlElement* target ) const { // superclass: TiXmlNode::CopyTo( target ); // Element class: // Clone the attributes, then clone the children. const TiXmlAttribute* attribute = 0; for( attribute = attributeSet.First(); attribute; attribute = attribute->Next() ) { target->SetAttribute( attribute->Name(), attribute->Value() ); } TiXmlNode* node = 0; for ( node = firstChild; node; node = node->NextSibling() ) { target->LinkEndChild( node->Clone() ); } } bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const { if ( visitor->VisitEnter( *this, attributeSet.First() ) ) { for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) break; } } return visitor->VisitExit( *this ); } TiXmlNode* TiXmlElement::Clone() const { TiXmlElement* clone = new TiXmlElement( Value() ); if ( !clone ) return 0; CopyTo( clone ); return clone; } const char* TiXmlElement::GetText() const { const TiXmlNode* child = this->FirstChild(); if ( child ) { const TiXmlText* childText = child->ToText(); if ( childText ) { return childText->Value(); } } return 0; } TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; ClearError(); } TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; value = documentName; ClearError(); } #ifdef TIXML_USE_STL TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; value = documentName; ClearError(); } #endif TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { copy.CopyTo( this ); } void TiXmlDocument::operator=( const TiXmlDocument& copy ) { Clear(); copy.CopyTo( this ); } bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) { return LoadFile( Value(), encoding ); } bool TiXmlDocument::SaveFile() const { return SaveFile( Value() ); } bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) { TIXML_STRING filename( _filename ); value = filename; // reading in binary mode so that tinyxml can normalize the EOL FILE* file = TiXmlFOpen( value.c_str (), "rb" ); if ( file ) { bool result = LoadFile( file, encoding ); fclose( file ); return result; } else { SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } } bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) { if ( !file ) { SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Delete the existing data: Clear(); location.Clear(); // Get the file size, so we can pre-allocate the string. HUGE speed impact. long length = 0; fseek( file, 0, SEEK_END ); length = ftell( file ); fseek( file, 0, SEEK_SET ); // Strange case, but good to handle up front. if ( length <= 0 ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Subtle bug here. TinyXml did use fgets. But from the XML spec: // 2.11 End-of-Line Handling // // // ...the XML processor MUST behave as if it normalized all line breaks in external // parsed entities (including the document entity) on input, before parsing, by translating // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to // a single #xA character. // // // It is not clear fgets does that, and certainly isn't clear it works cross platform. // Generally, you expect fgets to translate from the convention of the OS to the c/unix // convention, and not work generally. /* while( fgets( buf, sizeof(buf), file ) ) { data += buf; } */ char* buf = new char[ length+1 ]; buf[0] = 0; if ( fread( buf, length, 1, file ) != 1 ) { delete [] buf; SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Process the buffer in place to normalize new lines. (See comment above.) // Copies from the 'p' to 'q' pointer, where p can advance faster if // a newline-carriage return is hit. // // Wikipedia: // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 const char* p = buf; // the read head char* q = buf; // the write head const char CR = 0x0d; const char LF = 0x0a; buf[length] = 0; while( *p ) { assert( p < (buf+length) ); assert( q <= (buf+length) ); assert( q <= p ); if ( *p == CR ) { *q++ = LF; p++; if ( *p == LF ) { // check for CR+LF (and skip LF) p++; } } else { *q++ = *p++; } } assert( q <= (buf+length) ); *q = 0; Parse( buf, 0, encoding ); delete [] buf; return !Error(); } bool TiXmlDocument::SaveFile( const char * filename ) const { // The old c stuff lives on... FILE* fp = TiXmlFOpen( filename, "w" ); if ( fp ) { bool result = SaveFile( fp ); fclose( fp ); return result; } return false; } bool TiXmlDocument::SaveFile( FILE* fp ) const { if ( useMicrosoftBOM ) { const unsigned char TIXML_UTF_LEAD_0 = 0xefU; const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; fputc( TIXML_UTF_LEAD_0, fp ); fputc( TIXML_UTF_LEAD_1, fp ); fputc( TIXML_UTF_LEAD_2, fp ); } Print( fp, 0 ); return (ferror(fp) == 0); } void TiXmlDocument::CopyTo( TiXmlDocument* target ) const { TiXmlNode::CopyTo( target ); target->error = error; target->errorId = errorId; target->errorDesc = errorDesc; target->tabsize = tabsize; target->errorLocation = errorLocation; target->useMicrosoftBOM = useMicrosoftBOM; TiXmlNode* node = 0; for ( node = firstChild; node; node = node->NextSibling() ) { target->LinkEndChild( node->Clone() ); } } TiXmlNode* TiXmlDocument::Clone() const { TiXmlDocument* clone = new TiXmlDocument(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlDocument::Print( FILE* cfile, int depth ) const { assert( cfile ); for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { node->Print( cfile, depth ); fprintf( cfile, "\n" ); } } bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const { if ( visitor->VisitEnter( *this ) ) { for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) break; } } return visitor->VisitExit( *this ); } const TiXmlAttribute* TiXmlAttribute::Next() const { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( next->value.empty() && next->name.empty() ) return 0; return next; } /* TiXmlAttribute* TiXmlAttribute::Next() { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( next->value.empty() && next->name.empty() ) return 0; return next; } */ const TiXmlAttribute* TiXmlAttribute::Previous() const { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( prev->value.empty() && prev->name.empty() ) return 0; return prev; } /* TiXmlAttribute* TiXmlAttribute::Previous() { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( prev->value.empty() && prev->name.empty() ) return 0; return prev; } */ void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const { TIXML_STRING n, v; EncodeString( name, &n ); EncodeString( value, &v ); if (value.find ('\"') == TIXML_STRING::npos) { if ( cfile ) { fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); } if ( str ) { (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; } } else { if ( cfile ) { fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); } if ( str ) { (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; } } } int TiXmlAttribute::QueryIntValue( int* ival ) const { if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } int TiXmlAttribute::QueryDoubleValue( double* dval ) const { + //save old locale + char * oldLocale; + oldLocale = setlocale( LC_ALL, 0 ); + + //set new locale + setlocale( LC_ALL, "C" ); + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + { + //restore locale + setlocale( LC_ALL, oldLocale ); return TIXML_SUCCESS; + } + //restore locale + setlocale( LC_ALL, oldLocale ); return TIXML_WRONG_TYPE; } void TiXmlAttribute::SetIntValue( int _value ) { char buf [64]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); #else sprintf (buf, "%d", _value); #endif SetValue (buf); } void TiXmlAttribute::SetDoubleValue( double _value ) { char buf [256]; + + //save old locale + char * oldLocale; + oldLocale = setlocale( LC_ALL, 0 ); + + //set new locale + setlocale( LC_ALL, "C" ); + #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); #else sprintf (buf, "%g", _value); #endif SetValue (buf); + + //restore locale + setlocale( LC_ALL, oldLocale ); + } int TiXmlAttribute::IntValue() const { return atoi (value.c_str ()); } double TiXmlAttribute::DoubleValue() const { return atof (value.c_str ()); } TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { copy.CopyTo( this ); } void TiXmlComment::operator=( const TiXmlComment& base ) { Clear(); base.CopyTo( this ); } void TiXmlComment::Print( FILE* cfile, int depth ) const { assert( cfile ); for ( int i=0; i", value.c_str() ); } void TiXmlComment::CopyTo( TiXmlComment* target ) const { TiXmlNode::CopyTo( target ); } bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlComment::Clone() const { TiXmlComment* clone = new TiXmlComment(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlText::Print( FILE* cfile, int depth ) const { assert( cfile ); if ( cdata ) { int i; fprintf( cfile, "\n" ); for ( i=0; i\n", value.c_str() ); // unformatted output } else { TIXML_STRING buffer; EncodeString( value, &buffer ); fprintf( cfile, "%s", buffer.c_str() ); } } void TiXmlText::CopyTo( TiXmlText* target ) const { TiXmlNode::CopyTo( target ); target->cdata = cdata; } bool TiXmlText::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlText::Clone() const { TiXmlText* clone = 0; clone = new TiXmlText( "" ); if ( !clone ) return 0; CopyTo( clone ); return clone; } TiXmlDeclaration::TiXmlDeclaration( const char * _version, const char * _encoding, const char * _standalone ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { version = _version; encoding = _encoding; standalone = _standalone; } #ifdef TIXML_USE_STL TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, const std::string& _encoding, const std::string& _standalone ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { version = _version; encoding = _encoding; standalone = _standalone; } #endif TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { copy.CopyTo( this ); } void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) { Clear(); copy.CopyTo( this ); } void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const { if ( cfile ) fprintf( cfile, "" ); if ( str ) (*str) += "?>"; } void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const { TiXmlNode::CopyTo( target ); target->version = version; target->encoding = encoding; target->standalone = standalone; } bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlDeclaration::Clone() const { TiXmlDeclaration* clone = new TiXmlDeclaration(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlUnknown::Print( FILE* cfile, int depth ) const { for ( int i=0; i", value.c_str() ); } void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const { TiXmlNode::CopyTo( target ); } bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlUnknown::Clone() const { TiXmlUnknown* clone = new TiXmlUnknown(); if ( !clone ) return 0; CopyTo( clone ); return clone; } TiXmlAttributeSet::TiXmlAttributeSet() { sentinel.next = &sentinel; sentinel.prev = &sentinel; } TiXmlAttributeSet::~TiXmlAttributeSet() { assert( sentinel.next == &sentinel ); assert( sentinel.prev == &sentinel ); } void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) { #ifdef TIXML_USE_STL assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. #else assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. #endif addMe->next = &sentinel; addMe->prev = sentinel.prev; sentinel.prev->next = addMe; sentinel.prev = addMe; } void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) { TiXmlAttribute* node; for( node = sentinel.next; node != &sentinel; node = node->next ) { if ( node == removeMe ) { node->prev->next = node->next; node->next->prev = node->prev; node->next = 0; node->prev = 0; return; } } assert( 0 ); // we tried to remove a non-linked attribute. } #ifdef TIXML_USE_STL TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const { for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( node->name == name ) return node; } return 0; } TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) { TiXmlAttribute* attrib = Find( _name ); if ( !attrib ) { attrib = new TiXmlAttribute(); Add( attrib ); attrib->SetName( _name ); } return attrib; } #endif TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const { for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( strcmp( node->name.c_str(), name ) == 0 ) return node; } return 0; } TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) { TiXmlAttribute* attrib = Find( _name ); if ( !attrib ) { attrib = new TiXmlAttribute(); Add( attrib ); attrib->SetName( _name ); } return attrib; } #ifdef TIXML_USE_STL std::istream& operator>> (std::istream & in, TiXmlNode & base) { TIXML_STRING tag; tag.reserve( 8 * 1000 ); base.StreamIn( &in, &tag ); base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); return in; } #endif #ifdef TIXML_USE_STL std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) { TiXmlPrinter printer; printer.SetStreamPrinting(); base.Accept( &printer ); out << printer.Str(); return out; } std::string& operator<< (std::string& out, const TiXmlNode& base ) { TiXmlPrinter printer; printer.SetStreamPrinting(); base.Accept( &printer ); out.append( printer.Str() ); return out; } #endif TiXmlHandle TiXmlHandle::FirstChild() const { if ( node ) { TiXmlNode* child = node->FirstChild(); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const { if ( node ) { TiXmlNode* child = node->FirstChild( value ); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChildElement() const { if ( node ) { TiXmlElement* child = node->FirstChildElement(); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const { if ( node ) { TiXmlElement* child = node->FirstChildElement( value ); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::Child( int count ) const { if ( node ) { int i; TiXmlNode* child = node->FirstChild(); for ( i=0; child && iNextSibling(), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const { if ( node ) { int i; TiXmlNode* child = node->FirstChild( value ); for ( i=0; child && iNextSibling( value ), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::ChildElement( int count ) const { if ( node ) { int i; TiXmlElement* child = node->FirstChildElement(); for ( i=0; child && iNextSiblingElement(), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const { if ( node ) { int i; TiXmlElement* child = node->FirstChildElement( value ); for ( i=0; child && iNextSiblingElement( value ), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) { return true; } bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) { return true; } bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { DoIndent(); buffer += "<"; buffer += element.Value(); for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) { buffer += " "; attrib->Print( 0, 0, &buffer ); } if ( !element.FirstChild() ) { buffer += " />"; DoLineBreak(); } else { buffer += ">"; if ( element.FirstChild()->ToText() && element.LastChild() == element.FirstChild() && element.FirstChild()->ToText()->CDATA() == false ) { simpleTextPrint = true; // no DoLineBreak()! } else { DoLineBreak(); } } ++depth; return true; } bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) { --depth; if ( !element.FirstChild() ) { // nothing. } else { if ( simpleTextPrint ) { simpleTextPrint = false; } else { DoIndent(); } buffer += ""; DoLineBreak(); } return true; } bool TiXmlPrinter::Visit( const TiXmlText& text ) { if ( text.CDATA() ) { DoIndent(); buffer += ""; DoLineBreak(); } else if ( simpleTextPrint ) { TIXML_STRING str; TiXmlBase::EncodeString( text.ValueTStr(), &str ); buffer += str; } else { DoIndent(); TIXML_STRING str; TiXmlBase::EncodeString( text.ValueTStr(), &str ); buffer += str; DoLineBreak(); } return true; } bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) { DoIndent(); declaration.Print( 0, 0, &buffer ); DoLineBreak(); return true; } bool TiXmlPrinter::Visit( const TiXmlComment& comment ) { DoIndent(); buffer += ""; DoLineBreak(); return true; } bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) { DoIndent(); buffer += "<"; buffer += unknown.Value(); buffer += ">"; DoLineBreak(); return true; }