Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

ScaleSpaceFilter.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2019 The Board of Trustees of the
00004  *cr                        University of Illinois
00005  *cr                         All Rights Reserved
00006  *cr
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: ScaleSpaceFilter.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.22 $        $Date: 2020/10/15 16:07:31 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *   Perform scale-space filtering on 3-D volumes, e.g., cryo-EM density maps,
00019  *   for use in subsequent image segmentation algorithms.
00020  ***************************************************************************/
00021 
00022 #include <string.h>
00023 #include <stdio.h>
00024 #include <map>
00025 #include "ScaleSpaceFilter.h"
00026 #include "Watershed.h"
00027 #if defined(VMDCUDA)
00028 #include "CUDAGaussianBlur.h"
00029 #include "CUDASegmentation.h"
00030 #endif
00031 #include "ProfileHooks.h"
00032 
00033 #define px_offset    (1)
00034 #define py_offset    (width)
00035 #define pz_offset    (heightWidth)
00036 #define nx_offset    (-1)
00037 #define ny_offset    (-width)
00038 #define nz_offset    (-heightWidth)
00039 
00040 #define nx_ny_offset (-1 - width)
00041 #define nx_py_offset (-1 + width)
00042 #define px_py_offset (1 + width)
00043 #define px_ny_offset (1 - width)
00044 
00045 #define px_pz_offset (1 + heightWidth)
00046 #define nx_nz_offset (-1 - heightWidth)
00047 #define nx_pz_offset (-1 + heightWidth)
00048 #define px_nz_offset (1 - heightWidth)
00049 
00050 #define py_pz_offset (width + heightWidth)
00051 #define ny_nz_offset (-width - heightWidth)
00052 #define ny_pz_offset (-width + heightWidth)
00053 #define py_nz_offset (width - heightWidth)
00054 
00055 
00056 template <typename GROUP_T, typename IMAGE_T>
00057 ScaleSpaceFilter<GROUP_T, IMAGE_T>::ScaleSpaceFilter(int w, int h, int d,
00058                                                      long nGrps,
00059                                                      float initial_blur_sigma,
00060                                                      float new_blur_multiple,
00061                                                      bool cuda) {
00062   width = w;
00063   height = h;
00064   depth = d;
00065   heightWidth = long(height) * long(width);
00066   nVoxels = heightWidth * long(depth);
00067   nGroups = nGrps;
00068   current_blur = initial_blur_sigma;
00069   blur_multiple = new_blur_multiple;
00070   use_cuda = cuda;
00071   memset(&gpu_seq_tmp, 0, sizeof(gpu_seq_tmp));
00072   memset(&gpu_scanwork_tmp, 0, sizeof(gpu_scanwork_tmp));
00073   memset(&gpu_grpmaxidx_tmp, 0, sizeof(gpu_grpmaxidx_tmp));
00074 
00075   max_idx = allocate_array<unsigned long>(nVoxels);
00076   group_map = allocate_array<GROUP_T>(nVoxels);
00077 }
00078 
00079 
00080 template <typename GROUP_T, typename IMAGE_T>
00081 ScaleSpaceFilter<GROUP_T, IMAGE_T>::~ScaleSpaceFilter() {
00082   free_array<unsigned long>(max_idx);
00083   free_array<GROUP_T>(group_map);
00084 
00085 #if defined(VMDCUDA)
00086   free_gpu_temp_storage(&gpu_seq_tmp);
00087   free_gpu_temp_storage(&gpu_scanwork_tmp);
00088   free_gpu_temp_storage(&gpu_grpmaxidx_tmp);
00089 #endif
00090 }
00091 
00092 
00093 template <typename GROUP_T, typename IMAGE_T>
00094 template <typename ARRAY_T>
00095 ARRAY_T* ScaleSpaceFilter<GROUP_T, IMAGE_T>::allocate_array(long num_elements) {
00096   PROFILE_PUSH_RANGE("ScaleSpaceFilter::allocate_array()", 1);
00097 
00098   ARRAY_T* arr;
00099 #if defined(VMDCUDA)
00100   if (use_cuda) {
00101     arr = (ARRAY_T*) alloc_cuda_array(num_elements * sizeof(ARRAY_T));
00102   } else 
00103 #endif
00104   {
00105     arr = new ARRAY_T[num_elements];
00106   }
00107 
00108   PROFILE_POP_RANGE();
00109 
00110   return arr;
00111 }
00112 
00113 
00114 template <typename GROUP_T, typename IMAGE_T>
00115 template <typename ARRAY_T>
00116 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::free_array(ARRAY_T*& arr) {
00117   PROFILE_PUSH_RANGE("ScaleSpaceFilter::free_array()", 2);
00118 
00119 #if defined(VMDCUDA)
00120   if (use_cuda) {
00121     free_cuda_array(arr);
00122   } else 
00123 #endif
00124   {
00125     delete [] arr;
00126   }
00127 
00128   PROFILE_POP_RANGE();
00129 
00130   arr = NULL;
00131 }
00132 
00133 
00134 template <typename GROUP_T, typename IMAGE_T>
00135 long ScaleSpaceFilter<GROUP_T, IMAGE_T>::merge(GROUP_T* segments, GaussianBlur<IMAGE_T>* gaussian, MERGE_POLICY policy) {
00136   PROFILE_PUSH_RANGE("ScaleSpaceFilter::merge()", 3);
00137 
00138   find_groups_max_idx(segments, gaussian);
00139 
00140   gaussian->blur(current_blur);
00141 
00142   if (policy == MERGE_HILL_CLIMB) {
00143     hill_climb_merge(segments, gaussian);
00144   } else if (policy == MERGE_WATERSHED_HILL_CLIMB) {
00145     watershed_hill_climb_merge(segments, gaussian);
00146   } else if (policy == MERGE_WATERSHED_OVERLAP) {
00147     watershed_overlap_merge(segments, gaussian);
00148   } else {
00149     fprintf(stderr, "ERROR: invalid merge policy.\n");
00150     return 0;
00151   }
00152 
00153   current_blur *= blur_multiple;
00154 
00155   PROFILE_POP_RANGE();
00156 
00157   return nGroups;
00158 }
00159 
00160 
00161 template <typename GROUP_T, typename IMAGE_T>
00162 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::watershed_hill_climb_merge(GROUP_T* segments, GaussianBlur<IMAGE_T>* gaussian) {
00163 
00164   GROUP_T* new_segments = allocate_array<GROUP_T>(nVoxels);
00165   int imageongpu=1;
00166   IMAGE_T *imageptr = gaussian->get_image_d();
00167   if (!imageptr) {
00168     imageptr = gaussian->get_image();
00169     imageongpu=0;
00170   }
00171   Watershed<GROUP_T, IMAGE_T> watershed(height, width, depth, use_cuda);
00172   watershed.watershed(imageptr, imageongpu, new_segments, false);
00173 
00174   long n_new_groups = sequentialize_group_nums(new_segments, nVoxels);
00175 
00176 #ifdef VMDCUDA
00177   if (use_cuda) {
00178     watershed_hill_climb_merge_cuda<GROUP_T>(segments, new_segments, imageptr, group_map, max_idx, height, width, depth, nGroups);
00179   } else
00180 #endif
00181   {
00182     watershed_hill_climb_merge_cpu(segments, new_segments, imageptr);
00183   }
00184 
00185   nGroups = sequentialize_group_nums(segments, n_new_groups);
00186   
00187   free_array(new_segments);
00188 }
00189 
00190 
00191 template <typename GROUP_T, typename IMAGE_T>
00192 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::watershed_overlap_merge(GROUP_T* segments, GaussianBlur<IMAGE_T>* gaussian) {
00193 
00194   GROUP_T* new_segments = allocate_array<GROUP_T>(nVoxels);
00195 
00196 
00197   int imageongpu=1;
00198   IMAGE_T *imageptr = gaussian->get_image_d();
00199   if (!imageptr) {
00200     imageptr = gaussian->get_image();
00201     imageongpu=0;
00202   }
00203   Watershed<GROUP_T, IMAGE_T> watershed(height, width, depth, use_cuda);
00204   watershed.watershed(imageptr, imageongpu, new_segments, false);
00205   long max_group_num = sequentialize_group_nums(segments, nVoxels);
00206 
00207 #ifdef VMDCUDA
00208   if (use_cuda) {
00209     printf("CUDA watershed overlap merge not implemented.\n");
00210     //watershed_overlap_merge_cuda<GROUP_T>(segments, new_segments, group_map);
00211   } else
00212 #endif
00213   {
00214     watershed_overlap_merge_cpu(segments, new_segments);
00215   }
00216 
00217   nGroups = sequentialize_group_nums(segments, max_group_num);
00218   
00219   free_array(new_segments);
00220 }
00221 
00222 
00223 template <typename GROUP_T, typename IMAGE_T>
00224 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::hill_climb_merge(GROUP_T* segments, GaussianBlur<IMAGE_T>* gaussian) {
00225   PROFILE_PUSH_RANGE("ScaleSpaceFilter::hill_climb_merge()", 4);
00226 
00227 #if defined(VMDCUDA)
00228   if (use_cuda) {
00229       hill_climb_merge_cuda<GROUP_T, IMAGE_T>(segments, gaussian->get_image_d(), max_idx,
00230                                        group_map, height, width, depth, nGroups);
00231 
00232   } else 
00233 #endif
00234   {
00235     hill_climb_merge_cpu(segments, gaussian->get_image());
00236   }
00237   nGroups = sequentialize_group_nums(segments, nGroups);
00238 
00239   PROFILE_POP_RANGE();
00240 }
00241 
00242 
00243 template <typename GROUP_T, typename IMAGE_T>
00244 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::watershed_hill_climb_merge_cpu(GROUP_T* segments, GROUP_T* new_segments, IMAGE_T* image) {
00245   for (long g = 0; g < nGroups; g++) {
00246     long idx = find_local_maxima(max_idx[g], image);
00247     group_map[g] = new_segments[idx];
00248   }
00249   for (long v = 0; v < nVoxels; v++) {
00250     segments[v] = group_map[segments[v]];
00251   }
00252 }
00253 
00254 
00255 template <typename GROUP_T, typename IMAGE_T>
00256 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::watershed_overlap_merge_cpu(GROUP_T* segments, GROUP_T* new_segments) {
00257   std::map<GROUP_T, long>* group_counts = new std::map<GROUP_T, long>[nGroups];
00258   //std::map<GROUP_T, long> group_counts[nGroups];
00259   long v;
00260  
00261   for (v = 0; v < nVoxels; v++) {
00262     GROUP_T curr_group = segments[v];
00263     GROUP_T overlapping_group = new_segments[v];
00264     std::map<GROUP_T, long>& counts = group_counts[curr_group];
00265     counts[overlapping_group]++;
00266 
00267     //group_counts[segments[v]][new_segments[v]]++;  
00268   }
00269   //test = true;
00270 
00271   for (long g = 0; g < nGroups; g++) {
00272     long max_count = 0;
00273     long max_g = -1;
00274     for (typename std::map<GROUP_T, long>::iterator it = group_counts[g].begin(); it != group_counts[g].end(); it++) {
00275       if (it->second >= max_count) {
00276         max_count = it->second;
00277         max_g = it->first;
00278       }
00279     }
00280     group_map[g] = GROUP_T(max_g);
00281   }
00282 
00283   for (v = 0; v < nVoxels; v++) {
00284     segments[v] = group_map[segments[v]];
00285   }
00286 
00287   delete [] group_counts;
00288 }
00289 
00290 
00291 template <typename GROUP_T, typename IMAGE_T>
00292 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::hill_climb_merge_cpu(GROUP_T* segments, IMAGE_T* image) {
00293   PROFILE_PUSH_RANGE("ScaleSpaceFilter::hill_climb_merge_cpu()", 5);
00294 
00295 #ifdef USE_OMP
00296 #pragma omp parallel for
00297 #endif
00298   for (long g = 0; g < nGroups; ++g) {
00299     long x = max_idx[g];
00300     long new_group_idx = find_local_maxima(x, image);
00301     group_map[g] = segments[new_group_idx];
00302 
00303   }
00304 
00305 #ifdef USE_OMP
00306 #pragma omp parallel for
00307 #endif
00308   for (long idx = 0; idx < nVoxels; ++idx) {
00309     long curr_group = segments[idx];
00310     segments[idx] = group_map[curr_group];
00311   }
00312 
00313   PROFILE_POP_RANGE();
00314 }
00315 
00316 
00317 template <typename GROUP_T, typename IMAGE_T>
00318 long ScaleSpaceFilter<GROUP_T, IMAGE_T>::sequentialize_group_nums_cpu(GROUP_T* segments, long max_group_num) {
00319   PROFILE_PUSH_RANGE("ScaleSpaceFilter::sequentialize_group_nums_cpu()", 6);
00320 
00321   GROUP_T max_val = GROUP_T(0) - 1;
00322   long numGroups = 0;
00323 
00324   memset(group_map, max_val, max_group_num * sizeof(GROUP_T));
00325 
00326   for (long i=0; i<nVoxels; ++i) {
00327     const GROUP_T curr_group = segments[i];
00328     if (group_map[curr_group] == max_val) {
00329       group_map[curr_group] = GROUP_T(numGroups++);
00330     }
00331     segments[i] = group_map[curr_group];
00332   }
00333 
00334   PROFILE_POP_RANGE();
00335 
00336   return numGroups;
00337 }
00338 
00339 
00340 template <typename GROUP_T, typename IMAGE_T>
00341 long ScaleSpaceFilter<GROUP_T, IMAGE_T>::sequentialize_group_nums(GROUP_T* segments, long max_group_num) {
00342   PROFILE_PUSH_RANGE("ScaleSpaceFilter::sequentialize_group_nums()", 7);
00343   long numGroups = 0;
00344 
00345 #if defined(VMDCUDA)
00346   if (use_cuda) {
00347     numGroups = sequentialize_groups_cuda<GROUP_T>(segments, group_map, nVoxels, max_group_num, &gpu_seq_tmp, &gpu_scanwork_tmp);
00348   } else 
00349 #endif
00350   {
00351     numGroups = sequentialize_group_nums_cpu(segments, max_group_num);
00352   }
00353 
00354   PROFILE_POP_RANGE();
00355 
00356   return numGroups;
00357 }
00358 
00359 
00360 template <typename GROUP_T, typename IMAGE_T>
00361 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::find_groups_max_idx(GROUP_T* segments, GaussianBlur<IMAGE_T>* gaussian) {
00362   PROFILE_PUSH_RANGE("ScaleSpaceFilter::find_groups_max_idx()", 8);
00363 
00364 #if defined(VMDCUDA)
00365   if (use_cuda) {
00366     find_groups_max_idx_cuda<GROUP_T, IMAGE_T>(segments, gaussian->get_image_d(),
00367                             max_idx, nVoxels, nGroups, &gpu_grpmaxidx_tmp);
00368   } else 
00369 #endif
00370   {
00371     find_groups_max_idx_cpu(segments, gaussian->get_image());
00372   }
00373 
00374   PROFILE_POP_RANGE();
00375 }
00376 
00377 
00378 template <typename GROUP_T, typename IMAGE_T>
00379 void ScaleSpaceFilter<GROUP_T, IMAGE_T>::find_groups_max_idx_cpu(GROUP_T* segments, IMAGE_T* voxel_values) {
00380   PROFILE_PUSH_RANGE("ScaleSpaceFilter::find_groups_max_idx_cpu()", 9);
00381 
00382   memset(max_idx, -1, nGroups * sizeof(long)); //TODO change -1 use for unsigned types
00383   for (long idx = 0; idx < nVoxels; ++idx) {
00384     const GROUP_T group_number = segments[idx];
00385     const long group_max_idx = max_idx[group_number];
00386     if (group_max_idx == -1) {
00387       max_idx[group_number] = idx;
00388     } else if (voxel_values[group_max_idx] < voxel_values[idx]) {
00389       max_idx[group_number] = idx;
00390     }
00391   }
00392 
00393   PROFILE_POP_RANGE();
00394 }
00395 
00396 
00397 #define FIND_STEEPEST(offset, _x, _y, _z) {\
00398   if (image[curr_idx + offset] > current_value) {\
00399     current_value = image[curr_idx + offset];\
00400     new_x = _x;\
00401     new_y = _y;\
00402     new_z = _z;\
00403   }\
00404 }
00405 
00406 template <typename GROUP_T, typename IMAGE_T>
00407 long ScaleSpaceFilter<GROUP_T, IMAGE_T>::find_local_maxima(long curr_idx, IMAGE_T* image) {
00408   PROFILE_PUSH_RANGE("ScaleSpaceFilter::find_local_maxima()", 10);
00409 
00410   long new_idx = curr_idx;
00411   int z = new_idx / heightWidth;
00412   long r = new_idx % heightWidth;
00413   int y = r / width;
00414   int x = r % width;
00415   do {
00416     // need x,y,z coords for bounds checking
00417     int new_x = x;
00418     int new_y = y;
00419     int new_z = z;
00420     curr_idx = new_idx;
00421     IMAGE_T current_value = image[curr_idx];
00422 
00423 #if 1
00424     if (x < width-1) {
00425       FIND_STEEPEST(px_offset, x+1, y, z);
00426 
00427       if (y < height-1) {
00428         FIND_STEEPEST(px_offset + py_offset, x+1, y+1, z);
00429       }
00430       if (y > 0) {
00431         FIND_STEEPEST(px_offset + ny_offset, x+1, y-1, z);
00432       }
00433 
00434       if (z < depth-1) {
00435         FIND_STEEPEST(px_offset + pz_offset, x+1, y, z+1);
00436       }
00437       if (z > 0) {
00438         FIND_STEEPEST(px_offset + nz_offset, x+1, y, z-1);
00439       }
00440     }
00441     if (x > 0) {
00442       FIND_STEEPEST(nx_offset, x-1, y, z);
00443 
00444       if (y < height-1) {
00445         FIND_STEEPEST(nx_offset + py_offset, x-1, y+1, z);
00446       }
00447       if (y > 0) {
00448         FIND_STEEPEST(nx_offset + ny_offset, x-1, y-1, z);
00449       }
00450 
00451       if (z < depth-1) {
00452         FIND_STEEPEST(nx_offset + pz_offset, x-1, y, z+1);
00453       }
00454       if (z > 0) {
00455         FIND_STEEPEST(nx_offset + nz_offset, x-1, y, z-1);
00456       }
00457     }
00458 
00459     if (y < height-1) {
00460       FIND_STEEPEST(py_offset, x, y+1, z);
00461       
00462       if (z < depth-1) {
00463         FIND_STEEPEST(py_offset + pz_offset, x, y+1, z+1);
00464       }
00465       if (z > 0) {
00466         FIND_STEEPEST(py_offset + nz_offset, x, y+1, z-1);
00467       }
00468     }
00469     if (y > 0) {
00470       FIND_STEEPEST(ny_offset, x, y-1, z);
00471 
00472       if (z < depth-1) {
00473         FIND_STEEPEST(ny_offset + pz_offset, x, y-1, z+1);
00474       }
00475       if (z > 0) {
00476         FIND_STEEPEST(ny_offset + nz_offset, x, y-1, z-1);
00477       }
00478     }
00479 
00480     if (z < depth-1) {
00481       FIND_STEEPEST(pz_offset, x, y, z+1);
00482     }
00483     if (z > 0) {
00484       FIND_STEEPEST(nz_offset, x, y, z-1);
00485     }
00486 #else
00487     if (x < width-1 && image[curr_idx + px_offset] > current_value) {
00488       current_value = image[curr_idx + px_offset];
00489       new_x = x + 1;
00490       new_y = y;
00491       new_z = z;
00492     }
00493     if (x > 0 && image[curr_idx + nx_offset] > current_value) {
00494       current_value = image[curr_idx + nx_offset];
00495       new_x = x - 1;
00496       new_y = y;
00497       new_z = z;
00498     }
00499 
00500     if (y < height-1 && image[curr_idx + py_offset] > current_value) {
00501       current_value = image[curr_idx + py_offset];
00502       new_x = x;
00503       new_y = y + 1;
00504       new_z = z;
00505     }
00506     if (y > 0 && image[curr_idx + ny_offset] > current_value) {
00507       current_value = image[curr_idx + ny_offset];
00508       new_x = x;
00509       new_y = y - 1;
00510       new_z = z;
00511     }
00512 
00513     if (z < depth-1 && image[curr_idx + pz_offset] > current_value) {
00514       current_value = image[curr_idx + pz_offset];
00515       new_x = x;
00516       new_y = y;
00517       new_z = z + 1;
00518     }
00519     if (z > 0 && image[curr_idx + nz_offset] > current_value) {
00520       current_value = image[curr_idx + nz_offset];
00521       new_x = x;
00522       new_y = y;
00523       new_z = z - 1;
00524     }
00525 #endif
00526 
00527     x = new_x;
00528     y = new_y;
00529     z = new_z;
00530     new_idx = z*heightWidth + y*long(width) + x;
00531     // Keep going until none of our neighbors have a higher value 
00532   } while (new_idx != curr_idx);
00533 
00534   PROFILE_POP_RANGE();
00535 
00536   return new_idx;
00537 }
00538 
00539 
00540 // template instantiation convenience macros
00541 #define INST_SCALE_SPACE(G_T) \
00542 template class ScaleSpaceFilter<G_T, float>;\
00543 template class ScaleSpaceFilter<G_T, unsigned short>;\
00544 template class ScaleSpaceFilter<G_T, unsigned char>;
00545 
00546 INST_SCALE_SPACE(unsigned long)
00547 INST_SCALE_SPACE(unsigned int)
00548 INST_SCALE_SPACE(unsigned short)
00549 
00550 
00551 
00552 

Generated on Fri Apr 19 02:45:20 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002