diff --git a/Modules/Classification/CLLibSVM/src/svm.cpp b/Modules/Classification/CLLibSVM/src/svm.cpp index a7e247e499..c590df9544 100644 --- a/Modules/Classification/CLLibSVM/src/svm.cpp +++ b/Modules/Classification/CLLibSVM/src/svm.cpp @@ -1,3316 +1,3307 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*=================================================================== Copyright (c) 2000-2014 Chih-Chung Chang and Chih-Jen Lin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither name of copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ===================================================================*/ #include #include #include #include #include #include #include #include #include +#include #include "svm.h" int libsvm_version = LIBSVM_VERSION; typedef float Qfloat; typedef signed char schar; #ifndef min template static inline T min(T x,T y) { return (x static inline T max(T x,T y) { return (x>y)?x:y; } #endif template static inline void swap(T& x, T& y) { T t=x; x=y; y=t; } template static inline void clone(T*& dst, S* src, int n) { dst = new T[n]; memcpy((void *)dst,(void *)src,sizeof(T)*n); } static inline double powi(double base, int times) { double tmp = base, ret = 1.0; for(int t=times; t>0; t/=2) { if(t%2==1) ret*=tmp; tmp = tmp * tmp; } return ret; } #define INF HUGE_VAL #define TAU 1e-12 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) static void print_string_stdout(const char *s) { fputs(s,stdout); fflush(stdout); } static void (*svm_print_string) (const char *) = &print_string_stdout; #if 1 static void info(const char *fmt,...) { char buf[BUFSIZ]; va_list ap; va_start(ap,fmt); vsprintf(buf,fmt,ap); va_end(ap); (*svm_print_string)(buf); } #else static void info(const char *fmt,...) {} #endif // // Kernel Cache // // l is the number of total data items // size is the cache size limit in bytes // class Cache { public: Cache(int l,long int size); ~Cache(); // request data [0,len) // return some position p where [p,len) need to be filled // (p >= len if nothing needs to be filled) int get_data(const int index, Qfloat **data, int len); void swap_index(int i, int j); private: int l; long int size; struct head_t { head_t *prev, *next; // a circular list Qfloat *data; int len; // data[0,len) is cached in this entry }; head_t *head; head_t lru_head; void lru_delete(head_t *h); void lru_insert(head_t *h); }; Cache::Cache(int l_,long int size_):l(l_),size(size_) { head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0 size /= sizeof(Qfloat); size -= l * sizeof(head_t) / sizeof(Qfloat); size = max(size, 2 * (long int) l); // cache must be large enough for two columns lru_head.next = lru_head.prev = &lru_head; } Cache::~Cache() { for(head_t *h = lru_head.next; h != &lru_head; h=h->next) free(h->data); free(head); } void Cache::lru_delete(head_t *h) { // delete from current location h->prev->next = h->next; h->next->prev = h->prev; } void Cache::lru_insert(head_t *h) { // insert to last position h->next = &lru_head; h->prev = lru_head.prev; h->prev->next = h; h->next->prev = h; } int Cache::get_data(const int index, Qfloat **data, int len) { head_t *h = &head[index]; if(h->len) lru_delete(h); int more = len - h->len; if(more > 0) { // free old space while(size < more) { head_t *old = lru_head.next; lru_delete(old); free(old->data); size += old->len; old->data = 0; old->len = 0; } // allocate new space h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len); size -= more; swap(h->len,len); } lru_insert(h); *data = h->data; return len; } void Cache::swap_index(int i, int j) { if(i==j) return; if(head[i].len) lru_delete(&head[i]); if(head[j].len) lru_delete(&head[j]); swap(head[i].data,head[j].data); swap(head[i].len,head[j].len); if(head[i].len) lru_insert(&head[i]); if(head[j].len) lru_insert(&head[j]); if(i>j) swap(i,j); for(head_t *h = lru_head.next; h!=&lru_head; h=h->next) { if(h->len > i) { if(h->len > j) swap(h->data[i],h->data[j]); else { // give up lru_delete(h); free(h->data); size += h->len; h->data = 0; h->len = 0; } } } } // // Kernel evaluation // // the static method k_function is for doing single kernel evaluation // the constructor of Kernel prepares to calculate the l*l kernel matrix // the member function get_Q is for getting one column from the Q Matrix // class QMatrix { public: virtual Qfloat *get_Q(int column, int len) const = 0; virtual double *get_QD() const = 0; virtual void swap_index(int i, int j) const = 0; virtual ~QMatrix() {} }; class Kernel: public QMatrix { public: Kernel(int l, svm_node * const * x, const svm_parameter& param); virtual ~Kernel(); static double k_function(const svm_node *x, const svm_node *y, const svm_parameter& param); virtual Qfloat *get_Q(int column, int len) const = 0; virtual double *get_QD() const = 0; virtual void swap_index(int i, int j) const // no so const... { swap(x[i],x[j]); if(x_square) swap(x_square[i],x_square[j]); } protected: double (Kernel::*kernel_function)(int i, int j) const; private: const svm_node **x; double *x_square; // svm_parameter const int kernel_type; const int degree; const double gamma; const double coef0; static double dot(const svm_node *px, const svm_node *py); double kernel_linear(int i, int j) const { return dot(x[i],x[j]); } double kernel_poly(int i, int j) const { return powi(gamma*dot(x[i],x[j])+coef0,degree); } double kernel_rbf(int i, int j) const { return exp(-gamma*(x_square[i]+x_square[j]-2*dot(x[i],x[j]))); } double kernel_sigmoid(int i, int j) const { return tanh(gamma*dot(x[i],x[j])+coef0); } double kernel_precomputed(int i, int j) const { return x[i][(int)(x[j][0].value)].value; } }; Kernel::Kernel(int l, svm_node * const * x_, const svm_parameter& param) :kernel_type(param.kernel_type), degree(param.degree), gamma(param.gamma), coef0(param.coef0) { switch(kernel_type) { case LINEAR: kernel_function = &Kernel::kernel_linear; break; case POLY: kernel_function = &Kernel::kernel_poly; break; case RBF: kernel_function = &Kernel::kernel_rbf; break; case SIGMOID: kernel_function = &Kernel::kernel_sigmoid; break; case PRECOMPUTED: kernel_function = &Kernel::kernel_precomputed; break; } clone(x,x_,l); if(kernel_type == RBF) { x_square = new double[l]; for(int i=0;iindex != -1 && py->index != -1) { if(px->index == py->index) { sum += px->value * py->value; ++px; ++py; } else { if(px->index > py->index) ++py; else ++px; } } return sum; } double Kernel::k_function(const svm_node *x, const svm_node *y, const svm_parameter& param) { switch(param.kernel_type) { case LINEAR: return dot(x,y); case POLY: return powi(param.gamma*dot(x,y)+param.coef0,param.degree); case RBF: { double sum = 0; while(x->index != -1 && y->index !=-1) { if(x->index == y->index) { double d = x->value - y->value; sum += d*d; ++x; ++y; } else { if(x->index > y->index) { sum += y->value * y->value; ++y; } else { sum += x->value * x->value; ++x; } } } while(x->index != -1) { sum += x->value * x->value; ++x; } while(y->index != -1) { sum += y->value * y->value; ++y; } return exp(-param.gamma*sum); } case SIGMOID: return tanh(param.gamma*dot(x,y)+param.coef0); case PRECOMPUTED: //x: test (validation), y: SV return x[(int)(y->value)].value; default: return 0; // Unreachable } } // An SMO algorithm in Fan et al., JMLR 6(2005), p. 1889--1918 // Solves: // // min 0.5(\alpha^T Q \alpha) + p^T \alpha // // y^T \alpha = \delta // y_i = +1 or -1 // 0 <= alpha_i <= Cp for y_i = 1 // 0 <= alpha_i <= Cn for y_i = -1 // // Given: // // Q, p, y, Cp, Cn, and an initial feasible point \alpha // l is the size of vectors and matrices // eps is the stopping tolerance // // solution will be put in \alpha, objective value will be put in obj // class Solver { public: Solver() {}; virtual ~Solver() {}; struct SolutionInfo { double obj; double rho; double *upper_bound; double r; // for Solver_NU }; void Solve(int l, const QMatrix& Q, const double *p_, const schar *y_, double *alpha_, const double* C_, double eps, SolutionInfo* si, int shrinking); protected: int active_size; schar *y; double *G; // gradient of objective function enum { LOWER_BOUND, UPPER_BOUND, FREE }; char *alpha_status; // LOWER_BOUND, UPPER_BOUND, FREE double *alpha; const QMatrix *Q; const double *QD; double eps; double Cp,Cn; double *C; double *p; int *active_set; double *G_bar; // gradient, if we treat free variables as 0 int l; bool unshrink; // XXX double get_C(int i) { return C[i]; } void update_alpha_status(int i) { if(alpha[i] >= get_C(i)) alpha_status[i] = UPPER_BOUND; else if(alpha[i] <= 0) alpha_status[i] = LOWER_BOUND; else alpha_status[i] = FREE; } bool is_upper_bound(int i) { return alpha_status[i] == UPPER_BOUND; } bool is_lower_bound(int i) { return alpha_status[i] == LOWER_BOUND; } bool is_free(int i) { return alpha_status[i] == FREE; } void swap_index(int i, int j); void reconstruct_gradient(); virtual int select_working_set(int &i, int &j); virtual double calculate_rho(); virtual void do_shrinking(); private: bool be_shrunk(int i, double Gmax1, double Gmax2); }; void Solver::swap_index(int i, int j) { Q->swap_index(i,j); swap(y[i],y[j]); swap(G[i],G[j]); swap(alpha_status[i],alpha_status[j]); swap(alpha[i],alpha[j]); swap(p[i],p[j]); swap(active_set[i],active_set[j]); swap(G_bar[i],G_bar[j]); swap(C[i],C[j]); } void Solver::reconstruct_gradient() { // reconstruct inactive elements of G from G_bar and free variables if(active_size == l) return; int i,j; int nr_free = 0; for(j=active_size;j 2*active_size*(l-active_size)) { for(i=active_size;iget_Q(i,active_size); for(j=0;jget_Q(i,l); double alpha_i = alpha[i]; for(j=active_size;jl = l; this->Q = &Q; QD=Q.get_QD(); clone(p, p_,l); clone(y, y_,l); clone(alpha,alpha_,l); clone(C,C_,l); this->eps = eps; unshrink = false; // initialize alpha_status { alpha_status = new char[l]; for(int i=0;iINT_MAX/100 ? INT_MAX : 100*l); int counter = min(l,1000)+1; while(iter < max_iter) { // show progress and do shrinking if(--counter == 0) { counter = min(l,1000); if(shrinking) do_shrinking(); info("."); } int i,j; if(select_working_set(i,j)!=0) { // reconstruct the whole gradient reconstruct_gradient(); // reset active set size and check active_size = l; info("*"); if(select_working_set(i,j)!=0) break; else counter = 1; // do shrinking next iteration } ++iter; // update alpha[i] and alpha[j], handle bounds carefully const Qfloat *Q_i = Q.get_Q(i,active_size); const Qfloat *Q_j = Q.get_Q(j,active_size); double C_i = get_C(i); double C_j = get_C(j); double old_alpha_i = alpha[i]; double old_alpha_j = alpha[j]; if(y[i]!=y[j]) { double quad_coef = QD[i]+QD[j]+2*Q_i[j]; if (quad_coef <= 0) quad_coef = TAU; double delta = (-G[i]-G[j])/quad_coef; double diff = alpha[i] - alpha[j]; alpha[i] += delta; alpha[j] += delta; if(diff > 0) { if(alpha[j] < 0) { alpha[j] = 0; alpha[i] = diff; } } else { if(alpha[i] < 0) { alpha[i] = 0; alpha[j] = -diff; } } if(diff > C_i - C_j) { if(alpha[i] > C_i) { alpha[i] = C_i; alpha[j] = C_i - diff; } } else { if(alpha[j] > C_j) { alpha[j] = C_j; alpha[i] = C_j + diff; } } } else { double quad_coef = QD[i]+QD[j]-2*Q_i[j]; if (quad_coef <= 0) quad_coef = TAU; double delta = (G[i]-G[j])/quad_coef; double sum = alpha[i] + alpha[j]; alpha[i] -= delta; alpha[j] += delta; if(sum > C_i) { if(alpha[i] > C_i) { alpha[i] = C_i; alpha[j] = sum - C_i; } } else { if(alpha[j] < 0) { alpha[j] = 0; alpha[i] = sum; } } if(sum > C_j) { if(alpha[j] > C_j) { alpha[j] = C_j; alpha[i] = sum - C_j; } } else { if(alpha[i] < 0) { alpha[i] = 0; alpha[j] = sum; } } } // update G double delta_alpha_i = alpha[i] - old_alpha_i; double delta_alpha_j = alpha[j] - old_alpha_j; for(int k=0;k= max_iter) { if(active_size < l) { // reconstruct the whole gradient to calculate objective value reconstruct_gradient(); active_size = l; info("*"); } fprintf(stderr,"\nWARNING: reaching max number of iterations\n"); } // calculate rho si->rho = calculate_rho(); // calculate objective value { double v = 0; int i; for(i=0;iobj = v/2; } // put back the solution { for(int i=0;iupper_bound[i] = C[i]; info("\noptimization finished, #iter = %d\n",iter); delete[] p; delete[] y; delete[] C; delete[] alpha; delete[] alpha_status; delete[] active_set; delete[] G; delete[] G_bar; } // return 1 if already optimal, return 0 otherwise int Solver::select_working_set(int &out_i, int &out_j) { // return i,j such that // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) // j: minimizes the decrease of obj value // (if quadratic coefficeint <= 0, replace it with tau) // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) double Gmax = -INF; double Gmax2 = -INF; int Gmax_idx = -1; int Gmin_idx = -1; double obj_diff_min = INF; for(int t=0;t= Gmax) { Gmax = -G[t]; Gmax_idx = t; } } else { if(!is_lower_bound(t)) if(G[t] >= Gmax) { Gmax = G[t]; Gmax_idx = t; } } int i = Gmax_idx; const Qfloat *Q_i = NULL; if(i != -1) // NULL Q_i not accessed: Gmax=-INF if i=-1 Q_i = Q->get_Q(i,active_size); for(int j=0;j= Gmax2) Gmax2 = G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[i]+QD[j]-2.0*y[i]*Q_i[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } else { if (!is_upper_bound(j)) { double grad_diff= Gmax-G[j]; if (-G[j] >= Gmax2) Gmax2 = -G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[i]+QD[j]+2.0*y[i]*Q_i[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } } if(Gmax+Gmax2 < eps) return 1; out_i = Gmax_idx; out_j = Gmin_idx; return 0; } bool Solver::be_shrunk(int i, double Gmax1, double Gmax2) { if(is_upper_bound(i)) { if(y[i]==+1) return(-G[i] > Gmax1); else return(-G[i] > Gmax2); } else if(is_lower_bound(i)) { if(y[i]==+1) return(G[i] > Gmax2); else return(G[i] > Gmax1); } else return(false); } void Solver::do_shrinking() { int i; double Gmax1 = -INF; // max { -y_i * grad(f)_i | i in I_up(\alpha) } double Gmax2 = -INF; // max { y_i * grad(f)_i | i in I_low(\alpha) } // find maximal violating pair first for(i=0;i= Gmax1) Gmax1 = -G[i]; } if(!is_lower_bound(i)) { if(G[i] >= Gmax2) Gmax2 = G[i]; } } else { if(!is_upper_bound(i)) { if(-G[i] >= Gmax2) Gmax2 = -G[i]; } if(!is_lower_bound(i)) { if(G[i] >= Gmax1) Gmax1 = G[i]; } } } if(unshrink == false && Gmax1 + Gmax2 <= eps*10) { unshrink = true; reconstruct_gradient(); active_size = l; info("*"); } for(i=0;i i) { if (!be_shrunk(active_size, Gmax1, Gmax2)) { swap_index(i,active_size); break; } active_size--; } } } double Solver::calculate_rho() { double r; int nr_free = 0; double ub = INF, lb = -INF, sum_free = 0; for(int i=0;i0) r = sum_free/nr_free; else r = (ub+lb)/2; return r; } // // Solver for nu-svm classification and regression // // additional constraint: e^T \alpha = constant // class Solver_NU: public Solver { public: Solver_NU() {} void Solve(int l, const QMatrix& Q, const double *p, const schar *y, double *alpha, double* C_, double eps, SolutionInfo* si, int shrinking) { this->si = si; Solver::Solve(l,Q,p,y,alpha,C_,eps,si,shrinking); } private: SolutionInfo *si; int select_working_set(int &i, int &j); double calculate_rho(); bool be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4); void do_shrinking(); }; // return 1 if already optimal, return 0 otherwise int Solver_NU::select_working_set(int &out_i, int &out_j) { // return i,j such that y_i = y_j and // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) // j: minimizes the decrease of obj value // (if quadratic coefficeint <= 0, replace it with tau) // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) double Gmaxp = -INF; double Gmaxp2 = -INF; int Gmaxp_idx = -1; double Gmaxn = -INF; double Gmaxn2 = -INF; int Gmaxn_idx = -1; int Gmin_idx = -1; double obj_diff_min = INF; for(int t=0;t= Gmaxp) { Gmaxp = -G[t]; Gmaxp_idx = t; } } else { if(!is_lower_bound(t)) if(G[t] >= Gmaxn) { Gmaxn = G[t]; Gmaxn_idx = t; } } int ip = Gmaxp_idx; int in = Gmaxn_idx; const Qfloat *Q_ip = NULL; const Qfloat *Q_in = NULL; if(ip != -1) // NULL Q_ip not accessed: Gmaxp=-INF if ip=-1 Q_ip = Q->get_Q(ip,active_size); if(in != -1) Q_in = Q->get_Q(in,active_size); for(int j=0;j= Gmaxp2) Gmaxp2 = G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[ip]+QD[j]-2*Q_ip[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } else { if (!is_upper_bound(j)) { double grad_diff=Gmaxn-G[j]; if (-G[j] >= Gmaxn2) Gmaxn2 = -G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[in]+QD[j]-2*Q_in[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } } if(max(Gmaxp+Gmaxp2,Gmaxn+Gmaxn2) < eps) return 1; if (y[Gmin_idx] == +1) out_i = Gmaxp_idx; else out_i = Gmaxn_idx; out_j = Gmin_idx; return 0; } bool Solver_NU::be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4) { if(is_upper_bound(i)) { if(y[i]==+1) return(-G[i] > Gmax1); else return(-G[i] > Gmax4); } else if(is_lower_bound(i)) { if(y[i]==+1) return(G[i] > Gmax2); else return(G[i] > Gmax3); } else return(false); } void Solver_NU::do_shrinking() { double Gmax1 = -INF; // max { -y_i * grad(f)_i | y_i = +1, i in I_up(\alpha) } double Gmax2 = -INF; // max { y_i * grad(f)_i | y_i = +1, i in I_low(\alpha) } double Gmax3 = -INF; // max { -y_i * grad(f)_i | y_i = -1, i in I_up(\alpha) } double Gmax4 = -INF; // max { y_i * grad(f)_i | y_i = -1, i in I_low(\alpha) } // find maximal violating pair first int i; for(i=0;i Gmax1) Gmax1 = -G[i]; } else if(-G[i] > Gmax4) Gmax4 = -G[i]; } if(!is_lower_bound(i)) { if(y[i]==+1) { if(G[i] > Gmax2) Gmax2 = G[i]; } else if(G[i] > Gmax3) Gmax3 = G[i]; } } if(unshrink == false && max(Gmax1+Gmax2,Gmax3+Gmax4) <= eps*10) { unshrink = true; reconstruct_gradient(); active_size = l; } for(i=0;i i) { if (!be_shrunk(active_size, Gmax1, Gmax2, Gmax3, Gmax4)) { swap_index(i,active_size); break; } active_size--; } } } double Solver_NU::calculate_rho() { int nr_free1 = 0,nr_free2 = 0; double ub1 = INF, ub2 = INF; double lb1 = -INF, lb2 = -INF; double sum_free1 = 0, sum_free2 = 0; for(int i=0;i 0) r1 = sum_free1/nr_free1; else r1 = (ub1+lb1)/2; if(nr_free2 > 0) r2 = sum_free2/nr_free2; else r2 = (ub2+lb2)/2; si->r = (r1+r2)/2; return (r1-r2)/2; } // // Q matrices for various formulations // class SVC_Q: public Kernel { public: SVC_Q(const svm_problem& prob, const svm_parameter& param, const schar *y_) :Kernel(prob.l, prob.x, param) { clone(y,y_,prob.l); cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); QD = new double[prob.l]; for(int i=0;i*kernel_function)(i,i); } Qfloat *get_Q(int i, int len) const { Qfloat *data; int start, j; if((start = cache->get_data(i,&data,len)) < len) { for(j=start;j*kernel_function)(i,j)); } return data; } double *get_QD() const { return QD; } void swap_index(int i, int j) const { cache->swap_index(i,j); Kernel::swap_index(i,j); swap(y[i],y[j]); swap(QD[i],QD[j]); } ~SVC_Q() { delete[] y; delete cache; delete[] QD; } private: schar *y; Cache *cache; double *QD; }; class ONE_CLASS_Q: public Kernel { public: ONE_CLASS_Q(const svm_problem& prob, const svm_parameter& param) :Kernel(prob.l, prob.x, param) { cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); QD = new double[prob.l]; for(int i=0;i*kernel_function)(i,i); } Qfloat *get_Q(int i, int len) const { Qfloat *data; int start, j; if((start = cache->get_data(i,&data,len)) < len) { for(j=start;j*kernel_function)(i,j); } return data; } double *get_QD() const { return QD; } void swap_index(int i, int j) const { cache->swap_index(i,j); Kernel::swap_index(i,j); swap(QD[i],QD[j]); } ~ONE_CLASS_Q() { delete cache; delete[] QD; } private: Cache *cache; double *QD; }; class SVR_Q: public Kernel { public: SVR_Q(const svm_problem& prob, const svm_parameter& param) :Kernel(prob.l, prob.x, param) { l = prob.l; cache = new Cache(l,(long int)(param.cache_size*(1<<20))); QD = new double[2*l]; sign = new schar[2*l]; index = new int[2*l]; for(int k=0;k*kernel_function)(k,k); QD[k+l] = QD[k]; } buffer[0] = new Qfloat[2*l]; buffer[1] = new Qfloat[2*l]; next_buffer = 0; } void swap_index(int i, int j) const { swap(sign[i],sign[j]); swap(index[i],index[j]); swap(QD[i],QD[j]); } Qfloat *get_Q(int i, int len) const { Qfloat *data; int j, real_i = index[i]; if(cache->get_data(real_i,&data,l) < l) { for(j=0;j*kernel_function)(real_i,j); } // reorder and copy Qfloat *buf = buffer[next_buffer]; next_buffer = 1 - next_buffer; schar si = sign[i]; for(j=0;jl; double *minus_ones = new double[l]; schar *y = new schar[l]; double *C = new double[l]; int i; for(i=0;iy[i] > 0) { y[i] = +1; C[i] = prob->W[i]*Cp; } else { y[i] = -1; C[i] = prob->W[i]*Cn; } } Solver s; s.Solve(l, SVC_Q(*prob,*param,y), minus_ones, y, alpha, C, param->eps, si, param->shrinking); /* double sum_alpha=0; for(i=0;il)); */ for(i=0;il; double nu = param->nu; schar *y = new schar[l]; double *C = new double[l]; for(i=0;iy[i]>0) y[i] = +1; else y[i] = -1; C[i] = prob->W[i]; } double nu_l = 0; for(i=0;ieps, si, param->shrinking); double r = si->r; info("C = %f\n",1/r); for(i=0;iupper_bound[i] /= r; } si->rho /= r; si->obj /= (r*r); delete[] C; delete[] y; delete[] zeros; } static void solve_one_class( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double *zeros = new double[l]; schar *ones = new schar[l]; double *C = new double[l]; int i; double nu_l = 0; for(i=0;iW[i]; nu_l += C[i] * param->nu; } i = 0; while(nu_l > 0) { alpha[i] = min(C[i],nu_l); nu_l -= alpha[i]; ++i; } for(;ieps, si, param->shrinking); delete[] C; delete[] zeros; delete[] ones; } static void solve_epsilon_svr( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double *alpha2 = new double[2*l]; double *linear_term = new double[2*l]; double *C = new double[2*l]; schar *y = new schar[2*l]; int i; for(i=0;ip - prob->y[i]; y[i] = 1; C[i] = prob->W[i]*param->C; alpha2[i+l] = 0; linear_term[i+l] = param->p + prob->y[i]; y[i+l] = -1; C[i+l] = prob->W[i]*param->C; } Solver s; s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, alpha2, C, param->eps, si, param->shrinking); double sum_alpha = 0; for(i=0;iC*l)); delete[] alpha2; delete[] linear_term; delete[] C; delete[] y; } static void solve_nu_svr( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double *C = new double[2*l]; double *alpha2 = new double[2*l]; double *linear_term = new double[2*l]; schar *y = new schar[2*l]; int i; double sum = 0; for(i=0;iW[i]*param->C; sum += C[i] * param->nu; } sum /= 2; for(i=0;iy[i]; y[i] = 1; linear_term[i+l] = prob->y[i]; y[i+l] = -1; } Solver_NU s; s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, alpha2, C, param->eps, si, param->shrinking); info("epsilon = %f\n",-si->r); for(i=0;il); Solver::SolutionInfo si; switch(param->svm_type) { case C_SVC: si.upper_bound = Malloc(double,prob->l); solve_c_svc(prob,param,alpha,&si,Cp,Cn); break; case NU_SVC: si.upper_bound = Malloc(double,prob->l); solve_nu_svc(prob,param,alpha,&si); break; case ONE_CLASS: si.upper_bound = Malloc(double,prob->l); solve_one_class(prob,param,alpha,&si); break; case EPSILON_SVR: si.upper_bound = Malloc(double,2*prob->l); solve_epsilon_svr(prob,param,alpha,&si); break; case NU_SVR: si.upper_bound = Malloc(double,2*prob->l); solve_nu_svr(prob,param,alpha,&si); break; } info("obj = %f, rho = %f\n",si.obj,si.rho); // output SVs int nSV = 0; int nBSV = 0; for(int i=0;il;i++) { if(fabs(alpha[i]) > 0) { ++nSV; if(prob->y[i] > 0) { if(fabs(alpha[i]) >= si.upper_bound[i]) ++nBSV; } else { if(fabs(alpha[i]) >= si.upper_bound[i]) ++nBSV; } } } free(si.upper_bound); info("nSV = %d, nBSV = %d\n",nSV,nBSV); decision_function f; f.alpha = alpha; f.rho = si.rho; return f; } // Platt's binary SVM Probablistic Output: an improvement from Lin et al. static void sigmoid_train( int l, const double *dec_values, const double *labels, double& A, double& B) { double prior1=0, prior0 = 0; int i; for (i=0;i 0) prior1+=1; else prior0+=1; int max_iter=100; // Maximal number of iterations double min_step=1e-10; // Minimal step taken in line search double sigma=1e-12; // For numerically strict PD of Hessian double eps=1e-5; double hiTarget=(prior1+1.0)/(prior1+2.0); double loTarget=1/(prior0+2.0); double *t=Malloc(double,l); double fApB,p,q,h11,h22,h21,g1,g2,det,dA,dB,gd,stepsize; double newA,newB,newf,d1,d2; int iter; // Initial Point and Initial Fun Value A=0.0; B=log((prior0+1.0)/(prior1+1.0)); double fval = 0.0; for (i=0;i0) t[i]=hiTarget; else t[i]=loTarget; fApB = dec_values[i]*A+B; if (fApB>=0) fval += t[i]*fApB + log(1+exp(-fApB)); else fval += (t[i] - 1)*fApB +log(1+exp(fApB)); } for (iter=0;iter= 0) { p=exp(-fApB)/(1.0+exp(-fApB)); q=1.0/(1.0+exp(-fApB)); } else { p=1.0/(1.0+exp(fApB)); q=exp(fApB)/(1.0+exp(fApB)); } d2=p*q; h11+=dec_values[i]*dec_values[i]*d2; h22+=d2; h21+=dec_values[i]*d2; d1=t[i]-p; g1+=dec_values[i]*d1; g2+=d1; } // Stopping Criteria if (fabs(g1)= min_step) { newA = A + stepsize * dA; newB = B + stepsize * dB; // New function value newf = 0.0; for (i=0;i= 0) newf += t[i]*fApB + log(1+exp(-fApB)); else newf += (t[i] - 1)*fApB +log(1+exp(fApB)); } // Check sufficient decrease if (newf=max_iter) info("Reaching maximal iterations in two-class probability estimates\n"); free(t); } static double sigmoid_predict(double decision_value, double A, double B) { double fApB = decision_value*A+B; // 1-p used later; avoid catastrophic cancellation if (fApB >= 0) return exp(-fApB)/(1.0+exp(-fApB)); else return 1.0/(1+exp(fApB)) ; } // Method 2 from the multiclass_prob paper by Wu, Lin, and Weng static void multiclass_probability(int k, double **r, double *p) { int t,j; int iter = 0, max_iter=max(100,k); double **Q=Malloc(double *,k); double *Qp=Malloc(double,k); double pQp, eps=0.005/k; for (t=0;tmax_error) max_error=error; } if (max_error=max_iter) info("Exceeds max_iter in multiclass_prob\n"); for(t=0;tl); double *dec_values = Malloc(double,prob->l); // random shuffle for(i=0;il;i++) perm[i]=i; for(i=0;il;i++) { int j = i+rand()%(prob->l-i); swap(perm[i],perm[j]); } for(i=0;il/nr_fold; int end = (i+1)*prob->l/nr_fold; int j,k; struct svm_problem subprob; subprob.l = prob->l-(end-begin); subprob.x = Malloc(struct svm_node*,subprob.l); subprob.y = Malloc(double,subprob.l); subprob.W = Malloc(double,subprob.l); k=0; for(j=0;jx[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } for(j=end;jl;j++) { subprob.x[k] = prob->x[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } int p_count=0,n_count=0; for(j=0;j0) p_count++; else n_count++; if(p_count==0 && n_count==0) for(j=begin;j 0 && n_count == 0) for(j=begin;j 0) for(j=begin;jx[perm[j]],&(dec_values[perm[j]])); // ensure +1 -1 order; reason not using CV subroutine dec_values[perm[j]] *= submodel->label[0]; } svm_free_and_destroy_model(&submodel); svm_destroy_param(&subparam); } free(subprob.x); free(subprob.y); free(subprob.W); } sigmoid_train(prob->l,dec_values,prob->y,probA,probB); free(dec_values); free(perm); } // Return parameter of a Laplace distribution static double svm_svr_probability( const svm_problem *prob, const svm_parameter *param) { int i; int nr_fold = 5; double *ymv = Malloc(double,prob->l); double mae = 0; svm_parameter newparam = *param; newparam.probability = 0; svm_cross_validation(prob,&newparam,nr_fold,ymv); for(i=0;il;i++) { ymv[i]=prob->y[i]-ymv[i]; mae += fabs(ymv[i]); } mae /= prob->l; double std=sqrt(2*mae*mae); int count=0; mae=0; for(i=0;il;i++) if (fabs(ymv[i]) > 5*std) count=count+1; else mae+=fabs(ymv[i]); mae /= (prob->l-count); info("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma= %g\n",mae); free(ymv); return mae; } // label: label name, start: begin of each class, count: #data of classes, perm: indices to the original data // perm, length l, must be allocated before calling this subroutine static void svm_group_classes(const svm_problem *prob, int *nr_class_ret, int **label_ret, int **start_ret, int **count_ret, int *perm) { int l = prob->l; int max_nr_class = 16; int nr_class = 0; int *label = Malloc(int,max_nr_class); int *count = Malloc(int,max_nr_class); int *data_label = Malloc(int,l); int i; for(i=0;iy[i]; int j; for(j=0;j 0. // static void remove_zero_weight(svm_problem *newprob, const svm_problem *prob) { int i; int l = 0; for(i=0;il;i++) if(prob->W[i] > 0) l++; *newprob = *prob; newprob->l = l; newprob->x = Malloc(svm_node*,l); newprob->y = Malloc(double,l); newprob->W = Malloc(double,l); int j = 0; for(i=0;il;i++) if(prob->W[i] > 0) { newprob->x[j] = prob->x[i]; newprob->y[j] = prob->y[i]; newprob->W[j] = prob->W[i]; j++; } } // // Interface functions // svm_model *svm_train(const svm_problem *prob, const svm_parameter *param) { svm_problem newprob; remove_zero_weight(&newprob, prob); prob = &newprob; svm_model *model = Malloc(svm_model,1); model->param = *param; model->free_sv = 0; // XXX if(param->svm_type == ONE_CLASS || param->svm_type == EPSILON_SVR || param->svm_type == NU_SVR) { // regression or one-class-svm model->nr_class = 2; model->label = NULL; model->nSV = NULL; model->probA = NULL; model->probB = NULL; model->sv_coef = Malloc(double *,1); if(param->probability && (param->svm_type == EPSILON_SVR || param->svm_type == NU_SVR)) { model->probA = Malloc(double,1); model->probA[0] = svm_svr_probability(prob,param); } decision_function f = svm_train_one(prob,param,0,0); model->rho = Malloc(double,1); model->rho[0] = f.rho; int nSV = 0; int i; for(i=0;il;i++) if(fabs(f.alpha[i]) > 0) ++nSV; model->l = nSV; model->SV = Malloc(svm_node *,nSV); model->sv_coef[0] = Malloc(double,nSV); model->sv_indices = Malloc(int,nSV); int j = 0; for(i=0;il;i++) if(fabs(f.alpha[i]) > 0) { model->SV[j] = prob->x[i]; model->sv_coef[0][j] = f.alpha[i]; model->sv_indices[j] = i+1; ++j; } free(f.alpha); } else { // classification int l = prob->l; int nr_class; int *label = NULL; int *start = NULL; int *count = NULL; int *perm = Malloc(int,l); // group training data of the same class svm_group_classes(prob,&nr_class,&label,&start,&count,perm); if(nr_class == 1) info("WARNING: training data in only one class. See README for details.\n"); svm_node **x = Malloc(svm_node *,l); double *W; W = Malloc(double,l); int i; for(i=0;ix[perm[i]]; W[i] = prob->W[perm[i]]; } // calculate weighted C double *weighted_C = Malloc(double, nr_class); for(i=0;iC; for(i=0;inr_weight;i++) { int j; for(j=0;jweight_label[i] == label[j]) break; if(j == nr_class) fprintf(stderr,"WARNING: class label %d specified in weight is not found\n", param->weight_label[i]); else weighted_C[j] *= param->weight[i]; } // train k*(k-1)/2 models bool *nonzero = Malloc(bool,l); for(i=0;iprobability) { probA=Malloc(double,nr_class*(nr_class-1)/2); probB=Malloc(double,nr_class*(nr_class-1)/2); } int p = 0; for(i=0;iprobability) svm_binary_svc_probability(&sub_prob,param,weighted_C[i],weighted_C[j],probA[p],probB[p]); f[p] = svm_train_one(&sub_prob,param,weighted_C[i],weighted_C[j]); for(k=0;k 0) nonzero[si+k] = true; for(k=0;k 0) nonzero[sj+k] = true; free(sub_prob.x); free(sub_prob.y); free(sub_prob.W); ++p; } // build output model->nr_class = nr_class; model->label = Malloc(int,nr_class); for(i=0;ilabel[i] = label[i]; model->rho = Malloc(double,nr_class*(nr_class-1)/2); for(i=0;irho[i] = f[i].rho; if(param->probability) { model->probA = Malloc(double,nr_class*(nr_class-1)/2); model->probB = Malloc(double,nr_class*(nr_class-1)/2); for(i=0;iprobA[i] = probA[i]; model->probB[i] = probB[i]; } } else { model->probA=NULL; model->probB=NULL; } int total_sv = 0; int *nz_count = Malloc(int,nr_class); model->nSV = Malloc(int,nr_class); for(i=0;inSV[i] = nSV; nz_count[i] = nSV; } info("Total nSV = %d\n",total_sv); model->l = total_sv; model->SV = Malloc(svm_node *,total_sv); model->sv_indices = Malloc(int,total_sv); p = 0; for(i=0;iSV[p] = x[i]; model->sv_indices[p++] = perm[i] + 1; } int *nz_start = Malloc(int,nr_class); nz_start[0] = 0; for(i=1;isv_coef = Malloc(double *,nr_class-1); for(i=0;isv_coef[i] = Malloc(double,total_sv); p = 0; for(i=0;isv_coef[j-1][q++] = f[p].alpha[k]; q = nz_start[j]; for(k=0;ksv_coef[i][q++] = f[p].alpha[ci+k]; ++p; } free(label); free(probA); free(probB); free(count); free(perm); free(start); free(W); free(x); free(weighted_C); free(nonzero); for(i=0;il; int *perm = Malloc(int,l); int nr_class; if (nr_fold > l) { nr_fold = l; fprintf(stderr,"WARNING: # folds > # data. Will use # folds = # data instead (i.e., leave-one-out cross validation)\n"); } fold_start = Malloc(int,nr_fold+1); // stratified cv may not give leave-one-out rate // Each class to l folds -> some folds may have zero elements if((param->svm_type == C_SVC || param->svm_type == NU_SVC) && nr_fold < l) { int *start = NULL; int *label = NULL; int *count = NULL; svm_group_classes(prob,&nr_class,&label,&start,&count,perm); // random shuffle and then data grouped by fold using the array perm int *fold_count = Malloc(int,nr_fold); int c; int *index = Malloc(int,l); for(i=0;ix[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } for(j=end;jx[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } struct svm_model *submodel = svm_train(&subprob,param); if(param->probability && (param->svm_type == C_SVC || param->svm_type == NU_SVC)) { double *prob_estimates=Malloc(double,svm_get_nr_class(submodel)); for(j=begin;jx[perm[j]],prob_estimates); free(prob_estimates); } else for(j=begin;jx[perm[j]]); svm_free_and_destroy_model(&submodel); free(subprob.x); free(subprob.y); free(subprob.W); } free(fold_start); free(perm); } int svm_get_svm_type(const svm_model *model) { return model->param.svm_type; } int svm_get_nr_class(const svm_model *model) { return model->nr_class; } void svm_get_labels(const svm_model *model, int* label) { if (model->label != NULL) for(int i=0;inr_class;i++) label[i] = model->label[i]; } void svm_get_sv_indices(const svm_model *model, int* indices) { if (model->sv_indices != NULL) for(int i=0;il;i++) indices[i] = model->sv_indices[i]; } int svm_get_nr_sv(const svm_model *model) { return model->l; } double svm_get_svr_probability(const svm_model *model) { if ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && model->probA!=NULL) return model->probA[0]; else { fprintf(stderr,"Model doesn't contain information for SVR probability inference\n"); return 0; } } double svm_predict_values(const svm_model *model, const svm_node *x, double* dec_values) { int i; if(model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) { double *sv_coef = model->sv_coef[0]; double sum = 0; for(i=0;il;i++) sum += sv_coef[i] * Kernel::k_function(x,model->SV[i],model->param); sum -= model->rho[0]; *dec_values = sum; if(model->param.svm_type == ONE_CLASS) return (sum>0)?1:-1; else return sum; } else { int nr_class = model->nr_class; int l = model->l; double *kvalue = Malloc(double,l); for(i=0;iSV[i],model->param); int *start = Malloc(int,nr_class); start[0] = 0; for(i=1;inSV[i-1]; int *vote = Malloc(int,nr_class); for(i=0;inSV[i]; int cj = model->nSV[j]; int k; double *coef1 = model->sv_coef[j-1]; double *coef2 = model->sv_coef[i]; for(k=0;krho[p]; dec_values[p] = sum; if(dec_values[p] > 0) ++vote[i]; else ++vote[j]; p++; } int vote_max_idx = 0; for(i=1;i vote[vote_max_idx]) vote_max_idx = i; free(kvalue); free(start); free(vote); return model->label[vote_max_idx]; } } double svm_predict(const svm_model *model, const svm_node *x) { int nr_class = model->nr_class; double *dec_values; if(model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) dec_values = Malloc(double, 1); else dec_values = Malloc(double, nr_class*(nr_class-1)/2); double pred_result = svm_predict_values(model, x, dec_values); free(dec_values); return pred_result; } double svm_predict_probability( const svm_model *model, const svm_node *x, double *prob_estimates) { if ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && model->probA!=NULL && model->probB!=NULL) { int i; int nr_class = model->nr_class; double *dec_values = Malloc(double, nr_class*(nr_class-1)/2); svm_predict_values(model, x, dec_values); double min_prob=1e-7; double **pairwise_prob=Malloc(double *,nr_class); for(i=0;iprobA[k],model->probB[k]),min_prob),1-min_prob); pairwise_prob[j][i]=1-pairwise_prob[i][j]; k++; } multiclass_probability(nr_class,pairwise_prob,prob_estimates); int prob_max_idx = 0; for(i=1;i prob_estimates[prob_max_idx]) prob_max_idx = i; for(i=0;ilabel[prob_max_idx]; } else return svm_predict(model, x); } static const char *svm_type_table[] = { "c_svc","nu_svc","one_class","epsilon_svr","nu_svr",NULL }; static const char *kernel_type_table[]= { "linear","polynomial","rbf","sigmoid","precomputed",NULL }; int svm_save_model(const char *model_file_name, const svm_model *model) { FILE *fp = fopen(model_file_name,"w"); if(fp==NULL) return -1; - char *old_locale = strdup(setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, "C"); + mitk::LocaleSwitch localeSwitch("C"); const svm_parameter& param = model->param; fprintf(fp,"svm_type %s\n", svm_type_table[param.svm_type]); fprintf(fp,"kernel_type %s\n", kernel_type_table[param.kernel_type]); if(param.kernel_type == POLY) fprintf(fp,"degree %d\n", param.degree); if(param.kernel_type == POLY || param.kernel_type == RBF || param.kernel_type == SIGMOID) fprintf(fp,"gamma %g\n", param.gamma); if(param.kernel_type == POLY || param.kernel_type == SIGMOID) fprintf(fp,"coef0 %g\n", param.coef0); int nr_class = model->nr_class; int l = model->l; fprintf(fp, "nr_class %d\n", nr_class); fprintf(fp, "total_sv %d\n",l); { fprintf(fp, "rho"); for(int i=0;irho[i]); fprintf(fp, "\n"); } if(model->label) { fprintf(fp, "label"); for(int i=0;ilabel[i]); fprintf(fp, "\n"); } if(model->probA) // regression has probA only { fprintf(fp, "probA"); for(int i=0;iprobA[i]); fprintf(fp, "\n"); } if(model->probB) { fprintf(fp, "probB"); for(int i=0;iprobB[i]); fprintf(fp, "\n"); } if(model->nSV) { fprintf(fp, "nr_sv"); for(int i=0;inSV[i]); fprintf(fp, "\n"); } fprintf(fp, "SV\n"); const double * const *sv_coef = model->sv_coef; const svm_node * const *SV = model->SV; for(int i=0;ivalue)); else while(p->index != -1) { fprintf(fp,"%d:%.8g ",p->index,p->value); p++; } fprintf(fp, "\n"); } - setlocale(LC_ALL, old_locale); - free(old_locale); - if (ferror(fp) != 0 || fclose(fp) != 0) return -1; else return 0; } static char *line = NULL; static int max_line_len; static char* readline(FILE *input) { int len; if(fgets(line,max_line_len,input) == NULL) return NULL; while(strrchr(line,'\n') == NULL) { max_line_len *= 2; line = (char *) realloc(line,max_line_len); len = (int) strlen(line); if(fgets(line+len,max_line_len-len,input) == NULL) break; } return line; } // // FSCANF helps to handle fscanf failures. // Its do-while block avoids the ambiguity when // if (...) // FSCANF(); // is used // #define FSCANF(_stream, _format, _var) do{ if (fscanf(_stream, _format, _var) != 1) return false; }while(0) bool read_model_header(FILE *fp, svm_model* model) { svm_parameter& param = model->param; char cmd[81]; while(1) { FSCANF(fp,"%80s",cmd); if(strcmp(cmd,"svm_type")==0) { FSCANF(fp,"%80s",cmd); int i; for(i=0;svm_type_table[i];i++) { if(strcmp(svm_type_table[i],cmd)==0) { param.svm_type=i; break; } } if(svm_type_table[i] == NULL) { fprintf(stderr,"unknown svm type.\n"); return false; } } else if(strcmp(cmd,"kernel_type")==0) { FSCANF(fp,"%80s",cmd); int i; for(i=0;kernel_type_table[i];i++) { if(strcmp(kernel_type_table[i],cmd)==0) { param.kernel_type=i; break; } } if(kernel_type_table[i] == NULL) { fprintf(stderr,"unknown kernel function.\n"); return false; } } else if(strcmp(cmd,"degree")==0) FSCANF(fp,"%d",¶m.degree); else if(strcmp(cmd,"gamma")==0) FSCANF(fp,"%lf",¶m.gamma); else if(strcmp(cmd,"coef0")==0) FSCANF(fp,"%lf",¶m.coef0); else if(strcmp(cmd,"nr_class")==0) FSCANF(fp,"%d",&model->nr_class); else if(strcmp(cmd,"total_sv")==0) FSCANF(fp,"%d",&model->l); else if(strcmp(cmd,"rho")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->rho = Malloc(double,n); for(int i=0;irho[i]); } else if(strcmp(cmd,"label")==0) { int n = model->nr_class; model->label = Malloc(int,n); for(int i=0;ilabel[i]); } else if(strcmp(cmd,"probA")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->probA = Malloc(double,n); for(int i=0;iprobA[i]); } else if(strcmp(cmd,"probB")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->probB = Malloc(double,n); for(int i=0;iprobB[i]); } else if(strcmp(cmd,"nr_sv")==0) { int n = model->nr_class; model->nSV = Malloc(int,n); for(int i=0;inSV[i]); } else if(strcmp(cmd,"SV")==0) { while(1) { int c = getc(fp); if(c==EOF || c=='\n') break; } break; } else { fprintf(stderr,"unknown text in model file: [%s]\n",cmd); return false; } } return true; } svm_model *svm_load_model(const char *model_file_name) { FILE *fp = fopen(model_file_name,"rb"); if(fp==NULL) return NULL; - char *old_locale = strdup(setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, "C"); + mitk::LocaleSwitch localeSwitch("C"); // read parameters svm_model *model = Malloc(svm_model,1); model->rho = NULL; model->probA = NULL; model->probB = NULL; model->sv_indices = NULL; model->label = NULL; model->nSV = NULL; // read header if (!read_model_header(fp, model)) { fprintf(stderr, "ERROR: fscanf failed to read model\n"); - setlocale(LC_ALL, old_locale); - free(old_locale); free(model->rho); free(model->label); free(model->nSV); free(model); return NULL; } // read sv_coef and SV int elements = 0; long pos = ftell(fp); max_line_len = 1024; line = Malloc(char,max_line_len); char *p,*endptr,*idx,*val; while(readline(fp)!=NULL) { p = strtok(line,":"); while(1) { p = strtok(NULL,":"); if(p == NULL) break; ++elements; } } elements += model->l; fseek(fp,pos,SEEK_SET); int m = model->nr_class - 1; int l = model->l; model->sv_coef = Malloc(double *,m); int i; for(i=0;isv_coef[i] = Malloc(double,l); model->SV = Malloc(svm_node*,l); svm_node *x_space = NULL; if(l>0) x_space = Malloc(svm_node,elements); int j=0; for(i=0;iSV[i] = &x_space[j]; p = strtok(line, " \t"); model->sv_coef[0][i] = strtod(p,&endptr); for(int k=1;ksv_coef[k][i] = strtod(p,&endptr); } while(1) { idx = strtok(NULL, ":"); val = strtok(NULL, " \t"); if(val == NULL) break; x_space[j].index = (int) strtol(idx,&endptr,10); x_space[j].value = strtod(val,&endptr); ++j; } x_space[j++].index = -1; } free(line); - setlocale(LC_ALL, old_locale); - free(old_locale); - if (ferror(fp) != 0 || fclose(fp) != 0) return NULL; model->free_sv = 1; // XXX return model; } void svm_free_model_content(svm_model* model_ptr) { if(model_ptr->free_sv && model_ptr->l > 0 && model_ptr->SV != NULL) free((void *)(model_ptr->SV[0])); if(model_ptr->sv_coef) { for(int i=0;inr_class-1;i++) free(model_ptr->sv_coef[i]); } free(model_ptr->SV); model_ptr->SV = NULL; free(model_ptr->sv_coef); model_ptr->sv_coef = NULL; free(model_ptr->rho); model_ptr->rho = NULL; free(model_ptr->label); model_ptr->label= NULL; free(model_ptr->probA); model_ptr->probA = NULL; free(model_ptr->probB); model_ptr->probB= NULL; free(model_ptr->sv_indices); model_ptr->sv_indices = NULL; free(model_ptr->nSV); model_ptr->nSV = NULL; } void svm_free_and_destroy_model(svm_model** model_ptr_ptr) { if(model_ptr_ptr != NULL && *model_ptr_ptr != NULL) { svm_free_model_content(*model_ptr_ptr); free(*model_ptr_ptr); *model_ptr_ptr = NULL; } } void svm_destroy_param(svm_parameter* param) { free(param->weight_label); free(param->weight); } const char *svm_check_parameter(const svm_problem *prob, const svm_parameter *param) { // svm_type int svm_type = param->svm_type; if(svm_type != C_SVC && svm_type != NU_SVC && svm_type != ONE_CLASS && svm_type != EPSILON_SVR && svm_type != NU_SVR) return "unknown svm type"; // kernel_type, degree int kernel_type = param->kernel_type; if(kernel_type != LINEAR && kernel_type != POLY && kernel_type != RBF && kernel_type != SIGMOID && kernel_type != PRECOMPUTED) return "unknown kernel type"; if(param->gamma < 0) return "gamma < 0"; if(param->degree < 0) return "degree of polynomial kernel < 0"; // cache_size,eps,C,nu,p,shrinking if(param->cache_size <= 0) return "cache_size <= 0"; if(param->eps <= 0) return "eps <= 0"; if(svm_type == C_SVC || svm_type == EPSILON_SVR || svm_type == NU_SVR) if(param->C <= 0) return "C <= 0"; if(svm_type == NU_SVC || svm_type == ONE_CLASS || svm_type == NU_SVR) if(param->nu <= 0 || param->nu > 1) return "nu <= 0 or nu > 1"; if(svm_type == EPSILON_SVR) if(param->p < 0) return "p < 0"; if(param->shrinking != 0 && param->shrinking != 1) return "shrinking != 0 and shrinking != 1"; if(param->probability != 0 && param->probability != 1) return "probability != 0 and probability != 1"; if(param->probability == 1 && svm_type == ONE_CLASS) return "one-class SVM probability output not supported yet"; // check whether nu-svc is feasible if(svm_type == NU_SVC) { int l = prob->l; int max_nr_class = 16; int nr_class = 0; int *label = Malloc(int,max_nr_class); double *count = Malloc(double,max_nr_class); int i; for(i=0;iy[i]; int j; for(j=0;jW[i]; break; } if(j == nr_class) { if(nr_class == max_nr_class) { max_nr_class *= 2; label = (int *)realloc(label,max_nr_class*sizeof(int)); count = (double *)realloc(count,max_nr_class*sizeof(double)); } label[nr_class] = this_label; count[nr_class] = prob->W[i]; ++nr_class; } } for(i=0;inu*(n1+n2)/2 > min(n1,n2)) { free(label); free(count); return "specified nu is infeasible"; } } } free(label); free(count); } return NULL; } int svm_check_probability_model(const svm_model *model) { return ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && model->probA!=NULL && model->probB!=NULL) || ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && model->probA!=NULL); } void svm_set_print_string_function(void (*print_func)(const char *)) { if(print_func == NULL) svm_print_string = &print_string_stdout; else svm_print_string = print_func; } diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index a4f20e11f3..50ad9a9713 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,310 +1,311 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkShaderProperty.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkDicomSeriesReader.cpp IO/mitkDicomSeriesReaderService.cpp IO/mitkDicomSR_GantryTiltInformation.cpp IO/mitkDicomSR_ImageBlockDescriptor.cpp IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp IO/mitkDicomSR_LoadDICOMRGBPixel.cpp IO/mitkDicomSR_LoadDICOMScalar4D.cpp IO/mitkDicomSR_LoadDICOMScalar.cpp IO/mitkDicomSR_SliceGroupingResult.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp + IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkVtkLoggingAdapter.cpp Rendering/mitkAbstractOverlayLayouter.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkIShaderRepository.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper.cpp Rendering/mitkOverlay.cpp Rendering/mitkOverlayManager.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkOverlay2D.cpp Rendering/mitkVtkOverlay3D.cpp Rendering/mitkVtkOverlay.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/DisplayConfigMITKNoCrosshair.xml Interactions/DisplayConfigMITKRotation.xml Interactions/DisplayConfigMITKRotationUnCoupled.xml Interactions/DisplayConfigMITKSwivel.xml Interactions/DisplayConfigMITKLimited.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml ) diff --git a/Modules/Core/include/mitkLocaleSwitch.h b/Modules/Core/include/mitkLocaleSwitch.h index 617851ab5a..78184e3726 100644 --- a/Modules/Core/include/mitkLocaleSwitch.h +++ b/Modules/Core/include/mitkLocaleSwitch.h @@ -1,94 +1,67 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkLocaleSwitch_h #define __mitkLocaleSwitch_h #include "MitkCoreExports.h" -#include - namespace mitk { /** \brief Convenience class to temporarily change the current locale. This helper class can be used to switch to a specific locale for a couple of operations. Once the class is destroyed, the previous locale will be restored. This avoids calling or forgetting to call setlocale() in multiple return locations. Typically this is used to switch to a "C" locale when parsing or printing numbers, in order to consistently get "." and not "," as a decimal separator. \code std::string toString(int number) { mitk::LocaleSwitch localeSwitch("C");// installs C locale until the end of the function std::stringstream parser; parser << number; return parser.str(); } \endcode */ + struct MITKCORE_EXPORT LocaleSwitch { - LocaleSwitch(const std::string& newLocale) - : m_NewLocale( newLocale ) - { - // query and keep the current locale - const char* currentLocale = std::setlocale(LC_ALL, nullptr); - if (currentLocale != nullptr) - m_OldLocale = currentLocale; - else - m_OldLocale = ""; - - // install the new locale if it different from the current one - if (m_NewLocale != m_OldLocale) - { - if ( ! std::setlocale(LC_ALL, m_NewLocale.c_str()) ) - { - MITK_INFO << "Could not switch to locale " << m_NewLocale; - m_OldLocale = ""; - } - } - } + explicit LocaleSwitch(const char* newLocale); - ~LocaleSwitch() - { - if ( !m_OldLocale.empty() && m_OldLocale != m_NewLocale && !std::setlocale(LC_ALL, m_OldLocale.c_str()) ) - { - MITK_INFO << "Could not reset original locale " << m_OldLocale; - } - } + ~LocaleSwitch(); + LocaleSwitch(LocaleSwitch&) = delete; + LocaleSwitch operator=(LocaleSwitch&) = delete; private: - /// locale at instantiation of object - std::string m_OldLocale; - - /// locale during life-time of object - const std::string m_NewLocale; + struct Impl; + Impl* m_LocaleSwitchImpl; }; } #endif // __mitkLocaleSwitch_h diff --git a/Modules/Core/src/IO/mitkDicomSeriesReader.cpp b/Modules/Core/src/IO/mitkDicomSeriesReader.cpp index c05eec77fa..668c334fb2 100644 --- a/Modules/Core/src/IO/mitkDicomSeriesReader.cpp +++ b/Modules/Core/src/IO/mitkDicomSeriesReader.cpp @@ -1,1787 +1,1785 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include +#include #include #include #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { std::string DicomSeriesReader::ReaderImplementationLevelToString( const ReaderImplementationLevel& enumValue ) { switch (enumValue) { case ReaderImplementationLevel_Supported: return "Supported"; case ReaderImplementationLevel_PartlySupported: return "PartlySupported"; case ReaderImplementationLevel_Implemented: return "Implemented"; case ReaderImplementationLevel_Unsupported: return "Unsupported"; default: return ""; }; } std::string DicomSeriesReader::PixelSpacingInterpretationToString( const PixelSpacingInterpretation& enumValue ) { switch (enumValue) { case PixelSpacingInterpretation_SpacingInPatient: return "In Patient"; case PixelSpacingInterpretation_SpacingAtDetector: return "At Detector"; case PixelSpacingInterpretation_SpacingUnknown: return "Unknown spacing"; default: return ""; }; } const DicomSeriesReader::TagToPropertyMapType& DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; // Image Pixel module dictionary["0028|0004"] = "dicom.pixel.PhotometricInterpretation"; dictionary["0028|0010"] = "dicom.pixel.Rows"; dictionary["0028|0011"] = "dicom.pixel.Columns"; // Image Plane module dictionary["0028|0030"] = "dicom.PixelSpacing"; dictionary["0018|1164"] = "dicom.ImagerPixelSpacing"; initialized = true; } return dictionary; } DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, correctTilt, callback, preLoadedImageBlock)) { if( filenames.empty() ) { return nullptr; } return node; } else { return nullptr; } } bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, itk::SmartPointer preLoadedImageBlock) { if( filenames.empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(nullptr); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR || io->GetPixelType() == itk::ImageIOBase::RGB) { LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); } if (node.GetData()) { return true; } } } catch(itk::MemoryAllocationError& e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch(std::exception& e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch(...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { //Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, itk::SmartPointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // axial gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018,0x602e> spaceTagY; gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018,0x6026> physicalTagY; gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); imageItk->SetSpacing(mySpacing); imageItk->SetRegions( myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX*dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane + timeCount*numberOfSlices*planeSize; // add time offset iterator.Set( new_pixels[ adressedPixel ] ); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } std::string DicomSeriesReader::ConstCharStarToString(const char* s) { return s ? std::string(s) : std::string(); } bool DicomSeriesReader::DICOMStringToSpacing(const std::string& s, ScalarType& spacingX, ScalarType& spacingY) { bool successful = false; std::istringstream spacingReader(s); std::string spacing; if ( std::getline( spacingReader, spacing, '\\' ) ) { spacingY = atof( spacing.c_str() ); if ( std::getline( spacingReader, spacing, '\\' ) ) { spacingX = atof( spacing.c_str() ); successful = true; } } return successful; } Point3D DicomSeriesReader::DICOMStringToPoint3D(const std::string& s, bool& successful) { Point3D p; successful = true; std::istringstream originReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( originReader, coordinate, '\\' ) && dim < 3) { p[dim++]= atof(coordinate.c_str()); } if (dim && dim != 3) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; } else if (dim == 0) { successful = false; p.Fill(0.0); // assume default (0,0,0) } return p; } void DicomSeriesReader::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) { successful = true; std::istringstream orientationReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( orientationReader, coordinate, '\\' ) && dim < 6 ) { if (dim<3) { right[dim++] = atof(coordinate.c_str()); } else { up[dim++ - 3] = atof(coordinate.c_str()); } } if (dim && dim != 6) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; } else if (dim == 0) { // fill with defaults right.Fill(0.0); right[0] = 1.0; up.Fill(0.0); up[1] = 1.0; successful = false; } } DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer& files, bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType& tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (auto fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagImagePositionPatient] ); if (thisOriginString.empty()) { // don't let such files be in a common group. Everything without position information will be loaded as a single slice: // with standard DICOM files this can happen to: CR, DX, SC MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis (no position information)"; // we already have one occupying this position if ( result.GetBlockFilenames().empty() ) // nothing WITH position information yet { // ==> this is a group of its own, stop processing, come back later result.AddFileToSortedBlock( *fileIter ); StringContainer remainingFiles; remainingFiles.insert( remainingFiles.end(), fileIter+1, files.end() ); result.AddFilesToUnsortedBlock( remainingFiles ); fileFitsIntoPattern = false; break; // no files anymore } else { // ==> this does not match, consider later result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; continue; // next file } } bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " /* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError ); GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) { // check if this is at least roughly the same angle as recorded in DICOM tags if ( tagValueMappings[fileIter->c_str()].find(tagGantryTilt) != tagValueMappings[fileIter->c_str()].end() ) { // read value, compare to calculated angle std::string tiltStr = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagGantryTilt] ); double angle = atof(tiltStr.c_str()); MITK_DEBUG << "Comparing recorded tilt angle " << angle << " against calculated value " << tiltInfo.GetTiltAngleInDegrees(); // TODO we probably want the signs correct, too (that depends: this is just a rough check, nothing serious) // TODO TODO TODO when angle -27 and tiltangle 63, this will never trigger the if-clause... useless check in this case! old bug..?! if ( fabs(angle) - tiltInfo.GetTiltAngleInDegrees() > 0.25) { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else // tilt angle from header is less than 0.25 degrees different from what we calculated, assume this is fine { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // we cannot check the calculated tilt angle against the one from the dicom header (so we assume we are right) { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // caller does not want tilt compensation OR shearing is more complicated than tilt { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } } else // not sheared { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if ( result.ContainsGantryTilt() ) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if ( result.GetBlockFilenames().size() == 2 ) { result.UndoPrematureGrouping(); } } return result; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer& /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID scanner.AddTag( tagSOPClassUID ); const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID scanner.AddTag( tagSeriesInstanceUID ); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag( tagImageOrientation ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing scanner.AddTag( tagImagerPixelSpacing ); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag( tagSliceThickness ); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag( tagNumberOfRows ); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag( tagNumberOfColumns ); const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt scanner.AddTag( tagGantryTilt ); const gdcm::Tag tagModality(0x0008, 0x0060); // modality scanner.AddTag( tagModality ); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag( tagNumberOfFrames ); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) scanner.AddTag( tagImagePositionPatient ); // TODO add further restrictions from arguments (when anybody asks for it) FileNamesGrouping result; // let GDCM scan files if ( !scanner.Scan( files ) ) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return result; } // assign files IDs that will separate them for loading into image blocks for (auto fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries if ( std::string(fileIter->first) == std::string("DICOMDIR") ) continue; /* sort out multi-frame if ( scanner.GetValue( fileIter->first , tagNumberOfFrames ) ) { MITK_INFO << "Ignoring " << fileIter->first << " because we cannot handle multi-frame images."; continue; } */ // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) ); result[ moreUniqueSeriesId ].AddFile( fileIter->first ); } // PART II: sort slices spatially (or at least consistently if this is NOT possible, see method) for ( FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter ) { try { result[ groupIter->first ] = ImageBlockDescriptor( SortSeriesSlices( groupIter->second.GetFilenames() ) ); // sort each slice group spatially } catch(...) { MITK_ERROR << "Caught something."; } } // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks // * this includes images which DO NOT PROVIDE spatial information, i.e. all images w/o ImagePositionPatient will be loaded separately FileNamesGrouping groupsOf3DPlusTBlocks; // final result of this function for ( FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter ) { FileNamesGrouping groupsOf3DBlocks; // intermediate result for only this group(!) StringContainer filesStillToAnalyze = groupIter->second.GetFilenames(); std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID << " of " << groupIter->second.GetFilenames().size() << " files"; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, groupImagesWithGantryTilt, scanner.GetMappings() ); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; ImageBlockDescriptor thisBlock( analysisResult.GetBlockFilenames() ); std::string firstFileInBlock = thisBlock.GetFilenames().front(); thisBlock.SetImageBlockUID( newGroupUID.str() ); thisBlock.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSeriesInstanceUID ) ) ); thisBlock.SetHasGantryTiltCorrected( analysisResult.ContainsGantryTilt() ); thisBlock.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSOPClassUID ) ) ); thisBlock.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagNumberOfFrames ) ) ); thisBlock.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagModality ) ) ); thisBlock.SetPixelSpacingInformation( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagPixelSpacing ) ), DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagImagerPixelSpacing ) ) ); thisBlock.SetHasMultipleTimePoints( false ); groupsOf3DBlocks[ newGroupUID.str() ] = thisBlock; //MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files"; MITK_DEBUG << "Result: sorted 3D group with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files"; StringContainer debugOutputFiles = analysisResult.GetBlockFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " IN " << *siter; ++subgroup; filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis for (StringContainer::const_iterator siter = filesStillToAnalyze.begin(); siter != filesStillToAnalyze.end(); ++siter) MITK_DEBUG << " OUT " << *siter; } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of groupsOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if ( !sortTo3DPlust ) { // copy 3D blocks to output groupsOf3DPlusTBlocks.insert( groupsOf3DBlocks.begin(), groupsOf3DBlocks.end() ); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for ( FileNamesGrouping::const_iterator block3DIter = groupsOf3DBlocks.begin(); block3DIter != groupsOf3DBlocks.end(); ++block3DIter ) { unsigned int numberOfFilesInThisBlock = block3DIter->second.GetFilenames().size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; MITK_DEBUG << " 3D+t group " << thisBlockKey; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ), *previous_origin_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ), *destination_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ), *previous_destination_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { std::string thisOriginString = ConstCharStarToString( origin_value ); std::string previousOriginString = ConstCharStarToString( previous_origin_value ); // also compare last origin, because this might differ if z-spacing is different std::string thisDestinationString = ConstCharStarToString( destination_value ); std::string previousDestinationString = ConstCharStarToString( previous_destination_value ); identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); } } catch(...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block groupsOf3DPlusTBlocks[previousBlockKey].AddFiles( block3DIter->second.GetFilenames() ); groupsOf3DPlusTBlocks[previousBlockKey].SetHasMultipleTimePoints(true); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; int numberOfTimeSteps = groupsOf3DPlusTBlocks[previousBlockKey].GetFilenames().size() / numberOfFilesInPreviousBlock; MITK_DEBUG << " ==> group closed with " << numberOfTimeSteps << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for ( FileNamesGrouping::const_iterator groupIter = groupsOf3DPlusTBlocks.begin(); groupIter != groupsOf3DPlusTBlocks.end(); ++groupIter ) { ImageBlockDescriptor block = groupIter->second; MITK_DEBUG << " " << block.GetFilenames().size() << " '" << block.GetModality() << "' images (" << block.GetSOPClassUIDAsString() << ") in volume " << block.GetImageBlockUID(); MITK_DEBUG << " (gantry tilt : " << (block.HasGantryTiltCorrected()?"Yes":"No") << "; " "pixel spacing : " << PixelSpacingInterpretationToString( block.GetPixelSpacingType() ) << "; " "3D+t: " << (block.HasMultipleTimePoints()?"Yes":"No") << "; " "reader support: " << ReaderImplementationLevelToString( block.GetReaderImplementationLevel() ) << ")"; StringContainer debugOutputFiles = block.GetFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " F " << *siter; } MITK_DEBUG << "================================================================================"; return groupsOf3DPlusTBlocks; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load( dir.c_str(), false ); // non-recursive return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ) { std::string result; try { result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") ); } catch (std::exception&) { // we are happy with even nothing, this will just group images of a series //MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ) { const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames const char* tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; if (!tagSeriesInstanceUid) { mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image, so stopping here."; } std::string constructedID = tagSeriesInstanceUid; constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImagerPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfFrames ); // be a bit tolerant for orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263) // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); if (tagValueMap.find(tagImageOrientation) != tagValueMap.end()) { bool conversionError(false); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); DICOMStringToOrientationVectors( tagValueMap[tagImageOrientation], right, up, conversionError ); //string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], right[1], right[2], up[0], up[1], up[2]); std::ostringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(5); ss << right[0] << "\\" << right[1] << "\\" << right[2] << "\\" << up[0] << "\\" << up[1] << "\\" << up[2]; std::string simplifiedOrientationString(ss.str()); constructedID += IDifyTagValue( simplifiedOrientationString ); } constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string& value) { std::string IDifiedValue( value ); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { FileNamesGrouping allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for ( FileNamesGrouping::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter ) { if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid { return idIter->second.GetFilenames(); } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns (each piece of information except the rows/columns might be missing) sorting with GdcmSortFunction tries its best by sorting by spatial position and more hints (acquisition number, acquisition time, trigger time) but will always produce a sorting by falling back to SOP Instance UID. */ gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { if (sorter.Sort(unsortedFilenames)) { return sorter.GetFilenames(); } else { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } catch(std::logic_error&) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { // This method MUST accept missing location and position information (and all else, too) // because we cannot rely on anything // (restriction on the sentence before: we have to provide consistent sorting, so we // rely on the minimum information all DICOM files need to provide: SOP Instance UID) /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns */ static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) static const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation // see if we have Image Position and Orientation if ( ds1.FindDataElement(tagImagePositionPatient) && ds1.FindDataElement(tagImageOrientation) && ds2.FindDataElement(tagImagePositionPatient) && ds2.FindDataElement(tagImageOrientation) ) { gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020,0x0032> image_pos2; gdcm::Attribute<0x0020,0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); /* we tolerate very small differences in image orientation, since we got to know about acquisitions where these values change across a single series (7th decimal digit) (http://bugs.mitk.org/show_bug.cgi?id=12263) still, we want to check if our assumption of 'almost equal' orientations is valid */ for (unsigned int dim = 0; dim < 6; ++dim) { if ( fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001 ) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; // this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } // if we can sort by just comparing the distance, we do exactly that if ( fabs(dist1 - dist2) >= mitk::eps) { // default: compare position return dist1 < dist2; } else // we need to check more properties to distinguish slices { // try to sort by Acquisition Number static const gdcm::Tag tagAcquisitionNumber(0x0020, 0x0012); if (ds1.FindDataElement(tagAcquisitionNumber) && ds2.FindDataElement(tagAcquisitionNumber)) { gdcm::Attribute<0x0020,0x0012> acquisition_number1; // Acquisition number gdcm::Attribute<0x0020,0x0012> acquisition_number2; acquisition_number1.Set(ds1); acquisition_number2.Set(ds2); if (acquisition_number1 != acquisition_number2) { return acquisition_number1 < acquisition_number2; } else // neither position nor acquisition number are good for sorting, so check more { // try to sort by Acquisition Time static const gdcm::Tag tagAcquisitionTime(0x0008, 0x0032); if (ds1.FindDataElement(tagAcquisitionTime) && ds2.FindDataElement(tagAcquisitionTime)) { gdcm::Attribute<0x0008,0x0032> acquisition_time1; // Acquisition time gdcm::Attribute<0x0008,0x0032> acquisition_time2; acquisition_time1.Set(ds1); acquisition_time2.Set(ds2); if (acquisition_time1 != acquisition_time2) { return acquisition_time1 < acquisition_time2; } else // we gave up on image position, acquisition number and acquisition time now { // let's try trigger time static const gdcm::Tag tagTriggerTime(0x0018, 0x1060); if (ds1.FindDataElement(tagTriggerTime) && ds2.FindDataElement(tagTriggerTime)) { gdcm::Attribute<0x0018,0x1060> trigger_time1; // Trigger time gdcm::Attribute<0x0018,0x1060> trigger_time2; trigger_time1.Set(ds1); trigger_time2.Set(ds2); if (trigger_time1 != trigger_time2) { return trigger_time1 < trigger_time2; } // ELSE! // for this and many previous ifs we fall through if nothing lets us sort } // . } // . } // . } } } } // . // LAST RESORT: all valuable information for sorting is missing. // Sort by some meaningless but unique identifiers to satisfy the sort function static const gdcm::Tag tagSOPInstanceUID(0x0008, 0x0018); if (ds1.FindDataElement(tagSOPInstanceUID) && ds2.FindDataElement(tagSOPInstanceUID)) { MITK_DEBUG << "Dicom images are missing attributes for a meaningful sorting, falling back to SOP instance UID comparison."; gdcm::Attribute<0x0008,0x0018> SOPInstanceUID1; // SOP instance UID is mandatory and unique gdcm::Attribute<0x0008,0x0018> SOPInstanceUID2; SOPInstanceUID1.Set(ds1); SOPInstanceUID2.Set(ds2); return SOPInstanceUID1 < SOPInstanceUID2; } else { // no DICOM file should really come down here, this should only be reached with unskillful and unlucky manipulation of files std::string error_message("Malformed DICOM images, which do not even contain a SOP Instance UID."); MITK_ERROR << error_message; throw std::logic_error( error_message ); } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif //configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor& blockInfo, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, blockInfo, image); } void DicomSeriesReader::CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); //DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for ( auto i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++ ) { const StringContainer& files = (*i); unsigned int slice(0); for ( auto fIter = files.begin(); fIter != files.end(); ++fIter, ++slice ) { filesForSlices.SetTableValue( slice, *fIter ); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if(tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if(tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if(tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) ); //If more than one time step add postfix ".t" + timestep if(timeStep != 0) { std::ostringstream postfix; postfix << ".t" << timeStep; propertyKeySliceLocation.append(postfix.str()); propertyKeyInstanceNumber.append(postfix.str()); propertyKeySOPInstanceNumber.append(postfix.str()); } image->SetProperty( propertyKeySliceLocation.c_str(), StringLookupTableProperty::New( sliceLocationForSlices ) ); image->SetProperty( propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New( instanceNumberForSlices ) ); image->SetProperty( propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New( SOPInstanceNumberForSlices ) ); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary& dict = io->GetMetaDataDictionary(); const TagToPropertyMapType& propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); auto dictIter = dict.Begin(); while ( dictIter != dict.End() ) { //MITK_DEBUG << "Key " << dictIter->first; std::string value; if ( itk::ExposeMetaData( dict, dictIter->first, value ) ) { //MITK_DEBUG << "Value " << value; auto valuePosition = propertyLookup.find( dictIter->first ); if ( valuePosition != propertyLookup.end() ) { std::string propertyKey = valuePosition->second; //MITK_DEBUG << "--> " << propertyKey; image->SetProperty( propertyKey.c_str(), StringProperty::New(value) ); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..." ; } ++dictIter; } // copy imageblockdescriptor as properties image->SetProperty("dicomseriesreader.SOPClass", StringProperty::New(blockInfo.GetSOPClassUIDAsString())); image->SetProperty("dicomseriesreader.ReaderImplementationLevelString", StringProperty::New(ReaderImplementationLevelToString( blockInfo.GetReaderImplementationLevel() ))); image->SetProperty("dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( blockInfo.GetReaderImplementationLevel() )); image->SetProperty("dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New(PixelSpacingInterpretationToString( blockInfo.GetPixelSpacingType() ))); image->SetProperty("dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New(blockInfo.GetPixelSpacingType())); image->SetProperty("dicomseriesreader.MultiFrameImage", BoolProperty::New(blockInfo.IsMultiFrameImage())); image->SetProperty("dicomseriesreader.GantyTiltCorrected", BoolProperty::New(blockInfo.HasGantryTiltCorrected())); image->SetProperty("dicomseriesreader.3D+t", BoolProperty::New(blockInfo.HasMultipleTimePoints())); } void DicomSeriesReader::FixSpacingInformation( mitk::Image* image, const ImageBlockDescriptor& imageBlockDescriptor ) { // spacing provided by ITK/GDCM Vector3D imageSpacing = image->GetGeometry()->GetSpacing(); ScalarType imageSpacingX = imageSpacing[0]; ScalarType imageSpacingY = imageSpacing[1]; // spacing as desired by MITK (preference for "in patient", else "on detector", or "1.0/1.0") ScalarType desiredSpacingX = imageSpacingX; ScalarType desiredSpacingY = imageSpacingY; imageBlockDescriptor.GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); MITK_DEBUG << "Loaded spacing: " << imageSpacingX << "/" << imageSpacingY; MITK_DEBUG << "Corrected spacing: " << desiredSpacingX << "/" << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; image->GetGeometry()->SetSpacing( imageSpacing ); } void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { - const char* previousCLocale = setlocale(LC_NUMERIC, nullptr); - setlocale(LC_NUMERIC, "C"); - std::locale previousCppLocale( std::cin.getloc() ); + mitk::LocaleSwitch localeSwitch("C"); + std::locale previousCppLocale(std::cin.getloc()); std::locale l( "C" ); std::cin.imbue(l); ImageBlockDescriptor imageBlockDescriptor; const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID const gdcm::Tag tagModality(0x0008, 0x0060); // modality const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames try { Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock; CallbackCommand *command = callback ? new CallbackCommand(callback) : nullptr; bool initialize_node = false; /* special case for Philips 3D+t ultrasound images */ if ( DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str()) ) { // TODO what about imageBlockDescriptor? // TODO what about preLoadedImageBlock? ReadPhilips3DDicom(filenames.front().c_str(), image); initialize_node = true; } else { /* default case: assume "normal" image blocks, possibly 3D+t */ bool canLoadAs4D(true); gdcm::Scanner scanner; ScanForSliceInformation(filenames, scanner); // need non-const access for map gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings()); std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, tagValueMappings, sort, canLoadAs4D ); unsigned int volume_count = imageBlocks.size(); imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSeriesInstanceUID ) ) ); imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSOPClassUID ) ) ); imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagModality ) ) ); imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagNumberOfFrames ) ) ); imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagPixelSpacing ) ), ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagImagerPixelSpacing ) ) ); GantryTiltInformation tiltInfo; // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice possible if ( !imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt) { // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents // check first and last position slice from tags, make some calculations to detect tilt std::string firstFilename(imageBlocks.front().front()); // calculate from first and last slice to minimize rounding errors std::string secondFilename(imageBlocks.front().back()); std::string imagePosition1( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImagePositionPatient ] ) ); std::string imageOrientation( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImageOrientation ] ) ); std::string imagePosition2( ConstCharStarToString( tagValueMappings[secondFilename.c_str() ][ tagImagePositionPatient ] ) ); bool ignoredConversionError(-42); // hard to get here, no graceful way to react Point3D origin1( DICOMStringToPoint3D( imagePosition1, ignoredConversionError ) ); Point3D origin2( DICOMStringToPoint3D( imagePosition2, ignoredConversionError ) ); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( imageOrientation, right, up, ignoredConversionError ); tiltInfo = GantryTiltInformation ( origin1, origin2, right, up, filenames.size()-1 ); correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt(); } else { correctTilt = false; // we CANNOT do that } imageBlockDescriptor.SetHasGantryTiltCorrected( correctTilt ); if (volume_count == 1 || !canLoadAs4D || !load4D) { DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock ); // load first 3D block imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded imageBlockDescriptor.SetHasMultipleTimePoints( false ); FixSpacingInformation( image, imageBlockDescriptor ); CopyMetaDataToImageProperties( imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image); initialize_node = true; } else if (volume_count > 1) { imageBlockDescriptor.AddFiles(filenames); // all is loaded imageBlockDescriptor.SetHasMultipleTimePoints( true ); DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock ); initialize_node = true; } } if (initialize_node) { // forward some image properties to node node.GetPropertyList()->ConcatenatePropertyList( image->GetPropertyList(), true ); std::string patientName = "NoName"; if(node.GetProperty("dicom.patient.PatientsName")) patientName = node.GetProperty("dicom.patient.PatientsName")->GetValueAsString(); node.SetData( image ); node.SetName(patientName); - setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); } MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):"; MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No"); MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0]; MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No"); MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No"); MITK_DEBUG << "--------------------------------------------------------------------------------"; } catch (std::exception& e) { // reset locale then throw up - setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom"; throw e; } } void DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner& scanner) { const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) scanner.AddTag(tagImagePositionPatient); const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID scanner.AddTag(tagSeriesInstanceUID); const gdcm::Tag tagImageOrientation(0x0020,0x0037); //Image orientation scanner.AddTag(tagImageOrientation); const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag( tagSliceLocation ); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag( tagInstanceNumber ); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag( tagSOPInstanceNumber ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing scanner.AddTag( tagImagerPixelSpacing ); const gdcm::Tag tagModality(0x0008, 0x0060); // Modality scanner.AddTag( tagModality ); const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID scanner.AddTag( tagSOPClassUID ); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag( tagNumberOfFrames ); scanner.Scan(filenames); // make available image information for each file } std::list DicomSeriesReader::SortIntoBlocksFor3DplusT( const StringContainer& presortedFilenames, const gdcm::Scanner::MappingType& tagValueMappings, bool /*sort*/, bool& canLoadAs4D ) { std::list imageBlocks; // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022) StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames); std::string firstPosition; unsigned int numberOfBlocks(0); // number of 3D image blocks static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) const gdcm::Tag tagModality(0x0008,0x0060); // loop files to determine number of image blocks for (StringContainer::const_iterator fileIter = sorted_filenames.begin(); fileIter != sorted_filenames.end(); ++fileIter) { gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find( fileIter->c_str() )->second; if(tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end()) { const std::string& modality = tagToValueMap.find(tagModality)->second; if ( modality.compare("RTIMAGE ") == 0 || modality.compare("RTIMAGE") == 0 ) { MITK_WARN << "Modality "<< modality <<" is not fully supported yet."; numberOfBlocks = 1; break; } else { // we expect to get images w/ missing position information ONLY as separated blocks. assert( presortedFilenames.size() == 1 ); numberOfBlocks = 1; break; } } std::string position = tagToValueMap.find(tagImagePositionPatient)->second; MITK_DEBUG << " " << *fileIter << " at " << position; if (firstPosition.empty()) { firstPosition = position; } if ( position == firstPosition ) { ++numberOfBlocks; } else { break; // enough information to know the number of image blocks } } MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps"; if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files // loop files to sort them into image blocks unsigned int numberOfExpectedSlices(0); for (unsigned int block = 0; block < numberOfBlocks; ++block) { StringContainer filesOfCurrentBlock; for ( StringContainer::const_iterator fileIter = sorted_filenames.begin() + block; fileIter != sorted_filenames.end(); //fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts ) { filesOfCurrentBlock.push_back( *fileIter ); for (unsigned int b = 0; b < numberOfBlocks; ++b) { if (fileIter != sorted_filenames.end()) ++fileIter; } } imageBlocks.push_back(filesOfCurrentBlock); if (block == 0) { numberOfExpectedSlices = filesOfCurrentBlock.size(); } else { if (filesOfCurrentBlock.size() != numberOfExpectedSlices) { MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks << " image blocks of " << numberOfExpectedSlices << " images each. Block " << block << " got " << filesOfCurrentBlock.size() << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much as possible) canLoadAs4D = false; } } } return imageBlocks; } Image::Pointer DicomSeriesReader ::MultiplexLoadDICOMByITK(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR) { return MultiplexLoadDICOMByITKScalar(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else if (io->GetPixelType() == itk::ImageIOBase::RGB) { return MultiplexLoadDICOMByITKRGBPixel(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else { return nullptr; } } Image::Pointer DicomSeriesReader ::MultiplexLoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(imageBlocks.front().front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR) { return MultiplexLoadDICOMByITK4DScalar(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else if (io->GetPixelType() == itk::ImageIOBase::RGB) { return MultiplexLoadDICOMByITK4DRGBPixel(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else { return nullptr; } } } // end namespace mitk diff --git a/Modules/Core/src/IO/mitkDicomSeriesReaderService.cpp b/Modules/Core/src/IO/mitkDicomSeriesReaderService.cpp index f8a6b20d78..cc33717a08 100644 --- a/Modules/Core/src/IO/mitkDicomSeriesReaderService.cpp +++ b/Modules/Core/src/IO/mitkDicomSeriesReaderService.cpp @@ -1,134 +1,132 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDicomSeriesReaderService.h" #include #include #include +#include #include #include #include namespace mitk { DicomSeriesReaderService::DicomSeriesReaderService() : AbstractFileReader(CustomMimeType(IOMimeTypes::DICOM_MIMETYPE()), "MITK DICOM Reader") { this->RegisterService(); } std::vector > DicomSeriesReaderService::Read() { std::vector result; - const char* previousCLocale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "C"); - std::locale previousCppLocale( std::cin.getloc() ); + mitk::LocaleSwitch localeSwitch("C"); + std::locale previousCppLocale(std::cin.getloc()); std::locale l( "C" ); std::cin.imbue(l); std::string fileName = this->GetLocalFileName(); if ( DicomSeriesReader::IsPhilips3DDicom(fileName) ) { MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; DataNode::Pointer node = DataNode::New(); mitk::DicomSeriesReader::StringContainer stringvec; stringvec.push_back(fileName); if (DicomSeriesReader::LoadDicomSeries(stringvec, *node)) { BaseData::Pointer data = node->GetData(); StringProperty::Pointer nameProp = StringProperty::New(itksys::SystemTools::GetFilenameName(fileName)); data->GetPropertyList()->SetProperty("name", nameProp); result.push_back(data); } - setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); return result; } DicomSeriesReader::FileNamesGrouping imageBlocks = DicomSeriesReader::GetSeries(itksys::SystemTools::GetFilenamePath(fileName), true); // true = group gantry tilt images const unsigned int size = imageBlocks.size(); ProgressBar::GetInstance()->AddStepsToDo(size); ProgressBar::GetInstance()->Progress(); unsigned int outputIndex = 0u; const DicomSeriesReader::FileNamesGrouping::const_iterator n_end = imageBlocks.end(); for (DicomSeriesReader::FileNamesGrouping::const_iterator n_it = imageBlocks.begin(); n_it != n_end; ++n_it) { const std::string &uid = n_it->first; DataNode::Pointer node = DataNode::New(); const DicomSeriesReader::ImageBlockDescriptor& imageBlockDescriptor( n_it->second ); MITK_INFO << "--------------------------------------------------------------------------------"; MITK_INFO << "DicomSeriesReader: Loading DICOM series " << outputIndex << ": Series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << std::endl; MITK_INFO << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; MITK_INFO << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No"); MITK_INFO << " reader support: " << DicomSeriesReader::ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); MITK_INFO << " pixel spacing type: " << DicomSeriesReader::PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ); MITK_INFO << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No"); MITK_INFO << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No"); MITK_INFO << "--------------------------------------------------------------------------------"; if (DicomSeriesReader::LoadDicomSeries(n_it->second.GetFilenames(), *node, true, true, true)) { BaseData::Pointer data = node->GetData(); PropertyList::Pointer dataProps = data->GetPropertyList(); std::string nodeName(uid); std::string studyDescription; if ( dataProps->GetStringProperty( "dicom.study.StudyDescription", studyDescription ) ) { nodeName = studyDescription; std::string seriesDescription; if ( dataProps->GetStringProperty( "dicom.series.SeriesDescription", seriesDescription ) ) { nodeName += "/" + seriesDescription; } } StringProperty::Pointer nameProp = StringProperty::New(nodeName); data->SetProperty("name", nameProp); result.push_back(data); ++outputIndex; } else { MITK_ERROR << "DicomSeriesReader: Skipping series " << outputIndex << " due to some unspecified error..." << std::endl; } ProgressBar::GetInstance()->Progress(); } - setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); return result; } DicomSeriesReaderService* DicomSeriesReaderService::Clone() const { return new DicomSeriesReaderService(*this); } } diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index 38136bbd8f..8a8f98dee2 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,691 +1,668 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { const char * const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char * const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char * const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char * const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; ItkImageIO::ItkImageIO(const ItkImageIO& other) : AbstractFileIO(other) , m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string& imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if (imageIOName == "GE4ImageIO" || imageIOName == "GE5ImageIO") { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()) , m_ImageIO(imageIO) { if (m_ImageIO.IsNull() ) { mitkThrow() << "ITK ImageIOBase argument must not be NULL"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for(std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin()+1, iter->end()); } customReaderMimeType.AddExtension(extension); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for(std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin()+1, iter->end()); } customWriterMimeType.AddExtension(extension); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType& mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()) , m_ImageIO(imageIO) { if (m_ImageIO.IsNull() ) { mitkThrow() << "ITK ImageIOBase argument must not be NULL"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } /**Helper function that converts the content of a meta data into a time point vector. * If MetaData is not valid or cannot be converted an empty vector is returned.*/ std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data) { const itk::MetaDataObject* timeGeometryTimeData = dynamic_cast*>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; std::vector ItkImageIO::Read() { std::vector result; - - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName( path ); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if ( ndim < MINDIM || ndim > MAXDIM ) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion( ndim ); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[ MAXDIM ]; dimensions[ 0 ] = 0; dimensions[ 1 ] = 0; dimensions[ 2 ] = 0; dimensions[ 3 ] = 0; ScalarType spacing[ MAXDIM ]; spacing[ 0 ] = 1.0f; spacing[ 1 ] = 1.0f; spacing[ 2 ] = 1.0f; spacing[ 3 ] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for ( i = 0; i < ndim ; ++i ) { ioStart[ i ] = 0; ioSize[ i ] = m_ImageIO->GetDimensions( i ); if(iGetDimensions( i ); spacing[ i ] = m_ImageIO->GetSpacing( i ); if(spacing[ i ] <= 0) spacing[ i ] = 1.0f; } if(i<3) { origin[ i ] = m_ImageIO->GetOrigin( i ); } } ioRegion.SetSize( ioSize ); ioRegion.SetIndex( ioStart ); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion( ioRegion ); void* buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read( buffer ); image->Initialize( MakePixelType(m_ImageIO), ndim, dimensions ); image->SetImportChannel( buffer, 0, Image::ManageMemory ); const itk::MetaDataDictionary& dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3? 3 : ndim); for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false,false,false); MITK_INFO << slicedGeometry->GetCornerPoint(true,true,true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { //also check for the name because of backwards compatibility. Past code version stored with the name and not with the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast*>(dictionary.Get( PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast*>(dictionary.Get( PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass() << std::endl; typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get( PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if(dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get( PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback" << std::endl; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); TimePointVector::const_iterator prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendTimeStepClone(slicedGeometry, *pos, *prePos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { //Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass() << std::endl; ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = NULL; MITK_INFO << "number of image components: "<< image->GetPixelType().GetNumberOfComponents() << std::endl; for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string& key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); //Check of there is already a info for the key and our mime type. IPropertyPersistence::InfoMapType infos = mitk::CoreServices::GetPropertyPersistence()->GetInfosByKey(key); auto predicate = [mimeTypeName](const std::pair& x){return x.second.IsNotNull() && x.second->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infos.begin(), infos.end(), predicate); if (finding == infos.end()) { auto predicateWild = [](const std::pair& x){return x.second.IsNotNull() && x.second->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infos.begin(), infos.end(), predicateWild); } PropertyPersistenceInfo::Pointer info; if (finding != infos.end()) { assumedPropertyName = finding->first; info = finding->second; } else { //we have not found anything suitable so we generate our own info info = PropertyPersistenceInfo::New(key); info->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); } std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey( false ); for( const auto &defaultKey : m_DefaultMetaDataKeys ) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if( !isDefaultKey ) { mitk::CoreServices::GetPropertyPersistence()->AddInfo(assumedPropertyName, info); } } } MITK_INFO << "...finished!" << std::endl; - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } - result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const mitk::Image* image = dynamic_cast(this->GetInput()); if (image == NULL) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int* const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents( pixelType.GetNumberOfComponents() ); itk::ImageIORegion ioRegion( dimension ); for(unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for(unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } //use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry const ArbitraryTimeGeometry* arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); std::stringstream stream; stream << arbitraryTG->GetTimeBounds(0)[0]; for (TimeStepType pos = 0; posCountTimeSteps(); ++pos) { stream << " " << arbitraryTG->GetTimeBounds(pos)[1]; } std::string data = stream.str(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, data); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for(const auto &property : *imagePropertyList->GetMap()) { PropertyPersistenceInfo::Pointer info = mitk::CoreServices::GetPropertyPersistence()->GetInfo(property.first, GetMimeType()->GetName(), true); if( info.IsNull()) { continue; } std::string value = info->GetSerializationFunction()(property.second); if( value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING ) { continue; } std::string key = info->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } ImageReadAccessor imageAccess(image); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception& e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const Image* image = dynamic_cast(this->GetInput()); if (image == NULL) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if ( ! m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO* ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/src/IO/mitkLocaleSwitch.cpp b/Modules/Core/src/IO/mitkLocaleSwitch.cpp new file mode 100644 index 0000000000..8f6917a58c --- /dev/null +++ b/Modules/Core/src/IO/mitkLocaleSwitch.cpp @@ -0,0 +1,81 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkLocaleSwitch.h" + +#include "mitkLogMacros.h" + +#include +#include + +namespace mitk { + +struct LocaleSwitch::Impl +{ + explicit Impl(const std::string& newLocale); + + ~Impl(); + +private: + + /// locale at instantiation of object + std::string m_OldLocale; + + /// locale during life-time of object + const std::string m_NewLocale; +}; + +LocaleSwitch::Impl::Impl(const std::string& newLocale) + : m_NewLocale(newLocale) +{ + // query and keep the current locale + const char* currentLocale = std::setlocale(LC_ALL, nullptr); + if (currentLocale != nullptr) + m_OldLocale = currentLocale; + else + m_OldLocale = ""; + + // install the new locale if it different from the current one + if (m_NewLocale != m_OldLocale) + { + if (!std::setlocale(LC_ALL, m_NewLocale.c_str())) + { + MITK_INFO << "Could not switch to locale " << m_NewLocale; + m_OldLocale = ""; + } + } +} + +LocaleSwitch::Impl::~Impl() +{ + if (!m_OldLocale.empty() && m_OldLocale != m_NewLocale && !std::setlocale(LC_ALL, m_OldLocale.c_str())) + { + MITK_INFO << "Could not reset original locale " << m_OldLocale; + } +} + +LocaleSwitch::LocaleSwitch(const char* newLocale) + : m_LocaleSwitchImpl(new Impl(newLocale)) +{ + +} + +LocaleSwitch::~LocaleSwitch() +{ + delete m_LocaleSwitchImpl; +} + +} diff --git a/Modules/Core/src/IO/mitkSurfaceStlIO.cpp b/Modules/Core/src/IO/mitkSurfaceStlIO.cpp index d01df4832a..430fc11719 100644 --- a/Modules/Core/src/IO/mitkSurfaceStlIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceStlIO.cpp @@ -1,168 +1,167 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceStlIO.h" #include "mitkSurface.h" #include "mitkIOMimeTypes.h" #include "mitkLocaleSwitch.h" #include #include #include #include #include #include #include namespace mitk { std::string SurfaceStlIO::OPTION_MERGE_POINTS() { static std::string s = "Merge points"; return s; } std::string SurfaceStlIO::OPTION_TAG_SOLIDS() { static std::string s = "Tag solids"; return s; } std::string SurfaceStlIO::OPTION_CLEAN() { static std::string s = "Clean poly data"; return s; } SurfaceStlIO::SurfaceStlIO() : SurfaceVtkIO(Surface::GetStaticNameOfClass(), IOMimeTypes::STEREOLITHOGRAPHY_MIMETYPE(), "Stereolithography") { Options defaultOptions; defaultOptions[OPTION_MERGE_POINTS()] = us::Any(true); defaultOptions[OPTION_TAG_SOLIDS()] = us::Any(false); defaultOptions[OPTION_CLEAN()] = us::Any(true); this->SetDefaultReaderOptions(defaultOptions); this->RegisterService(); } std::vector > SurfaceStlIO::Read() { LocaleSwitch localeSwitch("C"); Options options = this->GetReaderOptions(); mitk::Surface::Pointer output = mitk::Surface::New(); vtkSmartPointer stlReader = vtkSmartPointer::New(); stlReader->SetFileName(this->GetLocalFileName().c_str()); bool mergePoints = true; bool tagSolids = false; bool cleanData = true; try { mergePoints = us::any_cast(options[OPTION_MERGE_POINTS()]); tagSolids = us::any_cast(options[OPTION_TAG_SOLIDS()]); cleanData = us::any_cast(options[OPTION_CLEAN()]); } catch (const us::BadAnyCastException& e) { MITK_WARN << "Unexpected error: " << e.what(); } stlReader->SetMerging(mergePoints); stlReader->SetScalarTags(tagSolids); vtkSmartPointer normalsGenerator = vtkSmartPointer::New(); normalsGenerator->SetInputConnection(stlReader->GetOutputPort()); vtkSmartPointer algo = normalsGenerator; if (cleanData) { vtkSmartPointer cleanPolyDataFilter = vtkSmartPointer::New(); cleanPolyDataFilter->SetInputConnection(normalsGenerator->GetOutputPort()); cleanPolyDataFilter->PieceInvariantOff(); cleanPolyDataFilter->ConvertLinesToPointsOff(); cleanPolyDataFilter->ConvertPolysToLinesOff(); cleanPolyDataFilter->ConvertStripsToPolysOff(); if (mergePoints) { cleanPolyDataFilter->PointMergingOn(); } algo = cleanPolyDataFilter; } algo->Update(); if (algo->GetOutput() != NULL) { vtkSmartPointer surfaceWithNormals = algo->GetOutput(); output->SetVtkPolyData(surfaceWithNormals); } std::vector result; result.push_back(output.GetPointer()); return result; } void SurfaceStlIO::Write() { LocaleSwitch localeSwitch("C"); ValidateOutputLocation(); - const Surface* input = dynamic_cast(this->GetInput()); const unsigned int timesteps = input->GetTimeGeometry()->CountTimeSteps(); for(unsigned int t = 0; t < timesteps; ++t) { std::string fileName; vtkSmartPointer polyData = this->GetPolyData(t, fileName); vtkSmartPointer triangleFilter = vtkSmartPointer::New(); triangleFilter->SetInputData(polyData); vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputConnection(triangleFilter->GetOutputPort()); // The vtk stl writer cannot write to streams LocalFile localFile(this); writer->SetFileName(localFile.GetFileName().c_str()); if (writer->Write() == 0 || writer->GetErrorCode() != 0 ) { mitkThrow() << "Error during surface writing" << (writer->GetErrorCode() ? std::string(": ") + vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) : std::string()); } if (this->GetOutputStream() && input->GetTimeGeometry()->CountTimeSteps() > 1) { MITK_WARN << "Writing multiple time-steps to output streams is not supported. " << "Only the first time-step will be written"; break; } } } SurfaceStlIO* SurfaceStlIO::IOClone() const { return new SurfaceStlIO(*this); } } diff --git a/Modules/DICOMReaderServices/src/mitkBaseDICOMReaderService.cpp b/Modules/DICOMReaderServices/src/mitkBaseDICOMReaderService.cpp index fe460f8260..98185a1f78 100644 --- a/Modules/DICOMReaderServices/src/mitkBaseDICOMReaderService.cpp +++ b/Modules/DICOMReaderServices/src/mitkBaseDICOMReaderService.cpp @@ -1,122 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBaseDICOMReaderService.h" #include #include #include #include #include #include #include #include +#include #include namespace mitk { BaseDICOMReaderService::BaseDICOMReaderService(const std::string& description) : AbstractFileReader(CustomMimeType(IOMimeTypes::DICOM_MIMETYPE()), description) { } std::vector > BaseDICOMReaderService::Read() { std::vector result; //special handling of Philips 3D US DICOM. //Copied from DICOMSeriesReaderService std::string fileName = this->GetLocalFileName(); if (DicomSeriesReader::IsPhilips3DDicom(fileName)) { MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; - const char* previousCLocale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "C"); + mitk::LocaleSwitch localeSwitch("C"); std::locale previousCppLocale(std::cin.getloc()); std::locale l("C"); std::cin.imbue(l); DataNode::Pointer node = DataNode::New(); mitk::DicomSeriesReader::StringContainer stringvec; stringvec.push_back(fileName); if (DicomSeriesReader::LoadDicomSeries(stringvec, *node)) { BaseData::Pointer data = node->GetData(); StringProperty::Pointer nameProp = StringProperty::New(itksys::SystemTools::GetFilenameName(fileName)); data->GetPropertyList()->SetProperty("name", nameProp); result.push_back(data); } - setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); return result; } //Normal DICOM handling (It wasn't a Philips 3D US) mitk::StringList relevantFiles = this->GetRelevantFiles(); mitk::DICOMFileReader::Pointer reader = this->GetReader(relevantFiles); reader->SetAdditionalTagsOfInterest(mitk::GetCurrentDICOMTagsOfInterest()); reader->SetTagLookupTableToPropertyFunctor(mitk::GetDICOMPropertyForDICOMValuesFunctor); reader->SetInputFiles(relevantFiles); reader->AnalyzeInputFiles(); reader->LoadImages(); for (unsigned int i = 0; i < reader->GetNumberOfOutputs(); ++i) { const mitk::DICOMImageBlockDescriptor& desc = reader->GetOutput(i); mitk::BaseData::Pointer data = desc.GetMitkImage().GetPointer(); std::string nodeName = "Unnamed_DICOM"; std::string studyDescription = desc.GetPropertyAsString("studyDescription"); std::string seriesDescription = desc.GetPropertyAsString("seriesDescription"); if (!studyDescription.empty()) { nodeName = studyDescription; } if (!seriesDescription.empty()) { if (!studyDescription.empty()) { nodeName += "/"; } nodeName += seriesDescription; } StringProperty::Pointer nameProp = StringProperty::New(nodeName); data->SetProperty("name", nameProp); result.push_back(data); } return result; } StringList BaseDICOMReaderService::GetRelevantFiles() const { std::string fileName = this->GetLocalFileName(); mitk::StringList relevantFiles = mitk::GetDICOMFilesInSameDirectory(fileName); return relevantFiles; } } diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiReaderService.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiReaderService.cpp index d668193a52..657ac07702 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiReaderService.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiReaderService.cpp @@ -1,472 +1,450 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkDiffusionImageNiftiReaderService_cpp #define __mitkDiffusionImageNiftiReaderService_cpp #include "mitkDiffusionImageNiftiReaderService.h" #include #include // Diffusion properties #include #include #include #include // ITK includes #include #include #include "itksys/SystemTools.hxx" #include "itkImageFileReader.h" #include "itkMetaDataObject.h" #include "itkNiftiImageIO.h" #include "mitkCustomMimeType.h" #include "mitkDiffusionCoreIOMimeTypes.h" #include #include #include +#include #include "mitkIOUtil.h" namespace mitk { DiffusionImageNiftiReaderService:: DiffusionImageNiftiReaderService(const DiffusionImageNiftiReaderService & other) : AbstractFileReader(other) { } DiffusionImageNiftiReaderService* DiffusionImageNiftiReaderService::Clone() const { return new DiffusionImageNiftiReaderService(*this); } DiffusionImageNiftiReaderService:: ~DiffusionImageNiftiReaderService() {} DiffusionImageNiftiReaderService:: DiffusionImageNiftiReaderService() : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE_DESCRIPTION() ) { m_ServiceReg = this->RegisterService(); } std::vector > DiffusionImageNiftiReaderService:: Read() { std::vector > result; // Since everything is completely read in GenerateOutputInformation() it is stored // in a cache variable. A timestamp is associated. // If the timestamp of the cache variable is newer than the MTime, we only need to // assign the cache variable to the DataObject. // Otherwise, the tree must be read again from the file and OuputInformation must // be updated! if(m_OutputCache.IsNull()) InternalRead(); result.push_back(m_OutputCache.GetPointer()); return result; } void DiffusionImageNiftiReaderService::InternalRead() { OutputType::Pointer outputForCache = OutputType::New(); if ( this->GetInputLocation() == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename to be read is empty!"); } else { try { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } - + mitk::LocaleSwitch localeSwitch("C"); MITK_INFO << "DiffusionImageNiftiReaderService: reading image information"; VectorImageType::Pointer itkVectorImage; std::string ext = this->GetMimeType()->GetExtension( this->GetInputLocation() ); ext = itksys::SystemTools::LowerCase( ext ); if(ext == ".fsl" || ext == ".fslgz") { // create temporary file with correct ending for nifti-io std::string fname3 = "temp_dwi"; fname3 += ext == ".fsl" ? ".nii" : ".nii.gz"; itksys::SystemTools::CopyAFile(this->GetInputLocation().c_str(), fname3.c_str()); // create reader and read file typedef itk::Image ImageType4D; itk::NiftiImageIO::Pointer io2 = itk::NiftiImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(fname3); reader->SetImageIO(io2); reader->Update(); ImageType4D::Pointer img4 = reader->GetOutput(); // delete temporary file itksys::SystemTools::RemoveFile(fname3.c_str()); // convert 4D file to vector image itkVectorImage = VectorImageType::New(); VectorImageType::SpacingType spacing; ImageType4D::SpacingType spacing4 = img4->GetSpacing(); for(int i=0; i<3; i++) spacing[i] = spacing4[i]; itkVectorImage->SetSpacing( spacing ); // Set the image spacing VectorImageType::PointType origin; ImageType4D::PointType origin4 = img4->GetOrigin(); for(int i=0; i<3; i++) origin[i] = origin4[i]; itkVectorImage->SetOrigin( origin ); // Set the image origin VectorImageType::DirectionType direction; ImageType4D::DirectionType direction4 = img4->GetDirection(); for(int i=0; i<3; i++) for(int j=0; j<3; j++) direction[i][j] = direction4[i][j]; itkVectorImage->SetDirection( direction ); // Set the image direction VectorImageType::RegionType region; ImageType4D::RegionType region4 = img4->GetLargestPossibleRegion(); VectorImageType::RegionType::SizeType size; ImageType4D::RegionType::SizeType size4 = region4.GetSize(); for(int i=0; i<3; i++) size[i] = size4[i]; VectorImageType::RegionType::IndexType index; ImageType4D::RegionType::IndexType index4 = region4.GetIndex(); for(int i=0; i<3; i++) index[i] = index4[i]; region.SetSize(size); region.SetIndex(index); itkVectorImage->SetRegions( region ); itkVectorImage->SetVectorLength(size4[3]); itkVectorImage->Allocate(); itk::ImageRegionIterator it ( itkVectorImage, itkVectorImage->GetLargestPossibleRegion() ); typedef VectorImageType::PixelType VecPixType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); VectorImageType::IndexType currentIndex = it.GetIndex(); for(int i=0; i<3; i++) index4[i] = currentIndex[i]; for(unsigned int ind=0; indGetPixel(index4); } it.Set(vec); } } else if(ext == ".nii" || ext == ".nii.gz") { // create reader and read file typedef itk::Image ImageType4D; itk::NiftiImageIO::Pointer io2 = itk::NiftiImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName( this->GetInputLocation() ); reader->SetImageIO(io2); reader->Update(); ImageType4D::Pointer img4 = reader->GetOutput(); // convert 4D file to vector image itkVectorImage = VectorImageType::New(); VectorImageType::SpacingType spacing; ImageType4D::SpacingType spacing4 = img4->GetSpacing(); for(int i=0; i<3; i++) spacing[i] = spacing4[i]; itkVectorImage->SetSpacing( spacing ); // Set the image spacing VectorImageType::PointType origin; ImageType4D::PointType origin4 = img4->GetOrigin(); for(int i=0; i<3; i++) origin[i] = origin4[i]; itkVectorImage->SetOrigin( origin ); // Set the image origin VectorImageType::DirectionType direction; ImageType4D::DirectionType direction4 = img4->GetDirection(); for(int i=0; i<3; i++) for(int j=0; j<3; j++) direction[i][j] = direction4[i][j]; itkVectorImage->SetDirection( direction ); // Set the image direction VectorImageType::RegionType region; ImageType4D::RegionType region4 = img4->GetLargestPossibleRegion(); VectorImageType::RegionType::SizeType size; ImageType4D::RegionType::SizeType size4 = region4.GetSize(); for(int i=0; i<3; i++) size[i] = size4[i]; VectorImageType::RegionType::IndexType index; ImageType4D::RegionType::IndexType index4 = region4.GetIndex(); for(int i=0; i<3; i++) index[i] = index4[i]; region.SetSize(size); region.SetIndex(index); itkVectorImage->SetRegions( region ); itkVectorImage->SetVectorLength(size4[3]); itkVectorImage->Allocate(); itk::ImageRegionIterator it ( itkVectorImage, itkVectorImage->GetLargestPossibleRegion() ); typedef VectorImageType::PixelType VecPixType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); VectorImageType::IndexType currentIndex = it.GetIndex(); for(int i=0; i<3; i++) index4[i] = currentIndex[i]; for(unsigned int ind=0; indGetPixel(index4); } it.Set(vec); } } // Diffusion Image information START GradientDirectionContainerType::Pointer DiffusionVectors = GradientDirectionContainerType::New(); GradientDirectionContainerType::Pointer OriginalDiffusionVectors = GradientDirectionContainerType::New(); MeasurementFrameType MeasurementFrame; float BValue = -1; // Diffusion Image information END if(ext == ".fsl" || ext == ".fslgz" || ext == ".nii" || ext == ".nii.gz") { // some parsing depending on the extension bool useFSLstyle( true ); std::string bvecsExtension(""); std::string bvalsExtension(""); std::string base = itksys::SystemTools::GetFilenamePath( this->GetInputLocation() ) + "/" + this->GetMimeType()->GetFilenameWithoutExtension( this->GetInputLocation() ); // check for possible file names { if( useFSLstyle && itksys::SystemTools::FileExists( std::string( base + ".bvec").c_str() ) && itksys::SystemTools::FileExists( std::string( base + ".bval").c_str() ) ) { useFSLstyle = false; bvecsExtension = ".bvec"; bvalsExtension = ".bval"; } if( useFSLstyle && itksys::SystemTools::FileExists( std::string( base + ".bvecs").c_str() ) && itksys::SystemTools::FileExists( std::string( base + ".bvals").c_str() ) ) { useFSLstyle = false; bvecsExtension = ".bvecs"; bvalsExtension = ".bvals"; } } std::string line; std::vector bvec_entries; std::string fname = this->GetInputLocation(); if( useFSLstyle ) { fname += ".bvecs"; } else { fname = std::string( base + bvecsExtension); } std::ifstream myfile (fname.c_str()); if (myfile.is_open()) { while ( myfile.good() ) { std::string line2; getline (myfile,line2); std::stringstream iss; iss << line2; while(getline(iss,line, ' ')) { // remove any potenial control sequences that might be introduced by lines ending in a single space line.erase(std::remove_if(line.begin(), line.end(), [](char c) { return std::isspace(c) || std::iscntrl(c); } ), line.end()); if (line.length() > 0 ) // otherwise string contained only control characters before, empty string are converted to 0 by atof resulting in a broken b-value list { bvec_entries.push_back(atof(line.c_str())); } } } myfile.close(); } else { MITK_INFO << "Unable to open bvecs file. Expected name: " << fname; } std::vector bval_entries; std::string fname2 = this->GetInputLocation(); if( useFSLstyle ) { fname2 += ".bvals"; } else { fname2 = std::string( base + bvalsExtension); } std::ifstream myfile2 (fname2.c_str()); if (myfile2.is_open()) { while ( myfile2.good() ) { getline (myfile2,line, ' '); // remove any potenial control sequences that might be introduced by lines ending in a single space line.erase(std::remove_if(line.begin(), line.end(), [](char c) { return std::isspace(c) || std::iscntrl(c); } ), line.end()); if (line.length() > 0 ) // otherwise string contained only control characters before, empty string are converted to 0 by atof resulting in a broken b-value list { bval_entries.push_back(atof(line.c_str())); } } myfile2.close(); } else { MITK_INFO << "Unable to open bvals file. Expected name: " << fname2; } BValue = -1; unsigned int numb = bval_entries.size(); for(unsigned int i=0; i vec; vec[0] = bvec_entries.at(i); vec[1] = bvec_entries.at(i+numb); vec[2] = bvec_entries.at(i+2*numb); // Adjust the vector length to encode gradient strength float factor = b_val/BValue; if(vec.magnitude() > 0) { vec[0] = sqrt(factor)*vec[0]; vec[1] = sqrt(factor)*vec[1]; vec[2] = sqrt(factor)*vec[2]; } DiffusionVectors->InsertElement(i,vec); } for(int i=0; i<3; i++) for(int j=0; j<3; j++) MeasurementFrame[i][j] = i==j ? 1 : 0; } outputForCache = mitk::GrabItkImageMemory( itkVectorImage); // create BValueMap mitk::BValueMapProperty::BValueMap BValueMap = mitk::BValueMapProperty::CreateBValueMap(DiffusionVectors,BValue); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), mitk::GradientDirectionsProperty::New( DiffusionVectors ) ); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str(), mitk::GradientDirectionsProperty::New( OriginalDiffusionVectors ) ); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::MEASUREMENTFRAMEPROPERTYNAME.c_str(), mitk::MeasurementFrameProperty::New( MeasurementFrame ) ); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME.c_str(), mitk::BValueMapProperty::New( BValueMap ) ); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str(), mitk::FloatProperty::New( BValue ) ); // Since we have already read the tree, we can store it in a cache variable // so that it can be assigned to the DataObject in GenerateData(); m_OutputCache = outputForCache; m_CacheTime.Modified(); - - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } catch(std::exception& e) { MITK_INFO << "Std::Exception while reading file!!"; MITK_INFO << e.what(); throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { MITK_INFO << "Exception while reading file!!"; throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } } } //namespace MITK #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiWriterService.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiWriterService.cpp index 29232c65e0..3281ba0d2a 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiWriterService.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiWriterService.cpp @@ -1,441 +1,421 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkDiffusionImageNiftiWriterService__cpp #define __mitkDiffusionImageNiftiWriterService__cpp #include "mitkDiffusionImageNiftiWriterService.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNiftiImageIO.h" #include "itkImageFileWriter.h" #include "itksys/SystemTools.hxx" #include "mitkDiffusionCoreIOMimeTypes.h" #include "mitkImageCast.h" +#include #include #include mitk::DiffusionImageNiftiWriterService::DiffusionImageNiftiWriterService() : AbstractFileWriter(mitk::Image::GetStaticNameOfClass(), CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE_DESCRIPTION()) { RegisterService(); } mitk::DiffusionImageNiftiWriterService::DiffusionImageNiftiWriterService(const mitk::DiffusionImageNiftiWriterService& other) : AbstractFileWriter(other) { } mitk::DiffusionImageNiftiWriterService::~DiffusionImageNiftiWriterService() {} void mitk::DiffusionImageNiftiWriterService::Write() { mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); VectorImageType::Pointer itkImg; mitk::CastToItkImage(input,itkImg); if (input.IsNull()) { MITK_ERROR <<"Sorry, input to DiffusionImageNiftiWriterService is NULL!"; return; } if ( this->GetOutputLocation().empty() ) { MITK_ERROR << "Sorry, filename has not been set!"; return ; } - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); char keybuffer[512]; char valbuffer[512]; //itk::MetaDataDictionary dic = input->GetImage()->GetMetaDataDictionary(); vnl_matrix_fixed measurementFrame = mitk::DiffusionPropertyHelper::GetMeasurementFrame(input); if (measurementFrame(0,0) || measurementFrame(0,1) || measurementFrame(0,2) || measurementFrame(1,0) || measurementFrame(1,1) || measurementFrame(1,2) || measurementFrame(2,0) || measurementFrame(2,1) || measurementFrame(2,2)) { sprintf( valbuffer, " (%lf,%lf,%lf) (%lf,%lf,%lf) (%lf,%lf,%lf)", measurementFrame(0,0), measurementFrame(0,1), measurementFrame(0,2), measurementFrame(1,0), measurementFrame(1,1), measurementFrame(1,2), measurementFrame(2,0), measurementFrame(2,1), measurementFrame(2,2)); itk::EncapsulateMetaData(itkImg->GetMetaDataDictionary(),std::string("measurement frame"),std::string(valbuffer)); } sprintf( valbuffer, "DWMRI"); itk::EncapsulateMetaData(itkImg->GetMetaDataDictionary(),std::string("modality"),std::string(valbuffer)); if(mitk::DiffusionPropertyHelper::GetGradientContainer(input)->Size()) { sprintf( valbuffer, "%1f", mitk::DiffusionPropertyHelper::GetReferenceBValue(input) ); itk::EncapsulateMetaData(itkImg->GetMetaDataDictionary(),std::string("DWMRI_b-value"),std::string(valbuffer)); } for(unsigned int i=0; iSize(); i++) { sprintf( keybuffer, "DWMRI_gradient_%04d", i ); /*if(itk::ExposeMetaData(input->GetMetaDataDictionary(), std::string(keybuffer),tmp)) continue;*/ sprintf( valbuffer, "%1f %1f %1f", mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).get(0), mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).get(1), mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).get(2)); itk::EncapsulateMetaData(itkImg->GetMetaDataDictionary(),std::string(keybuffer),std::string(valbuffer)); } typedef itk::VectorImage ImageType; std::string ext = this->GetMimeType()->GetExtension(this->GetOutputLocation()); ext = itksys::SystemTools::LowerCase(ext); // default extension is .nii if( ext == "") { ext = ".nii"; this->SetOutputLocation(this->GetOutputLocation() + ext); } if (ext == ".fsl" || ext == ".fslgz") { MITK_INFO << "Writing Nifti-Image for FSL"; typedef itk::Image ImageType4D; ImageType4D::Pointer img4 = ImageType4D::New(); ImageType::SpacingType spacing = itkImg->GetSpacing(); ImageType4D::SpacingType spacing4; for(int i=0; i<3; i++) spacing4[i] = spacing[i]; spacing4[3] = 1; img4->SetSpacing( spacing4 ); // Set the image spacing ImageType::PointType origin = itkImg->GetOrigin(); ImageType4D::PointType origin4; for(int i=0; i<3; i++) origin4[i] = origin[i]; origin4[3] = 0; img4->SetOrigin( origin4 ); // Set the image origin ImageType::DirectionType direction = itkImg->GetDirection(); ImageType4D::DirectionType direction4; for(int i=0; i<3; i++) for(int j=0; j<3; j++) direction4[i][j] = direction[i][j]; for(int i=0; i<4; i++) direction4[i][3] = 0; for(int i=0; i<4; i++) direction4[3][i] = 0; direction4[3][3] = 1; img4->SetDirection( direction4 ); // Set the image direction ImageType::RegionType region = itkImg->GetLargestPossibleRegion(); ImageType4D::RegionType region4; ImageType::RegionType::SizeType size = region.GetSize(); ImageType4D::RegionType::SizeType size4; for(int i=0; i<3; i++) size4[i] = size[i]; size4[3] = itkImg->GetVectorLength(); ImageType::RegionType::IndexType index = region.GetIndex(); ImageType4D::RegionType::IndexType index4; for(int i=0; i<3; i++) index4[i] = index[i]; index4[3] = 0; region4.SetSize(size4); region4.SetIndex(index4); img4->SetRegions( region4 ); img4->Allocate(); itk::ImageRegionIterator it (itkImg, itkImg->GetLargestPossibleRegion() ); typedef ImageType::PixelType VecPixType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); ImageType::IndexType currentIndex = it.GetIndex(); for(unsigned int ind=0; indSetPixel(index4, vec[ind]); } } // create copy of file with correct ending for mitk std::string fname3 = this->GetOutputLocation(); std::string::iterator itend = fname3.end(); if (ext == ".fsl") fname3.replace( itend-3, itend, "nii"); else fname3.replace( itend-5, itend, "nii.gz"); itk::NiftiImageIO::Pointer io4 = itk::NiftiImageIO::New(); typedef itk::ImageFileWriter WriterType4; WriterType4::Pointer nrrdWriter4 = WriterType4::New(); nrrdWriter4->UseInputMetaDataDictionaryOn(); nrrdWriter4->SetInput( img4 ); nrrdWriter4->SetFileName(fname3); nrrdWriter4->UseCompressionOn(); nrrdWriter4->SetImageIO(io4); try { nrrdWriter4->Update(); } catch (itk::ExceptionObject e) { std::cout << e << std::endl; throw; } itksys::SystemTools::CopyAFile(fname3.c_str(), this->GetOutputLocation().c_str()); if(mitk::DiffusionPropertyHelper::GetGradientContainer(input)->Size()) { std::ofstream myfile; std::string fname = this->GetOutputLocation(); fname += ".bvals"; myfile.open (fname.c_str()); for(unsigned int i=0; iSize(); i++) { double twonorm = mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).two_norm(); myfile << mitk::DiffusionPropertyHelper::GetReferenceBValue(input)*twonorm*twonorm << " "; } myfile.close(); std::ofstream myfile2; std::string fname2 = this->GetOutputLocation(); fname2 += ".bvecs"; myfile2.open (fname2.c_str()); for(int j=0; j<3; j++) { for(unsigned int i=0; iSize(); i++) { //need to modify the length GradientDirectionContainerType::Pointer grads = mitk::DiffusionPropertyHelper::GetGradientContainer(input); GradientDirectionType direction = grads->ElementAt(i); direction.normalize(); myfile2 << direction.get(j) << " "; //myfile2 << input->GetDirections()->ElementAt(i).get(j) << " "; } myfile2 << std::endl; } std::ofstream myfile3; std::string fname4 = this->GetOutputLocation(); fname4 += ".ttk"; myfile3.open (fname4.c_str()); for(unsigned int i=0; iSize(); i++) { for(int j=0; j<3; j++) { myfile3 << mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).get(j) << " "; } myfile3 << std::endl; } } } else if (ext == ".nii" || ext == ".nii.gz") { MITK_INFO << "Writing Nifti-Image"; typedef itk::Image ImageType4D; ImageType4D::Pointer img4 = ImageType4D::New(); ImageType::SpacingType spacing = itkImg->GetSpacing(); ImageType4D::SpacingType spacing4; for(int i=0; i<3; i++) spacing4[i] = spacing[i]; spacing4[3] = 1; img4->SetSpacing( spacing4 ); // Set the image spacing ImageType::PointType origin = itkImg->GetOrigin(); ImageType4D::PointType origin4; for(int i=0; i<3; i++) origin4[i] = origin[i]; origin4[3] = 0; img4->SetOrigin( origin4 ); // Set the image origin ImageType::DirectionType direction = itkImg->GetDirection(); ImageType4D::DirectionType direction4; for(int i=0; i<3; i++) for(int j=0; j<3; j++) direction4[i][j] = direction[i][j]; for(int i=0; i<4; i++) direction4[i][3] = 0; for(int i=0; i<4; i++) direction4[3][i] = 0; direction4[3][3] = 1; img4->SetDirection( direction4 ); // Set the image direction ImageType::RegionType region = itkImg->GetLargestPossibleRegion(); ImageType4D::RegionType region4; ImageType::RegionType::SizeType size = region.GetSize(); ImageType4D::RegionType::SizeType size4; for(int i=0; i<3; i++) size4[i] = size[i]; size4[3] = itkImg->GetVectorLength(); ImageType::RegionType::IndexType index = region.GetIndex(); ImageType4D::RegionType::IndexType index4; for(int i=0; i<3; i++) index4[i] = index[i]; index4[3] = 0; region4.SetSize(size4); region4.SetIndex(index4); img4->SetRegions( region4 ); img4->Allocate(); itk::ImageRegionIterator it (itkImg, itkImg->GetLargestPossibleRegion() ); typedef ImageType::PixelType VecPixType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); ImageType::IndexType currentIndex = it.GetIndex(); for(unsigned int ind=0; indSetPixel(index4, vec[ind]); } } itk::NiftiImageIO::Pointer io4 = itk::NiftiImageIO::New(); typedef itk::ImageFileWriter WriterType4; WriterType4::Pointer nrrdWriter4 = WriterType4::New(); nrrdWriter4->UseInputMetaDataDictionaryOn(); nrrdWriter4->SetInput( img4 ); nrrdWriter4->SetFileName(this->GetOutputLocation()); nrrdWriter4->UseCompressionOn(); nrrdWriter4->SetImageIO(io4); try { nrrdWriter4->Update(); } catch (itk::ExceptionObject e) { std::cout << e << std::endl; throw; } if(mitk::DiffusionPropertyHelper::GetGradientContainer(input)->Size()) { std::ofstream myfile; std::string fname = itksys::SystemTools::GetFilenamePath( this->GetOutputLocation() ) + "/" + this->GetMimeType()->GetFilenameWithoutExtension( this->GetOutputLocation() ); fname += ".bvals"; myfile.open (fname.c_str()); for(unsigned int i=0; iSize(); i++) { double twonorm = mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).two_norm(); myfile << mitk::DiffusionPropertyHelper::GetReferenceBValue(input)*twonorm*twonorm << " "; } myfile.close(); std::ofstream myfile2; std::string fname2 = itksys::SystemTools::GetFilenamePath( this->GetOutputLocation() ) + "/" + this->GetMimeType()->GetFilenameWithoutExtension( this->GetOutputLocation() ); fname2 += ".bvecs"; myfile2.open (fname2.c_str()); for(int j=0; j<3; j++) { for(unsigned int i=0; iSize(); i++) { //need to modify the length GradientDirectionContainerType::Pointer grads = mitk::DiffusionPropertyHelper::GetGradientContainer(input); GradientDirectionType direction = grads->ElementAt(i); direction.normalize(); myfile2 << direction.get(j) << " "; //myfile2 << input->GetDirections()->ElementAt(i).get(j) << " "; } myfile2 << std::endl; } std::ofstream myfile3; std::string fname4 = itksys::SystemTools::GetFilenamePath( this->GetOutputLocation() ) + "/" + this->GetMimeType()->GetFilenameWithoutExtension( this->GetOutputLocation() ); fname4 += ".ttk"; myfile3.open (fname4.c_str()); for(unsigned int i=0; iSize(); i++) { for(int j=0; j<3; j++) { myfile3 << mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).get(j) << " "; } myfile3 << std::endl; } } } - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } mitk::DiffusionImageNiftiWriterService* mitk::DiffusionImageNiftiWriterService::Clone() const { return new DiffusionImageNiftiWriterService(*this); } mitk::IFileWriter::ConfidenceLevel mitk::DiffusionImageNiftiWriterService::GetConfidenceLevel() const { mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); if (input.IsNull() || !mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( input ) ) { return Unsupported; } else { return Supported; } } #endif //__mitkDiffusionImageNiftiWriterService__cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdReaderService.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdReaderService.cpp index dcad3ee363..99c7517586 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdReaderService.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdReaderService.cpp @@ -1,268 +1,246 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkDiffusionImageNrrdReaderService_cpp #define __mitkDiffusionImageNrrdReaderService_cpp #include "mitkDiffusionImageNrrdReaderService.h" #include #include // Diffusion properties #include #include #include #include // ITK includes #include #include #include "itksys/SystemTools.hxx" #include "itkImageFileReader.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "mitkCustomMimeType.h" #include "mitkDiffusionCoreIOMimeTypes.h" #include #include #include #include "mitkIOUtil.h" - +#include namespace mitk { DiffusionImageNrrdReaderService:: DiffusionImageNrrdReaderService(const DiffusionImageNrrdReaderService & other) : AbstractFileReader(other) { } DiffusionImageNrrdReaderService* DiffusionImageNrrdReaderService::Clone() const { return new DiffusionImageNrrdReaderService(*this); } DiffusionImageNrrdReaderService:: ~DiffusionImageNrrdReaderService() {} DiffusionImageNrrdReaderService:: DiffusionImageNrrdReaderService() : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE_DESCRIPTION() ) { m_ServiceReg = this->RegisterService(); } std::vector > DiffusionImageNrrdReaderService:: Read() { std::vector > result; // Since everything is completely read in GenerateOutputInformation() it is stored // in a cache variable. A timestamp is associated. // If the timestamp of the cache variable is newer than the MTime, we only need to // assign the cache variable to the DataObject. // Otherwise, the tree must be read again from the file and OuputInformation must // be updated! if(m_OutputCache.IsNull()) InternalRead(); result.push_back(m_OutputCache.GetPointer()); return result; } void DiffusionImageNrrdReaderService::InternalRead() { OutputType::Pointer outputForCache = OutputType::New(); if ( this->GetInputLocation() == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename to be read is empty!"); } else { try { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } - + mitk::LocaleSwitch localeSwitch("C"); MITK_INFO << "DiffusionImageNrrdReaderService: reading image information"; VectorImageType::Pointer itkVectorImage; std::string ext = this->GetMimeType()->GetExtension( this->GetInputLocation() ); ext = itksys::SystemTools::LowerCase( ext ); if (ext == ".hdwi" || ext == ".dwi" || ext == ".nrrd") { typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(this->GetInputLocation()); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); reader->SetImageIO(io); reader->Update(); itkVectorImage = reader->GetOutput(); } // Diffusion Image information START GradientDirectionContainerType::Pointer DiffusionVectors = GradientDirectionContainerType::New(); GradientDirectionContainerType::Pointer OriginalDiffusionVectors = GradientDirectionContainerType::New(); MeasurementFrameType MeasurementFrame; float BValue = -1; // Diffusion Image information END if (ext == ".hdwi" || ext == ".dwi" || ext == ".nrrd") { itk::MetaDataDictionary imgMetaDictionary = itkVectorImage->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; GradientDirectionType vect3d; int numberOfImages = 0; int numberOfGradientImages = 0; bool readb0 = false; double xx, xy, xz, yx, yy, yz, zx, zy, zz; for (; itKey != imgMetaKeys.end(); itKey ++) { double x,y,z; itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); if (itKey->find("DWMRI_gradient") != std::string::npos) { sscanf(metaString.c_str(), "%lf %lf %lf\n", &x, &y, &z); vect3d[0] = x; vect3d[1] = y; vect3d[2] = z; DiffusionVectors->InsertElement( numberOfImages, vect3d ); ++numberOfImages; // If the direction is 0.0, this is a reference image if (vect3d[0] == 0.0 && vect3d[1] == 0.0 && vect3d[2] == 0.0) { continue; } ++numberOfGradientImages;; } else if (itKey->find("DWMRI_b-value") != std::string::npos) { readb0 = true; BValue = atof(metaString.c_str()); } else if (itKey->find("measurement frame") != std::string::npos) { sscanf(metaString.c_str(), " ( %lf , %lf , %lf ) ( %lf , %lf , %lf ) ( %lf , %lf , %lf ) \n", &xx, &xy, &xz, &yx, &yy, &yz, &zx, &zy, &zz); if (xx>10e-10 || xy>10e-10 || xz>10e-10 || yx>10e-10 || yy>10e-10 || yz>10e-10 || zx>10e-10 || zy>10e-10 || zz>10e-10 ) { MeasurementFrame(0,0) = xx; MeasurementFrame(0,1) = xy; MeasurementFrame(0,2) = xz; MeasurementFrame(1,0) = yx; MeasurementFrame(1,1) = yy; MeasurementFrame(1,2) = yz; MeasurementFrame(2,0) = zx; MeasurementFrame(2,1) = zy; MeasurementFrame(2,2) = zz; } else { MeasurementFrame(0,0) = 1; MeasurementFrame(0,1) = 0; MeasurementFrame(0,2) = 0; MeasurementFrame(1,0) = 0; MeasurementFrame(1,1) = 1; MeasurementFrame(1,2) = 0; MeasurementFrame(2,0) = 0; MeasurementFrame(2,1) = 0; MeasurementFrame(2,2) = 1; } } } if(!readb0) { MITK_INFO << "BValue not specified in header file"; } } outputForCache = mitk::GrabItkImageMemory( itkVectorImage); // create BValueMap mitk::BValueMapProperty::BValueMap BValueMap = mitk::BValueMapProperty::CreateBValueMap(DiffusionVectors,BValue); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), mitk::GradientDirectionsProperty::New( DiffusionVectors ) ); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str(), mitk::GradientDirectionsProperty::New( OriginalDiffusionVectors ) ); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::MEASUREMENTFRAMEPROPERTYNAME.c_str(), mitk::MeasurementFrameProperty::New( MeasurementFrame ) ); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME.c_str(), mitk::BValueMapProperty::New( BValueMap ) ); outputForCache->SetProperty( mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME.c_str(), mitk::FloatProperty::New( BValue ) ); // Since we have already read the tree, we can store it in a cache variable // so that it can be assigned to the DataObject in GenerateData(); m_OutputCache = outputForCache; m_CacheTime.Modified(); - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } catch(std::exception& e) { MITK_INFO << "Std::Exception while reading file!!"; MITK_INFO << e.what(); throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { MITK_INFO << "Exception while reading file!!"; throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } } } //namespace MITK #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdWriterService.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdWriterService.cpp index a06ea92fbc..4c94bf3f7d 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdWriterService.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdWriterService.cpp @@ -1,185 +1,164 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkDiffusionImageNrrdWriterService__cpp #define __mitkDiffusionImageNrrdWriterService__cpp #include "mitkDiffusionImageNrrdWriterService.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkImageFileWriter.h" #include "itksys/SystemTools.hxx" #include "mitkDiffusionCoreIOMimeTypes.h" #include "mitkImageCast.h" +#include #include #include mitk::DiffusionImageNrrdWriterService::DiffusionImageNrrdWriterService() : AbstractFileWriter(mitk::Image::GetStaticNameOfClass(), CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE_DESCRIPTION()) { RegisterService(); } mitk::DiffusionImageNrrdWriterService::DiffusionImageNrrdWriterService(const mitk::DiffusionImageNrrdWriterService& other) : AbstractFileWriter(other) { } mitk::DiffusionImageNrrdWriterService::~DiffusionImageNrrdWriterService() {} void mitk::DiffusionImageNrrdWriterService::Write() { mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); VectorImageType::Pointer itkImg; mitk::CastToItkImage(input,itkImg); if (input.IsNull()) { MITK_ERROR <<"Sorry, input to DiffusionImageNrrdWriterService is NULL!"; return; } if ( this->GetOutputLocation().empty() ) { MITK_ERROR << "Sorry, filename has not been set!"; return ; } - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); char keybuffer[512]; char valbuffer[512]; //itk::MetaDataDictionary dic = input->GetImage()->GetMetaDataDictionary(); vnl_matrix_fixed measurementFrame = mitk::DiffusionPropertyHelper::GetMeasurementFrame(input); if (measurementFrame(0,0) || measurementFrame(0,1) || measurementFrame(0,2) || measurementFrame(1,0) || measurementFrame(1,1) || measurementFrame(1,2) || measurementFrame(2,0) || measurementFrame(2,1) || measurementFrame(2,2)) { sprintf( valbuffer, " (%lf,%lf,%lf) (%lf,%lf,%lf) (%lf,%lf,%lf)", measurementFrame(0,0), measurementFrame(0,1), measurementFrame(0,2), measurementFrame(1,0), measurementFrame(1,1), measurementFrame(1,2), measurementFrame(2,0), measurementFrame(2,1), measurementFrame(2,2)); itk::EncapsulateMetaData(itkImg->GetMetaDataDictionary(),std::string("measurement frame"),std::string(valbuffer)); } sprintf( valbuffer, "DWMRI"); itk::EncapsulateMetaData(itkImg->GetMetaDataDictionary(),std::string("modality"),std::string(valbuffer)); if(mitk::DiffusionPropertyHelper::GetGradientContainer(input)->Size()) { sprintf( valbuffer, "%1f", mitk::DiffusionPropertyHelper::GetReferenceBValue(input) ); itk::EncapsulateMetaData(itkImg->GetMetaDataDictionary(),std::string("DWMRI_b-value"),std::string(valbuffer)); } for(unsigned int i=0; iSize(); i++) { sprintf( keybuffer, "DWMRI_gradient_%04d", i ); /*if(itk::ExposeMetaData(input->GetMetaDataDictionary(), std::string(keybuffer),tmp)) continue;*/ sprintf( valbuffer, "%1f %1f %1f", mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).get(0), mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).get(1), mitk::DiffusionPropertyHelper::GetGradientContainer(input)->ElementAt(i).get(2)); itk::EncapsulateMetaData(itkImg->GetMetaDataDictionary(),std::string(keybuffer),std::string(valbuffer)); } typedef itk::VectorImage ImageType; std::string ext = this->GetMimeType()->GetExtension(this->GetOutputLocation()); ext = itksys::SystemTools::LowerCase(ext); // default extension is .dwi if( ext == "") { ext = ".nrrd"; this->SetOutputLocation(this->GetOutputLocation() + ext); } if (ext == ".hdwi" || ext == ".nrrd" || ext == ".dwi") { MITK_INFO << "Extension " << ext; itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); //io->SetNrrdVectorType( nrrdKindList ); io->SetFileType( itk::ImageIOBase::Binary ); io->UseCompressionOn(); typedef itk::ImageFileWriter WriterType; WriterType::Pointer nrrdWriter = WriterType::New(); nrrdWriter->UseInputMetaDataDictionaryOn(); nrrdWriter->SetInput( itkImg ); nrrdWriter->SetImageIO(io); nrrdWriter->SetFileName(this->GetOutputLocation()); nrrdWriter->UseCompressionOn(); nrrdWriter->SetImageIO(io); try { nrrdWriter->Update(); } catch (itk::ExceptionObject e) { std::cout << e << std::endl; throw; } } - - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } mitk::DiffusionImageNrrdWriterService* mitk::DiffusionImageNrrdWriterService::Clone() const { return new DiffusionImageNrrdWriterService(*this); } mitk::IFileWriter::ConfidenceLevel mitk::DiffusionImageNrrdWriterService::GetConfidenceLevel() const { mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); if (input.IsNull() || !mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( input ) ) { return Unsupported; } else { return Supported; } } #endif //__mitkDiffusionImageNrrdWriterService__cpp diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdQBallImageReader.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdQBallImageReader.cpp index 3de37be324..bb41705495 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdQBallImageReader.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdQBallImageReader.cpp @@ -1,138 +1,118 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNrrdQBallImageReader.h" #include #include "mitkDiffusionCoreIOMimeTypes.h" #include "itkImageFileReader.h" #include "itkImageRegionIterator.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "mitkITKImageImport.h" #include "mitkImageDataItem.h" +#include namespace mitk { NrrdQBallImageReader::NrrdQBallImageReader(const NrrdQBallImageReader& other) : mitk::AbstractFileReader(other) { } NrrdQBallImageReader::NrrdQBallImageReader() : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::QBI_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::QBI_MIMETYPE_DESCRIPTION() ) { m_ServiceReg = this->RegisterService(); } NrrdQBallImageReader::~NrrdQBallImageReader() { } std::vector > NrrdQBallImageReader::Read() { std::vector > result; std::string location = GetInputLocation(); if ( location == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename of the vessel tree to be read is empty!"); } else { try { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); typedef itk::VectorImage ImageType; itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(location); reader->Update(); ImageType::Pointer img = reader->GetOutput(); typedef itk::Image,3> VecImgType; VecImgType::Pointer vecImg = VecImgType::New(); vecImg->SetSpacing( img->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( img->GetOrigin() ); // Set the image origin vecImg->SetDirection( img->GetDirection() ); // Set the image direction vecImg->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); vecImg->SetBufferedRegion( img->GetLargestPossibleRegion() ); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot.GoToBegin(); itk::ImageRegionIterator it (img, img->GetLargestPossibleRegion() ); typedef ImageType::PixelType VarPixType; typedef VecImgType::PixelType FixPixType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VarPixType vec = it.Get(); FixPixType fixVec(vec.GetDataPointer()); ot.Set(fixVec); ++ot; } OutputType::Pointer resultImage = OutputType::New(); resultImage->InitializeByItk( vecImg.GetPointer() ); resultImage->SetVolume( vecImg->GetBufferPointer() ); result.push_back( resultImage.GetPointer() ); - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } catch(std::exception& e) { throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } return result; } } //namespace MITK mitk::NrrdQBallImageReader* mitk::NrrdQBallImageReader::Clone() const { return new NrrdQBallImageReader(*this); } diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdQBallImageWriter.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdQBallImageWriter.cpp index 09cbee95d6..57726ad3e2 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdQBallImageWriter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdQBallImageWriter.cpp @@ -1,149 +1,129 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNrrdQBallImageWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkImageFileWriter.h" #include "mitkImageCast.h" #include "mitkIOMimeTypes.h" #include "mitkDiffusionCoreIOMimeTypes.h" +#include mitk::NrrdQBallImageWriter::NrrdQBallImageWriter() : AbstractFileWriter(mitk::QBallImage::GetStaticNameOfClass(), CustomMimeType( mitk::DiffusionCoreIOMimeTypes::QBI_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::QBI_MIMETYPE_DESCRIPTION()) { RegisterService(); } mitk::NrrdQBallImageWriter::NrrdQBallImageWriter(const mitk::NrrdQBallImageWriter& other) : AbstractFileWriter(other) { } mitk::NrrdQBallImageWriter::~NrrdQBallImageWriter() {} void mitk::NrrdQBallImageWriter::Write() { InputType::ConstPointer input = dynamic_cast(this->GetInput()); if (input.IsNull()) { MITK_ERROR <<"Sorry, input to NrrdQBallImageWriter is NULL!"; return; } if ( this->GetOutputLocation().empty() ) { MITK_ERROR << "Sorry, filename has not been set!"; return ; } - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileType( itk::ImageIOBase::Binary ); io->UseCompressionOn(); typedef itk::VectorImage VecImgType; typedef itk::Image,3> ImageType; typedef itk::ImageFileWriter WriterType; WriterType::Pointer nrrdWriter = WriterType::New(); ImageType::Pointer outimage = ImageType::New(); CastToItkImage(input, outimage); VecImgType::Pointer vecImg = VecImgType::New(); vecImg->SetSpacing( outimage->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( outimage->GetOrigin() ); // Set the image origin vecImg->SetDirection( outimage->GetDirection() ); // Set the image direction vecImg->SetLargestPossibleRegion( outimage->GetLargestPossibleRegion()); vecImg->SetBufferedRegion( outimage->GetLargestPossibleRegion() ); vecImg->SetVectorLength(QBALL_ODFSIZE); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot.GoToBegin(); itk::ImageRegionIterator it (outimage, outimage->GetLargestPossibleRegion() ); typedef ImageType::PixelType VecPixType; typedef VecImgType::PixelType VarVecType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); VarVecType varVec(vec.GetVnlVector().data_block(), QBALL_ODFSIZE); ot.Set(varVec); ++ot; } nrrdWriter->SetInput( vecImg ); nrrdWriter->SetImageIO(io); nrrdWriter->SetFileName(this->GetOutputLocation().c_str()); nrrdWriter->UseCompressionOn(); try { nrrdWriter->Update(); } catch (itk::ExceptionObject e) { std::cout << e << std::endl; } - - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } mitk::NrrdQBallImageWriter* mitk::NrrdQBallImageWriter::Clone() const { return new NrrdQBallImageWriter(*this); } mitk::IFileWriter::ConfidenceLevel mitk::NrrdQBallImageWriter::GetConfidenceLevel() const { InputType::ConstPointer input = dynamic_cast(this->GetInput()); if (input.IsNull() ) { return Unsupported; } else { return Supported; } } diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdTensorImageReader.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdTensorImageReader.cpp index 611350278e..471d996649 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdTensorImageReader.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdTensorImageReader.cpp @@ -1,441 +1,420 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNrrdTensorImageReader.h" #include #include "mitkDiffusionCoreIOMimeTypes.h" #include "itkImageFileReader.h" #include "itkImageRegionIterator.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include #include "mitkITKImageImport.h" #include "mitkImageDataItem.h" +#include namespace mitk { NrrdTensorImageReader::NrrdTensorImageReader(const NrrdTensorImageReader& other) : mitk::AbstractFileReader(other) { } NrrdTensorImageReader::NrrdTensorImageReader() : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DTI_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DTI_MIMETYPE_DESCRIPTION() ) { m_ServiceReg = this->RegisterService(); } NrrdTensorImageReader::~NrrdTensorImageReader() { } std::vector > NrrdTensorImageReader::Read() { std::vector > result; std::string location = GetInputLocation(); if ( location == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename is empty!"); } else { try { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); try { std::string fname3 = "temp_dti.nii"; itksys::SystemTools::CopyAFile(location.c_str(), fname3.c_str()); typedef itk::VectorImage ImageType; itk::NiftiImageIO::Pointer io = itk::NiftiImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(fname3); reader->Update(); ImageType::Pointer img = reader->GetOutput(); typedef itk::Image,3> VecImgType; VecImgType::Pointer vecImg = VecImgType::New(); vecImg->SetSpacing( img->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( img->GetOrigin() ); // Set the image origin vecImg->SetDirection( img->GetDirection() ); // Set the image direction vecImg->SetRegions( img->GetLargestPossibleRegion()); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot.GoToBegin(); itk::ImageRegionIterator it (img, img->GetLargestPossibleRegion() ); it.GoToBegin(); typedef ImageType::PixelType VarPixType; typedef VecImgType::PixelType FixPixType; int numComponents = img->GetNumberOfComponentsPerPixel(); if (numComponents==6) { MITK_INFO << "Trying to load dti as 6-comp nifti ..."; while (!it.IsAtEnd()) { VarPixType vec = it.Get(); FixPixType fixVec(vec.GetDataPointer()); itk::DiffusionTensor3D tensor; tensor.SetElement(0, vec.GetElement(0)); tensor.SetElement(1, vec.GetElement(1)); tensor.SetElement(2, vec.GetElement(2)); tensor.SetElement(3, vec.GetElement(3)); tensor.SetElement(4, vec.GetElement(4)); tensor.SetElement(5, vec.GetElement(5)); fixVec = tensor; ot.Set(fixVec); ++ot; ++it; } } else if(numComponents==9) { MITK_INFO << "Trying to load dti as 9-comp nifti ..."; while (!it.IsAtEnd()) { VarPixType vec = it.Get(); itk::DiffusionTensor3D tensor; tensor.SetElement(0, vec.GetElement(0)); tensor.SetElement(1, vec.GetElement(1)); tensor.SetElement(2, vec.GetElement(2)); tensor.SetElement(3, vec.GetElement(4)); tensor.SetElement(4, vec.GetElement(5)); tensor.SetElement(5, vec.GetElement(8)); FixPixType fixVec(tensor); ot.Set(fixVec); ++ot; ++it; } } else if (numComponents==1) { MITK_INFO << "Trying to load dti as 4D nifti ..."; typedef itk::Image ImageType; typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(fname3); reader->Update(); ImageType::Pointer img = reader->GetOutput(); itk::Size<4> size = img->GetLargestPossibleRegion().GetSize(); while (!ot.IsAtEnd()) { itk::DiffusionTensor3D tensor; ImageType::IndexType idx; idx[0] = ot.GetIndex()[0]; idx[1] = ot.GetIndex()[1]; idx[2] = ot.GetIndex()[2]; if (size[3]==6) { for (unsigned int te=0; teGetPixel(idx)); } } else if (size[3]==9) { idx[3] = 0; tensor.SetElement(0, img->GetPixel(idx)); idx[3] = 1; tensor.SetElement(1, img->GetPixel(idx)); idx[3] = 2; tensor.SetElement(2, img->GetPixel(idx)); idx[3] = 4; tensor.SetElement(3, img->GetPixel(idx)); idx[3] = 5; tensor.SetElement(4, img->GetPixel(idx)); idx[3] = 8; tensor.SetElement(5, img->GetPixel(idx)); } else throw itk::ImageFileReaderException(__FILE__, __LINE__, "Unknown number of components for DTI file. Should be 6 or 9!"); FixPixType fixVec(tensor); ot.Set(fixVec); ++ot; } } OutputType::Pointer resultImage = OutputType::New(); resultImage->InitializeByItk( vecImg.GetPointer() ); resultImage->SetVolume( vecImg->GetBufferPointer() ); result.push_back( resultImage.GetPointer() ); } catch(...) { MITK_INFO << "Trying to load dti as nrrd ..."; typedef itk::VectorImage ImageType; itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(location); reader->Update(); ImageType::Pointer img = reader->GetOutput(); typedef itk::Image,3> VecImgType; VecImgType::Pointer vecImg = VecImgType::New(); vecImg->SetSpacing( img->GetSpacing() ); // Set the image spacing vecImg->SetOrigin( img->GetOrigin() ); // Set the image origin vecImg->SetDirection( img->GetDirection() ); // Set the image direction vecImg->SetRegions( img->GetLargestPossibleRegion()); vecImg->Allocate(); itk::ImageRegionIterator ot (vecImg, vecImg->GetLargestPossibleRegion() ); ot.GoToBegin(); itk::ImageRegionIterator it (img, img->GetLargestPossibleRegion() ); it.GoToBegin(); typedef ImageType::PixelType VarPixType; typedef VecImgType::PixelType FixPixType; int numComponents = img->GetNumberOfComponentsPerPixel(); itk::MetaDataDictionary imgMetaDictionary = img->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; bool readFrame = false; double xx, xy, xz, yx, yy, yz, zx, zy, zz; MeasurementFrameType measFrame; measFrame.SetIdentity(); MeasurementFrameType measFrameTransp; measFrameTransp.SetIdentity(); for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); if (itKey->find("measurement frame") != std::string::npos) { sscanf(metaString.c_str(), " ( %lf , %lf , %lf ) ( %lf , %lf , %lf ) ( %lf , %lf , %lf ) \n", &xx, &xy, &xz, &yx, &yy, &yz, &zx, &zy, &zz); if (xx>10e-10 || xy>10e-10 || xz>10e-10 || yx>10e-10 || yy>10e-10 || yz>10e-10 || zx>10e-10 || zy>10e-10 || zz>10e-10 ) { readFrame = true; measFrame(0,0) = xx; measFrame(0,1) = xy; measFrame(0,2) = xz; measFrame(1,0) = yx; measFrame(1,1) = yy; measFrame(1,2) = yz; measFrame(2,0) = zx; measFrame(2,1) = zy; measFrame(2,2) = zz; measFrameTransp = measFrame.GetTranspose(); } } } if (numComponents==6) { while (!it.IsAtEnd()) { // T'=RTR' VarPixType vec = it.Get(); FixPixType fixVec(vec.GetDataPointer()); if(readFrame) { itk::DiffusionTensor3D tensor; tensor.SetElement(0, vec.GetElement(0)); tensor.SetElement(1, vec.GetElement(1)); tensor.SetElement(2, vec.GetElement(2)); tensor.SetElement(3, vec.GetElement(3)); tensor.SetElement(4, vec.GetElement(4)); tensor.SetElement(5, vec.GetElement(5)); tensor = ConvertMatrixTypeToFixedArrayType(tensor.PreMultiply(measFrame)); tensor = ConvertMatrixTypeToFixedArrayType(tensor.PostMultiply(measFrameTransp)); fixVec = tensor; } ot.Set(fixVec); ++ot; ++it; } } else if(numComponents==9) { while (!it.IsAtEnd()) { VarPixType vec = it.Get(); itk::DiffusionTensor3D tensor; tensor.SetElement(0, vec.GetElement(0)); tensor.SetElement(1, vec.GetElement(1)); tensor.SetElement(2, vec.GetElement(2)); tensor.SetElement(3, vec.GetElement(4)); tensor.SetElement(4, vec.GetElement(5)); tensor.SetElement(5, vec.GetElement(8)); if(readFrame) { tensor = ConvertMatrixTypeToFixedArrayType(tensor.PreMultiply(measFrame)); tensor = ConvertMatrixTypeToFixedArrayType(tensor.PostMultiply(measFrameTransp)); } FixPixType fixVec(tensor); ot.Set(fixVec); ++ot; ++it; } } else if (numComponents==1) { typedef itk::Image ImageType; itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(location); reader->Update(); ImageType::Pointer img = reader->GetOutput(); itk::Size<4> size = img->GetLargestPossibleRegion().GetSize(); while (!ot.IsAtEnd()) { itk::DiffusionTensor3D tensor; ImageType::IndexType idx; idx[0] = ot.GetIndex()[0]; idx[1] = ot.GetIndex()[1]; idx[2] = ot.GetIndex()[2]; if (size[3]==6) { for (unsigned int te=0; teGetPixel(idx)); } } else if (size[3]==9) { idx[3] = 0; tensor.SetElement(0, img->GetPixel(idx)); idx[3] = 1; tensor.SetElement(1, img->GetPixel(idx)); idx[3] = 2; tensor.SetElement(2, img->GetPixel(idx)); idx[3] = 4; tensor.SetElement(3, img->GetPixel(idx)); idx[3] = 5; tensor.SetElement(4, img->GetPixel(idx)); idx[3] = 8; tensor.SetElement(5, img->GetPixel(idx)); } else throw itk::ImageFileReaderException(__FILE__, __LINE__, "Unknown number of komponents for DTI file. Should be 6 or 9!"); if(readFrame) { tensor = ConvertMatrixTypeToFixedArrayType(tensor.PreMultiply(measFrame)); tensor = ConvertMatrixTypeToFixedArrayType(tensor.PostMultiply(measFrameTransp)); } FixPixType fixVec(tensor); ot.Set(fixVec); ++ot; } } else { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Image has wrong number of pixel components!"); } OutputType::Pointer resultImage = OutputType::New(); resultImage->InitializeByItk( vecImg.GetPointer() ); resultImage->SetVolume( vecImg->GetBufferPointer() ); result.push_back( resultImage.GetPointer() ); } - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } - } catch(std::exception& e) { throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested DTI file!"); } } return result; } itk::DiffusionTensor3D NrrdTensorImageReader ::ConvertMatrixTypeToFixedArrayType(const itk::DiffusionTensor3D::Superclass::MatrixType & matrix) { /* | 0 1 2 | * | X 3 4 | * | X X 5 | */ itk::DiffusionTensor3D arr; arr.SetElement(0,matrix(0,0)); arr.SetElement(1,matrix(0,1)); arr.SetElement(2,matrix(0,2)); arr.SetElement(3,matrix(1,3)); arr.SetElement(4,matrix(1,4)); arr.SetElement(5,matrix(2,5)); return arr; } } //namespace MITK mitk::NrrdTensorImageReader* mitk::NrrdTensorImageReader::Clone() const { return new NrrdTensorImageReader(*this); } diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdTensorImageWriter.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdTensorImageWriter.cpp index c81aeef1be..82492a85aa 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdTensorImageWriter.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkNrrdTensorImageWriter.cpp @@ -1,122 +1,101 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNrrdTensorImageWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkImageFileWriter.h" #include "itkDiffusionTensor3D.h" #include "mitkImageCast.h" #include "mitkDiffusionCoreIOMimeTypes.h" +#include mitk::NrrdTensorImageWriter::NrrdTensorImageWriter() : AbstractFileWriter(mitk::TensorImage::GetStaticNameOfClass(), CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DTI_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DTI_MIMETYPE_DESCRIPTION() ) { RegisterService(); } mitk::NrrdTensorImageWriter::NrrdTensorImageWriter(const mitk::NrrdTensorImageWriter& other) : AbstractFileWriter(other) { } mitk::NrrdTensorImageWriter::~NrrdTensorImageWriter() {} void mitk::NrrdTensorImageWriter::Write() { InputType::ConstPointer input = dynamic_cast(this->GetInput()); if (input.IsNull() ) { MITK_ERROR <<"Sorry, input to NrrdTensorImageWriter is NULL!"; return; } if ( this->GetOutputLocation().empty() ) { MITK_ERROR << "Sorry, filename has not been set!" ; return ; } - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, NULL ); - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileType( itk::ImageIOBase::Binary ); io->UseCompressionOn(); typedef itk::Image,3> ImageType; typedef itk::ImageFileWriter WriterType; WriterType::Pointer nrrdWriter = WriterType::New(); ImageType::Pointer outimage = ImageType::New(); CastToItkImage(input, outimage); nrrdWriter->SetInput( outimage ); nrrdWriter->SetImageIO(io); nrrdWriter->SetFileName(this->GetOutputLocation().c_str()); nrrdWriter->UseCompressionOn(); try { nrrdWriter->Update(); } catch (itk::ExceptionObject e) { std::cout << e << std::endl; } - - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } - } mitk::NrrdTensorImageWriter* mitk::NrrdTensorImageWriter::Clone() const { return new NrrdTensorImageWriter(*this); } mitk::IFileWriter::ConfidenceLevel mitk::NrrdTensorImageWriter::GetConfidenceLevel() const { InputType::ConstPointer input = dynamic_cast(this->GetInput()); if (input.IsNull() ) { return Unsupported; } else { return Supported; } } diff --git a/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkDicomDiffusionImageHeaderReader.cpp b/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkDicomDiffusionImageHeaderReader.cpp index bf561c32bc..fbe24d0c63 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkDicomDiffusionImageHeaderReader.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkDicomDiffusionImageHeaderReader.cpp @@ -1,349 +1,328 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ - #include "mitkDicomDiffusionImageHeaderReader.h" +#include + #include "mitkGEDicomDiffusionImageHeaderReader.h" #include "mitkPhilipsDicomDiffusionImageHeaderReader.h" #include "mitkSiemensDicomDiffusionImageHeaderReader.h" #include "mitkSiemensMosaicDicomDiffusionImageHeaderReader.h" void InsertUnique( std::vector & vec, float value ) { int n = vec.size(); if (n == 0) { vec.push_back( value ); return; } for (int k = 0; k < n ; k++) { if (vec[k] == value) { return; } } // if we get here, it means value is not in vec. vec.push_back( value ); return; } mitk::DicomDiffusionImageHeaderReader::DicomDiffusionImageHeaderReader() { m_SliceOrderIS = true; m_SingleSeries = true; } mitk::DicomDiffusionImageHeaderReader::~DicomDiffusionImageHeaderReader() { } mitk::DicomDiffusionImageHeaderReader::SupportedVendors mitk::DicomDiffusionImageHeaderReader::GetVendorID() { // adapted from namic-sandbox // DicomToNrrdConverter.cxx m_GdcmIO = ImageIOType::New(); m_GdcmIO->LoadPrivateTagsOn(); m_GdcmIO->SetMaxSizeLoadEntry( 65536 ); m_VolumeReader = VolumeReaderType::New(); m_VolumeReader->SetImageIO( m_GdcmIO ); m_VolumeReader->SetFileNames( m_DicomFilenames ); try { m_VolumeReader->Update(); } catch (itk::ExceptionObject &excp) { std::cerr << "Exception thrown while reading slice" << std::endl; std::cerr << excp << std::endl; return SV_UNKNOWN_VENDOR; } VolumeReaderType::DictionaryArrayRawPointer inputDict = m_VolumeReader->GetMetaDataDictionaryArray(); std::string vendor; itk::ExposeMetaData ( *(*inputDict)[0], "0008|0070", vendor ); // std::cout << vendor << std::endl; std::string ImageType; itk::ExposeMetaData ( *(*inputDict)[0], "0008|0008", ImageType ); //std::cout << ImageType << std::endl; if ( vendor.find("GE") != std::string::npos ) { // for GE data return SV_GE; } else if( vendor.find("SIEMENS") != std::string::npos ) { if ( ImageType.find("MOSAIC") != std::string::npos ) { // for Siemens MOSAIC return SV_SIEMENS_MOSAIC; } else { // for Siemens SPLIT return SV_SIEMENS; } } else if( vendor.find("PHILIPS") != std::string::npos ) { // for philips data return SV_PHILIPS; } else { // for unrecognized vendors return SV_UNKNOWN_VENDOR; } } // do the work void mitk::DicomDiffusionImageHeaderReader::Update() { // check if there are filenames if(m_DicomFilenames.size()) { m_Output = mitk::DiffusionImageHeaderInformation::New(); m_Output->m_DicomFilenames = m_DicomFilenames; // create correct reader switch(GetVendorID()) { case(SV_GE): { GEDicomDiffusionImageHeaderReader::Pointer reader = GEDicomDiffusionImageHeaderReader::New(); reader->SetSeriesDicomFilenames(this->m_DicomFilenames); reader->SetGdcmIO(this->m_GdcmIO); reader->SetVolumeReader(this->m_VolumeReader); reader->SetOutputPointer(this->m_Output); reader->Update(); this->m_Output = reader->GetOutput(); break; } case(SV_SIEMENS): { SiemensDicomDiffusionImageHeaderReader::Pointer reader = SiemensDicomDiffusionImageHeaderReader::New(); reader->SetSeriesDicomFilenames(this->m_DicomFilenames); reader->SetGdcmIO(this->m_GdcmIO); reader->SetVolumeReader(this->m_VolumeReader); reader->SetOutputPointer(this->m_Output); reader->Update(); this->m_Output = reader->GetOutput(); break; } case(SV_SIEMENS_MOSAIC): { SiemensMosaicDicomDiffusionImageHeaderReader::Pointer reader = SiemensMosaicDicomDiffusionImageHeaderReader::New(); reader->SetSeriesDicomFilenames(this->m_DicomFilenames); reader->SetGdcmIO(this->m_GdcmIO); reader->SetVolumeReader(this->m_VolumeReader); reader->SetOutputPointer(this->m_Output); reader->Update(); this->m_Output = reader->GetOutput(); break; } case(SV_PHILIPS): { PhilipsDicomDiffusionImageHeaderReader::Pointer reader = PhilipsDicomDiffusionImageHeaderReader::New(); reader->SetSeriesDicomFilenames(this->m_DicomFilenames); reader->SetGdcmIO(this->m_GdcmIO); reader->SetVolumeReader(this->m_VolumeReader); reader->SetOutputPointer(this->m_Output); reader->Update(); this->m_Output = reader->GetOutput(); break; } case(SV_UNKNOWN_VENDOR): { std::cerr << "diffusion header reader: unknown vendor" << std::endl; break; } } } } // return output mitk::DiffusionImageHeaderInformation::Pointer mitk::DicomDiffusionImageHeaderReader::GetOutput() { return m_Output; } void mitk::DicomDiffusionImageHeaderReader::ReadPublicTags() { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, nullptr ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); VolumeReaderType::DictionaryArrayRawPointer inputDict = m_VolumeReader->GetMetaDataDictionaryArray(); // load in all public tags m_nSlice = inputDict->size(); std::string tag; tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0008|103e", tag ); this->m_Output->seriesDescription = tag.c_str(); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0020|0011", tag ); this->m_Output->seriesNumber = atoi(tag.c_str()); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0010|0010", tag ); this->m_Output->patientName = tag.c_str(); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0028|0010", tag ); this->m_Output->nRows = atoi( tag.c_str() ); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0028|0011", tag ); this->m_Output->nCols = atoi( tag.c_str() ); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0028|0030", tag ); sscanf( tag.c_str(), "%f\\%f", &this->m_Output->xRes, &this->m_Output->yRes ); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0020|0032", tag ); sscanf( tag.c_str(), "%f\\%f\\%f", &this->m_Output->xOrigin, &this->m_Output->yOrigin, &this->m_Output->zOrigin ); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0018|0050", tag ); this->m_Output->sliceThickness = atof( tag.c_str() ); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0018|0088", tag ); this->m_Output->sliceSpacing = atof( tag.c_str() ); // figure out how many slices are there in a volume, each unique // SliceLocation represent one slice for (int k = 0; k < m_nSlice; k++) { tag.clear(); itk::ExposeMetaData ( *(*inputDict)[k], "0020|1041", tag); float sliceLocation = atof( tag.c_str() ); InsertUnique( m_sliceLocations, sliceLocation ); } // check ImageOrientationPatient and figure out slice direction in // L-P-I (right-handed) system. // In Dicom, the coordinate frame is L-P by default. Look at // http://medical.nema.org/dicom/2007/07_03pu.pdf , page 301 tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0020|0037", tag ); float xRow, yRow, zRow, xCol, yCol, zCol, xSlice, ySlice, zSlice /*, orthoSliceSpacing*/; sscanf( tag.c_str(), "%f\\%f\\%f\\%f\\%f\\%f", &xRow, &yRow, &zRow, &xCol, &yCol, &zCol ); // In Dicom, the measurement frame is L-P by default. Look at // http://medical.nema.org/dicom/2007/07_03pu.pdf , page 301, in // order to make this compatible with Slicer's RAS frame, we // multiply the direction cosines by the negatives of the resolution // (resolution is required by nrrd format). Direction cosine is not // affacted since the resulting frame is still a right-handed frame. xRow = -xRow; yRow = -yRow; xCol = -xCol; yCol = -yCol; // Cross product, this gives I-axis direction xSlice = (yRow*zCol-zRow*yCol)*this->m_Output->sliceSpacing; ySlice = (zRow*xCol-xRow*zCol)*this->m_Output->sliceSpacing; zSlice = (xRow*yCol-yRow*xCol)*this->m_Output->sliceSpacing; xRow *= this->m_Output->xRes; yRow *= this->m_Output->xRes; zRow *= this->m_Output->xRes; xCol *= this->m_Output->yRes; yCol *= this->m_Output->yRes; zCol *= this->m_Output->yRes; this->m_Output->xRow = xRow; this->m_Output->yRow = yRow; this->m_Output->zRow = zRow; this->m_Output->xCol = xCol; this->m_Output->yCol = yCol; this->m_Output->zCol = zCol; this->m_Output->xSlice = xSlice; this->m_Output->ySlice = ySlice; this->m_Output->zSlice = zSlice; - - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } void mitk::DicomDiffusionImageHeaderReader::ReadPublicTags2() { if (!m_SliceOrderIS) { this->m_Output->xSlice = -this->m_Output->xSlice; this->m_Output->ySlice = -this->m_Output->ySlice; this->m_Output->zSlice = -this->m_Output->zSlice; } } void mitk::DicomDiffusionImageHeaderReader::TransformGradients() { // transform gradient directions into RAS frame if ( !m_SliceOrderIS ) { this->m_Output->DiffusionVector[2] = -this->m_Output->DiffusionVector[2]; // I -> S } } diff --git a/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp b/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp index bd0d758e2a..ec4b280d0e 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp @@ -1,147 +1,127 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ - #include "mitkGEDicomDiffusionImageHeaderReader.h" +#include + + #include "gdcmGlobal.h" #include "gdcmDict.h" #include "gdcmDicts.h" #include "gdcmDictEntry.h" #include "gdcmDictEntry.h" #include "gdcmDict.h" #include "gdcmFile.h" #include "gdcmSerieHelper.h" mitk::GEDicomDiffusionImageHeaderReader::GEDicomDiffusionImageHeaderReader() { } mitk::GEDicomDiffusionImageHeaderReader::~GEDicomDiffusionImageHeaderReader() { } // do the work void mitk::GEDicomDiffusionImageHeaderReader::Update() { // check if there are filenames if(m_DicomFilenames.size()) { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, nullptr ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); // adapted from namic-sandbox // DicomToNrrdConverter.cxx VolumeReaderType::DictionaryArrayRawPointer inputDict = m_VolumeReader->GetMetaDataDictionaryArray(); ReadPublicTags(); float x0, y0, z0; float x1, y1, z1; std::string tag; tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0020|0032", tag ); sscanf( tag.c_str(), "%f\\%f\\%f", &x0, &y0, &z0 ); std::cout << "Slice 0: " << tag << std::endl; tag.clear(); // assume volume interleaving, i.e. the second dicom file stores // the second slice in the same volume as the first dicom file itk::ExposeMetaData ( *(*inputDict)[1], "0020|0032", tag ); sscanf( tag.c_str(), "%f\\%f\\%f", &x1, &y1, &z1 ); std::cout << "Slice 1: " << tag << std::endl; x1 -= x0; y1 -= y0; z1 -= z0; x0 = x1*this->m_Output->xSlice + y1*this->m_Output->ySlice + z1*this->m_Output->zSlice; if (x0 < 0) { m_SliceOrderIS = false; } ReadPublicTags2(); int nSliceInVolume; int nVolume; nSliceInVolume = m_sliceLocations.size(); nVolume = m_nSlice/nSliceInVolume; // assume volume interleaving std::cout << "Number of Slices: " << m_nSlice << std::endl; std::cout << "Number of Volume: " << nVolume << std::endl; std::cout << "Number of Slices in each volume: " << nSliceInVolume << std::endl; for (int k = 0; k < m_nSlice; k += nSliceInVolume) { tag.clear(); bool exist = itk::ExposeMetaData ( *(*inputDict)[k], "0043|1039", tag); float b = atof( tag.c_str() ); this->m_Output->bValue = b; vnl_vector_fixed vect3d; if (!exist || b == 0) { vect3d.fill( 0 ); this->m_Output->DiffusionVector = vect3d; continue; } vect3d.fill( 0 ); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[k], "0019|10bb", tag); vect3d[0] = atof( tag.c_str() ); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[k], "0019|10bc", tag); vect3d[1] = atof( tag.c_str() ); tag.clear(); itk::ExposeMetaData ( *(*inputDict)[k], "0019|10bd", tag); vect3d[2] = atof( tag.c_str() ); vect3d.normalize(); this->m_Output->DiffusionVector = vect3d; } TransformGradients(); - - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } } diff --git a/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp b/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp index 2374fd1585..951716f314 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp @@ -1,264 +1,246 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ - #include "mitkSiemensDicomDiffusionImageHeaderReader.h" + +#include + + #include "gdcmGlobal.h" //#include #include "gdcmFile.h" #include "gdcmImageReader.h" #include "gdcmDictEntry.h" #include "gdcmDicts.h" #include "gdcmTag.h" mitk::SiemensDicomDiffusionImageHeaderReader::SiemensDicomDiffusionImageHeaderReader() { } mitk::SiemensDicomDiffusionImageHeaderReader::~SiemensDicomDiffusionImageHeaderReader() { } int mitk::SiemensDicomDiffusionImageHeaderReader::ExtractSiemensDiffusionInformation( std::string tagString, std::string nameString, std::vector& valueArray, int startPos ) { std::string::size_type atPosition = tagString.find( nameString, startPos ); if ( atPosition == std::string::npos) { return 0; } else { std::string infoAsString = tagString.substr( atPosition, tagString.size()-atPosition+1 ); const char * infoAsCharPtr = infoAsString.c_str(); int vm = *(infoAsCharPtr+64); int offset = 84; for (int k = 0; k < vm; k++) { int itemLength = *(infoAsCharPtr+offset+4); int strideSize = static_cast (ceil(static_cast(itemLength)/4) * 4); std::string valueString = infoAsString.substr( offset+16, itemLength ); valueArray.push_back( atof(valueString.c_str()) ); offset += 16+strideSize; } return vm; } } int mitk::SiemensDicomDiffusionImageHeaderReader::ExtractSiemensDiffusionGradientInformation( std::string tagString, std::string nameString, std::vector& valueArray ) { int nItems = 0; std::string::size_type pos = -1; while(nItems != 3) { nItems = ExtractSiemensDiffusionInformation( tagString, nameString, valueArray, pos+1 ); pos = tagString.find( nameString, pos+1 ); if ( pos == std::string::npos ) { break; } } return nItems; } // do the work void mitk::SiemensDicomDiffusionImageHeaderReader::Update() { // check if there are filenames if(m_DicomFilenames.size()) { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, nullptr ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); // adapted from slicer // DicomToNrrdConverter.cxx VolumeReaderType::DictionaryArrayRawPointer inputDict = m_VolumeReader->GetMetaDataDictionaryArray(); ReadPublicTags(); float x0, y0, z0; float x1, y1, z1; std::string tag; tag.clear(); itk::ExposeMetaData ( *(*inputDict)[0], "0020|0032", tag ); sscanf( tag.c_str(), "%f\\%f\\%f", &x0, &y0, &z0 ); //MITK_INFO << "Slice 0: " << tag << std::endl; tag.clear(); // assume volume interleaving, i.e. the second dicom file stores // the second slice in the same volume as the first dicom file if((*inputDict).size() > 1) { itk::ExposeMetaData ( *(*inputDict)[1], "0020|0032", tag ); sscanf( tag.c_str(), "%f\\%f\\%f", &x1, &y1, &z1 ); //MITK_INFO << "Slice 1: " << tag << std::endl; x1 -= x0; y1 -= y0; z1 -= z0; x0 = x1*this->m_Output->xSlice + y1*this->m_Output->ySlice + z1*this->m_Output->zSlice; if (x0 < 0) { m_SliceOrderIS = false; } } ReadPublicTags2(); int nStride = 1; this->m_Output->nSliceInVolume = m_sliceLocations.size(); nStride = this->m_Output->nSliceInVolume; MITK_INFO << m_DicomFilenames[0] << std::endl; MITK_INFO << "Dims " << this->m_Output->nRows << "x" << this->m_Output->nCols << "x" << this->m_Output->nSliceInVolume << " " << std::endl; for (int k = 0; k < m_nSlice; k += nStride ) { gdcm::ImageReader reader; reader.SetFileName( m_DicomFilenames[k].c_str() ); if( !reader.Read() ) { itkExceptionMacro(<< "Cannot read requested file"); } const gdcm::File &f = reader.GetFile(); const gdcm::DataSet &ds = f.GetDataSet(); // gdcm::DataSet ds = header0->GetDataSet(); auto it = ds.Begin(); // Copy of the header->content // copy information stored in 0029,1010 into a string for parsing for(; it != ds.End(); ++it) { const gdcm::DataElement &ref = *it; if (ref.GetTag() == gdcm::Tag(0x0029,0x1010)) { tag = std::string(ref.GetByteValue()->GetPointer(),ref.GetByteValue()->GetLength()); } } // parse B_value from 0029,1010 tag std::vector valueArray(0); vnl_vector_fixed vect3d; int nItems = ExtractSiemensDiffusionInformation(tag, "B_value", valueArray); if (nItems != 1 || valueArray[0] == 0) // did not find enough information { tag.clear(); MITK_INFO << "Reading diffusion info from 0019|100c and 0019|100e tags" << std::endl; bool success = false; for(it = ds.Begin(); it != ds.End(); ++it) { const gdcm::DataElement &ref = *it; if (ref.GetTag() == gdcm::Tag(0x0019,0x100c)) { tag = std::string(ref.GetByteValue()->GetPointer(),ref.GetByteValue()->GetLength()); this->m_Output->bValue = atof( tag.c_str() ); success = true; } } tag.clear(); if(success) { if(this->m_Output->bValue == 0) { MITK_INFO << "BV: 0 (Baseline image)"; continue; } success = false; for(it = ds.Begin(); it != ds.End(); ++it) { const gdcm::DataElement &ref = *it; if (ref.GetTag() == gdcm::Tag(0x0019,0x100e)) { tag = std::string(ref.GetByteValue()->GetPointer(),ref.GetByteValue()->GetLength()); success = true; } } if(success) { memcpy( &vect3d[0], tag.c_str()+0, 8 ); memcpy( &vect3d[1], tag.c_str()+8, 8 ); memcpy( &vect3d[2], tag.c_str()+16, 8 ); vect3d.normalize(); this->m_Output->DiffusionVector = vect3d; TransformGradients(); MITK_INFO << "BV: " << this->m_Output->bValue; MITK_INFO << " GD: " << this->m_Output->DiffusionVector; continue; } } } else { MITK_INFO << "Reading diffusion info from 0029,1010 tag" << std::endl; this->m_Output->bValue = valueArray[0]; if(this->m_Output->bValue == 0) { MITK_INFO << "BV: 0 (Baseline image)"; continue; } valueArray.resize(0); nItems = ExtractSiemensDiffusionGradientInformation(tag, "DiffusionGradientDirection", valueArray); if (nItems == 3) { vect3d[0] = valueArray[0]; vect3d[1] = valueArray[1]; vect3d[2] = valueArray[2]; vect3d.normalize(); this->m_Output->DiffusionVector = vect3d; TransformGradients(); MITK_INFO << "BV: " << this->m_Output->bValue; MITK_INFO << " GD: " << this->m_Output->DiffusionVector; continue; } } MITK_ERROR << "No diffusion info found, forcing to BASELINE image." << std::endl; this->m_Output->bValue = 0.0; vect3d.fill( 0.0 ); this->m_Output->DiffusionVector = vect3d; } - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } } diff --git a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.cpp b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.cpp index 7b3f7002b7..3f3f034edb 100644 --- a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.cpp +++ b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.cpp @@ -1,180 +1,173 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include "mitkNavigationDataReaderCSV.h" #include +#include // STL #include mitk::NavigationDataReaderCSV::NavigationDataReaderCSV() : AbstractFileReader( mitk::IGTMimeTypes::NAVIGATIONDATASETCSV_MIMETYPE(), "MITK NavigationData Reader (CSV)") { RegisterService(); } mitk::NavigationDataReaderCSV::NavigationDataReaderCSV(const mitk::NavigationDataReaderCSV& other) : AbstractFileReader(other) { } mitk::NavigationDataReaderCSV::~NavigationDataReaderCSV() { } mitk::NavigationDataReaderCSV* mitk::NavigationDataReaderCSV::Clone() const { return new NavigationDataReaderCSV(*this); } std::vector> mitk::NavigationDataReaderCSV::Read() { std::vector fileContent = GetFileContentLineByLine(GetInputLocation()); int NumOfTools = getNumberOfToolsInLine(fileContent[0]); mitk::NavigationDataSet::Pointer returnValue = mitk::NavigationDataSet::New(NumOfTools); std::vector result; result.push_back(returnValue.GetPointer()); // start from line 1 to leave out header for (unsigned int i = 1; iAddNavigationDatas(parseLine(fileContent[i], NumOfTools)); } return result; } int mitk::NavigationDataReaderCSV::getNumberOfToolsInLine(std::string line) { std::vector tokens=splitLine(line); int size = tokens.size(); int NumOfTools = (size)/9; if ( (size)%9 != 0 ) { MITK_ERROR("mitkNavigationDataReader") << "Illegal csv-file! Unexpected number of columns found! Assuming " << NumOfTools << " tools!"; } return NumOfTools ; } std::vector mitk::NavigationDataReaderCSV::splitLine(std::string line) { std::vector elems; std::stringstream ss(line); std::string item; while (std::getline(ss, item, ';')) { elems.push_back(item); } return elems; } mitk::NavigationData::Pointer mitk::NavigationDataReaderCSV::CreateNd(std::string timestamp, std::string valid, std::string X, std::string Y, std::string Z, std::string QX, std::string QY, std::string QZ, std::string QR) { mitk::NavigationData::Pointer result= mitk::NavigationData::New(); mitk::Point3D position; mitk::Quaternion orientation; bool isValid = false; double time; time = StringToDouble(timestamp); if (valid == "1") isValid = true; else isValid = false; position[0] = StringToDouble(X); position[1] = StringToDouble(Y); position[2] = StringToDouble(Z); orientation[0] = StringToDouble(QX); orientation[1] = StringToDouble(QY); orientation[2] = StringToDouble(QZ); orientation[3] = StringToDouble(QR); result->SetIGTTimeStamp(time); result->SetDataValid(isValid); result->SetPosition(position); result->SetOrientation(orientation); return result; } double mitk::NavigationDataReaderCSV::StringToDouble( const std::string& s ) { std::istringstream i(s); double x; if (!(i >> x)) return 0; return x; } std::vector mitk::NavigationDataReaderCSV::parseLine(std::string line, int NumOfTools) { std::vector parts = splitLine(line); std::vector result; for (int n = 0; n < NumOfTools; n++) { mitk::NavigationData::Pointer nd; int offset = n * 9; nd = CreateNd(parts[offset], parts[offset + 1], parts[offset + 2], parts[offset + 3], parts[offset + 4], parts[offset + 5], parts[offset + 6], parts[offset + 7], parts[offset + 8]); result.push_back(nd); } return result; } std::vector mitk::NavigationDataReaderCSV::GetFileContentLineByLine(std::string filename) { std::vector readData = std::vector(); -//save old locale -char * oldLocale; -oldLocale = setlocale( LC_ALL, nullptr ); - //define own locale -std::locale C("C"); -setlocale( LC_ALL, "C" ); +mitk::LocaleSwitch localeSwitch("C"); //read file std::ifstream file; file.open(filename.c_str(), std::ios::in); if (file.good()) { //read out file file.seekg(0L, std::ios::beg); // move to begin of file while (! file.eof()) { std::string buffer; std::getline(file,buffer); // read out file line by line if (buffer.size() > 0) readData.push_back(buffer); } } file.close(); -//switch back to old locale -setlocale( LC_ALL, oldLocale ); - return readData; } diff --git a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.cpp b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.cpp index e88cca719d..38970169ec 100644 --- a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.cpp +++ b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.cpp @@ -1,383 +1,368 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include "mitkNavigationDataReaderXML.h" #include +#include // Third Party #include #include #include mitk::NavigationDataReaderXML::NavigationDataReaderXML() : AbstractFileReader( mitk::IGTMimeTypes::NAVIGATIONDATASETXML_MIMETYPE(), "MITK NavigationData Reader (XML)"), m_parentElement(nullptr), m_currentNode(nullptr) { RegisterService(); } mitk::NavigationDataReaderXML::~NavigationDataReaderXML() { } mitk::NavigationDataReaderXML::NavigationDataReaderXML(const mitk::NavigationDataReaderXML& other) : AbstractFileReader(other), m_parentElement(nullptr), m_currentNode(nullptr) { } mitk::NavigationDataReaderXML* mitk::NavigationDataReaderXML::Clone() const { return new NavigationDataReaderXML(*this); } std::vector> mitk::NavigationDataReaderXML::Read() { mitk::NavigationDataSet::Pointer dataset; std::istream* in = GetInputStream(); if (in == nullptr) { dataset = Read(GetInputLocation()); } else { dataset = Read(in); } std::vector result; mitk::BaseData::Pointer base = dataset.GetPointer(); result.push_back(base); return result; } mitk::NavigationDataSet::Pointer mitk::NavigationDataReaderXML::Read(std::string fileName) { - //save old locale - char * oldLocale; - oldLocale = setlocale(LC_ALL, 0); - //define own locale - std::locale C("C"); - setlocale(LC_ALL, "C"); + mitk::LocaleSwitch localeSwitch("C"); m_FileName = fileName; TiXmlDocument document; if (!document.LoadFile(fileName)) { mitkThrowException(mitk::IGTIOException) << "File '" << fileName << "' could not be loaded."; } TiXmlElement* m_DataElem = document.FirstChildElement("Version"); if (!m_DataElem) { // for backwards compatibility of version tag m_DataElem = document.FirstChildElement("Data"); if (!m_DataElem) { mitkThrowException(mitk::IGTIOException) << "Data element not found."; } } if (m_DataElem->QueryIntAttribute("Ver", &m_FileVersion) != TIXML_SUCCESS) { if (m_DataElem->QueryIntAttribute("version", &m_FileVersion) != TIXML_SUCCESS) { mitkThrowException(mitk::IGTIOException) << "Version not specified in XML file."; } } if (m_FileVersion != 1) { mitkThrowException(mitk::IGTIOException) << "File format version " << m_FileVersion << " is not supported."; } m_parentElement = document.FirstChildElement("Data"); if (!m_parentElement) { mitkThrowException(mitk::IGTIOException) << "Data element not found."; } m_parentElement->QueryIntAttribute("ToolCount", &m_NumberOfOutputs); mitk::NavigationDataSet::Pointer navigationDataSet = this->ReadNavigationDataSet(); - //switch back to old locale - setlocale(LC_ALL, oldLocale); - return navigationDataSet; } mitk::NavigationDataSet::Pointer mitk::NavigationDataReaderXML::Read(std::istream* stream) { - //save old locale - char * oldLocale; - oldLocale = setlocale( LC_ALL, nullptr ); - //define own locale - std::locale C("C"); - setlocale( LC_ALL, "C" ); + mitk::LocaleSwitch localeSwitch("C"); // first get the file version m_FileVersion = this->GetFileVersion(stream); // check if we have a valid version: m_FileVersion has to be always bigger than 1 for playing if (m_FileVersion < 1) { StreamInvalid("Playing not possible. Invalid file version!"); return nullptr; } m_NumberOfOutputs = this->GetNumberOfNavigationDatas(stream); if (m_NumberOfOutputs == 0) { return nullptr; } mitk::NavigationDataSet::Pointer dataSet = this->ReadNavigationDataSet(); - //switch back to old locale - setlocale( LC_ALL, oldLocale ); - return dataSet; } mitk::NavigationDataSet::Pointer mitk::NavigationDataReaderXML::ReadNavigationDataSet() { mitk::NavigationDataSet::Pointer navigationDataSet = mitk::NavigationDataSet::New(m_NumberOfOutputs); mitk::NavigationData::Pointer curNavigationData; do { std::vector navDatas(m_NumberOfOutputs); for (int n = 0; n < m_NumberOfOutputs; ++n) { curNavigationData = this->ReadVersion1(); if (curNavigationData.IsNull()) { if (n != 0) { MITK_WARN("mitkNavigationDataReaderXML") << "Different number of NavigationData objects for different tools. Ignoring last ones."; } break; } navDatas.at(n) = curNavigationData; } if (curNavigationData.IsNotNull()) { navigationDataSet->AddNavigationDatas(navDatas); } } while (curNavigationData.IsNotNull()); return navigationDataSet; } mitk::NavigationData::Pointer mitk::NavigationDataReaderXML::ReadVersion1() { if ( !m_parentElement ) { mitkThrowException(mitk::IGTIOException) << "Reading XML is not possible. Parent element is not set."; } TiXmlElement* elem; m_currentNode = m_parentElement->IterateChildren(m_currentNode); bool delElem; if(m_currentNode) { elem = m_currentNode->ToElement(); if(elem==nullptr) { mitkThrowException(mitk::IGTException) << "Cannot find element: Is this file damaged?"; } delElem = false; } else { elem = new TiXmlElement(""); delElem = true; } mitk::NavigationData::Pointer nd = this->ReadNavigationData(elem); if(delElem) { delete elem; } return nd; } mitk::NavigationData::Pointer mitk::NavigationDataReaderXML::ReadNavigationData(TiXmlElement* elem) { if (elem == nullptr) {mitkThrow() << "Error: Element is NULL!";} mitk::NavigationData::Pointer nd = mitk::NavigationData::New(); mitk::NavigationData::PositionType position; mitk::NavigationData::OrientationType orientation(0.0,0.0,0.0,0.0); mitk::NavigationData::TimeStampType timestamp = -1; mitk::NavigationData::CovarianceMatrixType matrix; bool hasPosition = true; bool hasOrientation = true; bool dataValid = false; position.Fill(0.0); matrix.SetIdentity(); elem->QueryDoubleAttribute("Time",×tamp); if (timestamp == -1) { return nullptr; //the calling method should check the return value if it is valid/not NULL } elem->QueryDoubleAttribute("X", &position[0]); elem->QueryDoubleAttribute("Y", &position[1]); elem->QueryDoubleAttribute("Z", &position[2]); elem->QueryDoubleAttribute("QX", &orientation[0]); elem->QueryDoubleAttribute("QY", &orientation[1]); elem->QueryDoubleAttribute("QZ", &orientation[2]); elem->QueryDoubleAttribute("QR", &orientation[3]); elem->QueryDoubleAttribute("C00", &matrix[0][0]); elem->QueryDoubleAttribute("C01", &matrix[0][1]); elem->QueryDoubleAttribute("C02", &matrix[0][2]); elem->QueryDoubleAttribute("C03", &matrix[0][3]); elem->QueryDoubleAttribute("C04", &matrix[0][4]); elem->QueryDoubleAttribute("C05", &matrix[0][5]); elem->QueryDoubleAttribute("C10", &matrix[1][0]); elem->QueryDoubleAttribute("C11", &matrix[1][1]); elem->QueryDoubleAttribute("C12", &matrix[1][2]); elem->QueryDoubleAttribute("C13", &matrix[1][3]); elem->QueryDoubleAttribute("C14", &matrix[1][4]); elem->QueryDoubleAttribute("C15", &matrix[1][5]); int tmpval = 0; elem->QueryIntAttribute("Valid", &tmpval); if (tmpval == 0) dataValid = false; else dataValid = true; tmpval = 0; elem->QueryIntAttribute("hO", &tmpval); if (tmpval == 0) hasOrientation = false; else hasOrientation = true; tmpval = 0; elem->QueryIntAttribute("hP", &tmpval); if (tmpval == 0) hasPosition = false; else hasPosition = true; nd->SetIGTTimeStamp(timestamp); nd->SetPosition(position); nd->SetOrientation(orientation); nd->SetCovErrorMatrix(matrix); nd->SetDataValid(dataValid); nd->SetHasOrientation(hasOrientation); nd->SetHasPosition(hasPosition); return nd; } // -- deprecated | begin unsigned int mitk::NavigationDataReaderXML::GetFileVersion(std::istream* stream) { if (stream==nullptr) { MITK_ERROR << "No input stream set!"; mitkThrowException(mitk::IGTIOException)<<"No input stream set!"; } if (!stream->good()) { MITK_ERROR << "Stream is not good!"; mitkThrowException(mitk::IGTIOException)<<"Stream is not good!"; } int version = 1; auto dec = new TiXmlDeclaration(); *stream >> *dec; if(strcmp(dec->Version(),"") == 0) { MITK_ERROR << "The input stream seems to have XML incompatible format"; mitkThrowException(mitk::IGTIOException) << "The input stream seems to have XML incompatible format"; } m_parentElement = new TiXmlElement(""); *stream >> *m_parentElement; //2nd line this is the file version std::string tempValue = m_parentElement->Value(); if(tempValue != "Version") { if(tempValue == "Data"){ m_parentElement->QueryIntAttribute("version",&version); } } else { m_parentElement->QueryIntAttribute("Ver",&version); } if (version > 0) { return version; } else { return 0; } } unsigned int mitk::NavigationDataReaderXML::GetNumberOfNavigationDatas(std::istream* stream) { if (stream == nullptr) { MITK_ERROR << "No input stream set!"; mitkThrowException(mitk::IGTException)<<"No input stream set!"; } if (!stream->good()) { MITK_ERROR << "Stream not good!"; mitkThrowException(mitk::IGTException)<<"Stream not good!"; } //If something has changed in a future version of the XML definition e.g. navigationcount or addional parameters //catch this here with a select case block (see GenerateData() method) int numberOfTools = 0; std::string tempValue = m_parentElement->Value(); if(tempValue == "Version"){ *stream >> *m_parentElement; } m_parentElement->QueryIntAttribute("ToolCount",&numberOfTools); if (numberOfTools > 0) { return numberOfTools; } return 0; } void mitk::NavigationDataReaderXML::StreamInvalid(std::string message) { m_StreamEnd = true; m_ErrorMessage = message; m_StreamValid = false; mitkThrowException(mitk::IGTIOException) << "Invalid stream!"; } // -- deprecated | end diff --git a/Modules/IGTBase/autoload/IO/mitkNavigationDataSetWriterCSV.cpp b/Modules/IGTBase/autoload/IO/mitkNavigationDataSetWriterCSV.cpp index 60429db6ea..ad34765e3d 100644 --- a/Modules/IGTBase/autoload/IO/mitkNavigationDataSetWriterCSV.cpp +++ b/Modules/IGTBase/autoload/IO/mitkNavigationDataSetWriterCSV.cpp @@ -1,97 +1,91 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNavigationDataSetWriterCSV.h" #include #include +#include mitk::NavigationDataSetWriterCSV::NavigationDataSetWriterCSV() : AbstractFileWriter(NavigationDataSet::GetStaticNameOfClass(), mitk::IGTMimeTypes::NAVIGATIONDATASETCSV_MIMETYPE(), "MITK NavigationDataSet Reader (CSV)") { RegisterService(); } mitk::NavigationDataSetWriterCSV::~NavigationDataSetWriterCSV() {} mitk::NavigationDataSetWriterCSV::NavigationDataSetWriterCSV(const mitk::NavigationDataSetWriterCSV& other) : AbstractFileWriter(other) { } mitk::NavigationDataSetWriterCSV* mitk::NavigationDataSetWriterCSV::Clone() const { return new NavigationDataSetWriterCSV(*this); } void mitk::NavigationDataSetWriterCSV::Write() { std::ostream* out = GetOutputStream(); if (out == nullptr) { out = new std::ofstream(GetOutputLocation().c_str()); } mitk::NavigationDataSet::ConstPointer data = dynamic_cast (this->GetInput()); - //save old locale - char * oldLocale; - oldLocale = setlocale( LC_ALL, nullptr ); - //define own locale - std::locale C("C"); - setlocale( LC_ALL, "C" ); + mitk::LocaleSwitch localeSwitch("C"); //write header unsigned int numberOfTools = data->GetNumberOfTools(); for (unsigned int index = 0; index < numberOfTools; index++){ *out << "TimeStamp_Tool" << index << ";Valid_Tool" << index << ";X_Tool" << index << ";Y_Tool" << index << ";Z_Tool" << index << ";QX_Tool" << index << ";QY_Tool" << index << ";QZ_Tool" << index << ";QR_Tool" << index << ";";} *out << "\n"; out->precision(15); // rounding precision because we don't want to loose data. //write data MITK_INFO << "Number of timesteps: " << data->Size(); for (unsigned int i=0; iSize(); i++) { std::vector< mitk::NavigationData::Pointer > NavigationDatasOfCurrentStep = data->GetTimeStep(i); for (unsigned int toolIndex = 0; toolIndex < numberOfTools; toolIndex++) { mitk::NavigationData::Pointer nd = NavigationDatasOfCurrentStep.at(toolIndex); *out << nd->GetTimeStamp() << ";" << nd->IsDataValid() << ";" << nd->GetPosition()[0] << ";" << nd->GetPosition()[1] << ";" << nd->GetPosition()[2] << ";" << nd->GetOrientation()[0] << ";" << nd->GetOrientation()[1] << ";" << nd->GetOrientation()[2] << ";" << nd->GetOrientation()[3] << ";"; } *out << "\n"; } out->flush(); delete out; - //switch back to old locale - setlocale( LC_ALL, oldLocale ); } diff --git a/Modules/IGTBase/autoload/IO/mitkNavigationDataSetWriterXML.cpp b/Modules/IGTBase/autoload/IO/mitkNavigationDataSetWriterXML.cpp index b98061ec61..228163a7ab 100644 --- a/Modules/IGTBase/autoload/IO/mitkNavigationDataSetWriterXML.cpp +++ b/Modules/IGTBase/autoload/IO/mitkNavigationDataSetWriterXML.cpp @@ -1,147 +1,143 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include "mitkNavigationDataSetWriterXML.h" #include +#include // Third Party #include #include #include #include mitk::NavigationDataSetWriterXML::NavigationDataSetWriterXML() : AbstractFileWriter(NavigationDataSet::GetStaticNameOfClass(), mitk::IGTMimeTypes::NAVIGATIONDATASETXML_MIMETYPE(), "MITK NavigationDataSet Writer (XML)") { RegisterService(); } mitk::NavigationDataSetWriterXML::NavigationDataSetWriterXML(const mitk::NavigationDataSetWriterXML& other) : AbstractFileWriter(other) { } mitk::NavigationDataSetWriterXML::~NavigationDataSetWriterXML() { } mitk::NavigationDataSetWriterXML* mitk::NavigationDataSetWriterXML::Clone() const { return new NavigationDataSetWriterXML(*this); } void mitk::NavigationDataSetWriterXML::Write() { std::ostream* out = GetOutputStream(); if (out == nullptr) { out = new std::ofstream( GetOutputLocation().c_str() ); } mitk::NavigationDataSet::ConstPointer data = dynamic_cast (this->GetInput()); - //save old locale - char * oldLocale; - oldLocale = setlocale( LC_ALL, nullptr ); + mitk::LocaleSwitch localeSwitch("C"); StreamHeader(out, data); StreamData(out, data); StreamFooter(out); // Cleanup out->flush(); delete out; - - //switch back to old locale - setlocale( LC_ALL, oldLocale ); } void mitk::NavigationDataSetWriterXML::StreamHeader (std::ostream* stream, mitk::NavigationDataSet::ConstPointer data) { stream->precision(10); //TODO store date and GMT time //checking if the stream is good if (stream->good()) { *stream << "" << std::endl; /**m_Stream << "" << std::endl;*/ // should be a generic version, meaning a member variable, which has the actual version *stream << " " << "GetNumberOfTools() << "\" version=\"1.0\">" << std::endl; } } void mitk::NavigationDataSetWriterXML::StreamData (std::ostream* stream, mitk::NavigationDataSet::ConstPointer data) { // For each time step in the Dataset for (auto it = data->Begin(); it != data->End(); it++) { for (std::size_t toolIndex = 0; toolIndex < it->size(); toolIndex++) { mitk::NavigationData::Pointer nd = it->at(toolIndex); auto elem = new TiXmlElement("ND"); elem->SetDoubleAttribute("Time", nd->GetIGTTimeStamp()); // elem->SetAttribute("SystemTime", sysTimeStr); // tag for system time elem->SetDoubleAttribute("Tool", toolIndex); elem->SetDoubleAttribute("X", nd->GetPosition()[0]); elem->SetDoubleAttribute("Y", nd->GetPosition()[1]); elem->SetDoubleAttribute("Z", nd->GetPosition()[2]); elem->SetDoubleAttribute("QX", nd->GetOrientation()[0]); elem->SetDoubleAttribute("QY", nd->GetOrientation()[1]); elem->SetDoubleAttribute("QZ", nd->GetOrientation()[2]); elem->SetDoubleAttribute("QR", nd->GetOrientation()[3]); elem->SetDoubleAttribute("C00", nd->GetCovErrorMatrix()[0][0]); elem->SetDoubleAttribute("C01", nd->GetCovErrorMatrix()[0][1]); elem->SetDoubleAttribute("C02", nd->GetCovErrorMatrix()[0][2]); elem->SetDoubleAttribute("C03", nd->GetCovErrorMatrix()[0][3]); elem->SetDoubleAttribute("C04", nd->GetCovErrorMatrix()[0][4]); elem->SetDoubleAttribute("C05", nd->GetCovErrorMatrix()[0][5]); elem->SetDoubleAttribute("C10", nd->GetCovErrorMatrix()[1][0]); elem->SetDoubleAttribute("C11", nd->GetCovErrorMatrix()[1][1]); elem->SetDoubleAttribute("C12", nd->GetCovErrorMatrix()[1][2]); elem->SetDoubleAttribute("C13", nd->GetCovErrorMatrix()[1][3]); elem->SetDoubleAttribute("C14", nd->GetCovErrorMatrix()[1][4]); elem->SetDoubleAttribute("C15", nd->GetCovErrorMatrix()[1][5]); if (nd->IsDataValid()) elem->SetAttribute("Valid",1); else elem->SetAttribute("Valid",0); if (nd->GetHasOrientation()) elem->SetAttribute("hO",1); else elem->SetAttribute("hO",0); if (nd->GetHasPosition()) elem->SetAttribute("hP",1); else elem->SetAttribute("hP",0); *stream << " " << *elem << std::endl; delete elem; } } } void mitk::NavigationDataSetWriterXML::StreamFooter (std::ostream* stream) { *stream << "" << std::endl; } diff --git a/Modules/IOExt/Internal/mitkParRecFileReader.cpp b/Modules/IOExt/Internal/mitkParRecFileReader.cpp index b630e8f00b..3bc075151c 100644 --- a/Modules/IOExt/Internal/mitkParRecFileReader.cpp +++ b/Modules/IOExt/Internal/mitkParRecFileReader.cpp @@ -1,292 +1,290 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkParRecFileReader.h" #include +#include #ifdef __GNUC__ #define stricmp strcasecmp #endif void mitk::ParRecFileReader::GenerateOutputInformation() { mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_ReadHeaderTime.GetMTime())) return; itkDebugMacro(<<"Reading PAR file for GenerateOutputInformation()" << m_FileName); // Check to see if we can read the file given the name or prefix // if ( m_FileName == "" && m_FilePrefix == "" ) { throw itk::ImageFileReaderException(__FILE__, __LINE__, "One of FileName or FilePrefix must be non-empty"); } m_RecFileName = ""; if( m_FileName != "") { int extPos=m_FileName.find_last_of("."); if(extPos>=-1) { const char *ext=m_FileName.c_str()+extPos+1; if(stricmp(ext,"par")==0) m_RecFileName = m_FileName.substr(0,extPos); else m_RecFileName = m_FileName; } else m_RecFileName = m_FileName; m_RecFileName.append(".rec"); bool headerRead = false; bool signedCharType = true; unsigned int dimension=0; unsigned int dimensions[4]={0,0,1,1}; float sliceThickness=0.0; float sliceGap=0.0; float sliceSpacing=0.0; mitk::Vector3D thickness; thickness.Fill(1.0); mitk::Vector3D gap; gap.Fill(0.0); mitk::Vector3D spacing; FILE *f; f=fopen(m_FileName.c_str(), "r"); if(f!=nullptr) { while(!feof(f)) { char s[300], *p; char* ignored = fgets(s,200,f); ++ignored; if(strstr(s,"Max. number of cardiac phases")) { p=strchr(s,':')+1; dimensions[3]=atoi(p); if(dimensions[3]>1) dimension=4; } else if(strstr(s,"Max. number of slices/locations")) { p=strchr(s,':')+1; dimensions[2]=atoi(p); if(dimension==0) { if(dimensions[2]>1) dimension=3; else dimension=2; } } else if(strstr(s,"Image pixel size")) { p=strchr(s,':')+1; int bpe=atoi(p); if(bpe!=8) signedCharType = false; } else if(strstr(s,"Recon resolution")) { p=s+strcspn(s,"0123456789"); sscanf(p,"%u %u", dimensions, dimensions+1); } else if(strstr(s,"FOV (ap,fh,rl) [mm]")) { p=s+strcspn(s,"0123456789"); - char *oldLocale = setlocale(LC_ALL, nullptr); + mitk::LocaleSwitch localeSwitch("C"); sscanf(p,"%lf %lf %lf", &thickness[0], &thickness[1], &thickness[2]); - setlocale(LC_ALL, oldLocale); } else if(strstr(s,"Slice thickness [mm]")) { p=s+strcspn(s,"0123456789"); - char *oldLocale = setlocale(LC_ALL, nullptr); + mitk::LocaleSwitch localeSwitch("C"); sscanf(p,"%f", &sliceThickness); - setlocale(LC_ALL, oldLocale); } else if(strstr(s,"Slice gap [mm]")) { p=s+strcspn(s,"-0123456789"); - char *oldLocale = setlocale(LC_ALL, nullptr); + mitk::LocaleSwitch localeSwitch("C"); sscanf(p,"%f", &sliceGap); - setlocale(LC_ALL, oldLocale); } } fclose(f); //C:\home\ivo\data\coronaries\ucsf-wholeheart-2.par sliceSpacing = sliceThickness+sliceGap; if((dimension>0) && (dimensions[0]>0) && (dimensions[1]>0) && (sliceThickness>0) && (sliceSpacing>0)) { headerRead = true; if(fabs(thickness[0]/dimensions[2]-sliceSpacing)<0.0001) thickness[0]=thickness[1]; else if(fabs(thickness[1]/dimensions[2]-sliceSpacing)<0.0001) thickness[1]=thickness[0]; thickness[2]=sliceSpacing; thickness[0]/=dimensions[0]; thickness[1]/=dimensions[1]; spacing=thickness+gap; } } if( headerRead == false) { itk::ImageFileReaderException e(__FILE__, __LINE__); std::ostringstream msg; msg << " Could not read file " << m_FileName.c_str(); e.SetDescription(msg.str().c_str()); throw e; return; } // define types mitk::PixelType SCType = mitk::MakeScalarPixelType(); mitk::PixelType SSType = mitk::MakeScalarPixelType(); if( signedCharType ) output->Initialize(SCType, dimension, dimensions); else output->Initialize(SSType, dimension, dimensions); output->GetSlicedGeometry()->SetSpacing(spacing); //output->GetSlicedGeometry()->SetPlaneGeometry(mitk::Image::BuildStandardPlanePlaneGeometry(output->GetSlicedGeometry(), dimensions).GetPointer(), 0); output->GetSlicedGeometry()->SetEvenlySpaced(); } m_ReadHeaderTime.Modified(); } void mitk::ParRecFileReader::GenerateData() { mitk::Image::Pointer output = this->GetOutput(); // Check to see if we can read the file given the name or prefix // if ( m_RecFileName == "" ) { throw itk::ImageFileReaderException(__FILE__, __LINE__, "FileName for rec-file empty"); } if( m_RecFileName != "") { FILE *f = fopen(m_RecFileName.c_str(), "r"); if(f==nullptr) { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Could not open rec-file."); } int zstart, zmax; int tstart, tmax; zstart=output->GetRequestedRegion().GetIndex(2); tstart=output->GetRequestedRegion().GetIndex(3); zmax=zstart+output->GetRequestedRegion().GetSize(2); tmax=tstart+output->GetRequestedRegion().GetSize(3); int sliceSize=output->GetDimension(0)*output->GetDimension(1)*output->GetPixelType().GetBpe()/8; void *data = malloc(sliceSize); bool ignore4Dtopogram=false; { int slicePlusTimeSize=output->GetDimension(0)*output->GetDimension(1)*output->GetDimension(3)*output->GetPixelType().GetBpe()/8; if(output->GetDimension(3)>1) ignore4Dtopogram=true; int z,t; for(t=tstart;tSetSlice(data,z,t,0); } } //else //{ // for(;zSetSlice(data,z,0,0); // } //} free(data); fclose(f); } } bool mitk::ParRecFileReader::CanReadFile(const std::string filename, const std::string /*filePrefix*/, const std::string /*filePattern*/) { // First check the extension if( filename == "" ) { //MITK_INFO<<"No filename specified."< #include #include #include #include +#include // C-Standard library includes #include #include // STL-related includes #include #include #include #include #include // VTK-related includes #include #include #include #include #include #include #include #include #include #include #include // ITK-related includes #include #include #include #include #include #include #include #include #include #ifdef NOMINMAX # define DEF_NOMINMAX # undef NOMINMAX #endif #include #ifdef DEF_NOMINMAX # ifndef NOMINMAX # define NOMINMAX # endif # undef DEF_NOMINMAX #endif #include #include // MITK-related includes #include "mitkSurface.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkProperties.h" //#include "mitkMaterialProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkScalarModeProperty.h" #include "mitkImage.h" #include "mitkLookupTableProperty.h" #include "mitkLookupTable.h" #include "mitkImageChannelSelector.h" #include "mitkImageSliceSelector.h" #include "mitkCoreObjectFactory.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkProgressBar.h" #include bool mitk::DataNodeFactory::m_TextureInterpolationActive = false; // default value for texture interpolation if nothing is defined in global options (see QmitkMainTemplate.ui.h) mitk::DataNodeFactory::DataNodeFactory() { m_Serie = false; m_OldProgress = 0; this->Modified(); //ensure that a CoreObjectFactory has been instantiated mitk::CoreObjectFactory::GetInstance(); } mitk::DataNodeFactory::~DataNodeFactory() {} void mitk::DataNodeFactory::SetImageSerie(bool serie) { m_Serie = serie; } void mitk::DataNodeFactory::GenerateData() { // IF filename is something.pic, and something.pic does not exist, try to read something.pic.gz // if there are both, something.pic and something.pic.gz, only the requested file is read // not only for images, but for all formats std::ifstream exists(m_FileName.c_str()); if (!exists) { std::string testfilename = m_FileName + ".gz"; std::ifstream exists(testfilename.c_str()); if (exists.good()) { m_FileName += ".gz"; } else { testfilename = m_FileName + ".GZ"; std::ifstream exists(testfilename.c_str()); if (exists.good()) { m_FileName += ".GZ"; } else { std::string message("File does not exist, or cannot be read. Filename = "); message += m_FileName; MITK_ERROR << message; itkExceptionMacro( << message.str() ); } } } // part for DICOM // const char *numbers = "0123456789."; // std::string::size_type first_non_number; // first_non_number = itksys::SystemTools::GetFilenameName(m_FileName).find_first_not_of ( numbers ); if (DicomSeriesReader::IsDicom(this->m_FileName) /*|| first_non_number == std::string::npos*/) { this->ReadFileSeriesTypeDCM(); } else { bool usedNewDTNF = false; // the mitkBaseDataIO class returns a pointer of a vector of BaseData objects std::vector baseDataVector = mitk::BaseDataIO::LoadBaseDataFromFile( m_FileName, m_FilePrefix, m_FilePattern, m_Serie ); if( !baseDataVector.empty() ) this->ResizeOutputs((unsigned int)baseDataVector.size()); for(int i=0; i<(int)baseDataVector.size(); i++) { mitk::BaseData::Pointer baseData = baseDataVector.at(i); if( baseData.IsNotNull() ) { usedNewDTNF = true; mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(baseData); this->SetDefaultCommonProperties( node ); this->SetOutput(this->MakeNameFromOutputIndex(i), node); } } if(!usedNewDTNF && ( m_FileName != "" ) && !(m_Serie == false)) ReadFileSeriesTypeITKImageSeriesReader(); } } void mitk::DataNodeFactory::ResizeOutputs( const unsigned int& num ) { unsigned int prevNum = this->GetNumberOfOutputs(); this->SetNumberOfIndexedOutputs( num ); for ( unsigned int i = prevNum; i < num; ++i ) { this->SetNthOutput( i, this->MakeOutput( i ).GetPointer() ); } } bool mitk::DataNodeFactory::FileNameEndsWith( const std::string& name ) { if (m_FileName.size() < name.size()) return false; return m_FileName.substr(m_FileName.size() - name.size()) == name; } bool mitk::DataNodeFactory::FilePatternEndsWith( const std::string& name ) { return m_FilePattern.find( name ) != std::string::npos; } std::string mitk::DataNodeFactory::GetBaseFileName() { return itksys::SystemTools::GetFilenameName( m_FileName ); } std::string mitk::DataNodeFactory::GetBaseFilePrefix() { return itksys::SystemTools::GetFilenameName( m_FilePrefix ); } std::string mitk::DataNodeFactory::GetDirectory() { if ( !m_FileName.empty() ) return itksys::SystemTools::GetFilenamePath( m_FileName ); if ( !m_FilePrefix.empty() ) return itksys::SystemTools::GetFilenamePath( m_FilePrefix ); return std::string(); } void mitk::DataNodeFactory::ReadFileSeriesTypeDCM() { - const char* previousCLocale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "C"); + mitk::LocaleSwitch localeSwitch("C"); std::locale previousCppLocale( std::cin.getloc() ); std::locale l( "C" ); std::cin.imbue(l); if ( DicomSeriesReader::IsPhilips3DDicom(this->GetFileName()) ) { MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; this->ResizeOutputs(1); DataNode::Pointer node = this->GetOutput(); mitk::DicomSeriesReader::StringContainer stringvec; stringvec.push_back(this->GetFileName()); if (DicomSeriesReader::LoadDicomSeries(stringvec, *node)) { node->SetName(this->GetBaseFileName()); } - setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); return; } DicomSeriesReader::FileNamesGrouping imageBlocks = DicomSeriesReader::GetSeries(this->GetDirectory(), true, this->m_SeriesRestrictions); // true = group gantry tilt images const unsigned int size = imageBlocks.size(); this->ResizeOutputs(size); ProgressBar::GetInstance()->AddStepsToDo(size); ProgressBar::GetInstance()->Progress(); unsigned int outputIndex = 0u; const DicomSeriesReader::FileNamesGrouping::const_iterator n_end = imageBlocks.end(); for (DicomSeriesReader::FileNamesGrouping::const_iterator n_it = imageBlocks.begin(); n_it != n_end; ++n_it) { const std::string &uid = n_it->first; DataNode::Pointer node = this->GetOutput(outputIndex); const DicomSeriesReader::ImageBlockDescriptor& imageBlockDescriptor( n_it->second ); MITK_INFO << "--------------------------------------------------------------------------------"; MITK_INFO << "DataNodeFactory: Loading DICOM series " << outputIndex << ": Series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << std::endl; MITK_INFO << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; MITK_INFO << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No"); MITK_INFO << " reader support: " << DicomSeriesReader::ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); MITK_INFO << " pixel spacing type: " << DicomSeriesReader::PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ); MITK_INFO << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No"); MITK_INFO << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No"); MITK_INFO << "--------------------------------------------------------------------------------"; if (DicomSeriesReader::LoadDicomSeries(n_it->second.GetFilenames(), *node, true, true, true)) { std::string nodeName(uid); std::string studyDescription; if ( node->GetStringProperty( "dicom.study.StudyDescription", studyDescription ) ) { nodeName = studyDescription; std::string seriesDescription; if ( node->GetStringProperty( "dicom.series.SeriesDescription", seriesDescription ) ) { nodeName += "/" + seriesDescription; } } node->SetName(nodeName); ++outputIndex; } else { MITK_ERROR << "DataNodeFactory: Skipping series " << outputIndex << " due to some unspecified error..." << std::endl; } ProgressBar::GetInstance()->Progress(); } - setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); } void mitk::DataNodeFactory::ReadFileSeriesTypeITKImageSeriesReader() { typedef itk::Image ImageType; typedef itk::ImageSeriesReader< ImageType > ReaderType; if ( ! this->GenerateFileList() ) { itkWarningMacro( "Sorry, file list could not be generated!" ); return ; } if ( m_MatchedFileNames.size() == 0 ) { itkWarningMacro( "Sorry, no files matched the given filename ("<< m_FileName <<")!" ); return ; } // // Finally, initialize the ITK-reader and load the files! // ReaderType::Pointer reader = ReaderType::New(); reader->SetFileNames( m_MatchedFileNames ); try { reader->Update(); ResizeOutputs( reader->GetNumberOfOutputs() ); for ( unsigned int i = 0; i < reader->GetNumberOfOutputs(); ++i ) { //Initialize mitk image from itk mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( reader->GetOutput( i ) ); image->SetVolume( reader->GetOutput( i )->GetBufferPointer() ); //add the mitk image to the node mitk::DataNode::Pointer node = this->GetOutput( i ); node->SetData( image ); mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( m_FileName ); node->SetProperty( "name", nameProp ); } } catch ( const std::exception & e ) { itkWarningMacro( << e.what() ); return ; } } mitk::ColorProperty::Pointer mitk::DataNodeFactory::DefaultColorForOrgan( const std::string& organ ) { static bool initialized = false; static std::map< std::string, std::string > s_ColorMap; if (!initialized) { // all lowercase here, please! s_ColorMap.insert( std::make_pair( "ankle", "0xe38686") ); s_ColorMap.insert( std::make_pair( "appendix", "0xe38686") ); s_ColorMap.insert( std::make_pair( "blood vessels", "0xff3131") ); s_ColorMap.insert( std::make_pair( "bronchial tree", "0x3168ff") ); s_ColorMap.insert( std::make_pair( "bone", "0xd5d5d5") ); s_ColorMap.insert( std::make_pair( "brain", "0xff9cca") ); s_ColorMap.insert( std::make_pair( "coccyx", "0xe38686") ); s_ColorMap.insert( std::make_pair( "colon", "0xe38686") ); s_ColorMap.insert( std::make_pair( "cyst", "0xe38686") ); s_ColorMap.insert( std::make_pair( "elbow", "0xe38686") ); s_ColorMap.insert( std::make_pair( "eye", "0xe38686") ); s_ColorMap.insert( std::make_pair( "fallopian tube", "0xe38686") ); s_ColorMap.insert( std::make_pair( "fat", "0xff2bee") ); s_ColorMap.insert( std::make_pair( "hand", "0xe38686") ); s_ColorMap.insert( std::make_pair( "gall bladder", "0x567f18") ); s_ColorMap.insert( std::make_pair( "heart", "0xeb1d32") ); s_ColorMap.insert( std::make_pair( "hip", "0xe38686") ); s_ColorMap.insert( std::make_pair( "kidney", "0xd33f00") ); s_ColorMap.insert( std::make_pair( "knee", "0xe38686") ); s_ColorMap.insert( std::make_pair( "larynx", "0xe38686") ); s_ColorMap.insert( std::make_pair( "liver", "0xffcc3d") ); s_ColorMap.insert( std::make_pair( "lung", "0x6bdcff") ); s_ColorMap.insert( std::make_pair( "lymph node", "0xff0000") ); s_ColorMap.insert( std::make_pair( "muscle", "0xff456a") ); s_ColorMap.insert( std::make_pair( "nerve", "0xffea4f") ); s_ColorMap.insert( std::make_pair( "nose", "0xe38686") ); s_ColorMap.insert( std::make_pair( "oesophagus", "0xe38686") ); s_ColorMap.insert( std::make_pair( "ovaries", "0xe38686") ); s_ColorMap.insert( std::make_pair( "pancreas", "0xf9ab3d") ); s_ColorMap.insert( std::make_pair( "pelvis", "0xe38686") ); s_ColorMap.insert( std::make_pair( "penis", "0xe38686") ); s_ColorMap.insert( std::make_pair( "pharynx", "0xe38686") ); s_ColorMap.insert( std::make_pair( "prostate", "0xe38686") ); s_ColorMap.insert( std::make_pair( "rectum", "0xe38686") ); s_ColorMap.insert( std::make_pair( "sacrum", "0xe38686") ); s_ColorMap.insert( std::make_pair( "seminal vesicle", "0xe38686") ); s_ColorMap.insert( std::make_pair( "shoulder", "0xe38686") ); s_ColorMap.insert( std::make_pair( "spinal cord", "0xf5f93d") ); s_ColorMap.insert( std::make_pair( "spleen", "0xf96c3d") ); s_ColorMap.insert( std::make_pair( "stomach", "0xf96c3d") ); s_ColorMap.insert( std::make_pair( "teeth", "0xfffcd8") ); s_ColorMap.insert( std::make_pair( "testicles", "0xe38686") ); s_ColorMap.insert( std::make_pair( "thyroid", "0xfff694") ); s_ColorMap.insert( std::make_pair( "tongue", "0xe38686") ); s_ColorMap.insert( std::make_pair( "tumor", "0x937011") ); s_ColorMap.insert( std::make_pair( "urethra", "0xf8ff32") ); s_ColorMap.insert( std::make_pair( "urinary bladder", "0xf8ff32") ); s_ColorMap.insert( std::make_pair( "uterus", "0xe38686") ); s_ColorMap.insert( std::make_pair( "vagina", "0xe38686") ); s_ColorMap.insert( std::make_pair( "vertebra", "0xe38686") ); s_ColorMap.insert( std::make_pair( "wrist", "0xe38686") ); initialized = true; } std::string lowercaseOrgan(organ); for(unsigned int i = 0; i < organ.length(); i++) { lowercaseOrgan[i] = tolower(lowercaseOrgan[i]); } std::map< std::string, std::string >::iterator iter = s_ColorMap.find( lowercaseOrgan ); if ( iter != s_ColorMap.end() ) { std::string hexColor = iter->second; std::string hexRed = std::string("0x") + hexColor.substr( 2, 2 ); std::string hexGreen = std::string("0x") + hexColor.substr( 4, 2 ); std::string hexBlue = std::string("0x") + hexColor.substr( 6, 2 ); long int red = strtol( hexRed.c_str(), NULL, 16 ); long int green = strtol( hexGreen.c_str(), NULL, 16 ); long int blue = strtol( hexBlue.c_str(), NULL, 16 ); return ColorProperty::New( (float)red/ 255.0, (float)green/ 255.0, (float)blue/ 255.0 ); } else { // a default color (green) return ColorProperty::New( 0.0, 1.0, 0.0 ); } } void mitk::DataNodeFactory::SetDefaultCommonProperties(mitk::DataNode::Pointer &node) { // path mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenamePath( m_FileName ) ); node->SetProperty( StringProperty::PATH, pathProp ); // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if(nameProp.IsNull() || (strcmp(nameProp->GetValue(),"No Name!")==0)) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer() ); if(baseDataNameProp.IsNull() || (strcmp(baseDataNameProp->GetValue(),"No Name!")==0)) { // name neither defined in node, nor in BaseData -> name = filename if (FileNameEndsWith( ".gz" )) m_FileName = m_FileName.substr( 0, m_FileName.length()-3 ); nameProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenameWithoutLastExtension( m_FileName ) ); node->SetProperty( "name", nameProp ); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New( baseDataNameProp->GetValue() ); node->SetProperty( "name", nameProp ); } } // visibility if(!node->GetProperty("visible")) node->SetVisibility(true); } diff --git a/Modules/LegacyIO/mitkImageWriter.cpp b/Modules/LegacyIO/mitkImageWriter.cpp index fff9f2943c..404595988b 100644 --- a/Modules/LegacyIO/mitkImageWriter.cpp +++ b/Modules/LegacyIO/mitkImageWriter.cpp @@ -1,501 +1,480 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageWriter.h" #include "mitkItkPictureWrite.h" #include "mitkImage.h" #include "mitkImageTimeSelector.h" #include "mitkImageAccessByItk.h" #include "mitkImageReadAccessor.h" +#include #include #include mitk::ImageWriter::ImageWriter() : m_UseCompression( true ) { this->SetNumberOfRequiredInputs( 1 ); m_MimeType = ""; SetDefaultExtension(); } mitk::ImageWriter::~ImageWriter() { } void mitk::ImageWriter::SetFileName(const char* fileName) { if ( fileName && ( fileName == this->m_FileName ) ) { return; } if ( fileName ) { this->m_FileName = fileName; this->m_FileNameWithoutExtension = this->m_FileName; this->m_Extension.clear(); std::size_t pos = this->m_FileName.find_last_of("/\\"); if (pos != std::string::npos) { std::size_t ppos = this->m_FileName.find_first_of('.', pos); if (ppos != std::string::npos) { this->m_FileNameWithoutExtension = this->m_FileName.substr(0, ppos); this->m_Extension = this->m_FileName.substr(ppos); } } } else { this->m_FileName.clear(); this->m_FileNameWithoutExtension.clear(); this->m_Extension.clear(); } this->Modified(); } void mitk::ImageWriter::SetFileName(const std::string & fileName) { this->SetFileName( fileName.c_str() ); } void mitk::ImageWriter::SetExtension(const char* extension) { if ( extension && ( extension == this->m_Extension ) ) { return; } if ( extension ) { this->m_Extension = extension; this->m_FileName = this->m_FileNameWithoutExtension + this->m_Extension; } else { this->m_Extension.clear(); this->m_FileName = this->m_FileNameWithoutExtension; } this->Modified(); } void mitk::ImageWriter::SetExtension(const std::string & extension) { this->SetFileName( extension.c_str() ); } void mitk::ImageWriter::SetDefaultExtension() { this->m_Extension = ".mhd"; this->m_FileName = this->m_FileNameWithoutExtension + this->m_Extension; this->Modified(); } #include #include #include static void writeVti(const char * filename, mitk::Image* image, int t=0) { vtkXMLImageDataWriter * vtkwriter = vtkXMLImageDataWriter::New(); vtkwriter->SetFileName( filename ); vtkwriter->SetInputData(image->GetVtkImageData(t)); vtkwriter->Write(); vtkwriter->Delete(); } #include void mitk::ImageWriter::WriteByITK(mitk::Image* image, const std::string& fileName) { MITK_INFO << "Writing image: " << fileName << std::endl; // Pictures and picture series like .png are written via a different mechanism then volume images. // So, they are still multiplexed and thus not support vector images. if (fileName.find(".png") != std::string::npos || fileName.find(".tif") != std::string::npos || fileName.find(".jpg") != std::string::npos || fileName.find(".bmp") != std::string::npos) { try { // switch processing of single/multi-component images if( image->GetPixelType(0).GetNumberOfComponents() == 1) { AccessByItk_1( image, _mitkItkPictureWrite, fileName ); } else { AccessFixedPixelTypeByItk_1( image, _mitkItkPictureWriteComposite, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES_SEQ , fileName); } } catch(itk::ExceptionObject &e) { std::cerr << "Caught " << e.what() << std::endl; } catch(std::exception &e) { std::cerr << "Caught std::exception " << e.what() << std::endl; } return; } // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. unsigned int dimension = image->GetDimension(); unsigned int* dimensions = image->GetDimensions(); mitk::PixelType pixelType = image->GetPixelType(); mitk::Vector3D mitkSpacing = image->GetGeometry()->GetSpacing(); mitk::Point3D mitkOrigin = image->GetGeometry()->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have an valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have an valid value here itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( fileName.c_str(), itk::ImageIOFactory::WriteMode ); if(imageIO.IsNull()) { itkExceptionMacro(<< "Error: Could not create itkImageIO via factory for file " << fileName); } // Set the necessary information for imageIO imageIO->SetNumberOfDimensions(dimension); imageIO->SetPixelType( pixelType.GetPixelType() ); imageIO->SetComponentType( pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); imageIO->SetNumberOfComponents( pixelType.GetNumberOfComponents() ); itk::ImageIORegion ioRegion( dimension ); for(unsigned int i=0; iSetDimensions(i,dimensions[i]); imageIO->SetSpacing(i,spacing4D[i]); imageIO->SetOrigin(i,origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) direction4D[3] = 1; // homogenous component else direction4D[3] = 0; vnl_vector< double > axisDirection(dimension); for(unsigned int j=0; jSetDirection( i, axisDirection ); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i) ); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i) ); } //use compression if available imageIO->SetUseCompression( m_UseCompression ); imageIO->SetIORegion(ioRegion); imageIO->SetFileName(fileName); ImageReadAccessor imageAccess(image); imageIO->Write(imageAccess.GetData()); } void mitk::ImageWriter::GenerateData() { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, nullptr ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } + mitk::LocaleSwitch localeSwitch("C"); if ( m_FileName == "" ) { itkWarningMacro( << "Sorry, filename has not been set!" ); return ; } FILE* tempFile = fopen(m_FileName.c_str(),"w"); if (tempFile==nullptr) { itkExceptionMacro(<<"File location not writeable"); return; } fclose(tempFile); remove(m_FileName.c_str()); // Creating clone of input image, since i might change the geometry mitk::Image::Pointer input = const_cast(this->GetInput())->Clone(); // Check if geometry information will be lost if (input->GetDimension() == 2) { if (!input->GetGeometry()->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = input->GetGeometry()->GetSpacing(); mitk::Point3D origin = input->GetGeometry()->GetOrigin(); input->GetGeometry()->SetIndexToWorldTransform(affTrans); input->GetGeometry()->SetSpacing(spacing); input->GetGeometry()->SetOrigin(origin); } } bool vti = (m_Extension.find(".vti") != std::string::npos); // If the extension is NOT .pic and NOT .nrrd and NOT .nii and NOT .nii.gz the following block is entered if ( m_Extension.find(".pic") == std::string::npos && m_Extension.find(".nrrd") == std::string::npos && m_Extension.find(".nii") == std::string::npos && m_Extension.find(".nii.gz") == std::string::npos ) { if(input->GetDimension() > 3) { int t, timesteps; timesteps = input->GetDimension(3); ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(input); mitk::Image::Pointer image = timeSelector->GetOutput(); for(t = 0; t < timesteps; ++t) { std::ostringstream filename; timeSelector->SetTimeNr(t); timeSelector->Update(); if(input->GetTimeGeometry()->IsValidTimeStep(t)) { const mitk::TimeBounds& timebounds = input->GetTimeGeometry()->GetTimeBounds(t); filename << m_FileNameWithoutExtension << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0) << timebounds[1] << "_T" << t << m_Extension; } else { itkWarningMacro(<<"Error on write: TimeGeometry invalid of image " << filename.str() << "."); filename << m_FileNameWithoutExtension << "_T" << t << m_Extension; } if ( vti ) { writeVti(filename.str().c_str(), input, t); } else { WriteByITK(image, filename.str()); } } } else if ( vti ) { writeVti(m_FileName.c_str(), input); } else { WriteByITK(input, m_FileName); } } else { // use the PicFileWriter for the .pic data type if( m_Extension.find(".pic") != std::string::npos ) { /* PicFileWriter::Pointer picWriter = PicFileWriter::New(); size_t found; found = m_FileName.find( m_Extension ); // !!! HAS to be at the very end of the filename (not somewhere in the middle) if( m_FileName.length() > 3 && found != m_FileName.length() - 4 ) { //if Extension not in Filename std::ostringstream filename; filename << m_FileName.c_str() << m_Extension; picWriter->SetFileName( filename.str().c_str() ); } else { picWriter->SetFileName( m_FileName.c_str() ); } picWriter->SetInputImage( input ); picWriter->Write(); */ } // use the ITK .nrrd Image writer if( m_Extension.find(".nrrd") != std::string::npos || m_Extension.find(".nii") != std::string::npos || m_Extension.find(".nii.gz") != std::string::npos ) { WriteByITK(input, this->m_FileName); } } m_MimeType = "application/MITK.Pic"; - - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } } bool mitk::ImageWriter::CanWriteDataType( DataNode* input ) { if ( input ) { return this->CanWriteBaseDataType(input->GetData()); } return false; } void mitk::ImageWriter::SetInput( DataNode* input ) { if( input && CanWriteDataType( input ) ) this->ProcessObject::SetNthInput( 0, dynamic_cast( input->GetData() ) ); } std::string mitk::ImageWriter::GetWritenMIMEType() { return m_MimeType; } std::vector mitk::ImageWriter::GetPossibleFileExtensions() { std::vector possibleFileExtensions; possibleFileExtensions.push_back(".pic"); possibleFileExtensions.push_back(".pic.gz"); possibleFileExtensions.push_back(".bmp"); possibleFileExtensions.push_back(".dcm"); possibleFileExtensions.push_back(".DCM"); possibleFileExtensions.push_back(".dicom"); possibleFileExtensions.push_back(".DICOM"); possibleFileExtensions.push_back(".gipl"); possibleFileExtensions.push_back(".gipl.gz"); possibleFileExtensions.push_back(".mha"); possibleFileExtensions.push_back(".nii"); possibleFileExtensions.push_back(".nii.gz"); possibleFileExtensions.push_back(".nrrd"); possibleFileExtensions.push_back(".nhdr"); possibleFileExtensions.push_back(".png"); possibleFileExtensions.push_back(".PNG"); possibleFileExtensions.push_back(".spr"); possibleFileExtensions.push_back(".mhd"); possibleFileExtensions.push_back(".vtk"); possibleFileExtensions.push_back(".vti"); possibleFileExtensions.push_back(".hdr"); possibleFileExtensions.push_back(".img"); possibleFileExtensions.push_back(".img.gz"); possibleFileExtensions.push_back(".png"); possibleFileExtensions.push_back(".tif"); possibleFileExtensions.push_back(".jpg"); return possibleFileExtensions; } std::string mitk::ImageWriter::GetSupportedBaseData() const { return Image::GetStaticNameOfClass(); } std::string mitk::ImageWriter::GetFileExtension() { return m_Extension; } void mitk::ImageWriter::SetInput( mitk::Image* image ) { this->ProcessObject::SetNthInput( 0, image ); } const mitk::Image* mitk::ImageWriter::GetInput() { if ( this->GetNumberOfInputs() < 1 ) { return nullptr; } else { return static_cast< const mitk::Image * >( this->ProcessObject::GetInput( 0 ) ); } } const char* mitk::ImageWriter::GetDefaultFilename() { return "Image.nrrd"; } const char* mitk::ImageWriter::GetFileDialogPattern() { return "Nearly Raw Raster Data (*.nrrd);;" "NIfTI format (*.nii *.nii.gz);;" "VTK Image Data Files (*.vti);;" "NRRD with detached header (*.nhdr);;" "Analyze Format (*.hdr);;" "MetaImage (*.mhd);;" "Sets of 2D slices (*.png *.tiff *.jpg *.jpeg *.bmp);;" "DICOM (*.dcm *.DCM *.dicom *.DICOM);;" "UMDS GIPL Format Files (*.gipl *.gipl.gz)"; } const char *mitk::ImageWriter::GetDefaultExtension() { return ".nrrd"; } bool mitk::ImageWriter::CanWriteBaseDataType(BaseData::Pointer data) { return dynamic_cast( data.GetPointer() ); } void mitk::ImageWriter::DoWrite(BaseData::Pointer data) { if (this->CanWriteBaseDataType(data)) { this->SetInput(dynamic_cast(data.GetPointer())); this->Update(); } } void mitk::ImageWriter::SetUseCompression( bool useCompression ) { m_UseCompression = useCompression; } diff --git a/Modules/LegacyIO/mitkItkImageFileReader.cpp b/Modules/LegacyIO/mitkItkImageFileReader.cpp index a12c2e780a..82fde18374 100644 --- a/Modules/LegacyIO/mitkItkImageFileReader.cpp +++ b/Modules/LegacyIO/mitkItkImageFileReader.cpp @@ -1,237 +1,215 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkItkImageFileReader.h" #include "mitkConfig.h" #include "mitkException.h" #include +#include #include #include #include #include //#include #include #include #include #include //#include //#include //#include //#include //#include //#include void mitk::ItkImageFileReader::GenerateData() { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, nullptr ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO("mitkItkImageFileReader") << "Could not set locale " << locale; - } - } - + mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer image = this->GetOutput(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; MITK_INFO("mitkItkImageFileReader") << "loading " << m_FileName << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if ( m_FileName == "" ) { mitkThrow() << "Empty filename in mitk::ItkImageFileReader "; return ; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( m_FileName.c_str(), itk::ImageIOFactory::ReadMode ); if ( imageIO.IsNull() ) { //itkWarningMacro( << "File Type not supported!" ); mitkThrow() << "Could not create itk::ImageIOBase object for filename " << m_FileName; return ; } // Got to allocate space for the image. Determine the characteristics of // the image. imageIO->SetFileName( m_FileName.c_str() ); imageIO->ReadImageInformation(); unsigned int ndim = imageIO->GetNumberOfDimensions(); if ( ndim < MINDIM || ndim > MAXDIM ) { itkWarningMacro( << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D." ); ndim = MAXDIM; } itk::ImageIORegion ioRegion( ndim ); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[ MAXDIM ]; dimensions[ 0 ] = 0; dimensions[ 1 ] = 0; dimensions[ 2 ] = 0; dimensions[ 3 ] = 0; ScalarType spacing[ MAXDIM ]; spacing[ 0 ] = 1.0f; spacing[ 1 ] = 1.0f; spacing[ 2 ] = 1.0f; spacing[ 3 ] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for ( i = 0; i < ndim ; ++i ) { ioStart[ i ] = 0; ioSize[ i ] = imageIO->GetDimensions( i ); if(iGetDimensions( i ); spacing[ i ] = imageIO->GetSpacing( i ); if(spacing[ i ] <= 0) spacing[ i ] = 1.0f; } if(i<3) { origin[ i ] = imageIO->GetOrigin( i ); } } ioRegion.SetSize( ioSize ); ioRegion.SetIndex( ioStart ); MITK_INFO("mitkItkImageFileReader") << "ioRegion: " << ioRegion << std::endl; imageIO->SetIORegion( ioRegion ); void* buffer = new unsigned char[imageIO->GetImageSizeInBytes()]; imageIO->Read( buffer ); image->Initialize( MakePixelType(imageIO), ndim, dimensions ); image->SetImportChannel( buffer, 0, Image::ManageMemory ); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3? 3 : ndim); for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = imageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(image->GetSlicedGeometry(0)->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO("mitkItkImageFileReader") << slicedGeometry->GetCornerPoint(false,false,false); MITK_INFO("mitkItkImageFileReader") << slicedGeometry->GetCornerPoint(true,true,true); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO("mitkItkImageFileReader") << "number of image components: "<< image->GetPixelType().GetNumberOfComponents() << std::endl; // mitk::DataNode::Pointer node = this->GetOutput(); // node->SetData( image ); // add level-window property //if ( image->GetPixelType().GetNumberOfComponents() == 1 ) //{ // SetDefaultImageProperties( node ); //} MITK_INFO("mitkItkImageFileReader") << "...finished!" << std::endl; - - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO("mitkItkImageFileReader") << "Could not reset locale " << currLocale; - } } bool mitk::ItkImageFileReader::CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern) { // First check the extension if( filename == "" ) return false; // check if image is serie if( filePattern != "" && filePrefix != "" ) return false; itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO( filename.c_str(), itk::ImageIOFactory::ReadMode ); if ( imageIO.IsNull() ) return false; try { imageIO->SetFileName( filename.c_str() ); imageIO->ReadImageInformation(); itk::MetaDataDictionary imgMetaDictionary = imageIO->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); if (itKey->find("modality") != std::string::npos) { if (metaString.find("DWMRI") != std::string::npos) { return false; // DiffusionImageReader should handle this } } } }catch(...) { MITK_INFO("mitkItkImageFileReader") << "Could not read ImageInformation "; } return true; } mitk::ItkImageFileReader::ItkImageFileReader() : m_FileName(""), m_FilePrefix(""), m_FilePattern("") { } mitk::ItkImageFileReader::~ItkImageFileReader() { } diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp b/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp index 62f5efb990..910844e8ff 100644 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp +++ b/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp @@ -1,481 +1,459 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlanarFigureReader.h" #include "mitkPlanarAngle.h" #include "mitkPlanarCircle.h" #include "mitkPlanarLine.h" #include "mitkPlanarArrow.h" #include "mitkPlanarCross.h" #include "mitkPlanarFourPointAngle.h" #include "mitkPlanarPolygon.h" #include "mitkPlanarSubdivisionPolygon.h" #include "mitkPlanarRectangle.h" #include "mitkPlaneGeometry.h" #include "mitkPlanarEllipse.h" #include "mitkPlanarDoubleEllipse.h" #include "mitkPlanarBezierCurve.h" #include "mitkBasePropertySerializer.h" +#include #include #include mitk::PlanarFigureReader::PlanarFigureReader() : PlanarFigureSource(), FileReader(), m_FileName(""), m_FilePrefix(""), m_FilePattern(""), m_Success(false) { this->SetNumberOfRequiredOutputs(1); this->SetNumberOfIndexedOutputs(1); this->SetNthOutput(0, this->MakeOutput(0)); m_CanReadFromMemory = true; //this->Modified(); //this->GetOutput()->Modified(); //this->GetOutput()->ReleaseData(); } mitk::PlanarFigureReader::~PlanarFigureReader() {} void mitk::PlanarFigureReader::GenerateData() { - const std::string& locale = "C"; - const std::string& currLocale = setlocale( LC_ALL, nullptr ); - - if ( locale.compare(currLocale)!=0 ) - { - try - { - setlocale(LC_ALL, locale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not set locale " << locale; - } - } - + mitk::LocaleSwitch localeSwitch("C"); m_Success = false; this->SetNumberOfIndexedOutputs(0); // reset all outputs, we add new ones depending on the file content TiXmlDocument document; if(m_ReadFromMemory) { if(m_MemoryBuffer == nullptr || m_MemorySize == 0) { //check itkWarningMacro( << "Sorry, memory buffer has not been set!" ); return; } if(m_MemoryBuffer[ m_MemorySize - 1 ] == '\0') { document.Parse(m_MemoryBuffer); } else { auto tmpArray = new char[(int)m_MemorySize+1]; tmpArray[m_MemorySize] = '\0'; memcpy(tmpArray,m_MemoryBuffer,m_MemorySize); document.Parse(m_MemoryBuffer); delete [] tmpArray; } } else { if (m_FileName.empty()) { itkWarningMacro( << "Sorry, filename has not been set!" ); return; } if (this->CanReadFile( m_FileName.c_str()) == false) { itkWarningMacro( << "Sorry, can't read file " << m_FileName << "!" ); return; } if (!document.LoadFile(m_FileName)) { MITK_ERROR << "Could not open/read/parse " << m_FileName << ". TinyXML reports: '" << document.ErrorDesc() << "'. " << "The error occurred in row " << document.ErrorRow() << ", column " << document.ErrorCol() << "."; return; } } int fileVersion = 1; TiXmlElement* versionObject = document.FirstChildElement("Version"); if (versionObject != nullptr) { if ( versionObject->QueryIntAttribute( "FileVersion", &fileVersion ) != TIXML_SUCCESS ) { MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl; } } else { MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl; } if (fileVersion != 1) // add file version selection and version specific file parsing here, if newer file versions are created { MITK_WARN << "File version > 1 is not supported by this reader."; return; } /* file version 1 reader code */ for( TiXmlElement* pfElement = document.FirstChildElement("PlanarFigure"); pfElement != nullptr; pfElement = pfElement->NextSiblingElement("PlanarFigure") ) { if (pfElement == nullptr) continue; std::string type = pfElement->Attribute("type"); mitk::PlanarFigure::Pointer planarFigure = nullptr; if (type == "PlanarAngle") { planarFigure = mitk::PlanarAngle::New(); } else if (type == "PlanarCircle") { planarFigure = mitk::PlanarCircle::New(); } else if (type == "PlanarEllipse") { planarFigure = mitk::PlanarEllipse::New(); } else if (type == "PlanarCross") { planarFigure = mitk::PlanarCross::New(); } else if (type == "PlanarFourPointAngle") { planarFigure = mitk::PlanarFourPointAngle::New(); } else if (type == "PlanarLine") { planarFigure = mitk::PlanarLine::New(); } else if (type == "PlanarPolygon") { planarFigure = mitk::PlanarPolygon::New(); } else if (type == "PlanarSubdivisionPolygon") { planarFigure = mitk::PlanarSubdivisionPolygon::New(); } else if (type == "PlanarRectangle") { planarFigure = mitk::PlanarRectangle::New(); } else if (type == "PlanarArrow") { planarFigure = mitk::PlanarArrow::New(); } else if (type == "PlanarDoubleEllipse") { planarFigure = mitk::PlanarDoubleEllipse::New(); } else if (type == "PlanarBezierCurve") { planarFigure = mitk::PlanarBezierCurve::New(); } else { // unknown type MITK_WARN << "encountered unknown planar figure type '" << type << "'. Skipping this element."; continue; } // Read properties of the planar figure for( TiXmlElement* propertyElement = pfElement->FirstChildElement("property"); propertyElement != nullptr; propertyElement = propertyElement->NextSiblingElement("property") ) { const char* keya = propertyElement->Attribute("key"); const std::string key( keya ? keya : ""); const char* typea = propertyElement->Attribute("type"); const std::string type( typea ? typea : ""); // hand propertyElement to specific reader std::stringstream propertyDeserializerClassName; propertyDeserializerClassName << type << "Serializer"; const std::list readers = itk::ObjectFactoryBase::CreateAllInstance(propertyDeserializerClassName.str().c_str()); if (readers.size() < 1) { MITK_ERROR << "No property reader found for " << type; } if (readers.size() > 1) { MITK_WARN << "Multiple property readers found for " << type << ". Using arbitrary first one."; } for ( auto iter = readers.cbegin(); iter != readers.cend(); ++iter ) { if (BasePropertySerializer* reader = dynamic_cast( iter->GetPointer() ) ) { const BaseProperty::Pointer property = reader->Deserialize( propertyElement->FirstChildElement() ); if (property.IsNotNull()) { planarFigure->GetPropertyList()->ReplaceProperty(key, property); } else { MITK_ERROR << "There were errors while loading property '" << key << "' of type " << type << ". Your data may be corrupted"; } break; } } } // If we load a planarFigure, it has definitely been placed correctly. // If we do not set this property here, we cannot load old planarFigures // without messing up the interaction (PF-Interactor needs this property. planarFigure->GetPropertyList()->SetBoolProperty( "initiallyplaced", true ); // Which features (length or circumference etc) a figure has is decided by whether it is closed or not // the function SetClosed has to be called in case of PlanarPolygons to ensure they hold the correct feature PlanarPolygon* planarPolygon = dynamic_cast (planarFigure.GetPointer()); if (planarPolygon != nullptr) { bool isClosed = false; planarFigure->GetPropertyList()->GetBoolProperty( "closed", isClosed); planarPolygon->SetClosed(isClosed); } // Read geometry of containing plane TiXmlElement* geoElement = pfElement->FirstChildElement("Geometry"); if (geoElement != nullptr) { try { // Create plane geometry mitk::PlaneGeometry::Pointer planeGeo = mitk::PlaneGeometry::New(); // Extract and set plane transform parameters const DoubleList transformList = this->GetDoubleAttributeListFromXMLNode( geoElement->FirstChildElement( "transformParam" ), "param", 12 ); typedef mitk::BaseGeometry::TransformType TransformType; TransformType::ParametersType parameters; parameters.SetSize( 12 ); unsigned int i; DoubleList::const_iterator it; for ( it = transformList.cbegin(), i = 0; it != transformList.cend(); ++it, ++i ) { parameters.SetElement( i, *it ); } typedef mitk::BaseGeometry::TransformType TransformType; TransformType::Pointer affineGeometry = TransformType::New(); affineGeometry->SetParameters( parameters ); planeGeo->SetIndexToWorldTransform( affineGeometry ); // Extract and set plane bounds const DoubleList boundsList = this->GetDoubleAttributeListFromXMLNode( geoElement->FirstChildElement( "boundsParam" ), "bound", 6 ); typedef mitk::BaseGeometry::BoundsArrayType BoundsArrayType; BoundsArrayType bounds; for ( it = boundsList.cbegin(), i = 0; it != boundsList.cend(); ++it, ++i ) { bounds[i] = *it; } planeGeo->SetBounds( bounds ); // Extract and set spacing and origin const Vector3D spacing = this->GetVectorFromXMLNode(geoElement->FirstChildElement("Spacing")); planeGeo->SetSpacing( spacing ); const Point3D origin = this->GetPointFromXMLNode(geoElement->FirstChildElement("Origin")); planeGeo->SetOrigin( origin ); planarFigure->SetPlaneGeometry(planeGeo); } catch (...) { } } TiXmlElement* cpElement = pfElement->FirstChildElement("ControlPoints"); bool first = true; if (cpElement != nullptr) for( TiXmlElement* vertElement = cpElement->FirstChildElement("Vertex"); vertElement != nullptr; vertElement = vertElement->NextSiblingElement("Vertex") ) { if (vertElement == nullptr) continue; int id = 0; mitk::Point2D::ValueType x = 0.0; mitk::Point2D::ValueType y = 0.0; if (vertElement->QueryIntAttribute("id", &id) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? if (vertElement->QueryDoubleAttribute("x", &x) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? if (vertElement->QueryDoubleAttribute("y", &y) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? Point2D p; p.SetElement(0, x); p.SetElement(1, y); if (first == true) // needed to set m_FigurePlaced to true { planarFigure->PlaceFigure(p); first = false; } planarFigure->SetControlPoint(id, p, true); } // Calculate feature quantities of this PlanarFigure planarFigure->EvaluateFeatures(); // Make sure that no control point is currently selected planarFigure->DeselectControlPoint(); // \TODO: what about m_FigurePlaced and m_SelectedControlPoint ?? this->SetNthOutput( this->GetNumberOfOutputs(), planarFigure ); // add planarFigure as new output of this filter } - try - { - setlocale(LC_ALL, currLocale.c_str()); - } - catch(...) - { - MITK_INFO << "Could not reset locale " << currLocale; - } - m_Success = true; } mitk::Point3D mitk::PlanarFigureReader::GetPointFromXMLNode(TiXmlElement* e) { if (e == nullptr) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? mitk::Point3D point; mitk::ScalarType p(-1.0); if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(0, p); if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(1, p); if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(2, p); return point; } mitk::Vector3D mitk::PlanarFigureReader::GetVectorFromXMLNode(TiXmlElement* e) { if (e == nullptr) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? mitk::Vector3D vector; mitk::ScalarType p(-1.0); if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(0, p); if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(1, p); if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(2, p); return vector; } mitk::PlanarFigureReader::DoubleList mitk::PlanarFigureReader::GetDoubleAttributeListFromXMLNode(TiXmlElement* e, const char *attributeNameBase, unsigned int count) { DoubleList list; if (e == nullptr) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? for ( unsigned int i = 0; i < count; ++i ) { mitk::ScalarType p(-1.0); std::stringstream attributeName; attributeName << attributeNameBase << i; if (e->QueryDoubleAttribute( attributeName.str().c_str(), &p ) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? list.push_back( p ); } return list; } void mitk::PlanarFigureReader::GenerateOutputInformation() { } int mitk::PlanarFigureReader::CanReadFile ( const char *name ) { if (std::string(name).empty()) return false; return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(name)) == ".pf"); //assume, we can read all .pf files //TiXmlDocument document(name); //if (document.LoadFile() == false) // return false; //return (document.FirstChildElement("PlanarFigure") != NULL); } bool mitk::PlanarFigureReader::CanReadFile(const std::string filename, const std::string, const std::string) { if (filename.empty()) return false; return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(filename)) == ".pf"); //assume, we can read all .pf files //TiXmlDocument document(filename); //if (document.LoadFile() == false) // return false; //return (document.FirstChildElement("PlanarFigure") != NULL); } void mitk::PlanarFigureReader::ResizeOutputs( const unsigned int& num ) { unsigned int prevNum = this->GetNumberOfOutputs(); this->SetNumberOfIndexedOutputs( num ); for ( unsigned int i = prevNum; i < num; ++i ) { this->SetNthOutput( i, this->MakeOutput( i ).GetPointer() ); } } diff --git a/Plugins/org.mitk.gui.qt.dicom/src/internal/DicomEventHandler.cpp b/Plugins/org.mitk.gui.qt.dicom/src/internal/DicomEventHandler.cpp index a1fbb4ce0a..a2aedeec1d 100644 --- a/Plugins/org.mitk.gui.qt.dicom/src/internal/DicomEventHandler.cpp +++ b/Plugins/org.mitk.gui.qt.dicom/src/internal/DicomEventHandler.cpp @@ -1,376 +1,375 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPluginActivator.h" #include "DicomEventHandler.h" #include #include #include #include #include #include #include #include #include #include "mitkImage.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include DicomEventHandler::DicomEventHandler() { } DicomEventHandler::~DicomEventHandler() { } void DicomEventHandler::OnSignalAddSeriesToDataManager(const ctkEvent& ctkEvent) { QStringList listOfFilesForSeries; listOfFilesForSeries = ctkEvent.getProperty("FilesForSeries").toStringList(); if (!listOfFilesForSeries.isEmpty()) { //for rt data, if the modality tag isnt defined or is "CT" the image is handled like before if(ctkEvent.containsProperty("Modality") && (ctkEvent.getProperty("Modality").toString().compare("RTDOSE",Qt::CaseInsensitive) == 0 || ctkEvent.getProperty("Modality").toString().compare("RTSTRUCT",Qt::CaseInsensitive) == 0)) { QString modality = ctkEvent.getProperty("Modality").toString(); if(modality.compare("RTDOSE",Qt::CaseInsensitive) == 0) { mitk::RTDoseReader::Pointer doseReader = mitk::RTDoseReader::New(); mitk::DataNode::Pointer doseImageNode = mitk::DataNode::New(); mitk::DataNode::Pointer doseOutlineNode = mitk::DataNode::New(); doseImageNode = doseReader->LoadRTDose(listOfFilesForSeries.at(0).toStdString().c_str()); doseOutlineNode->SetData(doseImageNode->GetData()); if (doseImageNode.IsNotNull() && doseOutlineNode->GetData() != nullptr) { berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IPreferences::Pointer prefNode = prefService->GetSystemPreferences()->Node(mitk::RTUIConstants::ROOT_ISO_PRESETS_PREFERENCE_NODE_ID.c_str()); typedef QStringList NamesType; NamesType names = prefNode->ChildrenNames(); std::map presetMap; for (NamesType::const_iterator pos = names.begin(); pos != names.end(); ++pos) { berry::IPreferences::Pointer aPresetNode = prefNode->Node(*pos); if (aPresetNode.IsNull()) { mitkThrow() << "Error in preference interface. Cannot find preset node under given name. Name: " << (*pos).toStdString(); } mitk::IsoDoseLevelSet::Pointer levelSet = mitk::IsoDoseLevelSet::New(); NamesType levelNames = aPresetNode->ChildrenNames(); for (NamesType::const_iterator levelName = levelNames.begin(); levelName != levelNames.end(); ++levelName) { berry::IPreferences::Pointer levelNode = aPresetNode->Node(*levelName); if (aPresetNode.IsNull()) { mitkThrow() << "Error in preference interface. Cannot find level node under given preset name. Name: " << (*pos).toStdString() << "; Level id: " << (*levelName).toStdString(); } mitk::IsoDoseLevel::Pointer isoLevel = mitk::IsoDoseLevel::New(); isoLevel->SetDoseValue(levelNode->GetDouble(mitk::RTUIConstants::ISO_LEVEL_DOSE_VALUE_ID.c_str(), 0.0)); mitk::IsoDoseLevel::ColorType color; color.SetRed(levelNode->GetFloat(mitk::RTUIConstants::ISO_LEVEL_COLOR_RED_ID.c_str(), 1.0)); color.SetGreen(levelNode->GetFloat(mitk::RTUIConstants::ISO_LEVEL_COLOR_GREEN_ID.c_str(), 1.0)); color.SetBlue(levelNode->GetFloat(mitk::RTUIConstants::ISO_LEVEL_COLOR_BLUE_ID.c_str(), 1.0)); isoLevel->SetColor(color); isoLevel->SetVisibleIsoLine(levelNode->GetBool(mitk::RTUIConstants::ISO_LEVEL_VISIBILITY_ISOLINES_ID.c_str(), true)); isoLevel->SetVisibleColorWash(levelNode->GetBool(mitk::RTUIConstants::ISO_LEVEL_VISIBILITY_COLORWASH_ID.c_str(), true)); levelSet->SetIsoDoseLevel(isoLevel); } presetMap.insert(std::make_pair((*pos).toStdString(), levelSet)); } if (presetMap.size() == 0) { presetMap.insert(std::make_pair(std::string("Virtuos"), mitk::GeneratIsoLevels_Virtuos())); } prefNode = prefService->GetSystemPreferences()->Node(mitk::RTUIConstants::ROOT_DOSE_VIS_PREFERENCE_NODE_ID.c_str()); if (prefNode.IsNull()) { mitkThrow() << "Error in preference interface. Cannot find preset node under given name. Name: " << prefNode->ToString().toStdString(); } //set some specific colorwash and isoline properties bool showColorWashGlobal = prefNode->GetBool(mitk::RTUIConstants::GLOBAL_VISIBILITY_COLORWASH_ID.c_str(), true); doseImageNode->SetBoolProperty(mitk::RTConstants::DOSE_SHOW_COLORWASH_PROPERTY_NAME.c_str(), showColorWashGlobal); bool showIsolinesGlobal = prefNode->GetBool(mitk::RTUIConstants::GLOBAL_VISIBILITY_ISOLINES_ID.c_str(), true); doseOutlineNode->SetBoolProperty(mitk::RTConstants::DOSE_SHOW_ISOLINES_PROPERTY_NAME.c_str(), showIsolinesGlobal); //Set reference dose property double referenceDose = prefNode->GetDouble(mitk::RTUIConstants::REFERENCE_DOSE_ID.c_str(), mitk::RTUIConstants::DEFAULT_REFERENCE_DOSE_VALUE); doseImageNode->SetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), referenceDose); doseOutlineNode->SetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), referenceDose); QString presetName = prefNode->Get(mitk::RTUIConstants::SELECTED_ISO_PRESET_ID.c_str(), "Virtuos"); mitk::IsoDoseLevelSet::Pointer isoDoseLevelPreset = presetMap[presetName.toStdString()]; mitk::IsoDoseLevelSetProperty::Pointer levelSetProp = mitk::IsoDoseLevelSetProperty::New(isoDoseLevelPreset); doseImageNode->SetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str(), levelSetProp); doseOutlineNode->SetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str(), levelSetProp); mitk::IsoDoseLevelVector::Pointer levelVector = mitk::IsoDoseLevelVector::New(); mitk::IsoDoseLevelVectorProperty::Pointer levelVecProp = mitk::IsoDoseLevelVectorProperty::New(levelVector); doseImageNode->SetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str(), levelVecProp); doseOutlineNode->SetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str(), levelVecProp); mitk::RenderingModeProperty::Pointer renderingModeProp = mitk::RenderingModeProperty::New(); if (showColorWashGlobal) { //Generating the Colorwash vtkSmartPointer transferFunction = vtkSmartPointer::New(); for (mitk::IsoDoseLevelSet::ConstIterator itIsoDoseLevel = isoDoseLevelPreset->Begin(); itIsoDoseLevel != isoDoseLevelPreset->End(); ++itIsoDoseLevel) { float *hsv = new float[3]; //used for transfer rgb to hsv vtkSmartPointer cCalc = vtkSmartPointer::New(); if (itIsoDoseLevel->GetVisibleColorWash()){ cCalc->RGBToHSV(itIsoDoseLevel->GetColor()[0], itIsoDoseLevel->GetColor()[1], itIsoDoseLevel->GetColor()[2], &hsv[0], &hsv[1], &hsv[2]); transferFunction->AddHSVPoint(itIsoDoseLevel->GetDoseValue()*referenceDose, hsv[0], hsv[1], hsv[2], 1.0, 1.0); } } mitk::TransferFunction::Pointer mitkTransFunc = mitk::TransferFunction::New(); mitk::TransferFunctionProperty::Pointer mitkTransFuncProp = mitk::TransferFunctionProperty::New(); mitkTransFunc->SetColorTransferFunction(transferFunction); mitkTransFuncProp->SetValue(mitkTransFunc); doseImageNode->SetProperty("Image Rendering.Transfer Function", mitkTransFuncProp); renderingModeProp->SetValue(mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR); } else { //Set rendering mode to levelwindow color mode renderingModeProp->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); } doseImageNode->SetProperty("Image Rendering.Mode", renderingModeProp); doseImageNode->SetProperty("opacity", mitk::FloatProperty::New(0.5)); //set the outline properties doseOutlineNode->SetBoolProperty("outline binary", true); doseOutlineNode->SetProperty("helper object", mitk::BoolProperty::New(true)); doseOutlineNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); dataStorage->Add(doseImageNode); dataStorage->Add(doseOutlineNode, doseImageNode); //set the dose mapper for outline drawing; the colorwash is realized by the imagevtkmapper2D mitk::DoseImageVtkMapper2D::Pointer contourMapper = mitk::DoseImageVtkMapper2D::New(); doseOutlineNode->SetMapper(1, contourMapper); mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); }//END DOSE } else if(modality.compare("RTSTRUCT",Qt::CaseInsensitive) == 0) { mitk::RTStructureSetReader::Pointer structreader = mitk::RTStructureSetReader::New(); std::deque modelVector = structreader->ReadStructureSet(listOfFilesForSeries.at(0).toStdString().c_str()); if(modelVector.empty()) { MITK_ERROR << "No structuresets were created" << endl; } else { ctkServiceReference serviceReference =mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); for(int i=0; iAdd(modelVector.at(i)); } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); } } } else { mitk::StringList seriesToLoad; QStringListIterator it(listOfFilesForSeries); while (it.hasNext()) { seriesToLoad.push_back(it.next().toStdString()); } //Get Reference for default data storage. ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); //special handling of Philips 3D US DICOM. //Copied from DICOMSeriesReaderService if (!seriesToLoad.empty() && mitk::DicomSeriesReader::IsPhilips3DDicom(seriesToLoad.front())) { MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; - const char* previousCLocale = setlocale(LC_NUMERIC, NULL); - setlocale(LC_NUMERIC, "C"); + mitk::LocaleSwitch localeSwitch("C"); std::locale previousCppLocale(std::cin.getloc()); std::locale l("C"); std::cin.imbue(l); mitk::DataNode::Pointer node = mitk::DataNode::New(); mitk::DicomSeriesReader::StringContainer stringvec; stringvec.push_back(seriesToLoad.front()); if (mitk::DicomSeriesReader::LoadDicomSeries(stringvec, *node)) { mitk::BaseData::Pointer data = node->GetData(); mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenameName(seriesToLoad.front())); data->GetPropertyList()->SetProperty("name", nameProp); node->SetProperty("name", nameProp); dataStorage->Add(node); } - setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); return; } //Normal DICOM handling (It wasn't a Philips 3D US) mitk::DICOMFileReaderSelector::Pointer selector = mitk::DICOMFileReaderSelector::New(); selector->LoadBuiltIn3DConfigs(); selector->LoadBuiltIn3DnTConfigs(); selector->SetInputFiles(seriesToLoad); mitk::DICOMFileReader::Pointer reader = selector->GetFirstReaderWithMinimumNumberOfOutputImages(); //reset tag cache to ensure that additional tags of interest //will be regarded by the reader if set later on. reader->SetTagCache(nullptr); reader->SetAdditionalTagsOfInterest(mitk::GetCurrentDICOMTagsOfInterest()); reader->SetTagLookupTableToPropertyFunctor(mitk::GetDICOMPropertyForDICOMValuesFunctor); reader->SetInputFiles(seriesToLoad); reader->AnalyzeInputFiles(); reader->LoadImages(); for (unsigned int i = 0; i < reader->GetNumberOfOutputs(); ++i) { const mitk::DICOMImageBlockDescriptor& desc = reader->GetOutput(i); mitk::BaseData::Pointer data = desc.GetMitkImage().GetPointer(); std::string nodeName = "Unnamed_DICOM"; std::string studyDescription = desc.GetPropertyAsString("studyDescription"); std::string seriesDescription = desc.GetPropertyAsString("seriesDescription"); if (!studyDescription.empty()) { nodeName = studyDescription; } if (!seriesDescription.empty()) { if (!studyDescription.empty()) { nodeName += "/"; } nodeName += seriesDescription; } mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New(nodeName); data->SetProperty("name", nameProp); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(data); nameProp = mitk::StringProperty::New(nodeName); node->SetProperty("name", nameProp); dataStorage->Add(node); } if (reader->GetNumberOfOutputs() < 1) { MITK_ERROR << "Error loading series: " << ctkEvent.getProperty("SeriesName").toString().toStdString() << " id: " << ctkEvent.getProperty("SeriesUID").toString().toStdString(); } } } else { MITK_INFO << "There are no files for the current series"; } } void DicomEventHandler::OnSignalRemoveSeriesFromStorage(const ctkEvent& /*ctkEvent*/) { } void DicomEventHandler::SubscribeSlots() { ctkServiceReference ref = mitk::PluginActivator::getContext()->getServiceReference(); if (ref) { ctkEventAdmin* eventAdmin = mitk::PluginActivator::getContext()->getService(ref); ctkDictionary properties; properties[ctkEventConstants::EVENT_TOPIC] = "org/mitk/gui/qt/dicom/ADD"; eventAdmin->subscribeSlot(this, SLOT(OnSignalAddSeriesToDataManager(ctkEvent)), properties); properties[ctkEventConstants::EVENT_TOPIC] = "org/mitk/gui/qt/dicom/DELETED"; eventAdmin->subscribeSlot(this, SLOT(OnSignalRemoveSeriesFromStorage(ctkEvent)), properties); } }