00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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
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
00268 }
00269
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));
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
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
00532 } while (new_idx != curr_idx);
00533
00534 PROFILE_POP_RANGE();
00535
00536 return new_idx;
00537 }
00538
00539
00540
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