00001 /* 00002 Copyright (C) 2010 - 2014 Christoph Haenisch 00003 00004 Chair of Medical Engineering (mediTEC) 00005 RWTH Aachen University 00006 Pauwelsstr. 20 00007 52074 Aachen 00008 Germany 00009 00010 See license.txt for more information. 00011 00012 Version 0.3.2 (2013-08-20) 00013 */ 00014 00022 #ifndef CorrelationSphere_HPP_5629756123 00023 #define CorrelationSphere_HPP_5629756123 00024 00025 #include <vector> 00026 #include <cassert> 00027 #include <cstddef> 00028 00029 #include "TRTK/Tools.hpp" 00030 #include "ErrorObj.hpp" 00031 #include "Signals.hpp" 00032 00033 // If present, CImg can use the fftw3 lib for speed up. 00034 // Also note that CImg's native FFT seems to behave differently, thus 00035 // there is no guarantee this class works properly without fftw3 00036 #if FFTW3_FOUND 00037 #define cimg_use_fftw3 00038 #endif 00039 00040 #include "CImg.h" 00041 using namespace cimg_library; 00042 00043 namespace TRTK 00044 { 00045 00046 // Auxiliary type 00047 struct Sphere 00048 { 00049 Sphere() : x(0), y(0), z(0), radius(0) 00050 { 00051 } 00052 00053 Sphere(const double x, const double y, const double z, const double radius) : 00054 x(x), y(y), z(z), radius(radius) 00055 { 00056 } 00057 00058 double x, y, z, radius; 00059 }; 00060 00090 template<typename T> 00091 class CorrelationSphere 00092 { 00093 public: 00094 enum Error { UNKNOWN_ERROR }; 00095 00096 // Helper function 00097 CImg<double> * ConvertToGrayScaleDouble(const CImg<T>* input); 00098 00099 // Constructors 00100 CorrelationSphere(); 00101 CorrelationSphere(const CImg<T> * image); 00102 00103 // Destructor 00104 virtual ~CorrelationSphere(); 00105 00106 // Computes the parameter space values 00107 // (a 4-dimensional space: x, y, z and radius are the parameters) 00108 void compute(); 00109 00110 // Find the best matching parameters 00111 Sphere findSingleSphere() const; 00112 00113 // Setter & Getter 00114 std::vector<CImg<double> *> & getCorrelations(); 00115 void setImage(const CImg<T> * image); 00116 00117 void setRadii(int minRadius, int maxRadius); 00118 void setThickness(int thickness); 00119 00120 // Signal to output progress 00121 Signal<int> progress; 00122 00123 private: 00124 CImg<double> * image; 00125 std::vector<CImg<double> *> correlations; 00126 00127 int minRadius; 00128 int maxRadius; 00129 int thickness; 00130 }; 00131 00132 00133 00137 00138 template <typename T> 00139 CImg<double> * CorrelationSphere<T>::ConvertToGrayScaleDouble(const CImg<T>* input) 00140 { 00141 CImg<double> * output = new CImg<double>(input->width(), input->height(), input->depth(), 1); 00142 00143 // Is input an RGB color image? 00144 if (input->spectrum() == 3) 00145 { 00146 cimg_forXYZ(*output, x, y, z){ 00147 // Convert to gray ... 00148 double value = 0.299 * (*input->data(x, y, z, 0)) + 0.587 * (*input->data(x, y, z, 1)) + 0.114 * (*input->data(x, y, z, 2)); 00149 00150 // And adjust value range 00151 if (std::is_same<T, unsigned char>::value || std::is_same<T, char>::value || std::is_same<T, int>::value) 00152 value = static_cast<double>(value) / 255; 00153 00154 (*output)(x, y, z) = value; 00155 } 00156 } 00157 else if (input->spectrum() == 1) 00158 { 00159 cimg_forXYZ(*output, x, y, z){ 00160 // Get image value 00161 double value = (*input)(x, y, z); 00162 00163 // adjust value range 00164 if (std::is_same<T, unsigned char>::value || std::is_same<T, char>::value || std::is_same<T, int>::value) 00165 value = static_cast<double>(value) / 255; 00166 00167 (*output)(x, y, z) = value; 00168 } 00169 } 00170 else 00171 { 00172 ErrorObj error; 00173 error.setErrorMessage("Can't handle image type."); 00174 error.setFunctionName("ConvertToGrayScale32F"); 00175 throw error; 00176 } 00177 00178 return output; 00179 } 00180 00181 template<typename T> 00182 CorrelationSphere<T>::CorrelationSphere() : 00183 image(nullptr), 00184 minRadius(10), 00185 maxRadius(10), 00186 thickness(1) 00187 {} 00188 00189 00190 template<typename T> 00191 CorrelationSphere<T>::CorrelationSphere(const CImg<T> * image) : 00192 image(nullptr), 00193 minRadius(10), 00194 maxRadius(10), 00195 thickness(1) 00196 { 00197 try 00198 { 00199 this->image = ConvertToGrayScaleDouble(image); 00200 } 00201 catch (ErrorObj & err) 00202 { 00203 ErrorObj error; 00204 error.setErrorMessage("From '" + err.getFunctionName() + "': " + err.getErrorMessage()); 00205 error.setClassName("CorrelationCircle"); 00206 error.setFunctionName("CorrelationCircle"); 00207 error.setErrorCode(UNKNOWN_ERROR); 00208 throw error; 00209 } 00210 } 00211 00212 00213 template<typename T> 00214 CorrelationSphere<T>::~CorrelationSphere() 00215 { 00216 for (unsigned int i = 0; i < correlations.size(); ++i) 00217 { 00218 if (correlations[i]) delete correlations[i]; 00219 } 00220 00221 if (image) delete image; 00222 } 00223 00224 00225 template <typename T> 00226 void CorrelationSphere<T>::compute() 00227 { 00228 // initialize some variables 00229 int progress = 0; 00230 const int deltaRadius = maxRadius - minRadius; 00231 00232 for (unsigned int i = 0; i < correlations.size(); ++i) 00233 { 00234 delete correlations.at(i); 00235 } 00236 00237 correlations.resize(deltaRadius + 1); 00238 00239 // compute correlation 00240 00241 // Precompute FFT of original image as it is used multiple times 00242 CImg<double> * imReal = new CImg<double>(*image); 00243 CImg<double> * imImag = new CImg<double>(image->width(), image->height(), image->depth(), 1, 0); 00244 00245 CImg<double>::FFT(*imReal, *imImag); 00246 00247 for (int r = 0; r < deltaRadius + 1; ++r) 00248 { 00249 const int radius = minRadius + r; 00250 int weight = 0; 00251 00252 // Initialize kernel and result images 00253 CImg<double> * resultReal = new CImg<double>(image->width(), image->height(), image->depth(), 1, 0); 00254 CImg<double> * resultImag = new CImg<double>(image->width(), image->height(), image->depth(), 1, 0); 00255 CImg<double> * kernelReal = new CImg<double>(image->width(), image->height(), image->depth(), 1, 0); 00256 CImg<double> * kernelImag = new CImg<double>(image->width(), image->height(), image->depth(), 1, 0); 00257 00258 //Construct kernel 00259 //This may be optimized. For example the bresenham algorithm for spheres could be used 00260 //another optimization: Chunkwise 00261 double w = kernelReal->width() / 2.0; 00262 double h = kernelReal->height() / 2.0; 00263 double d = kernelReal->depth() / 2.0; 00264 00265 if (kernelReal->depth() > 1){ 00266 // 3D Case 00267 cimg_forXYZ(*kernelReal, x, y, z){ 00268 float dist = std::sqrt((x - w)*(x - w) + (y - h)*(y - h) + (z - d)*(z - d)); 00269 if ((dist > radius - 0.5 * thickness) && (dist < radius + 0.5 * thickness)){ 00270 (*kernelReal)(x, y, z) = 1; 00271 weight++; 00272 } 00273 } 00274 } 00275 else { 00276 // 2D Case 00277 cimg_forXY(*kernelReal, x, y){ 00278 float dist = std::sqrt((x - w)*(x - w) + (y - h)*(y - h)); 00279 if ((dist > radius - 0.5 * thickness) && (dist < radius + 0.5 * thickness)){ 00280 (*kernelReal)(x, y) = 1; 00281 weight++; 00282 } 00283 } 00284 } 00285 00286 // Apply FFT on image (done outside of loop) & kernel 00287 CImg<double>::FFT(*kernelReal, *kernelImag); 00288 00289 // Shift kernel such that the DC component is in the center 00290 cimg_forXYZ(*kernelReal, x, y, z){ 00291 (*kernelReal)(x, y, z) *= std::pow(-1, x + y + z); 00292 (*kernelImag)(x, y, z) *= std::pow(-1, x + y + z); 00293 } 00294 00295 // multiply image with kernel in fourier domain (= convolution in spatial domain) 00296 // [may be optimized to work on kernel & image only, saving the memory for result] 00297 cimg_forXYZ(*resultReal, x, y, z){ 00298 (*resultReal)(x, y, z) = (*imReal)(x, y, z) * (*kernelReal)(x, y, z) - (*imImag)(x, y, z) * (*kernelImag)(x, y, z); 00299 (*resultImag)(x, y, z) = (*imReal)(x, y, z) * (*kernelImag)(x, y, z) + (*imImag)(x, y, z) * (*kernelReal)(x, y, z); 00300 } 00301 00302 // Apply Inverse FFT 00303 CImg<double>::FFT(*resultReal, *resultImag, true); 00304 00305 // Normalize result 00306 cimg_forXYZ(*resultReal, x, y, z){ 00307 (*resultReal)(x, y, z) /= weight; 00308 } 00309 00310 // save result; 'correlations' forms a 4D parameter space whose 00311 // local maxima denote the sought circles 00312 correlations[r] = resultReal; 00313 00314 // release resources 00315 // do not release resultReal as we want to keep it in correlations 00316 delete kernelReal; 00317 delete kernelImag; 00318 delete resultImag; 00319 00320 // publish progress 00321 progress += 100 / (deltaRadius + 1); 00322 this->progress.send(progress); 00323 } 00324 00325 // release resources 00326 delete imReal; 00327 delete imImag; 00328 00329 if (progress != 100) this->progress.send(100); 00330 } 00331 00332 00333 template <typename T> 00334 Sphere CorrelationSphere<T>::findSingleSphere() const 00335 { 00336 int progress = 0; 00337 00338 int x0 = 0; 00339 int y0 = 0; 00340 int z0 = 0; 00341 int r0 = 0; 00342 00343 // iterate slice by slice through the parameter space and find the maximum 00344 // value; this value corresponds to the center point of the circle 00345 double globalMaximum = 0; 00346 00347 for (int r = 0; r < int(correlations.size()); ++r) 00348 { 00349 cimg_forXYZ(*correlations[r], x, y, z){ 00350 if ((*correlations[r])(x, y, z) > globalMaximum){ 00351 x0 = x; 00352 y0 = y; 00353 z0 = z; 00354 r0 = r; 00355 globalMaximum = (*correlations[r])(x, y, z); 00356 } 00357 } 00358 00359 progress += 100 / correlations.size(); 00360 this->progress.send(progress); 00361 } 00362 00363 r0 += minRadius; 00364 00365 if (progress != 100) this->progress.send(100); 00366 00367 return Sphere(x0, y0, z0, r0); 00368 } 00369 00370 00371 template<typename T> 00372 void CorrelationSphere<T>::setImage(const CImg<T> * image) 00373 { 00374 this->image = ConvertToGrayScaleDouble(image); 00375 } 00376 00377 00378 template<typename T> 00379 void CorrelationSphere<T>::setRadii(int minRadius, int maxRadius) 00380 { 00381 assert(minRadius >= 0); 00382 assert(maxRadius >= 0); 00383 assert(maxRadius >= minRadius); 00384 00385 this->minRadius = minRadius; 00386 this->maxRadius = maxRadius; 00387 } 00388 00389 00390 template<typename T> 00391 void CorrelationSphere<T>::setThickness(int thickness) 00392 { 00393 this->thickness = thickness; 00394 } 00395 00396 00397 template<typename T> 00398 std::vector<CImg<double> *> & CorrelationSphere<T>::getCorrelations() 00399 { 00400 return this->correlations; 00401 } 00402 00403 } // namespace TRTK 00404 00405 00406 #endif // CorrelationSphere_HPP_5629756123
Documentation generated by Doxygen