#include "FusionHelper.h" /// /// 构造函数 /// FusionHelper::FusionHelper() { _fixedImage = VolumeImageType::New(); _movingImage = VolumeImageType::New(); _fixedImageImpporter = ImportFilterType::New(); _movingImageImporter = ImportFilterType::New(); _spacing = 0; _colorType = Gray8; } /// /// 析构函数 /// FusionHelper::~FusionHelper() { } /// /// 读入基础体数据 /// /// bool FusionHelper::LoadBaseVolumeData(UniformVolumeDataInfo baseVolumeInfo) { try { _spacing = baseVolumeInfo.spacing; _colorType = baseVolumeInfo.colorType; _fixedImageImpporter = CreateITKImageFromImportVolumeDatas(baseVolumeInfo,true); _fixedImage = _fixedImageImpporter->GetOutput(); _fixedImage->Update(); #if ENABLE_OBSERVE WriteMhaImage(_fixedImage, "fixedImage"); #endif return true; } catch (const std::exception& ex) { ErrorMsg::SetErrorMsg(FusionError, { ex.what() }); return false; } } /// /// 将已有的体数据与另一个融合在一起 /// /// bool FusionHelper::FuseWithAnotherVolumeData( UniformVolumeDataInfo anotherVolumeInfo) { try { // 判断spacing是否一致 if (anotherVolumeInfo.spacing != _spacing) { char strMsgBuff[32]; std::snprintf(strMsgBuff, 32, "unexpected spacing value ( %f ) ", anotherVolumeInfo.spacing); ErrorMsg::SetErrorMsg(FusionError, { strMsgBuff }); return false; } // 创建移动图像 _movingImageImporter = CreateITKImageFromImportVolumeDatas(anotherVolumeInfo); _movingImage = _movingImageImporter->GetOutput(); _movingImage->Update(); #if ENABLE_OBSERVE WriteMhaImage(_movingImage, "movingImage"); #endif //// 第一阶段配准,平移变换,归一化互相关测度,将两个体数据在Z轴对齐 auto parameters3d = Get3DTransformationsWithCorrelationMetrics(); const double translation3dX = parameters3d[0]; const double translation3dY = parameters3d[1]; const double translation3dZ = parameters3d[2]; //const double translation3dX = -0.28574895842683723; //const double translation3dY = 0.27410058291910605; //const double translation3dZ = 0.055393289309434761; ////第二阶段配准。根据第一阶段得到的Z轴偏移量,找到左右两侧体数据的Z轴中心位置附近的断面2D图像 ////2D平移变换 作为变换在X-O-Y平面内配准 ////采用梯度差值测度 Gradient Difference,该测度在旧的ITK配准框架里 auto parameter2d = Get2DTransformationsWithGradientDifferenceMetric(translation3dZ); const double translation2dX = parameter2d[0]; const double translation2dY = parameter2d[1]; //const double translation2dX = 0.38425032458639652; //const double translation2dY = -0.0059800695074633745; // ToDo 观察拼接缝隙处 // 对3D体数据进行重采样 // 根据配准计算得到的变换矩阵,对移动图像进行重采样,此时两个体数据在世界坐标对齐 // 注:配准过程是在世界坐标系下进行的,每进行一次变换,图像中的每个像素都按照矩阵M在世界坐标系中做一次重采样 // 一般情况下,是直接将移动图像根据计算得到的变换矩阵进行变换,然后将其与固定图像拼在一起即可 // 但考虑到一般图像的边缘处成像效果都不是很好(和皮肤接触的不够好,可能有黑色的区域), // 如果直接将移动图像进行平移后拼接,可能会在接缝处有黑色区域,影响视觉效果 // 因此,这里将原本应该全部由移动图像进行的平移,分一部分给固定图像,将两个图像分别移动一部分,这样接缝处不会有黑色区域,整体视觉效果会比较好 // 如果resampleFixedImage为false,则全部由移动图像完成平移\ // 设置固定图像的平移初值(固定不动,全0) Transform3DType::OutputVectorType translation3dFixed; translation3dFixed[1] = 0.0; translation3dFixed[2] = 0.0; // 设置移动图像的平移初值 Transform3DType::OutputVectorType translation3dMoving; // 考虑到扫查时,y方向都是贴紧皮肤的,可以认为Y方向偏移量为0 translation3dMoving[1] = translation2dY; translation3dMoving[2] = translation3dZ; // 一般情况下, // 如果translation2dX>0,直接将移动图像X方向裁掉abs(translation2dX),然后将其与固定图像拼在一起即可 // 如果translation2dX<0, 直接将固定图像X方向裁掉abs(translation2dX),然后将其与固定图像拼在一起即可 // 但考虑到一般图像的边缘处成像效果都不是很好(和皮肤接触的不够好,可能有黑色的区域), // 如果直接将移动图像进行平移后拼接,可能会在接缝处有黑色区域,影响视觉效果 // 因此,这里将原本应该全部由移动图像进行的平移,分一部分给固定图像,将两个图像分别移动一部分,这样接缝处不会有黑色区域,整体视觉效果会比较好 // 如果要移动的距离太小(小于transThreshold),则不再将平移量分给两个图像分别进行,由一个图像完成即可 bool resampleFixedImage = true; double transThreshold = 3 * _spacing; double absTranslation2dX = std::abs(translation2dX); if(translation2dX <0) { if(absTranslation2dX >= transThreshold) { translation3dFixed[0] = absTranslation2dX * 0.7; translation3dMoving[0] = absTranslation2dX * 0.3; } else { translation3dFixed[0] = absTranslation2dX; translation3dMoving[0] = 0; } } if(translation2dX ==0) { translation3dFixed[0] = 0; translation3dMoving[0] = 0; resampleFixedImage = false; } if(translation2dX > 0) { if(absTranslation2dX >= transThreshold) { translation3dFixed[0] = absTranslation2dX * 0.3; translation3dMoving[0] = absTranslation2dX * 0.7; } else { translation3dFixed[0] = 0; translation3dMoving[0] = absTranslation2dX; resampleFixedImage = false; } } // 分别对两幅图像进行平移变换 // 平移固定图像 VolumeImageType::Pointer translatedFixedImage; if(resampleFixedImage) { translatedFixedImage = Translate3DImage(_fixedImage, translation3dFixed); } else { translatedFixedImage = _fixedImage; } // 平移移动图像 VolumeImageType::Pointer translatedMovingImage = Translate3DImage(_movingImage, translation3dMoving); #if ENABLE_OBSERVE WriteMhaImage(translatedFixedImage, "translatedFixImage"); WriteMhaImage(translatedMovingImage, "translatedMovingImage"); #endif // 将两幅图拼接在一起,拼好后的图像直接替换fixedImage(以便继续下一次拼接) // 由于固定图像在创建的时候是翻转了的,因此这里得到的拼接图像也应该是翻转的 // 因此是movingImage在左,将movingImage翻转,fixedImage在右,fixedImage不翻转 _fixedImageImpporter = ConcatTwoVolumeImageAlongX(translatedMovingImage, true,translatedFixedImage,false); _fixedImage = _fixedImageImpporter->GetOutput(); _fixedImage->Update(); #if ENABLE_OBSERVE WriteMhaImage(_fixedImage, "fusedImage"); #endif return true; } catch (const std::exception& ex) { ErrorMsg::SetErrorMsg(FusionError, { ex.what() }); return false; } } /// /// 得到融合后的体数据 /// /// bool FusionHelper::GetFusedVolumeData(UniformVolumeDataInfo& fusedVolumeInfo) { try { // 将固定图像(即融合后的图像)沿着x轴翻转回来 using FlipFilterType = itk::FlipImageFilter; FlipFilterType::FlipAxesArrayType flipArray; flipArray[0] = 1; flipArray[1] = 0; flipArray[2] = 0; FlipFilterType::Pointer flipFilter = FlipFilterType::New(); flipFilter->SetFlipAxes(flipArray); flipFilter->SetInput(_fixedImage); VolumeImageType::Pointer flippedImageFixed = flipFilter->GetOutput(); flippedImageFixed->Update(); // 将得到的图像进行转换 using CastFilterType = itk::CastImageFilter; CastFilterType::Pointer caster = CastFilterType::New(); caster->SetInput(flippedImageFixed); OutputImageType::Pointer outputImage= caster->GetOutput(); outputImage->Update(); // 将融合后的图像复制到fusedVolumeInfo里 VolumeImageType::RegionType imgRegion = flippedImageFixed->GetLargestPossibleRegion(); VolumeImageType::SizeType imgSize = imgRegion.GetSize(); int x = imgSize[0]; int y = imgSize[1]; int z = imgSize[2]; OutputPixelType* imageData = outputImage->GetBufferPointer(); int pixelNum = x * y * z; // ToDo 暂未将图像类型转换成不同的ColorType,只支持8位灰度图 memcpy(fusedVolumeInfo.dataBuffer, imageData, pixelNum); fusedVolumeInfo.x = x; fusedVolumeInfo.y = y; fusedVolumeInfo.z = z; fusedVolumeInfo.spacing = _spacing; fusedVolumeInfo.colorType = _colorType; return true; } catch (const std::exception& ex) { ErrorMsg::SetErrorMsg(FusionError, { ex.what() }); return false; } } /// /// 从输入的VolumeData里创建ITKImage /// /// /// /// /// ImportFilterType::Pointer FusionHelper::CreateITKImageFromImportVolumeDatas(UniformVolumeDataInfo volumeInfo, bool flip) { uint8_t* dataBuffer = volumeInfo.dataBuffer; int x = volumeInfo.x; int y = volumeInfo.y; int z = volumeInfo.z; // region的size SizeType size; size[0] = x; size[1] = y; size[2] = z; // region的origin IndexType start; start.Fill(0); // region RegionType region; region.SetIndex(start); region.SetSize(size); ImportFilterType::Pointer importFilter = ImportFilterType::New(); importFilter->SetRegion(region); // origin OriginType origin[3] = { 0.0,0.0,0.0 }; importFilter->SetOrigin(origin); // spacing SpacingType spacing[3]; spacing[0] = volumeInfo.spacing; spacing[1] = volumeInfo.spacing; spacing[2] = volumeInfo.spacing; importFilter->SetSpacing(spacing); // 创建自己的Buffer(因为要把输入的volumeData里的数据转换一下数据类型) int numPixels = x * y * z; // 注意:itk采用SmartPointer(引用计数为0时,会自动调用Delete), // 如果itkOwnTheBuffer设为true,则由itk来管理所占用的空间, // 因为itkOwnTheBuffer为true,所以这里new出来的可以不用delete PixelType* localDataBuffer = new PixelType[numPixels]; int bytesPerPixel = ImageHelper::GetBytesPerPixel(volumeInfo.colorType); // ToDo 这里没有根据ColorType将彩色图像转换成所需的灰度值 if(flip) { for(int ni=0; ni(pixelVal); } } } else { for (int ni = 0; ni < numPixels; ni++) { auto pixelVal = dataBuffer[ni * bytesPerPixel]; localDataBuffer[ni] = static_cast(pixelVal); } } importFilter->SetImportPointer(localDataBuffer, numPixels, ItkOwnTheBuffer); // 返回图像 return importFilter; } /// /// 计算fixedImage和movingImage之间的平移 /// 用基于互相关的测度,用梯度下降的优化器 /// /// Transform3DType::ParametersType FusionHelper::Get3DTransformationsWithCorrelationMetrics() { using Optimizer3DType = itk::RegularStepGradientDescentOptimizerv4; using Metric3DType = itk::CorrelationImageToImageMetricv4; using Registration3DType = itk::ImageRegistrationMethodv4; // 设置regist3d Optimizer3DType::Pointer optimizer3d = Optimizer3DType::New(); Metric3DType::Pointer metric3d = Metric3DType::New(); Registration3DType::Pointer regist3d = Registration3DType::New(); regist3d->SetOptimizer(optimizer3d); regist3d->SetMetric(metric3d); // 设置初始值(xyz两个方向的初始值都设为0) // 读入两幅图的尺寸 Transform3DType::Pointer initTrans3d = Transform3DType::New(); Optimizer3DType::ParametersType initialPara3d(initTrans3d->GetNumberOfParameters()); initialPara3d[0] = 0.0; initialPara3d[1] = 0.0; initialPara3d[2] = 0.0; initTrans3d->SetParameters(initialPara3d); regist3d->SetMovingInitialTransform(initTrans3d); // 设置固定图像和移动图像 regist3d->SetFixedImage(_fixedImage); regist3d->SetMovingImage(_movingImage); regist3d->SetObjectName("TranslationRegistration"); // 设置收缩和平滑参数 constexpr unsigned int numberOfLevels = 1; Registration3DType::ShrinkFactorsArrayType shrinkFactorsPerLevel; shrinkFactorsPerLevel.SetSize(numberOfLevels); shrinkFactorsPerLevel[0] = 8; Registration3DType::SmoothingSigmasArrayType smoothingSigmasPerLevel1; smoothingSigmasPerLevel1.SetSize(numberOfLevels); smoothingSigmasPerLevel1[0] = 2.0; regist3d->SetNumberOfLevels(numberOfLevels); regist3d->SetShrinkFactorsPerLevel(shrinkFactorsPerLevel); regist3d->SetSmoothingSigmasPerLevel(smoothingSigmasPerLevel1); // 设置学习率和补偿 optimizer3d->SetLearningRate(20 * _spacing); optimizer3d->SetMinimumStepLength(0.5 * _spacing); //每次导数的方向突然改变时,优化器假定已经通过了一个局部极值, //并通过一个松弛因子(Relaxation Factor)来减小步长。取值范围 0~1,默认0.5 optimizer3d->SetRelaxationFactor(0.9); optimizer3d->SetNumberOfIterations(100); optimizer3d->SetReturnBestParametersAndValue(true); #if ENABLE_OBSERVE CommandIterationUpdate1::Pointer observer1 = CommandIterationUpdate1::New(); optimizer3d->AddObserver(itk::IterationEvent(), observer1); using TranslationCommandType = RegistrationInterfaceCommand1; TranslationCommandType::Pointer command1 = TranslationCommandType::New(); regist3d->AddObserver(itk::MultiResolutionIterationEvent(), command1); #endif regist3d->Update(); #if ENABLE_OBSERVE std::cout << std::endl; std::cout << "regist3d stop condition: " << regist3d->GetOptimizer()->GetStopConditionDescription() << std::endl; #endif Transform3DType::ConstPointer transTransform = regist3d->GetTransform(); Transform3DType::ParametersType parameters3d = transTransform->GetParameters(); #if ENABLE_OBSERVE const unsigned int numberOfIterations1 = optimizer3d->GetCurrentIteration(); const double bestValue1 = optimizer3d->GetValue(); const double translation3d_x = parameters3d[0]; const double translation3d_y = parameters3d[1]; const double translation3d_z = parameters3d[2]; std::cout << "3D transformation Result = " << std::endl; std::cout << " Iterations = " << numberOfIterations1 << std::endl; std::cout << " Metric value = " << bestValue1 << std::endl; std::cout << " Translation X = " << translation3d_x << std::endl; std::cout << " Translation Y = " << translation3d_y << std::endl; std::cout << " Translation Z = " << translation3d_z << std::endl; #endif return parameters3d; } /// /// 计算fixedImage和movingImage之间的平移 /// 用基于梯度差值的测度,用梯度下降的优化器 /// /// Transform2DType::ParametersType FusionHelper::Get2DTransformationsWithGradientDifferenceMetric(double translationZ) { // 第一步:抽取出z方向中心位置处的一张图(取_fixedImage和_moxingImage重叠部分的中间值) int pixelTransZ = std::abs(std::ceil(translationZ / _spacing)); // 先得到固定图像和移动图像的z VolumeImageType::RegionType fixedImgRegion = _fixedImage->GetLargestPossibleRegion(); VolumeImageType::SizeType fixedImgSize = fixedImgRegion.GetSize(); int fixedImgZ = fixedImgSize[2]; VolumeImageType::RegionType movingImgRegion = _movingImage->GetLargestPossibleRegion(); VolumeImageType::SizeType movingImgSize = movingImgRegion.GetSize(); int movingImgZ = movingImgSize[2]; // 如果固定图像和移动图像之间没有重合区域,则直接报错 int minImgZ = std::min(movingImgZ, fixedImgZ); if(pixelTransZ > minImgZ) { char strMsgBuff[32]; std::snprintf(strMsgBuff, 32, "translationZ ( %f ) out of range.", translationZ); throw std::invalid_argument(strMsgBuff); } int indexCenter = (pixelTransZ + minImgZ) /2; int indexInFix, indexInMoving; if(translationZ <0) { indexInFix = indexCenter; indexInMoving = indexCenter - pixelTransZ; } else { indexInFix = indexCenter + pixelTransZ; indexInMoving = indexCenter; } // 提取出固定图像和移动图像中所需的那一帧 auto sliceFixed = ExtractOneFrame(_fixedImage,indexInFix); auto sliceMoving = ExtractOneFrame(_movingImage,indexInMoving); sliceFixed->Update(); sliceMoving->Update(); #if ENABLE_OBSERVE WriteBmpImage(sliceFixed, "sliceInCenterFixed"); WriteBmpImage(sliceMoving, "sliceInCenterMoving"); #endif //// 第二步:把两幅图分别flip (为了让我们感兴趣的重叠区域在图像的左侧) // 注:因为改变策略,直接让固定图像翻转了,因此这里不用再翻转 //using FlipFilterType = itk::FlipImageFilter; //// 只翻转0轴,即x轴 //FlipFilterType::FlipAxesArrayType flipArray; //flipArray[0] = 1; //flipArray[1] = 0; //// 翻转固定图像 //FlipFilterType::Pointer filterFixed = FlipFilterType::New(); //filterFixed->SetFlipAxes(flipArray); //filterFixed->SetInput(sliceFixed); //SliceImageType::Pointer flipImageFixed = filterFixed->GetOutput(); //flipImageFixed->Update(); //// 翻转移动图像 //FlipFilterType::Pointer filterMoving = FlipFilterType::New(); //filterMoving->SetFlipAxes(flipArray); //filterMoving->SetInput(sliceMoving); //SliceImageType::Pointer flipImageMoving = filterMoving->GetOutput(); //flipImageMoving->Update(); // 第三步:对图像做高斯平滑 using GaussianFilterType = itk::DiscreteGaussianImageFilter; double sigma = 0.25; double variance = sigma * sigma; // 最大的核尺寸,默认是32 const unsigned int maxKernelWidth = 32; // 平滑固定图像 GaussianFilterType::Pointer gaussianFilterFixed = GaussianFilterType::New(); gaussianFilterFixed->SetInput(sliceFixed); gaussianFilterFixed->SetVariance(variance); gaussianFilterFixed->SetMaximumKernelWidth(maxKernelWidth); gaussianFilterFixed->Update(); SliceImageType::Pointer gaussianImageFixed = gaussianFilterFixed->GetOutput(); gaussianImageFixed->Update(); // 平滑移动图像 GaussianFilterType::Pointer gaussianFilterMoving = GaussianFilterType::New(); gaussianFilterMoving->SetInput(sliceMoving); gaussianFilterMoving->SetVariance(variance); gaussianFilterMoving->SetMaximumKernelWidth(maxKernelWidth); gaussianFilterMoving->Update(); SliceImageType::Pointer gaussianImageMoving = gaussianFilterMoving->GetOutput(); gaussianImageMoving->Update(); #if ENABLE_OBSERVE unsigned int dim = gaussianFilterFixed->GetFilterDimensionality(); itk::FixedArray sigmaArr = gaussianFilterFixed->GetSigmaArray(); double sigmaval = gaussianFilterFixed->GetSigma(); itk::FixedArray errors = gaussianFilterFixed->GetMaximumError(); int kernelWidth = gaussianFilterFixed->GetMaximumKernelWidth(); std::cout << " dim = " << dim << " sigmaArr = " << sigmaArr << " sigma1 = " << sigmaval << " errors = " << errors << " kernelWidth = " << kernelWidth << std::endl; WriteBmpImage(gaussianImageFixed, "gaussianImageFixed"); WriteBmpImage(gaussianImageMoving, "gaussianImageMoving"); #endif // 第四步 设置ROI using RoiFilterType = itk::RegionOfInterestImageFilter; float offsetX = 0.2f; int offsetXInPixel = offsetX * movingImgSize[0]; // 给固定图像设置ROI RoiFilterType::Pointer roiFilterFixed = RoiFilterType::New(); SliceImageType::RegionType roiFixed; SliceImageType::SizeType roiSizeFixed; roiSizeFixed[0] = fixedImgSize[0]; roiSizeFixed[1] = fixedImgSize[1]; roiSizeFixed[0] -= offsetXInPixel; SliceImageType::IndexType roiStartFixed; roiStartFixed.Fill(0); roiFixed.SetIndex(roiStartFixed); roiFixed.SetSize(roiSizeFixed); roiFilterFixed->SetRegionOfInterest(roiFixed); roiFilterFixed->SetInput(gaussianImageFixed); SliceImageType::Pointer roiImageFixed = roiFilterFixed->GetOutput(); roiImageFixed->Update(); // 给移动图像设置ROI RoiFilterType::Pointer roiFilterMoving = RoiFilterType::New(); SliceImageType::RegionType roiMoving; SliceImageType::SizeType roiSizeMoving; roiSizeMoving[0] = movingImgSize[0]; roiSizeMoving[1] = movingImgSize[1]; roiSizeMoving[0] -= offsetXInPixel; SliceImageType::IndexType roiStartMoving; roiStartMoving.Fill(0); roiMoving.SetIndex(roiStartMoving); roiMoving.SetSize(roiSizeMoving); roiFilterMoving->SetRegionOfInterest(roiMoving); roiFilterMoving->SetInput(gaussianImageMoving); SliceImageType::Pointer roiImageMoving = roiFilterMoving->GetOutput(); roiImageMoving->Update(); // 第五步,将固定图像和移动图像进行2D配准 using Optimizer2DType = itk::RegularStepGradientDescentOptimizer; using Registration2DType = itk::ImageRegistrationMethod; using Metric2DType = itk::GradientDifferenceImageToImageMetric; using InterpolatorType = itk::LinearInterpolateImageFunction; // 配准相关参数初始化 Transform2DType::Pointer initTrans2d = Transform2DType::New(); Optimizer2DType::Pointer optimizer2d = Optimizer2DType::New(); InterpolatorType::Pointer interpolator2d = InterpolatorType::New(); Registration2DType::Pointer regist2d = Registration2DType::New(); Metric2DType::Pointer metric2d = Metric2DType::New(); // 设置配准参数 regist2d->SetOptimizer(optimizer2d); regist2d->SetTransform(initTrans2d); regist2d->SetInterpolator(interpolator2d); metric2d->SetDerivativeDelta(0.5); regist2d->SetMetric(metric2d); // 设置移动和固定图像 regist2d->SetFixedImage(roiImageFixed); regist2d->SetMovingImage(roiImageMoving); regist2d->SetFixedImageRegion(gaussianImageFixed->GetBufferedRegion()); // 设置初始值 Registration2DType::ParametersType initialPara2d(initTrans2d->GetNumberOfParameters()); initialPara2d[0] = 0.0; initialPara2d[1] = 0.0; regist2d->SetInitialTransformParameters(initialPara2d); // 设置步长 // 根据spacing来设置步长?(Danzel原来代码里写的1mm是0.1个像素,所以步长设为了1.5, 最小步长是0.1) optimizer2d->SetMaximumStepLength(15 * _spacing); optimizer2d->SetMinimumStepLength(0.5 * _spacing); optimizer2d->SetNumberOfIterations(100); optimizer2d->SetGradientMagnitudeTolerance(1e-40); optimizer2d->MaximizeOn(); #if ENABLE_OBSERVE CommandIterationUpdate2::Pointer observer = CommandIterationUpdate2::New(); optimizer2d->AddObserver(itk::IterationEvent(), observer); #endif regist2d->Update(); #if ENABLE_OBSERVE std::cout << "Optimizer stop condition: " << regist2d->GetOptimizer()->GetStopConditionDescription() << std::endl; #endif const Transform2DType::ParametersType parameters2d = regist2d->GetLastTransformParameters(); #if ENABLE_OBSERVE const unsigned int numberOfIterations2 = optimizer2d->GetCurrentIteration(); const double bestValue2 = optimizer2d->GetValue(); double translation2d_x = parameters2d[0]; double translation2d_y = parameters2d[1]; // Print out results std::cout << "在 X-O-Y 平面的配准 Result = " << std::endl; std::cout << " Iterations = " << numberOfIterations2 << std::endl; std::cout << " Metric value = " << bestValue2 << std::endl; std::cout << " Translation X = " << translation2d_x << std::endl; std::cout << " Translation Y = " << translation2d_y << std::endl; #endif return parameters2d; } /// /// 取其中的一帧图 /// /// /// /// SliceImageType::Pointer FusionHelper::ExtractOneFrame(VolumeImageType::Pointer image,int indexZ) { using FilterType = itk::ExtractImageFilter; FilterType::Pointer filter = FilterType::New(); filter->SetDirectionCollapseToSubmatrix(); filter->InPlaceOn(); RegionType imgRegion = image->GetLargestPossibleRegion(); SizeType size = imgRegion.GetSize(); size[2] = 0; IndexType start; start[0] = 0; start[1] = 0; start[2] = indexZ; RegionType desiredRegion; desiredRegion.SetSize(size); desiredRegion.SetIndex(start); filter->SetExtractionRegion(desiredRegion); filter->SetInput(image); SliceImageType::Pointer sliceImage = filter->GetOutput(); sliceImage->Update(); return sliceImage; } /// /// 将体数据写成mha格式的文件到指定路径 /// /// /// void FusionHelper::WriteMhaImage(VolumeImageType::Pointer imagePointer, std::string fileName) { // 创建目录 if(access(_outputFolderName.c_str(),0) == -1) { mkdir(_outputFolderName.c_str()); } std::string subFolder = _outputFolderName +"\\Mha"; if (access(subFolder.c_str(), 0) == -1) { mkdir(subFolder.c_str()); } using ImageIOType = itk::MetaImageIO; ImageIOType::Pointer metaIO = itk::MetaImageIO::New(); using WriteImageType = itk::Image; using CastSliceFilterType = itk::CastImageFilter; using WriterType = itk::ImageFileWriter; WriterType::Pointer writer = WriterType::New(); CastSliceFilterType::Pointer cast = CastSliceFilterType::New(); cast->SetInput(imagePointer); writer->SetImageIO(metaIO); writer->SetInput(cast->GetOutput()); writer->SetFileName(subFolder+"\\"+fileName+".mha"); writer->Update(); } /// /// 将切面图像写成bmp格式的文件到指定路径 /// /// /// void FusionHelper::WriteBmpImage(SliceImageType* image, std::string fileName) { // 创建目录 if (access(_outputFolderName.c_str(), 0) == -1) { mkdir(_outputFolderName.c_str()); } std::string subFolder = _outputFolderName + "\\Bmp"; if (access(subFolder.c_str(), 0) == -1) { mkdir(subFolder.c_str()); } using WriteImageType = itk::Image; using CastSliceFilterType = itk::CastImageFilter; using WriterType = itk::ImageFileWriter; WriterType::Pointer writer = WriterType::New(); CastSliceFilterType::Pointer cast = CastSliceFilterType::New(); cast->SetInput(image); writer->SetImageIO(itk::BMPImageIO::New()); writer->SetInput(cast->GetOutput()); writer->SetFileName(subFolder+"\\" + fileName + ".bmp"); writer->Update(); } /// /// 移动一幅图像 /// /// /// /// VolumeImageType::Pointer FusionHelper::Translate3DImage(VolumeImageType::Pointer image,Transform3DType::OutputVectorType translation) { // 平移固定图像 using ResampleFilterType = itk::ResampleImageFilter; OutputImageType* outputImage = nullptr; Transform3DType::Pointer transform = Transform3DType::New(); transform->SetOffset(translation); ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetTransform(transform); resampler->SetInput(image); VolumeImageType::RegionType imgRegion = image->GetLargestPossibleRegion(); VolumeImageType::SizeType imgSize = imgRegion.GetSize(); resampler->SetSize(imgSize); auto origin = image->GetOrigin(); resampler->SetOutputOrigin(origin); auto spacing = image->GetSpacing(); resampler->SetOutputSpacing(spacing); auto direction = image->GetDirection(); resampler->SetOutputDirection(direction); resampler->SetDefaultPixelValue(0); auto resultImage = resampler->GetOutput(); resultImage->Update(); return resultImage; } /// /// 沿着x轴方向将两个Itk三维图像直接拼到一起 /// /// /// /// ImportFilterType::Pointer FusionHelper::ConcatTwoVolumeImageAlongX(VolumeImageType::Pointer oneImage, bool flipOneImage, VolumeImageType::Pointer anotherImage, bool flipAnotherImage) { // 获取两幅图像的数据指针 PixelType* imageDataOne = oneImage->GetBufferPointer(); PixelType* imageDataAnother = anotherImage->GetBufferPointer(); // 获取两幅图像的尺寸信息 VolumeImageType::RegionType imgRegionOne = oneImage->GetLargestPossibleRegion(); VolumeImageType::SizeType imgSizeOne = imgRegionOne.GetSize(); int sizeXOne = imgSizeOne[0]; int sizeYOne = imgSizeOne[1]; int sizeZOne = imgSizeOne[2]; VolumeImageType::RegionType imgRegionAnother = anotherImage->GetLargestPossibleRegion(); VolumeImageType::SizeType imgSizeAnother = imgRegionAnother.GetSize(); int sizeXAnother = imgSizeAnother[0]; int sizeYAnother = imgSizeAnother[1]; int sizeZAnother = imgSizeAnother[2]; // 确定输出图像的尺寸 int sizeXConcat = sizeXOne + sizeXAnother; int sizeYConcat = std::max(sizeYOne, sizeYAnother); int sizeZConcat = std::max(sizeZOne, sizeZAnother); // 创建所需空间 int numPixels = sizeXConcat * sizeYConcat * sizeZConcat; PixelType* imageDataConcat = new PixelType[numPixels]; memset(imageDataConcat, 0, sizeof(PixelType) * numPixels); int indexDst = 0; int indexSrc = 0; // 拷贝第一幅图到指定位置(只能拷在图像范围内的像素值) for (int nz = 0; nz < sizeZOne; nz++) { for (int ny = 0; ny < sizeYOne; ny++) { for (int nx = 0; nx < sizeXOne; nx++) { if(flipOneImage) { indexSrc = nz * sizeYOne * sizeXOne + ny * sizeXOne + sizeXOne - nx; } else { indexSrc = nz * sizeYOne * sizeXOne + ny * sizeXOne + nx; } indexDst = nz * sizeYConcat * sizeXConcat + ny * sizeXConcat + nx; imageDataConcat[indexDst] = imageDataOne[indexSrc]; } } } // 拷贝第二幅图到指定位置(只能拷在图像范围内的像素值)将其直接贴在第一幅图的右侧 for (int nz = 0; nz < sizeZAnother; nz++) { for (int ny = 0; ny < sizeYAnother; ny++) { for (int nx = 0; nx < sizeXAnother; nx++) { if(flipAnotherImage) { indexSrc = nz * sizeYAnother * sizeXAnother + ny * sizeXAnother + sizeXAnother - nx; } else { indexSrc = nz * sizeYAnother * sizeXAnother + ny * sizeXAnother + nx; } indexDst = nz * sizeYConcat * sizeXConcat + ny * sizeXConcat + nx + sizeXOne; imageDataConcat[indexDst] = imageDataAnother[indexSrc]; } } } // 从已知dataBuffer里创建图像 ImportFilterType::Pointer importFilter = ImportFilterType::New(); // region的size SizeType size; size[0] = sizeXConcat; size[1] = sizeYConcat; size[2] = sizeZConcat; // region的origin IndexType start; start.Fill(0); // region RegionType region; region.SetIndex(start); region.SetSize(size); importFilter->SetRegion(region); // origin OriginType origin[3] = { 0.0,0.0,0.0 }; importFilter->SetOrigin(origin); // spacing SpacingType spacing[3]; spacing[0] = _spacing; spacing[1] = _spacing; spacing[2] = _spacing; importFilter->SetSpacing(spacing); // 图像区指针 importFilter->SetImportPointer(imageDataConcat, numPixels, ItkOwnTheBuffer); // 返回图像 return importFilter; }