diff --git a/Modules/Video/Core/include/itkImageToVideoFilter.h b/Modules/Video/Core/include/itkImageToVideoFilter.h new file mode 100644 index 000000000000..c36872effae0 --- /dev/null +++ b/Modules/Video/Core/include/itkImageToVideoFilter.h @@ -0,0 +1,165 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkImageToVideoFilter_h +#define itkImageToVideoFilter_h + +#include "itkImage.h" +#include "itkVideoSource.h" +#include "itkVideoStream.h" +#include "itkTemporalRegion.h" +#include "itkMacro.h" + +namespace itk +{ + +/** + *\class ImageToVideoFilter + * \brief Converts Image to VideoStream representation with a temporal axis + * + * ImageToVideoFilter is a ProcessObject for converting an Image to a VideoStream. + * The user provides an input image of at least two dimensions and specifies one axis to interpret + * as the temporal axis. Image slices along the designated axis are grafted into + * video frames in the output VideoStream. The filter assumes that temporal samples are axis-aligned + * (time cannot be rotated) and the temporal accessor index matches its spatial index (no permutation). + * + * Other than the specified temporal axis, axis order is preserved and orientation and spacing + * information is copied over to each VideoStream frame from the original Image. + * Image orientation is understood as having LPS (left-posterior-superior) interpretation. + * + * ImageToVideoFilter inherits from VideoSource to indicate that its output is a VideoStream. + * However, many methods are overridden to properly handle Image rather than VideoStream + * input to ImageToVideoFilter. + * + * \ingroup ITKVideoCore + */ +template >> +class ITK_TEMPLATE_EXPORT ImageToVideoFilter : public VideoSource +{ +public: + ITK_DISALLOW_COPY_AND_MOVE(ImageToVideoFilter); + + /** Standard class type aliases */ + using InputImageType = TInputImage; + using OutputVideoStreamType = TOutputVideoStream; + using Self = ImageToVideoFilter; + using Superclass = VideoSource; + using Pointer = SmartPointer; + using ConstPointer = SmartPointer; + using ConstWeakPointer = WeakPointer; + + /** Output type alias */ + using OutputFrameType = typename Superclass::OutputFrameType; + using OutputFrameSpatialRegionType = typename Superclass::OutputFrameSpatialRegionType; + using OutputTemporalRegionType = typename TOutputVideoStream::TemporalRegionType; + + /** Input type alias */ + using InputImagePointer = typename InputImageType::Pointer; + using InputImageConstPointer = typename InputImageType::ConstPointer; + using InputImageRegionType = typename InputImageType::RegionType; + using InputImagePixelType = typename InputImageType::PixelType; + using InputImageIndexType = typename InputImageType::IndexType; + static constexpr unsigned int InputImageDimension = TInputImage::ImageDimension; + + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(ImageToVideoFilter, VideoSource); + + /** Set the input Image for this process object */ + using Superclass::SetInput; + virtual void + SetInput(const InputImageType * image); + + virtual void + SetInput(unsigned int idx, const InputImageType * videoStream); + + /** Get the input Image for this process object */ + const InputImageType * + GetInput() const; + + const InputImageType * + GetInput(unsigned int idx) const; + + /** Allow the user to specify the axis in the input image that will + * correspond to the temporal axis in the output temporal object. */ + itkGetMacro(FrameAxis, IndexValueType); + itkSetMacro(FrameAxis, IndexValueType); + +protected: + ImageToVideoFilter(); + ~ImageToVideoFilter() override = default; + + void + PrintSelf(std::ostream & os, Indent indent) const override; + + /** Get a non-const version of the input for internal use when setting + * input's requested regions. This is the only time input should be modified + */ + InputImageType * + GetInput(); + + InputImageType * + GetInput(unsigned int idx); + + /** Set up the output VideoStream via spatial and temporal regions + * derived from the spatial regions of the input Image. + */ + void + GenerateOutputInformation() override; + + /** Override the Superclass::UpdateOutputInformation method + * so that the temporal output region is defined entirely within + * the GenerateOutputInformation method. + */ + void + UpdateOutputInformation() override; + + /** Generate the requested regions in the output VideoStream + * from the size of the available input Image. + */ + void + GenerateOutputRequestedRegion(DataObject * output) override; + + /** Override the default implementation of GenerateInputRequestedRegion from + * VideoSource so that we only get spatial regions from the image input. + */ + void + GenerateInputRequestedRegion() override; + + /** Graft pixel data from input image onto output video frames */ + void + GenerateData() override; + +private: + /** Index representing the axis accessor index to use for slicing the input Image + * into frames for the output VideoStream. + * Default to slowest-moving axis in the input Image. + */ + IndexValueType m_FrameAxis{ TInputImage::ImageDimension - 1 }; + +}; // end class ImageToVideoFilter + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +# include "itkImageToVideoFilter.hxx" +#endif + +#endif // itkImageToVideoFilter_h diff --git a/Modules/Video/Core/include/itkImageToVideoFilter.hxx b/Modules/Video/Core/include/itkImageToVideoFilter.hxx new file mode 100644 index 000000000000..331cdaedf718 --- /dev/null +++ b/Modules/Video/Core/include/itkImageToVideoFilter.hxx @@ -0,0 +1,284 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ +#ifndef itkImageToVideoFilter_hxx +#define itkImageToVideoFilter_hxx + +#include "itkImageToVideoFilter.h" + +#include "itkExtractImageFilter.h" +#include "itkMacro.h" +#include "itkRealTimeInterval.h" +#include "itkRealTimeStamp.h" + +namespace itk +{ +template +ImageToVideoFilter::ImageToVideoFilter() +{ + this->SetNumberOfRequiredInputs(1); +} + +template +void +ImageToVideoFilter::PrintSelf(std::ostream & os, Indent indent) const +{ + Superclass::PrintSelf(os, indent); +} + +template +void +ImageToVideoFilter::SetInput(const TInputImage * image) +{ + // We keep this const_cast because in actuality, we do want to be able to + // change the requested regions on the input so we need a non-const version + this->SetInput(0, const_cast(image)); +} + +template +void +ImageToVideoFilter::SetInput(unsigned int idx, const TInputImage * image) +{ + // We keep this const_cast because in actuality, we do want to be able to + // change the requested regions on the input so we need a non-const version + this->TemporalProcessObject::SetNthInput(idx, const_cast(image)); +} + +template +const TInputImage * +ImageToVideoFilter::GetInput() const +{ + if (this->GetNumberOfInputs() < 1) + { + return nullptr; + } + return static_cast(this->ProcessObject::GetInput(0)); +} + +template +const TInputImage * +ImageToVideoFilter::GetInput(unsigned int idx) const +{ + return static_cast(this->ProcessObject::GetInput(idx)); +} + +template +TInputImage * +ImageToVideoFilter::GetInput() +{ + return GetInput(0); +} + +template +TInputImage * +ImageToVideoFilter::GetInput(unsigned int idx) +{ + return static_cast(this->ProcessObject::GetInput(idx)); +} + +template +void +ImageToVideoFilter::GenerateOutputInformation() +{ + // Get the input + const InputImageType * input = this->GetInput(); + + // Get first input frame's largest possible spatial region + InputImageRegionType inputRegion = input->GetLargestPossibleRegion(); + + // Set the output spatial region from the input image's largest spatial region, + // discarding along the user-defined image axis + OutputFrameSpatialRegionType outputSpatialRegion; + typename OutputFrameSpatialRegionType::IndexValueType outputIdx = 0; + for (IndexValueType idx = 0; idx < InputImageRegionType::ImageDimension; idx++) + { + if (idx != m_FrameAxis) + { + SizeValueType axisSize = inputRegion.GetSize(idx); + IndexValueType axisStart = inputRegion.GetIndex(idx); + outputSpatialRegion.SetSize(outputIdx, axisSize); + outputSpatialRegion.SetIndex(outputIdx, axisStart); + ++outputIdx; + } + } + + // Propagate this spatial region to output frames + this->GetOutput()->SetAllLargestPossibleSpatialRegions(outputSpatialRegion); + this->GetOutput()->SetRequestedRegionToLargestPossibleRegion(); + + // Propagate physical spacing and origin + typename InputImageType::SpacingType inputSpacing = input->GetSpacing(); + typename InputImageType::PointType inputOrigin = input->GetOrigin(); + typename OutputVideoStreamType::FrameType::SpacingType outputSpacing; + typename OutputVideoStreamType::FrameType::PointType outputOrigin; + for (IndexValueType inputIdx = 0, outputIdx = 0; inputIdx < InputImageType::ImageDimension; inputIdx++) + { + if (inputIdx != m_FrameAxis) + { + outputSpacing.SetElement(outputIdx, inputSpacing.GetElement(inputIdx)); + outputOrigin.SetElement(outputIdx, inputOrigin.GetElement(inputIdx)); + outputIdx++; + } + } + this->GetOutput()->SetAllFramesSpacing(outputSpacing); + + // Propagate spatial direction + typename InputImageType::DirectionType inputDirection = input->GetDirection(); + typename OutputVideoStreamType::FrameType::DirectionType outputDirection; + for (IndexValueType iinput = 0, ioutput = 0; iinput < InputImageType::ImageDimension; ++iinput) + { + for (IndexValueType jinput = 0, joutput = 0; jinput < InputImageType::ImageDimension; ++jinput) + { + if (iinput != m_FrameAxis && jinput != m_FrameAxis) + { + outputDirection(ioutput, joutput) = inputDirection(iinput, jinput); + ++joutput; + } + // Check that time is axis-aligned and accessor index matches spatial axis order + else if (iinput == jinput) + { + itkAssertOrThrowMacro(inputDirection(iinput, jinput) == 1, "Not axis aligned!"); + } + else + { + itkAssertOrThrowMacro(inputDirection(iinput, jinput) == 0, "Not axis aligned!"); + } + } + if (iinput != m_FrameAxis) + { + ++ioutput; + } + } + this->GetOutput()->SetAllFramesDirection(outputDirection); + + + // Set temporal frame + duration from user-defined frame axis in input image + OutputTemporalRegionType outputTemporalRegion; + outputTemporalRegion.SetFrameStart(inputRegion.GetIndex(m_FrameAxis)); + outputTemporalRegion.SetFrameDuration(inputRegion.GetSize(m_FrameAxis)); + + // TODO + // Interpret input temporal axis spatial spacing + origin in time domain + // RealTimeStamp realStart(static_cast(inputOrigin[m_FrameAxis]), + // static_cast(std::fmod(inputOrigin[m_FrameAxis],1) * 1e6)); + + auto realDurationRaw = inputOrigin[m_FrameAxis] + inputSpacing[m_FrameAxis] * inputRegion.GetSize(m_FrameAxis); + RealTimeInterval realDuration( + static_cast(realDurationRaw), + static_cast(std::fmod(realDurationRaw, 1) * 1e6)); + + // outputTemporalRegion.SetRealStart(realStart); + outputTemporalRegion.SetRealDuration(realDuration); + this->GetOutput()->SetLargestPossibleTemporalRegion(outputTemporalRegion); +} + +// The default implementation of UpdateOutputInformation in VideoSource attempts to set the +// largest possible output temporal region from the input, but this is best handled +// in GenerateOutputInformation(). Here we override to call the base +// UpdateOutputInformation() implementation in ProcessObject. +template +void +ImageToVideoFilter::UpdateOutputInformation() +{ + ProcessObject::UpdateOutputInformation(); +} + +template +void +ImageToVideoFilter::GenerateOutputRequestedRegion(DataObject * output) +{ + + // Check that output is a VideoStream object + auto * vsOutput = dynamic_cast(output); + if (vsOutput == nullptr) + { + itkExceptionMacro(<< "itk::ImageToVideoFilter::GenerateOutputRequestedRegion() " + << "cannot cast " << typeid(output).name() << " to " << typeid(OutputVideoStreamType *).name()); + } + + vsOutput->SetRequestedTemporalRegion(vsOutput->GetLargestPossibleTemporalRegion()); + + // Go through the requested temporal region and for any frame that doesn't + // have a requested spatial region, set it to the largest possible + SizeValueType outFrameStart = vsOutput->GetRequestedTemporalRegion().GetFrameStart(); + SizeValueType outFrameDuration = vsOutput->GetRequestedTemporalRegion().GetFrameDuration(); + for (SizeValueType i = outFrameStart; i < outFrameStart + outFrameDuration; ++i) + { + // Get the requested spatial region for this frame + OutputFrameSpatialRegionType spatialRegion = vsOutput->GetFrameRequestedSpatialRegion(i); + + // Check if the region has 0 size for all dimensions + bool validRegion = false; + for (unsigned int j = 0; j < OutputFrameType::ImageDimension; ++j) + { + if (spatialRegion.GetSize()[j]) + { + validRegion = true; + break; + } + } + + // If region has zero size, set it to match the largest possible region + if (!validRegion) + { + vsOutput->SetFrameRequestedSpatialRegion(i, vsOutput->GetFrameLargestPossibleSpatialRegion(i)); + } + } +} + +template +void +ImageToVideoFilter::GenerateInputRequestedRegion() +{ + this->GetInput()->SetRequestedRegion(this->GetInput()->GetLargestPossibleRegion()); +} + +template +void +ImageToVideoFilter::GenerateData() +{ + // Rely on SuperClass implementation to allocate output frames + this->AllocateOutputs(); + + // Set each frame in output to an image slice in the input image + InputImageType * input = this->GetInput(); + InputImageRegionType inputRegion = input->GetLargestPossibleRegion(); + + // Graft input image slices onto output frames + OutputVideoStreamType * output = this->GetOutput(); + SizeValueType outputStartFrame = output->GetRequestedTemporalRegion().GetFrameStart(); + SizeValueType outputDuration = output->GetRequestedTemporalRegion().GetFrameDuration(); + for (auto idx = outputStartFrame; idx < outputStartFrame + outputDuration; idx++) + { + InputImageRegionType inputSliceRegion = inputRegion; + inputSliceRegion.SetSize(m_FrameAxis, 0); + inputSliceRegion.SetIndex(m_FrameAxis, idx); + + using ExtractFilterType = typename itk::ExtractImageFilter; + typename ExtractFilterType::Pointer extractFilter = ExtractFilterType::New(); + extractFilter->SetDirectionCollapseToSubmatrix(); + + extractFilter->SetInput(input); + extractFilter->SetExtractionRegion(inputSliceRegion); + extractFilter->Update(); + + output->GetFrame(idx)->Graft(extractFilter->GetOutput()); + } +} +} // end namespace itk + +#endif // itkImageToVideoFilter_hxx diff --git a/Modules/Video/Core/include/itkTemporalRegion.h b/Modules/Video/Core/include/itkTemporalRegion.h index ece65f12a087..26b807fe579c 100644 --- a/Modules/Video/Core/include/itkTemporalRegion.h +++ b/Modules/Video/Core/include/itkTemporalRegion.h @@ -105,10 +105,15 @@ class ITKVideoCore_EXPORT TemporalRegion : public Region PrintSelf(std::ostream & os, Indent indent) const override; /** Time boundaries */ - RealTimeStamp m_RealStart; + /** Timestamp corresponding to the first frame in the region. */ + RealTimeStamp m_RealStart; + /** Time interval corresponding to the entire length of time + * represented by the region over ALL frames */ RealTimeInterval m_RealDuration; - FrameOffsetType m_FrameStart{ 0 }; - FrameOffsetType m_FrameDuration{ 0 }; + /** Index of the first frame in the region */ + FrameOffsetType m_FrameStart{ 0 }; + /** Total number of frames represented by the region (NOT individual frame duration) */ + FrameOffsetType m_FrameDuration{ 0 }; }; // end class TemporalRegion diff --git a/Modules/Video/Core/include/itkVideoSource.h b/Modules/Video/Core/include/itkVideoSource.h index 25717ca0609e..cd0dbd3f646f 100644 --- a/Modules/Video/Core/include/itkVideoSource.h +++ b/Modules/Video/Core/include/itkVideoSource.h @@ -32,7 +32,7 @@ namespace itk * produce a VideoStream as their output. This class defines GetOutput() which * returns a pointer to the VideoStream object on output port 0. * - * The other roll that VideoSource plays is to implement the framework for + * The other role that VideoSource plays is to implement the framework for * spatial streaming to complement the temporal streaming implemented in * TemporalProcessObject. This implementation mirrors the implementation in * ImageSource except that each thread will be able to operate on a spatial diff --git a/Modules/Video/Core/test/Baseline/HeadMRVolume_Frame0.mha.sha512 b/Modules/Video/Core/test/Baseline/HeadMRVolume_Frame0.mha.sha512 new file mode 100644 index 000000000000..9772c6fd8ec4 --- /dev/null +++ b/Modules/Video/Core/test/Baseline/HeadMRVolume_Frame0.mha.sha512 @@ -0,0 +1 @@ +f71cd0f962709575eeafd6be9806149b36440bf75a2cce1c4f5241083d4d94829e65c8dbd48313ee6bd5c9cfa4de5fd49b09c197e8b69d1fa03f315f3a4f36eb diff --git a/Modules/Video/Core/test/CMakeLists.txt b/Modules/Video/Core/test/CMakeLists.txt index 4a89c7023688..941b4a8341da 100644 --- a/Modules/Video/Core/test/CMakeLists.txt +++ b/Modules/Video/Core/test/CMakeLists.txt @@ -1,5 +1,6 @@ itk_module_test() set(ITKVideoCoreTests + itkImageToVideoFilterTest.cxx itkRingBufferTest.cxx itkTemporalRegionTest.cxx itkTemporalDataObjectTest.cxx @@ -56,3 +57,12 @@ itk_add_test( NAME TemporalDataObjectTest COMMAND ITKVideoCoreTestDriver itkTemporalDataObjectTest ) + +itk_add_test( + NAME ImageToVideoFilterTest + COMMAND ITKVideoCoreTestDriver + --compare DATA{Baseline/HeadMRVolume_Frame0.mha} + ${ITK_TEST_OUTPUT_DIR}/HeadMRVolume_Frame0.mha + itkImageToVideoFilterTest + DATA{${ITK_DATA_ROOT}/Input/HeadMRVolumeCompressed.mha} + ${ITK_TEST_OUTPUT_DIR}/HeadMRVolume_Frame0.mha) diff --git a/Modules/Video/Core/test/itkImageToVideoFilterTest.cxx b/Modules/Video/Core/test/itkImageToVideoFilterTest.cxx new file mode 100644 index 000000000000..3d1bb6b8fa51 --- /dev/null +++ b/Modules/Video/Core/test/itkImageToVideoFilterTest.cxx @@ -0,0 +1,130 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +#include "itkImageToVideoFilter.h" + +#include + +#include "itkImage.h" +#include "itkVideoStream.h" + +#include "itkImageFileReader.h" +#include "itkImageFileWriter.h" +#include "itkImageSliceConstIteratorWithIndex.h" +#include "itkImageLinearConstIteratorWithIndex.h" +#include "itkTestingMacros.h" + +int +itkImageToVideoFilterTest(int argc, char * argv[]) +{ + if (argc < 3) + { + std::cerr << "Usage: " << argv[0]; + std::cerr << " inputFile outputFile"; + std::cerr << std::endl; + return EXIT_FAILURE; + } + + using PixelType = float; + const unsigned int Dimension = 3; + + using ImageType = itk::Image; + + // Get 3D image to represent a temporal dataset of 2D frames + const auto inputImage = itk::ReadImage(argv[1]); + typename ImageType::DirectionType inputDirection; + /* Set input image direction matrix to + * 1 0 0 + * 0 0 1 + * 0 -1 0 + * with axis 0 representing time and others representing spatial dimensions + */ + inputDirection(0, 0) = 1; + inputDirection(1, 2) = 1; + inputDirection(2, 1) = -1; + inputImage->SetDirection(inputDirection); + + using VideoFilterType = itk::ImageToVideoFilter; + VideoFilterType::Pointer videoFilter = VideoFilterType::New(); + videoFilter->SetInput(inputImage); + // Arbitrarily set 0th axis as temporal dimension to split frames + itk::IndexValueType frameAxis = 0; + videoFilter->SetFrameAxis(frameAxis); + + ITK_TRY_EXPECT_NO_EXCEPTION(videoFilter->Update()); + + auto videoOutput = videoFilter->GetOutput(); + auto imageRegion = inputImage->GetLargestPossibleRegion(); + + // Verify start frame and frame duration in output match size of designated temporal axis in input + ITK_TEST_EXPECT_EQUAL( + static_cast(videoOutput->GetLargestPossibleTemporalRegion().GetFrameStart()), + static_cast(inputImage->GetLargestPossibleRegion().GetIndex(frameAxis))); + ITK_TEST_EXPECT_EQUAL( + static_cast(videoOutput->GetLargestPossibleTemporalRegion().GetFrameDuration()), + static_cast(inputImage->GetLargestPossibleRegion().GetSize(frameAxis))); + + // Verify spatial dimensions, spacing, and origin in output frames match size of non-temporal axes in input + for (itk::IndexValueType idx = 1; idx < ImageType::ImageDimension; idx++) + { + ITK_TEST_EXPECT_EQUAL(videoOutput->GetFrame(0)->GetLargestPossibleRegion().GetSize(idx - 1), + inputImage->GetLargestPossibleRegion().GetSize(idx)); + ITK_TEST_EXPECT_EQUAL(videoOutput->GetFrame(0)->GetSpacing()[idx - 1], inputImage->GetSpacing()[idx]); + ITK_TEST_EXPECT_EQUAL(videoOutput->GetFrame(0)->GetOrigin()[idx - 1], inputImage->GetOrigin()[idx]); + } + + // Verify spatial direction in output frames match input direction + typename VideoFilterType::OutputFrameType::DirectionType outputDirection; + outputDirection(0, 1) = 1; + outputDirection(1, 0) = -1; + for (auto frameIdx = videoOutput->GetLargestPossibleTemporalRegion().GetFrameStart(); + frameIdx < videoOutput->GetLargestPossibleTemporalRegion().GetFrameDuration(); + ++frameIdx) + { + ITK_TEST_EXPECT_EQUAL(videoOutput->GetFrame(frameIdx)->GetDirection(), outputDirection); + } + + // Iterate over 3D input + video output to verify pixel data matches across each slice/frame + using ImageIteratorType = typename itk::ImageSliceConstIteratorWithIndex; + ImageIteratorType it(inputImage, inputImage->GetLargestPossibleRegion()); + it.SetFirstDirection(1); // fastest moving remaining spatial axis + it.SetSecondDirection(2); // second-fastest moving remaining spatial axis + it.GoToBegin(); + + while (!it.IsAtEnd()) + { + while (!it.IsAtEndOfSlice()) + { + while (!it.IsAtEndOfLine()) + { + auto idx = it.GetIndex(); + auto frame = videoOutput->GetFrame(idx[frameAxis]); + ITK_TEST_EXPECT_EQUAL(frame->GetPixel({ idx[1], idx[2] }), it.Get()); + + ++it; + } + it.NextLine(); + } + it.NextSlice(); + } + + // Write out one frame from output VideoStream for baseline comparison + ITK_TRY_EXPECT_NO_EXCEPTION(itk::WriteImage(videoOutput->GetFrame(0), argv[2])); + + return EXIT_SUCCESS; +} diff --git a/Modules/Video/Core/wrapping/itkImageToVideoFilter.wrap b/Modules/Video/Core/wrapping/itkImageToVideoFilter.wrap new file mode 100644 index 000000000000..beabb95995c4 --- /dev/null +++ b/Modules/Video/Core/wrapping/itkImageToVideoFilter.wrap @@ -0,0 +1,20 @@ +itk_wrap_include("itkImage.h") + +# Do not wrap for input of lowest dimension D as this would result in +# output frames of dimension D-1 which are not supported. +# Find minimum and remove from list +set(m_d "9999999") +foreach(d ${ITK_WRAP_IMAGE_DIMS}) + if(${d} LESS ${m_d}) + set(m_d ${d}) + endif() +endforeach() +string(REPLACE ${m_d} "" INPUT_DIMS "${ITK_WRAP_IMAGE_DIMS}") + +itk_wrap_class("itk::ImageToVideoFilter" POINTER) + foreach(d ${INPUT_DIMS}) + foreach(t ${WRAP_ITK_SCALAR}) + itk_wrap_template("I${ITKM_${t}}${d}" "itk::Image<${ITKT_${t}}, ${d}>") + endforeach() + endforeach() +itk_end_wrap_class() diff --git a/Modules/Video/Core/wrapping/itkTemporalProcessObject.wrap b/Modules/Video/Core/wrapping/itkTemporalProcessObject.wrap new file mode 100644 index 000000000000..a587b8dff514 --- /dev/null +++ b/Modules/Video/Core/wrapping/itkTemporalProcessObject.wrap @@ -0,0 +1 @@ +itk_wrap_simple_class("itk::TemporalProcessObject" POINTER) diff --git a/Modules/Video/Core/wrapping/itkVideoSource.wrap b/Modules/Video/Core/wrapping/itkVideoSource.wrap new file mode 100644 index 000000000000..185c81cc787e --- /dev/null +++ b/Modules/Video/Core/wrapping/itkVideoSource.wrap @@ -0,0 +1,10 @@ +itk_wrap_include("itkImage.h") +itk_wrap_include("itkVideoStream.h") + +itk_wrap_class("itk::VideoSource" POINTER) + foreach(d ${ITK_WRAP_IMAGE_DIMS}) + foreach(t ${WRAP_ITK_SCALAR}) + itk_wrap_template("VSI${ITKM_${t}}${d}" "itk::VideoStream>") + endforeach() + endforeach() +itk_end_wrap_class()