bool configInit() { if( !initializeVolume( )) return false; initializeCache(); return true; }
// ----------------------------------------------------------------------------- // Main ICD reconstruction // ----------------------------------------------------------------------------- void BFReconstructionEngine::execute() { uint64_t totalTime = EIMTOMO_getMilliSeconds(); int32_t err = 0; std::stringstream ss; // int16_t i,j,k,Idx; size_t dims[3]; uint16_t maxNumberOfDetectorElts; uint8_t status; //set to 1 if ICD has converged Real_t checksum = 0, temp; RealImageType::Pointer voxelProfile; RealVolumeType::Pointer detectorResponse; RealVolumeType::Pointer h_t; RealVolumeType::Pointer y_Est; //Estimated Sinogram RealVolumeType::Pointer finalSinogram; //To store and write the final sinogram resulting from our reconstruction RealVolumeType::Pointer errorSino; //Error Sinogram std::string indent(""); //#ifdef COST_CALCULATE //Commented out because if not the code fails to run. std::string filepath(m_TomoInputs->tempDir); filepath = filepath.append(MXADir::getSeparator()).append(MBIR::Defaults::CostFunctionFile); CostData::Pointer cost = CostData::New(); cost->initOutputFile(filepath); //#endif #if OpenMBIR_USE_PARALLEL_ALGORITHMS tbb::task_scheduler_init init; #endif // Initialize the Sinogram if(m_TomoInputs == NULL) { setErrorCondition(-1); notify("Error: The TomoInput Structure was NULL. The proper API is to supply this class with that structure,", 100, Observable::UpdateErrorMessage); return; } //Based on the inputs , calculate the "other" variables in the structure definition if(m_Sinogram == NULL) { setErrorCondition(-1); notify("Error: The Sinogram Structure was NULL. The proper API is to supply this class with that structure,", 100, Observable::UpdateErrorMessage); return; } if(m_Geometry == NULL) { setErrorCondition(-1); notify("Error: The Geometry Structure was NULL. The proper API is to supply this class with that structure,", 100, Observable::UpdateErrorMessage); return; } if (m_ForwardModel.get() == NULL) { setErrorCondition(-1); notify("Error: The forward model was not set.", 100, Observable::UpdateErrorMessage); return; } // Read the Input data from the supplied data file - reconstruction from previous resolution ? err = readInputData(); if(err < 0) { return; } if (getCancel() == true) { setErrorCondition(-999); return; } if (getCancel() == true) { setErrorCondition(-999); return; } //Additional parameters associated with the system err = m_ForwardModel->createNuisanceParameters(m_Sinogram); if(err < 0) { return; } //Periodically checking if user has sent a cancel signal from the GUI if (getCancel() == true) { setErrorCondition(-999); return; } m_ForwardModel->printNuisanceParameters(m_Sinogram); //Take log of the input data after subtracting offset m_ForwardModel->processRawCounts(m_Sinogram); // Initialize the Geometry data from a rough reconstruction err = initializeRoughReconstructionData(); if(err < 0) { return; } if (getCancel() == true) { setErrorCondition(-999); return; } // Get the actual boundaries in the X Direction since we "Extend Object" which makes // the output mrc file much wider than they really need to be. uint16_t cropStart = 0; uint16_t cropEnd = m_Geometry->N_x; computeOriginalXDims(cropStart, cropEnd); // std::cout << "Crop Start: " << cropStart << std::endl; // std::cout << "Crop End: " << cropEnd << std::endl; m_ForwardModel->allocateNuisanceParameters(m_Sinogram); //#ifdef COST_CALCULATE // dims[0] = (m_TomoInputs->NumIter+1)*m_TomoInputs->NumOuterIter*3; //#endif dims[0] = m_Sinogram->N_theta; dims[1] = m_Sinogram->N_r; dims[2] = m_Sinogram->N_t; y_Est = RealVolumeType::New(dims, "y_Est");//y_Est = A*x_{initial} errorSino = RealVolumeType::New(dims, "ErrorSino");// y - y_est m_ForwardModel->weightInitialization(dims); //Initialize the \lambda matrix finalSinogram = RealVolumeType::New(dims, "Final Sinogram"); //Debug variable to store the final A*x_{Final} /*************** Computation of partial Amatrix *************/ //calculate the trapezoidal voxel profile for each angle.Also the angles in the Sinogram // Structure are converted to radians voxelProfile = calculateVoxelProfile(); //This is used for computation of the partial A matrix //Pre compute sine and cos theta to speed up computations DetectorParameters::Pointer haadfParameters = DetectorParameters::New(); haadfParameters->setOffsetR ( ((m_TomoInputs->delta_xz / sqrt(3.0)) + m_Sinogram->delta_r / 2) / m_AdvParams->DETECTOR_RESPONSE_BINS); haadfParameters->setOffsetT ( ((m_TomoInputs->delta_xz / 2) + m_Sinogram->delta_t / 2) / m_AdvParams->DETECTOR_RESPONSE_BINS); haadfParameters->setBeamWidth(m_Sinogram->delta_r); haadfParameters->calculateSinCos(m_Sinogram); //Initialize the e-beam haadfParameters->initializeBeamProfile(m_Sinogram, m_AdvParams); //The shape of the averaging kernel for the detector if (getCancel() == true) { setErrorCondition(-999); return; } //calculate sine and cosine of all angles and store in the global arrays sine and cosine DetectorResponse::Pointer dResponseFilter = DetectorResponse::New(); dResponseFilter->setTomoInputs(m_TomoInputs); dResponseFilter->setSinogram(m_Sinogram); dResponseFilter->setAdvParams(m_AdvParams); dResponseFilter->setDetectorParameters(haadfParameters); dResponseFilter->setVoxelProfile(voxelProfile); dResponseFilter->setObservers(getObservers()); dResponseFilter->setVerbose(getVerbose()); dResponseFilter->setVeryVerbose(getVeryVerbose()); dResponseFilter->execute(); if(dResponseFilter->getErrorCondition() < 0) { ss.str(""); ss << "Error Calling function detectorResponse in file " << __FILE__ << "(" << __LINE__ << ")" << std::endl; setErrorCondition(-2); notify(ss.str(), 100, Observable::UpdateErrorMessage); return; } detectorResponse = dResponseFilter->getResponse(); // Writer the Detector Response to an output file DetectorResponseWriter::Pointer responseWriter = DetectorResponseWriter::New(); responseWriter->setTomoInputs(m_TomoInputs); responseWriter->setSinogram(m_Sinogram); responseWriter->setAdvParams(m_AdvParams); responseWriter->setObservers(getObservers()); responseWriter->setResponse(detectorResponse); responseWriter->setVerbose(getVerbose()); responseWriter->setVeryVerbose(getVeryVerbose()); responseWriter->execute(); if(responseWriter->getErrorCondition() < 0) { ss.str(""); ss << __FILE__ << "(" << __LINE__ << ") " << "Error writing detector response to file." << std::endl; setErrorCondition(-3); notify(ss.str(), 100, Observable::UpdateErrorMessage); return; } if (getCancel() == true) { setErrorCondition(-999); return; } // Initialize H_t volume dims[0] = 1; dims[1] = m_Sinogram->N_theta; dims[2] = m_AdvParams->DETECTOR_RESPONSE_BINS; h_t = RealVolumeType::New(dims, "h_t"); initializeHt(h_t, haadfParameters->getOffsetT() ); checksum = 0; ss.str(""); ss << "Calculating A Matrix...."; notify(ss.str(), 0, Observable::UpdateProgressMessage); std::vector<AMatrixCol::Pointer> voxelLineResponse(m_Geometry->N_y); maxNumberOfDetectorElts = (uint16_t)((m_TomoInputs->delta_xy / m_Sinogram->delta_t) + 2); dims[0] = maxNumberOfDetectorElts; for (uint16_t i = 0; i < m_Geometry->N_y; i++) { AMatrixCol::Pointer vlr = AMatrixCol::New(dims, 0); voxelLineResponse[i] = vlr; } //Calculating A-Matrix one column at a time //For each entry the idea is to initially allocate space for Sinogram.N_theta * Sinogram.N_x // And then store only the non zero entries by allocating a new array of the desired size std::vector<AMatrixCol::Pointer> tempCol(m_Geometry->N_x * m_Geometry->N_z); checksum = 0; temp = 0; uint32_t voxel_count = 0; for (uint16_t z = 0; z < m_Geometry->N_z; z++) { for (uint16_t x = 0; x < m_Geometry->N_x; x++) { tempCol[voxel_count] = AMatrixCol::calculateAMatrixColumnPartial(m_Sinogram, m_Geometry, m_TomoInputs, m_AdvParams, z, x, 0, detectorResponse, haadfParameters); temp += tempCol[voxel_count]->count; if(0 == tempCol[voxel_count]->count ) { //If this line is never hit and the Object is badly initialized //set it to zero for (uint16_t y = 0; y < m_Geometry->N_y; y++) { m_Geometry->Object->setValue(0.0, z, x, y); } } voxel_count++; } } storeVoxelResponse(h_t, voxelLineResponse, haadfParameters); if (getCancel() == true) { setErrorCondition(-999); return; } if(getVerbose()) { printf("Number of non zero entries of the forward projector is %lf\n", temp); printf("Geometry-Z %d\n", m_Geometry->N_z); } /************ End of A matrix partial computations **********************/ //Gain and Offset Parameters Initialization of the forward model m_ForwardModel->gainAndOffsetInitialization(m_Sinogram->N_theta); initializeVolume(y_Est, 0.0); if (getCancel() == true) { setErrorCondition(-999); return; } //Forward Project Geometry->Object one slice at a time and compute the Sinogram for each slice // Forward Project using the Forward Model err = m_ForwardModel->forwardProject(m_Sinogram, m_Geometry, tempCol, voxelLineResponse, y_Est , errorSino); if (err < 0) { return; } if (getCancel() == true) { setErrorCondition(-999); return; } #ifdef FORWARD_PROJECT_MODE return 0; //exit the program once we finish forward projecting the object #endif//Forward Project mode // Initialize the Prior Model parameters - here we are using a QGGMRF Prior Model QGGMRF::QGGMRF_Values QGGMRF_values; QGGMRF::initializePriorModel(m_TomoInputs, &QGGMRF_values, NULL); #ifdef COST_CALCULATE err = calculateCost(cost, m_Sinogram, m_Geometry, errorSino, &QGGMRF_values); #endif //Cost calculation endif Real_t TempBraggValue, DesBraggValue; #ifdef BRAGG_CORRECTION if(m_TomoInputs->NumIter > 1) { //Get the value of the Bragg threshold from the User Interface the first time DesBraggValue = m_ForwardModel->getBraggThreshold(); if (getVeryVerbose()) {std::cout << "Desired Bragg threshold =" << DesBraggValue << std::endl;} TempBraggValue = DefBraggThreshold;//m_ForwardModel->getBraggThreshold(); m_ForwardModel->setBraggThreshold(TempBraggValue); //setting the Threshold T of \beta_{T,\delta} //the \delta value is set in BFMultiResolutionReconstruction.cpp } else { TempBraggValue = m_ForwardModel->getBraggThreshold(); m_ForwardModel->setBraggThreshold(TempBraggValue); } #endif //Bragg correction if (getVeryVerbose()) {std::cout << "Bragg threshold =" << TempBraggValue << std::endl;} //Generate a List if (getVeryVerbose()) {std::cout << "Generating a list of voxels to update" << std::endl;} //Takes all the voxels along x-z slice and forms a list m_VoxelIdxList = VoxelUpdateList::GenRegularList(m_Geometry->N_z, m_Geometry->N_x); //Randomize the list of voxels m_VoxelIdxList = VoxelUpdateList::GenRandList(m_VoxelIdxList); if(getVerbose()) { std::cout << "Initial random order list .." << std::endl; m_VoxelIdxList->printMaxList(std::cout); } // Create a copy of the Voxel Update List because we are going to adjust the VoxelUpdateList instance variable // with values for the array pointer and number of elements based on the iteration VoxelUpdateList::Pointer TempList = VoxelUpdateList::New(m_VoxelIdxList->numElements(), m_VoxelIdxList->getArray() ); uint32_t listselector = 0; //For the homogenous updates this cycles through the random list one sublist at a time //Initialize the update magnitude arrays (for NHICD mainly) & stopping criteria dims[0] = m_Geometry->N_z; //height dims[1] = m_Geometry->N_x; //width dims[2] = 0; RealImageType::Pointer magUpdateMap = RealImageType::New(dims, "Update Map for voxel lines"); RealImageType::Pointer filtMagUpdateMap = RealImageType::New(dims, "Filter Update Map for voxel lines"); UInt8Image_t::Pointer magUpdateMask = UInt8Image_t::New(dims, "Update Mask for selecting voxel lines NHICD");//TODO: Remove this variable. Under the new formulation not needed Real_t PrevMagSum = 0; magUpdateMap->initializeWithZeros(); filtMagUpdateMap->initializeWithZeros(); #if ROI //A mask to check the stopping criteria for the algorithm initializeROIMask(magUpdateMask); #endif //Debugging variable to check if all voxels are visited dims[0] = m_Geometry->N_z; dims[1] = m_Geometry->N_x; dims[2] = 0; UInt8Image_t::Pointer m_VisitCount = UInt8Image_t::New(dims, "VisitCount"); uint32_t EffIterCount = 0; //Maintains number of calls to updatevoxelroutine //Loop through every voxel updating it by solving a cost function for (int16_t reconOuterIter = 0; reconOuterIter < m_TomoInputs->NumOuterIter; reconOuterIter++) { ss.str(""); // Clear the string stream indent = ""; //The first time we may need to update voxels multiple times and then on //just optimize over I,d,sigma,f once each outer loop; //Each outer loop after the first outer iter updates a subset of the voxels //followed by other parameters if needed if(reconOuterIter > 0) { m_TomoInputs->NumIter = 1;//Inner iterations m_ForwardModel->setBraggThreshold(TempBraggValue); } for (int16_t reconInnerIter = 0; reconInnerIter < m_TomoInputs->NumIter; reconInnerIter++) { // If at the inner most loops at the coarsest resolution donot apply Bragg // Only at the coarsest scale the NumIter > 1 if(m_TomoInputs->NumIter > 1 && reconInnerIter == 0) { m_ForwardModel->setBraggThreshold(DefBraggThreshold); } #ifdef DEBUG //Prints the ratio of the sinogram entries selected m_ForwardModel->printRatioSelected(m_Sinogram); #endif //DEBUG ss.str(""); ss << "Outer Iterations: " << reconOuterIter << "/" << m_TomoInputs->NumOuterIter << " Inner Iterations: " << reconInnerIter << "/" << m_TomoInputs->NumIter << std::endl; indent = " "; #ifdef DEBUG // This is all done PRIOR to calling what will become a method if (getVeryVerbose()) {std::cout << "Bragg Threshold =" << m_ForwardModel->getBraggThreshold() << std::endl;} #endif //DEBUG // This could contain multiple Subloops also - voxel update /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */ #ifdef NHICD //NHICD alternates between updating a fixed subset of voxels and a //list created on the fly based on the previous iterations // Creating a sublist of voxels for the Homogenous // iterations - cycle through the partial sub lists if(EffIterCount % 2 == 0) //Even iterations == Homogenous update { listselector %= MBIR::Constants::k_NumHomogeniousIter;// A variable to cycle through the randmoized list of voxels int32_t nElements = floor((Real_t)TempList->numElements() / MBIR::Constants::k_NumHomogeniousIter); //If the number of voxels is NOT exactly divisible by NUM_NON .. then compensate and make the last of the lists longer if(listselector == MBIR::Constants::k_NumHomogeniousIter - 1) { nElements += (TempList->numElements() - nElements * MBIR::Constants::k_NumHomogeniousIter); if(getVeryVerbose()) { std::cout << "**********Adjusted number of voxel lines for last iter**************" << std::endl; std::cout << nElements << std::endl; std::cout << "**********************************" << std::endl; } } //Set the list array for updating voxels int32_t start = (uint32_t)(floor(((Real_t)TempList->numElements() / MBIR::Constants::k_NumHomogeniousIter)) * listselector); m_VoxelIdxList = TempList->subList(nElements, start); //m_VoxelIdxList.Array = &(TempList.Array[]); if (getVeryVerbose()) { std::cout << "Partial random order list for homogenous ICD .." << std::endl; m_VoxelIdxList->printMaxList(std::cout); } listselector++; //printList(m_VoxelIdxList); } #endif //NHICD #ifdef DEBUG Real_t TempSum = 0; for (int32_t j = 0; j < m_Geometry->N_z; j++) { for (int32_t k = 0; k < m_Geometry->N_x; k++) { TempSum += magUpdateMap->getValue(j, k); } } if (getVeryVerbose()) { std::cout << "**********************************************" << std::endl; std::cout << "Average mag prior to calling update voxel = " << TempSum / (m_Geometry->N_z * m_Geometry->N_x) << std::endl; std::cout << "**********************************************" << std::endl; } #endif //debug status = updateVoxels(reconOuterIter, reconInnerIter, tempCol, errorSino, voxelLineResponse, cost, &QGGMRF_values, magUpdateMap, filtMagUpdateMap, magUpdateMask, m_VisitCount, PrevMagSum, EffIterCount); #ifdef NHICD if(EffIterCount % MBIR::Constants::k_NumNonHomogeniousIter == 0) // At the end of half an //equivalent iteration compute average Magnitude of recon to test //stopping criteria { PrevMagSum = roiVolumeSum(magUpdateMask); if (getVeryVerbose()) {std::cout << " Previous Magnitude of the Recon = " << PrevMagSum << std::endl;} } #else PrevMagSum = roiVolumeSum(magUpdateMask); std::cout << " Previous Magnitude of the Recon = " << PrevMagSum << std::endl; #endif //NHICD //Debugging information to test if every voxel is getting touched #ifdef NHICD //Debug every equivalent iteration if(EffIterCount % (2 * MBIR::Constants::k_NumNonHomogeniousIter) == 0 && EffIterCount > 0) #endif //NHICD { for (int16_t j = 0; j < m_Geometry->N_z; j++) { for (int16_t k = 0; k < m_Geometry->N_x; k++) { if(m_VisitCount->getValue(j, k) == 0) { printf("Pixel (%d %d) not visited\n", j, k); } } } //Reset the visit counter once we have cycled through //all voxels once via the homogenous updates for (int16_t j = 0; j < m_Geometry->N_z; j++) { for (int16_t k = 0; k < m_Geometry->N_x; k++) { m_VisitCount->setValue(0, j, k); } } } //end of debug EffIterCount++; //cycles between the two types of voxel updates /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */ if(status == 0) { break; //stop inner loop if we have hit the threshold value for x } // Check to see if we are canceled. if (getCancel() == true) { setErrorCondition(-999); return; } // Write out the MRC File ; If NHICD only after half an equit do a write #ifdef NHICD if(EffIterCount % (MBIR::Constants::k_NumNonHomogeniousIter) == 0) #endif //NHICD { ss.str(""); ss << m_TomoInputs->tempDir << MXADir::getSeparator() << reconOuterIter << "_" << reconInnerIter << "_" << MBIR::Defaults::ReconstructedMrcFile; writeMRCFile(ss.str(), cropStart, cropEnd); notify(ss.str(), 0, Observable::UpdateIntermediateImage); m_TomoInputs->tempFiles.push_back(ss.str()); } #ifdef COST_CALCULATE //typically run only for debugging /*********************Cost Calculation*************************************/ int16_t err = calculateCost(cost, m_Sinogram, m_Geometry, errorSino, &QGGMRF_values); if(err < 0) { std::cout << "Cost went up after voxel update" << std::endl; return; // break; } /**************************************************************************/ #endif //Cost calculation endif #ifdef BRAGG_CORRECTION //If at the last iteration of the inner loops at coarsest resolution adjust parameters if(reconInnerIter == m_TomoInputs->NumIter - 1 && reconInnerIter != 0) { //The first time at the coarsest resolution at the end of // inner iterations set the Bragg Threshold TempBraggValue = DesBraggValue;//threshold; m_ForwardModel->setBraggThreshold(TempBraggValue); } #endif //Bragg correction } /* ++++++++++ END Inner Iteration Loop +++++++++++++++ */ if(0 == status && reconOuterIter >= 1) //if stopping criteria is met and //number of iterations is greater than 1 { if (getVeryVerbose()) {std::cout << "Exiting the code because status =0" << std::endl;} break; } if(getVeryVerbose()) { std::cout << " Starting nuisance parameter estimation" << std::endl; } if(m_TomoInputs->NumOuterIter > 1) //Dont update any parameters if we just have one outer iteration { if(m_AdvParams->JOINT_ESTIMATION) { m_ForwardModel->jointEstimation(m_Sinogram, errorSino, y_Est, cost); m_ForwardModel->updateSelector(m_Sinogram, errorSino); #ifdef COST_CALCULATE //Debug info int16_t err = calculateCost(cost, m_Sinogram, m_Geometry, errorSino, &QGGMRF_values); if(err < 0) { std::cout << "Cost went up after offset update" << std::endl; break; } #endif//cost } //Joint estimation endif if(m_AdvParams->NOISE_ESTIMATION) { m_ForwardModel->updateWeights(m_Sinogram, errorSino); m_ForwardModel->updateSelector(m_Sinogram, errorSino); #ifdef COST_CALCULATE //err = calculateCost(cost, Weight, errorSino); err = calculateCost(cost, m_Sinogram, m_Geometry, errorSino, &QGGMRF_values); if (err < 0 && reconOuterIter > 1) { std::cout << "Cost went up after variance update" << std::endl; std::cout << "Effective iterations =" << EffIterCount << std::endl; return; //break; } #endif//cost } if(getVeryVerbose()) { std::cout << " Ending nuisance parameter estimation" << std::endl; } } }/* ++++++++++ END Outer Iteration Loop +++++++++++++++ */ indent = ""; #if DEBUG_COSTS cost->printCosts(std::cout); #endif #ifdef FORWARD_PROJECT_MODE int fileError = 0; FILE* Fp6; MAKE_OUTPUT_FILE(Fp6, fileError, m_TomoInputs->tempDir, MBIR::Defaults::BFForwardProjectedObjectFile); if (fileError == 1) { } #endif if (getCancel() == true) { setErrorCondition(-999); return; } /* Write the Gains,Offsets and variance to an output file */ m_ForwardModel->writeNuisanceParameters(m_Sinogram); //Compute final value of Ax_{final} Real_t temp_final = 0.0; for (uint16_t i_theta = 0; i_theta < m_Sinogram->N_theta; i_theta++) { for (uint16_t i_r = 0; i_r < m_Sinogram->N_r; i_r++) { for (uint16_t i_t = 0; i_t < m_Sinogram->N_t; i_t++) { temp_final = m_Sinogram->counts->getValue(i_theta, i_r, i_t) - errorSino->getValue(i_theta, i_r, i_t); finalSinogram->setValue(temp_final, i_theta, i_r, i_t); } } } // This is writing the "ReconstructedSinogram.bin" file m_ForwardModel->writeSinogramFile(m_Sinogram, finalSinogram); // Writes the sinogram to a file if (getCancel() == true) { setErrorCondition(-999); return; } // Writes ReconstructedObject.bin file for next resolution { std::stringstream ss; ss << m_TomoInputs->tempDir << MXADir::getSeparator() << MBIR::Defaults::ReconstructedObjectFile; writeReconstructionFile(ss.str()); } // Write out the VTK file if (m_TomoInputs->vtkOutputFile.empty() == false) { writeVtkFile(m_TomoInputs->vtkOutputFile, cropStart, cropEnd); } // Write out the MRC File if (m_TomoInputs->mrcOutputFile.empty() == false) { writeMRCFile(m_TomoInputs->mrcOutputFile, cropStart, cropEnd); } //Write out avizo fire file if (m_TomoInputs->avizoOutputFile.empty() == false) { writeAvizoFile(m_TomoInputs->avizoOutputFile, cropStart, cropEnd); } //Debug : Writing out the selector array as an MRC file m_ForwardModel->writeSelectorMrc(m_TomoInputs->braggSelectorFile, m_Sinogram, m_Geometry, errorSino); if (getVerbose()) { std::cout << "Final Dimensions of Object: " << std::endl; std::cout << " Nx = " << m_Geometry->N_x << std::endl; std::cout << " Ny = " << m_Geometry->N_y << std::endl; std::cout << " Nz = " << m_Geometry->N_z << std::endl; #ifdef NHICD std::cout << "Number of equivalet iterations taken =" << EffIterCount / MBIR::Constants::k_NumNonHomogeniousIter << std::endl; #else std::cout << "Number of equivalet iterations taken =" << EffIterCount / MBIR::Constants::k_NumHomogeniousIter << std::endl; #endif //NHICD } notify("Reconstruction Complete", 100, Observable::UpdateProgressValueAndMessage); setErrorCondition(0); if (getVerbose()) {std::cout << "Total Running Time for Execute: " << (EIMTOMO_getMilliSeconds() - totalTime) / 1000 << std::endl;} return; }