#include "Export.h" #include "VolumeDataPreProcessor.h" /// /// 获取错误详情 /// /// /// /// /// extern "C" __declspec(dllexport) void GetErrorCodeAndMsg(ErrorCode & errorCode, char* errorMsg, const int errorMaxLen) { ErrorMsg::GetErrorMsg(errorCode, errorMsg, errorMaxLen); } /// /// 图像数据解码 /// /// /// /// /// /// extern "C" __declspec(dllexport) bool ImDataDecode(const uint8_t * srcImgData, const int srcDataSize, uint8_t * dstImgData, const int dstDataSize, ImreadModes imReadMode) { // 如果失败,已经在函数内部写了ErrorMsg了,不用再重复赋值 return _encodeDecode.Decode(srcImgData, srcDataSize, dstImgData, dstDataSize, imReadMode); } /// /// 图像数据编码 /// /// /// /// /// /// /// /// extern "C" __declspec(dllexport) bool ImDataEncode(ImageInfo srcImgInfo, ImwriteExtension extension, ImwriteParam * imwriteParams, int paramCount, uint8_t * dstImgData, int& dstDataSize) { // 如果失败,已经在函数内部写了ErrorMsg了,不用再重复赋值 return _encodeDecode.Encode(srcImgInfo, extension, imwriteParams, paramCount, dstImgData, dstDataSize); } /// /// 将直线扫查的数据转成均一立方体 /// /// /// /// extern "C" __declspec(dllexport) bool StraightScanDataToUniformCube(VolumeDataPreProcessorInfo volumeInfo, uint8_t * dstVolumeData) { try { // 创建容器 VolumeDataPreProcessor preprocessor = VolumeDataPreProcessor(2, volumeInfo); // 根据目标图像数量逐帧插值 // 考虑到是直线扫查得到的数据,因此在求每一个插值帧的每个像素点灰度时, // 相当于xy完全落在网格点处,只有z可能会落在前后帧之间,因此只需要在z方向取前后帧的xy位置处的灰度, // 直接进行线性插值即可获得所需灰度 int imgCount = volumeInfo.origImgCount; int dstImgCount = volumeInfo.desiredImgCount; if(dstImgCount <=1) { char strMsgBuff[32]; std::snprintf(strMsgBuff, 32, "unexpected desiredImgCount value ( %i ) ", dstImgCount); ErrorMsg::SetErrorMsg(StraightScanDataUniformError, { strMsgBuff }); return false; } float ratio = (imgCount -1) * 1.0f / (dstImgCount -1); int bytePerPixel = ImageHelper::GetBytesPerPixel(volumeInfo.colorType); int dstBytePerImg = volumeInfo.desiredImgWidth * volumeInfo.desiredImgHeight * bytePerPixel; uint8_t* pDstData; for (int ni = 0; ni < dstImgCount; ni++) { float position = ratio * ni; int posFront = floor(position); float posRatio = position - posFront; // 很靠近前一个点或后一个点,就直接只参考前一个点或后一个点的灰度即可 // 因为就算后一个点的灰度是255,乘以比值0.001后,对当前位置处也只有0.255的贡献,可以忽略不计 if (posRatio < 0.001) { posRatio = 0; } if (posRatio > 0.99) { posRatio = 1; } int posBack = posFront + 1; // 不要超过范围 posFront = std::min(imgCount-1,std::max(0, posFront)); posBack = std::min(imgCount - 1, std::max(0, posBack)); cv::Mat dstImg; // 只取前一帧图,直接复制即可 if (posRatio == 0) { dstImg = preprocessor.GetImage(posFront); } // 只取后一帧图,直接复制即可 else if (posRatio == 1) { dstImg = preprocessor.GetImage(posBack); } // 需要取前后各一帧,逐像素进行插值 else { auto frontImg = preprocessor.GetImage(posFront); auto backImg = preprocessor.GetImage(posBack); dstImg = UniformHelper::LinearInterpolation(frontImg, backImg, posRatio); } // 将dstImg中的数据复制到所需位置 pDstData = dstVolumeData + ni * dstBytePerImg; std::memcpy(pDstData, dstImg.data, dstBytePerImg); } return true; } catch (const std::exception& ex) { ErrorMsg::SetErrorMsg(StraightScanDataUniformError,{ ex.what() }); return false; } } /// /// 获取立方体的六个表面 /// /// /// /// /// extern "C" __declspec(dllexport) bool GetSurfacePicsFromUniformCube(UniformVolumeDataInfo volumeDataInfo, int surfaceNum, SurfacePicInfo * surfaceInfos) { try { int x = volumeDataInfo.x; int y = volumeDataInfo.y; int z = volumeDataInfo.z; float spacing = volumeDataInfo.spacing; // 创建SliceHelper SliceHelper _sliceHelper; // 把体数据加载到SliceHelper里 _sliceHelper.LoadVolumeData(volumeDataInfo); // 逐个表面获取 for(int ni=0; ni< surfaceNum;ni++) { auto surfaceInfo = surfaceInfos[ni]; auto surfaceType = surfaceInfo.surfaceType; auto imgInfo = surfaceInfo.imgInfo; AxisName axisName = X; int intersectionVal = 0; switch(surfaceType) { case Right: axisName = X; intersectionVal = x-1; break; case Left: axisName = X; intersectionVal = 0; break; case Behind: axisName = Z; intersectionVal = z-1; break; case Front: axisName = Z; intersectionVal = 0; break; case Top: axisName = Y; intersectionVal = 0; break; case Bottom: axisName = Y; intersectionVal = y-1; break; } _sliceHelper.GetVerticalToAxisSlicePlaneImage(axisName, intersectionVal,imgInfo); // 赋值回去 surfaceInfos[ni].imgInfo.width = imgInfo.width; surfaceInfos[ni].imgInfo.height = imgInfo.height; surfaceInfos[ni].imgInfo.colorType = imgInfo.colorType; int imgByteCounts = imgInfo.width * imgInfo.height * ImageHelper::GetBytesPerPixel(imgInfo.colorType); } return true; } catch(const std::exception & ex) { ErrorMsg::SetErrorMsg(GetSurfaceError, { ex.what() }); return false; } } /// /// 基于ITK的x方向融合 /// /// /// /// extern "C" __declspec(dllexport) bool ITKBasedFusionAlongX(UniformVolumeDataInfo * volumeDataInfos, int volumeDataCount, UniformVolumeDataInfo & fusedDataInfo) { if (volumeDataCount < 2) { char strMsgBuff[32]; std::snprintf(strMsgBuff, 32, "unexpected volumeDataCount ( %i ).", volumeDataCount); ErrorMsg::SetErrorMsg(FusionError, { strMsgBuff }); return false; } // 直接按照给入的顺序,第一个作为基础体数据,其他顺序与其融合 FusionHelper fusionHelper = FusionHelper(); const int baseVolumeIndex = 0; if (!fusionHelper.LoadBaseVolumeData(volumeDataInfos[baseVolumeIndex])) { // 已经在函数内部写了ErrorMsg了 return false; } for (int ni = 1; ni < volumeDataCount; ni++) { if(!fusionHelper.FuseWithAnotherVolumeData(volumeDataInfos[ni])) { // 已经在函数内部写了ErrorMsg了,不用再重复赋值 return false; } } // 取出融合后的结果 if(!fusionHelper.GetFusedVolumeData(fusedDataInfo)) { // 已经在函数内部写了ErrorMsg了,不用再重复赋值 return false; } return true; } /// /// 图像数据编码并保存至本地 /// /// /// /// /// /// /// extern "C" __declspec(dllexport) bool ImDataSave(ImageInfo srcImgInfo, ImwriteExtension extension, ImwriteParam * imwriteParams, int paramCount, const char* savePath) { // 如果失败,已经在函数内部写了ErrorMsg了,不用再重复赋值 return _encodeDecode.SaveImage(srcImgInfo, extension, imwriteParams, paramCount, savePath); }