Skip to content

Commit d0c5b7d

Browse files
committed
Merge branch 'newTestForGradient' into develop
2 parents aa793bf + e4da85b commit d0c5b7d

File tree

6 files changed

+243
-61
lines changed

6 files changed

+243
-61
lines changed

src/algorithm/APRConverter.hpp

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,13 @@ bool APRConverter<ImageType>::get_apr_method(APR<ImageType> &aAPR, MeshData<T>&
104104

105105
//assuming uint16, the total memory cost shoudl be approximately (1 + 1 + 1/8 + 2/8 + 2/8) = 2 5/8 original image size in u16bit
106106
//storage of the particle cell tree for computing the pulling scheme
107-
MeshData<ImageType> image_temp; // global image variable useful for passing between methods, or re-using memory (should be the only full sized copy of the image)
108-
MeshData<ImageType> grad_temp; // should be a down-sampled image
109-
MeshData<float> local_scale_temp; // Used as down-sampled images for some averaging steps where it is useful to not lose precision, or get over-flow errors
110-
MeshData<float> local_scale_temp2;
111-
112107
allocation_timer.start_timer("init and copy image");
113-
image_temp.init(input_image);
108+
MeshData<ImageType> image_temp(input_image, false /* don't copy */); // global image variable useful for passing between methods, or re-using memory (should be the only full sized copy of the image)
109+
MeshData<ImageType> grad_temp; // should be a down-sampled image
114110
grad_temp.initDownsampled(input_image.y_num, input_image.x_num, input_image.z_num, 0);
111+
MeshData<float> local_scale_temp; // Used as down-sampled images for some averaging steps where it is useful to not lose precision, or get over-flow errors
115112
local_scale_temp.initDownsampled(input_image.y_num, input_image.x_num, input_image.z_num);
113+
MeshData<float> local_scale_temp2;
116114
local_scale_temp2.initDownsampled(input_image.y_num, input_image.x_num, input_image.z_num);
117115
allocation_timer.stop_timer();
118116

@@ -126,7 +124,7 @@ bool APRConverter<ImageType>::get_apr_method(APR<ImageType> &aAPR, MeshData<T>&
126124
//offset image by factor (this is required if there are zero areas in the background with uint16_t and uint8_t images, as the Bspline co-efficients otherwise may be negative!)
127125
// Warning both of these could result in over-flow (if your image is non zero, with a 'buffer' and has intensities up to uint16_t maximum value then set image_type = "", i.e. uncomment the following line)
128126
float bspline_offset = 0;
129-
if(std::is_same<uint16_t, ImageType>::value){
127+
if (std::is_same<uint16_t, ImageType>::value) {
130128
bspline_offset = 100;
131129
image_temp.copyFromMeshWithUnaryOp(input_image, [=](const auto &a) { return (a + bspline_offset); });
132130
} else if (std::is_same<uint8_t, ImageType>::value){
@@ -227,15 +225,10 @@ void APRConverter<ImageType>::get_local_particle_cell_set(MeshData<ImageType> &g
227225

228226
template<typename ImageType>
229227
void APRConverter<ImageType>::get_gradient(MeshData<ImageType> &image_temp, MeshData<ImageType> &grad_temp, MeshData<float> &local_scale_temp, MeshData<float> &local_scale_temp2, float bspline_offset) {
230-
//
231228
// Bevan Cheeseman 2018
232-
//
233229
// Calculate the gradient from the input image. (You could replace this method with your own)
234-
//
235230
// Input: full sized image.
236-
//
237231
// Output: down-sampled by 2 gradient magnitude (Note, the gradient is calculated at pixel level then maximum down sampled within the loops below)
238-
//
239232

240233
fine_grained_timer.verbose_flag = false;
241234

src/algorithm/ComputeGradient.hpp

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#define PARTPLAY_GRADIENT_HPP
77

88
#include "src/data_structures/Mesh/MeshData.hpp"
9+
#include "src/io/TiffUtils.hpp"
10+
911
#ifdef HAVE_OPENMP
1012
#include "omp.h"
1113
#endif
@@ -49,9 +51,9 @@ class ComputeGradient {
4951

5052
// Gradient computation
5153

52-
template<typename T, typename S>
54+
template<typename S>
5355
void
54-
calc_bspline_fd_ds_mag(MeshData<T> &input, MeshData<S> &grad, const float hx, const float hy, const float hz);
56+
calc_bspline_fd_ds_mag(const MeshData<S> &input, MeshData<S> &grad, const float hx, const float hy, const float hz);
5557

5658
template<typename T,typename S>
5759
void mask_gradient(MeshData<T>& grad_ds,MeshData<S>& temp_ds,MeshData<T>& temp_full,APRParameters& par);
@@ -719,74 +721,85 @@ void ComputeGradient::get_smooth_bspline_3D(MeshData<T>& input,APRParameters& pa
719721
spline_timer.stop_timer();
720722
}
721723

722-
template<typename T,typename S>
723-
void ComputeGradient::calc_bspline_fd_ds_mag(MeshData<T> &input, MeshData<S> &grad, const float hx, const float hy,const float hz){
724-
//
725-
// Bevan Cheeseman 2016
726-
//
727-
// Calculate fd filt, for xgrad with bsplines
728-
724+
/**
725+
* Calculates downsampled gradient (maximum magnitude) with 'replicate' boundary approach (nearest border value)
726+
* @param input - input mesh
727+
* @param grad - output gradient (must be initialized)
728+
* @param hx - step in x dir
729+
* @param hy - step in y dir
730+
* @param hz - step in z dir
731+
*/
732+
template<typename S>
733+
void ComputeGradient::calc_bspline_fd_ds_mag(const MeshData<S> &input, MeshData<S> &grad, const float hx, const float hy,const float hz) {
729734
const size_t z_num = input.z_num;
730735
const size_t x_num = input.x_num;
731736
const size_t y_num = input.y_num;
732737

733738
const size_t x_num_ds = grad.x_num;
734739
const size_t y_num_ds = grad.y_num;
735740

736-
const float a1 = -1.0/2.0;
737-
const float a3 = 1.0/2.0;
738-
739-
std::vector<S> temp(y_num,0);
741+
std::vector<S> temp(y_num, 0);
740742
const size_t xnumynum = x_num * y_num;
741743

742744
#ifdef HAVE_OPENMP
743-
#pragma omp parallel for default(shared)firstprivate(temp)
745+
#pragma omp parallel for default(shared) firstprivate(temp)
744746
#endif
745-
for (size_t j = 0;j < z_num; ++j) {
746-
S *temp_vec_1 = input.mesh.begin() + j*x_num*y_num + 1*y_num;
747-
S *temp_vec_2 = input.mesh.begin() + j*x_num*y_num;
747+
for (size_t z = 0; z < z_num; ++z) {
748+
// Belows pointers up, down... are forming stencil in X (left <-> right) and Z ( up <-> down) direction and
749+
// are pointing to whole Y column. If out of bounds then 'replicate' (nearest array border value) approach is used.
750+
//
751+
// up
752+
// ... left center right ...
753+
// down
754+
const S *left = input.mesh.begin() + z * xnumynum + 0 * y_num; // boundary value is chosen
755+
const S *center = input.mesh.begin() + z * xnumynum + 0 * y_num;
748756

749757
//LHS boundary condition is accounted for wiht this initialization
750-
const size_t j_m = j > 0 ? j - 1 : 0;
751-
const size_t j_p = std::min(z_num-1, j+1);
758+
const size_t zMinus = z > 0 ? z - 1 : 0 /* boundary */;
759+
const size_t zPlus = std::min(z + 1, z_num - 1 /* boundary */);
752760

753-
for (size_t i = 0; i < x_num-1; ++i) {
754-
S *temp_vec_4 = input.mesh.begin() + j_m*xnumynum + i * y_num;
755-
S *temp_vec_5 = input.mesh.begin() + j_p*xnumynum + i * y_num;
756-
S *temp_vec_3 = input.mesh.begin() + j*x_num*y_num + (i+1)*y_num;
761+
for (size_t x = 0; x < x_num; ++x) {
762+
const S *up = input.mesh.begin() + zMinus * xnumynum + x * y_num;
763+
const S *down = input.mesh.begin() + zPlus * xnumynum + x * y_num;
764+
const size_t xPlus = std::min(x + 1, x_num - 1 /* boundary */);
765+
const S *right = input.mesh.begin() + z * xnumynum + xPlus * y_num;
757766

758767
//compute the boundary values
759-
temp[0] = sqrt(pow((a1*temp_vec_1[0] + a3*temp_vec_3[0])/hx,2.0) + pow((a1*temp_vec_4[0] + a3*temp_vec_5[0])/hz,2.0));
768+
if (y_num >= 2) {
769+
temp[0] = sqrt(pow((right[0] - left[0]) / (2 * hx), 2.0) + pow((down[0] - up[0]) / (2 * hz), 2.0) + pow((center[1] - center[0 /* boundary */]) / (2 * hy), 2.0));
770+
temp[y_num - 1] = sqrt(pow((right[y_num - 1] - left[y_num - 1]) / (2 * hx), 2.0) + pow((down[y_num - 1] - up[y_num - 1]) / (2 * hz), 2.0) + pow((center[y_num - 1 /* boundary */] - center[y_num - 2]) / (2 * hy), 2.0));
771+
}
772+
else {
773+
temp[0] = 0; // same values minus same values in x/y/z
774+
}
760775

761-
//do the y gradient
776+
//do the y gradient in range 1..y_num-2
762777
#ifdef HAVE_OPENMP
763-
#pragma omp simd
778+
#pragma omp simd
764779
#endif
765-
for (size_t k = 1; k < (y_num-1); ++k) {
766-
temp[k] = sqrt(pow((a1*temp_vec_4[k] + a3*temp_vec_5[k])/hz,2.0) + pow((a1*temp_vec_2[k-1] + a3*temp_vec_2[k+1])/hy,2.0) + pow((a1*temp_vec_1[k] + a3*temp_vec_3[k])/hx,2.0));
780+
for (size_t y = 1; y < y_num - 1; ++y) {
781+
temp[y] = sqrt(pow((right[y] - left[y]) / (2 * hx), 2.0) + pow((down[y] - up[y]) / (2 * hz), 2.0) + pow((center[y + 1] - center[y - 1]) / (2 * hy), 2.0));
767782
}
768783

769-
temp[y_num - 1] = sqrt(pow((a1*temp_vec_1[(y_num-1)] + a3*temp_vec_3[(y_num-1)])/hx,2.0) + pow((a1*temp_vec_4[(y_num-1)] + a3*temp_vec_5[(y_num-1)])/hz,2.0));
770-
771-
int64_t j_2 = j/2;
772-
int64_t i_2 = i/2;
773-
774-
for (size_t k = 0; k < (y_num_ds); ++k) {
775-
size_t k_s = std::min(2*k+1, y_num-1);
776-
const size_t idx = j_2 * x_num_ds * y_num_ds + i_2 * y_num_ds + k;
784+
// Set as a downsampled gradient maximum from 2x2x2 gradient cubes
785+
int64_t z_2 = z / 2;
786+
int64_t x_2 = x / 2;
787+
for (size_t k = 0; k < y_num_ds; ++k) {
788+
size_t k_s = std::min(2 * k + 1, y_num - 1);
789+
const size_t idx = z_2 * x_num_ds * y_num_ds + x_2 * y_num_ds + k;
777790
grad.mesh[idx] = std::max(temp[2 * k], std::max(temp[k_s], grad.mesh[idx]));
778791
}
779792

780-
std::swap(temp_vec_1, temp_vec_2);
781-
std::swap(temp_vec_2, temp_vec_3);
793+
// move left, center to current center, right (both +1 to right)
794+
std::swap(left, center);
795+
std::swap(center, right);
782796
}
783797
}
784798
}
785799

786-
/*
800+
/**
787801
* Caclulation of signal value from B-Spline co-efficients
788802
*/
789-
790803
template<typename T>
791804
void ComputeGradient::calc_inv_bspline_y(MeshData<T>& input){
792805
//

src/data_structures/Mesh/MeshData.hpp

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <cmath>
1818
#include <memory>
1919
#include <sstream>
20+
#include <iostream>
21+
#include <iomanip>
2022

2123
#include "src/misc/APRTimer.hpp"
2224

@@ -306,6 +308,33 @@ public :
306308
init(y_num_ds, x_num_ds, z_num_ds, aInitVal);
307309
}
308310

311+
/**
312+
* Initializes mesh with size of half of provided mesh dimensions (rounding up if not divisible by 2)
313+
* @param aMesh - mesh used to get dimensions
314+
*/
315+
template <typename U>
316+
void initDownsampled(const MeshData<U> &aMesh) {
317+
const int z_num_ds = ceil(1.0*aMesh.z_num/2.0);
318+
const int x_num_ds = ceil(1.0*aMesh.x_num/2.0);
319+
const int y_num_ds = ceil(1.0*aMesh.y_num/2.0);
320+
321+
init(y_num_ds, x_num_ds, z_num_ds);
322+
}
323+
324+
/**
325+
* Initializes mesh with size of half of provided mesh dimensions (rounding up if not divisible by 2) and initialize values
326+
* @param aMesh - mesh used to get dimensions
327+
* @param aInitVal
328+
*/
329+
template <typename U>
330+
void initDownsampled(const MeshData<U> &aMesh, T aInitVal) {
331+
const int z_num_ds = ceil(1.0*aMesh.z_num/2.0);
332+
const int x_num_ds = ceil(1.0*aMesh.x_num/2.0);
333+
const int y_num_ds = ceil(1.0*aMesh.y_num/2.0);
334+
335+
init(y_num_ds, x_num_ds, z_num_ds, aInitVal);
336+
}
337+
309338
/**
310339
* Swaps data of meshes this <-> aObj
311340
* @param aObj
@@ -369,18 +398,32 @@ public :
369398
}
370399

371400
/**
372-
* Prints X-Y planes of mesh (for debug/test purposses - use only on small meshes)
401+
* Prints X-Y or X-Z planes of mesh (for debug/test purposses - use only on small meshes)
373402
*/
374-
void printMesh() const {
375-
for (size_t z = 0; z < z_num; ++z) {
376-
std::cout << "z=" << z << "\n";
403+
void printMesh(int aColumnWidth, bool aXYplanes = true) const {
404+
if (aXYplanes) {
405+
for (size_t z = 0; z < z_num; ++z) {
406+
std::cout << "z=" << z << "\n";
407+
for (size_t y = 0; y < y_num; ++y) {
408+
for (size_t x = 0; x < x_num; ++x) {
409+
std::cout << std::setw(aColumnWidth) << at(y, x, z) << " ";
410+
}
411+
std::cout << "\n";
412+
}
413+
std::cout << std::endl;
414+
}
415+
}
416+
else { // X-Z planes
377417
for (size_t y = 0; y < y_num; ++y) {
378-
for (size_t x = 0; x < x_num; ++x) {
379-
std::cout << at(y, x, z) << " ";
418+
std::cout << "y=" << y << "\n";
419+
for (size_t z = 0; z < z_num; ++z) {
420+
for (size_t x = 0; x < x_num; ++x) {
421+
std::cout << std::setw(aColumnWidth) << at(y, x, z) << " ";
422+
}
423+
std::cout << "\n";
380424
}
381-
std::cout << "\n";
425+
std::cout << std::endl;
382426
}
383-
std::cout << std::endl;
384427
}
385428
}
386429

test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
add_executable(testMeshData MeshDataTest.cpp)
22
add_executable(testTiff TiffTest.cpp)
33
add_executable(testAPR APRTest.cpp)
4+
add_executable(testComputeGradient ComputeGradientTest.cpp)
45

56
target_link_libraries(testMeshData ${GTEST_LIBRARIES} libapr)
67
target_link_libraries(testTiff ${GTEST_LIBRARIES} libapr)
78
target_link_libraries(testAPR ${GTEST_LIBRARIES} libapr)
9+
target_link_libraries(testComputeGradient ${GTEST_LIBRARIES} libapr)

test/ComputeGradientTest.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Created by Krzysztof Gonciarz 2018
3+
*/
4+
5+
#include <gtest/gtest.h>
6+
#include "src/data_structures/Mesh/MeshData.hpp"
7+
#include "src/algorithm/ComputeGradient.hpp"
8+
9+
namespace {
10+
/**
11+
* Compares mesh with provided data
12+
* @param mesh
13+
* @param data - data with [Z][Y][X] structure
14+
* @return true if same
15+
*/
16+
template <typename T>
17+
bool compare(MeshData<T> &mesh, const float *data, const float epsilon) {
18+
size_t dataIdx = 0;
19+
for (size_t z = 0; z < mesh.z_num; ++z) {
20+
for (size_t y = 0; y < mesh.y_num; ++y) {
21+
for (size_t x = 0; x < mesh.x_num; ++x) {
22+
bool v = std::abs(mesh(y, x, z) - data[dataIdx]) < epsilon;
23+
if (v == false) {
24+
std::cerr << "Mesh and expected data differ. First place at (Y, X, Z) = " << y << ", " << x << ", " << z << ") " << mesh(y, x, z) << " vs " << data[dataIdx] << std::endl;
25+
return false;
26+
}
27+
++dataIdx;
28+
}
29+
}
30+
}
31+
return true;
32+
}
33+
34+
TEST(ComputeGradientTest, 2D_XY) {
35+
{ // Corner points
36+
MeshData<float> m(6, 6, 1, 0);
37+
// expect gradient is 3x3 X/Y plane
38+
float expect[] = {1.41, 0, 4.24,
39+
0, 0, 0,
40+
2.82, 0, 5.65};
41+
// put values in corners
42+
m(0, 0, 0) = 2;
43+
m(5, 0, 0) = 4;
44+
m(0, 5, 0) = 6;
45+
m(5, 5, 0) = 8;
46+
MeshData<float> grad;
47+
grad.initDownsampled(m, 0);
48+
ComputeGradient cg;
49+
cg.calc_bspline_fd_ds_mag(m, grad, 1, 1, 1);
50+
ASSERT_TRUE(compare(grad, expect, 0.01));
51+
}
52+
{ // In the middle
53+
MeshData<float> m(6, 6, 1, 0);
54+
// expect gradient is 3x3 X/Y plane
55+
float expect[] = {1, 1, 0,
56+
1, 0, 0,
57+
0, 0, 0};
58+
// put values in corners
59+
m(1, 1, 0) = 2;
60+
MeshData<float> grad;
61+
grad.initDownsampled(m, 0);
62+
ComputeGradient cg;
63+
cg.calc_bspline_fd_ds_mag(m, grad, 1, 1, 1);
64+
ASSERT_TRUE(compare(grad, expect, 0.01));
65+
}
66+
{ // One pixel image 1x1x1
67+
MeshData<float> m(1, 1, 1, 0);
68+
// expect gradient is 3x3 X/Y plane
69+
float expect[] = {0};
70+
// put values in corners
71+
m(0, 0, 0) = 2;
72+
MeshData<float> grad;
73+
grad.initDownsampled(m, 0);
74+
ComputeGradient cg;
75+
cg.calc_bspline_fd_ds_mag(m, grad, 1, 1, 1);
76+
ASSERT_TRUE(compare(grad, expect, 0.01));
77+
}
78+
79+
}
80+
81+
TEST(ComputeGradientTest, Corners3D) {
82+
MeshData<float> m(6, 6, 4, 0);
83+
// expect gradient is 3x3x2 X/Y/Z plane
84+
float expect[] = { 1.73, 0, 5.19,
85+
0, 0, 0,
86+
3.46, 0, 6.92,
87+
88+
8.66, 0, 12.12,
89+
0, 0, 0,
90+
10.39, 0, 13.85 };
91+
// put values in corners
92+
m(0, 0, 0) = 2;
93+
m(5, 0, 0) = 4;
94+
m(0, 5, 0) = 6;
95+
m(5, 5, 0) = 8;
96+
m(0, 0, 3) = 10;
97+
m(5, 0, 3) = 12;
98+
m(0, 5, 3) = 14;
99+
m(5, 5, 3) = 16;
100+
101+
MeshData<float> grad;
102+
grad.initDownsampled(m, 0);
103+
ComputeGradient cg;
104+
cg.calc_bspline_fd_ds_mag(m, grad, 1, 1, 1);
105+
ASSERT_TRUE(compare(grad, expect, 0.01));
106+
}
107+
}
108+
109+
int main(int argc, char **argv) {
110+
testing::InitGoogleTest(&argc, argv);
111+
return RUN_ALL_TESTS();
112+
}

0 commit comments

Comments
 (0)