Skip to content

Commit 2e95a62

Browse files
authored
Merge pull request #179 from AdaptiveParticles/patch_178
- avoid overflow in addition of bspline offset for integer-type converters - modify handling of googletest - revise some examples: - fix an issue with `Example_reconstruct_image` where noise was added to the reconstructed image - use float converters in `Example_get_apr*` - add `-auto_parameters` to `Example_get_apr_by_block`
2 parents dca7e7f + 6b2a8f6 commit 2e95a62

23 files changed

+249
-229
lines changed

.github/workflows/cmake.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ jobs:
3737
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
3838

3939
steps:
40-
- uses: actions/checkout@v2
40+
- uses: actions/checkout@v4
4141
with:
4242
submodules: true
4343

4444
# Setup the build machine with the most recent versions of CMake and Ninja. Both are cached if not already: on subsequent runs both will be quickly restored from GitHub cache service.
4545
- uses: lukka/get-cmake@latest
4646
# Restore both vcpkg and its artifacts from the GitHub cache service.
4747
- name: Restore vcpkg and its artifacts.
48-
uses: actions/cache@v2
48+
uses: actions/cache@v3
4949
with:
5050
# The first path is where vcpkg generates artifacts while consuming the vcpkg.json manifest file.
5151
# The second path is the location of vcpkg (it contains the vcpkg executable and data files).
@@ -64,7 +64,7 @@ jobs:
6464
6565
- name: Install OpenMP dependencies with brew for OSX
6666
if: contains(matrix.os,'macos')
67-
run: brew install libomp #todo caching
67+
run: brew install libomp pkg-config #todo caching
6868

6969
- name: Show content of workspace after cache has been restored
7070
run: find $RUNNER_WORKSPACE
@@ -75,7 +75,7 @@ jobs:
7575
# Run CMake to generate Ninja project files, using the vcpkg's toolchain file to resolve and install the dependencies as specified in vcpkg.json.
7676
- name: Install dependencies and generate project files
7777
run: |
78-
cmake -S "${{ github.workspace }}" -B "${{ env.CMAKE_BUILD_DIR }}" -DCMAKE_TOOLCHAIN_FILE="${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake" -DAPR_BUILD_STATIC_LIB=ON -DAPR_BUILD_SHARED_LIB=OFF -DAPR_BUILD_EXAMPLES=ON -DAPR_TESTS=ON -DAPR_USE_CUDA=OFF -DAPR_PREFER_EXTERNAL_BLOSC=${{ matrix.extblosc }} -DAPR_PREFER_EXTERNAL_BLOSC=${{ matrix.extblosc }} -DAPR_USE_OPENMP=${{ matrix.openmp }} ${{ matrix.buildargs }}
78+
cmake -S "${{ github.workspace }}" -B "${{ env.CMAKE_BUILD_DIR }}" -DCMAKE_TOOLCHAIN_FILE="${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake" -DCMAKE_BUILD_TYPE=Release -DAPR_BUILD_STATIC_LIB=ON -DAPR_BUILD_SHARED_LIB=OFF -DAPR_BUILD_EXAMPLES=ON -DAPR_TESTS=ON -DAPR_USE_CUDA=OFF -DAPR_PREFER_EXTERNAL_BLOSC=${{ matrix.extblosc }} -DAPR_USE_OPENMP=${{ matrix.openmp }} ${{ matrix.buildargs }}
7979
8080
# Build the whole project with Ninja (which is spawn by CMake).
8181
- name: Build
@@ -85,7 +85,7 @@ jobs:
8585
# Run tests
8686
- name: tests
8787
run: |
88-
ctest --test-dir "${{ env.CMAKE_BUILD_DIR }}"
88+
ctest --test-dir "${{ env.CMAKE_BUILD_DIR }}" --output-on-failure
8989
9090
- name: Show content of workspace at its completion
9191
run: find $RUNNER_WORKSPACE

.gitmodules

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
[submodule "external/glm"]
22
path = external/glm
33
url = https://github.com/g-truc/glm.git
4-
[submodule "external/gtest"]
5-
path = external/gtest
6-
url = https://github.com/google/googletest
74
[submodule "external/c-blosc"]
85
path = external/c-blosc
96
url = https://github.com/Blosc/c-blosc

CMakeLists.txt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
###############################################################################
22
# APR - Adaptive Particle Representation
33
###############################################################################
4-
cmake_minimum_required(VERSION 3.10)
4+
cmake_minimum_required(VERSION 3.14)
55
project(APR DESCRIPTION "Adaptive Particle Representation library")
66

77
message(STATUS "CMAKE VERSION ${CMAKE_VERSION}")
88

99
set(CMAKE_CXX_STANDARD 14)
1010
set(CMAKE_CXX_STANDARD_REQUIRED ON)
1111

12+
if(POLICY CMP0135)
13+
cmake_policy(SET CMP0135 NEW)
14+
endif(POLICY CMP0135)
15+
1216
# APR build options:
1317
option(APR_INSTALL "Install APR library" OFF)
1418
option(APR_BUILD_SHARED_LIB "Builds shared library" OFF)
@@ -17,7 +21,7 @@ option(APR_BUILD_EXAMPLES "Build APR examples" OFF)
1721
option(APR_USE_LIBTIFF "Use LibTIFF" ON)
1822
option(APR_TESTS "Build APR tests" OFF)
1923
option(APR_PREFER_EXTERNAL_GTEST "When found, use the installed GTEST libs instead of included sources" ON)
20-
option(APR_PREFER_EXTERNAL_BLOSC "When found, use the installed BLOSC libs instead of included sources" ON)
24+
option(APR_PREFER_EXTERNAL_BLOSC "When found, use the installed BLOSC libs instead of included sources" OFF)
2125
option(APR_USE_CUDA "should APR use CUDA? (experimental - under development)" OFF)
2226
option(APR_USE_OPENMP "should APR use OpenMP?" ON)
2327
option(APR_BENCHMARK "add benchmarking code" OFF)
@@ -359,13 +363,24 @@ if(APR_TESTS)
359363
endif()
360364
if(GTEST_FOUND)
361365
include_directories(${GTEST_INCLUDE_DIRS})
366+
message(STATUS "Gtest found: ${GTEST_INCLUDE_DIRS}")
362367
else(GTEST_FOUND)
368+
message(STATUS "APR: GTest not found, it will be downloaded and built")
369+
370+
include(FetchContent)
371+
FetchContent_Declare(
372+
googletest
373+
# Specify the commit you depend on and update it regularly.
374+
URL https://github.com/google/googletest/archive/refs/tags/v1.13.0.zip
375+
)
376+
377+
# For Windows: Prevent overriding the parent project's compiler/linker settings
378+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
363379
set(BUILD_GMOCK OFF CACHE BOOL "" FORCE)
364-
set(BUILD_GTEST ON CACHE BOOL "" FORCE)
365380
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
366-
message(STATUS "APR: GTest not found, using internal gtest")
367-
add_subdirectory("external/gtest")
368-
set(GTEST_LIBRARIES gtest)
381+
FetchContent_MakeAvailable(googletest)
382+
383+
set(GTEST_LIBRARIES GTest::gtest_main)
369384
endif(GTEST_FOUND)
370385

371386
enable_testing()

examples/Example_get_apr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ int runAPR(cmdLineOptions options) {
4848
//the apr datastructure
4949
APR apr;
5050

51-
APRConverter<uint16_t> aprConverter;
51+
APRConverter<float> aprConverter;
5252

5353
//read in the command line options into the parameters file
5454
aprConverter.par.Ip_th = options.Ip_th;

examples/Example_get_apr_by_block.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ must be used. The exact influence of this has not yet been studied.
4444
int runAPR(cmdLineOptions options) {
4545

4646
APR apr;
47-
APRConverterBatch<uint16_t> aprConverter;
47+
APRConverterBatch<float> aprConverter;
4848

4949
//read in the command line options into the parameters file
5050
aprConverter.par.Ip_th = options.Ip_th;
@@ -55,6 +55,7 @@ int runAPR(cmdLineOptions options) {
5555
aprConverter.par.neighborhood_optimization = options.neighborhood_optimization;
5656
aprConverter.par.output_steps = options.output_steps;
5757
aprConverter.par.grad_th = options.grad_th;
58+
aprConverter.par.auto_parameters = options.auto_parameters;
5859

5960
//where things are
6061
aprConverter.par.input_image_name = options.input;
@@ -234,7 +235,6 @@ cmdLineOptions read_command_line_options(int argc, char **argv){
234235
if(command_option_exists(argv, argv + argc, "-neighborhood_optimization_off"))
235236
{
236237
result.neighborhood_optimization = false;
237-
238238
}
239239

240240
if(command_option_exists(argv, argv + argc, "-output_steps"))
@@ -247,5 +247,10 @@ cmdLineOptions read_command_line_options(int argc, char **argv){
247247
result.store_tree = true;
248248
}
249249

250+
if(command_option_exists(argv, argv + argc, "-auto_parameters"))
251+
{
252+
result.auto_parameters = true;
253+
}
254+
250255
return result;
251256
}

examples/Example_get_apr_by_block.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ struct cmdLineOptions{
3030
float rel_error = 0.1;
3131
float grad_th = 1;
3232

33+
bool auto_parameters = false;
34+
3335
int z_block_size = 128;
3436
int z_ghost = 16; // number of "ghost slices" to use in the APR pipeline
3537
int z_ghost_sampling = 64; // number of "ghost slices" to use when sampling intensities

examples/Example_reconstruct_image.cpp

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ struct cmdLineOptions{
4747
bool output_spatial_properties = false;
4848
bool output_pc_recon = false;
4949
bool output_smooth_recon = false;
50-
50+
float gaussian_noise_sigma = 0.0f;
5151
};
5252

5353
static bool command_option_exists(char **begin, char **end, const std::string &option) {
@@ -99,6 +99,10 @@ static cmdLineOptions read_command_line_options(int argc, char **argv) {
9999
result.output_spatial_properties = true;
100100
}
101101

102+
if(command_option_exists(argv, argv + argc, "-noise")) {
103+
result.gaussian_noise_sigma = std::stof(std::string(get_command_option(argv, argv + argc, "-noise")));
104+
}
105+
102106
if(!(result.output_pc_recon || result.output_smooth_recon || result.output_spatial_properties)){
103107
//default is pc recon
104108
result.output_pc_recon = true;
@@ -107,7 +111,7 @@ static cmdLineOptions read_command_line_options(int argc, char **argv) {
107111
return result;
108112
}
109113
template<typename T>
110-
void add_random_to_img(PixelData<T>& img,float sd){
114+
void add_random_to_img(PixelData<T>& img, float sd){
111115

112116
std::default_random_engine generator;
113117
std::normal_distribution<float> distribution(0.0,sd);
@@ -149,32 +153,26 @@ int main(int argc, char **argv) {
149153
apr.name = options.output;
150154
timer.stop_timer();
151155

152-
// Intentionaly block-scoped since local recon_pc will be destructed when block ends and release memory.
153-
{
154-
155-
if(options.output_pc_recon) {
156-
//create mesh data structure for reconstruction
157-
bool add_random_gitter = true;
158-
159-
PixelData<uint16_t> recon_pc;
156+
if(options.output_pc_recon) {
157+
//create mesh data structure for reconstruction
158+
PixelData<uint16_t> recon_pc;
160159

161-
timer.start_timer("pc interp");
162-
//perform piece-wise constant interpolation
163-
APRReconstruction::reconstruct_constant(apr,recon_pc, parts);
164-
timer.stop_timer();
160+
timer.start_timer("pc interp");
161+
//perform piece-wise constant interpolation
162+
APRReconstruction::reconstruct_constant(apr, recon_pc, parts);
163+
timer.stop_timer();
165164

166-
if(add_random_gitter){
167-
add_random_to_img(recon_pc,1.0f);
168-
}
165+
if(options.gaussian_noise_sigma > 0.0f) {
166+
add_random_to_img(recon_pc, options.gaussian_noise_sigma);
167+
}
169168

170-
float elapsed_seconds = timer.t2 - timer.t1;
171-
std::cout << "PC recon "
172-
<< (recon_pc.x_num * recon_pc.y_num * recon_pc.z_num * 2) / (elapsed_seconds * 1000000.0f)
173-
<< " MB per second" << std::endl;
169+
float elapsed_seconds = timer.t2 - timer.t1;
170+
std::cout << "PC recon "
171+
<< (recon_pc.x_num * recon_pc.y_num * recon_pc.z_num * 2) / (elapsed_seconds * 1000000.0f)
172+
<< " MB per second" << std::endl;
174173

175-
// write output as tiff
176-
TiffUtils::saveMeshAsTiff(options.directory + apr.name + "_pc.tif", recon_pc);
177-
}
174+
// write output as tiff
175+
TiffUtils::saveMeshAsTiff(options.directory + apr.name + "_pc.tif", recon_pc);
178176
}
179177

180178
//////////////////////////

external/c-blosc

Submodule c-blosc updated 438 files

external/gtest

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/algorithm/APRConverter.hpp

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ class APRConverter {
9999
return true;
100100
}
101101

102+
float bspline_offset = 0;
103+
104+
102105
protected:
103106

104107
template<typename T>
@@ -108,8 +111,6 @@ class APRConverter {
108111

109112
//get apr without setting parameters, and with an already loaded image.
110113

111-
float bspline_offset = 0;
112-
113114
//DATA (so it can be re-used)
114115

115116
PixelData<ImageType> grad_temp; // should be a down-sampled image
@@ -133,26 +134,6 @@ class APRConverter {
133134
};
134135

135136

136-
template <typename T>
137-
struct MinMax{T min; T max; };
138-
139-
template <typename T>
140-
static MinMax<T> getMinMax(const PixelData<T>& input_image) {
141-
T minVal = std::numeric_limits<T>::max();
142-
T maxVal = std::numeric_limits<T>::min();
143-
144-
#ifdef HAVE_OPENMP
145-
#pragma omp parallel for default(shared) reduction(max:maxVal) reduction(min:minVal)
146-
#endif
147-
for (size_t i = 0; i < input_image.mesh.size(); ++i) {
148-
T val = input_image.mesh[i];
149-
if (val > maxVal) maxVal = val;
150-
if (val < minVal) minVal = val;
151-
}
152-
153-
return MinMax<T>{minVal, maxVal};
154-
}
155-
156137
template<typename ImageType>
157138
void APRConverter<ImageType>::initPipelineMemory(int y_num,int x_num,int z_num){
158139
//initializes the internal memory to be used in the pipeline.
@@ -224,7 +205,7 @@ void APRConverter<ImageType>::computeL(APR& aAPR,PixelData<T>& input_image){
224205
//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
225206
//storage of the particle cell tree for computing the pulling scheme
226207
allocation_timer.start_timer("init and copy image");
227-
PixelData<ImageType> image_temp(input_image, false /* don't copy */, true /* pinned memory */); // global image variable useful for passing between methods, or re-using memory (should be the only full sized copy of the image)
208+
PixelData<ImageType> image_temp(input_image, false /* don't copy */, false /* pinned memory */); // global image variable useful for passing between methods, or re-using memory (should be the only full sized copy of the image)
228209

229210
allocation_timer.stop_timer();
230211

@@ -233,17 +214,12 @@ void APRConverter<ImageType>::computeL(APR& aAPR,PixelData<T>& input_image){
233214
////////////////////////
234215

235216
fine_grained_timer.start_timer("offset image");
236-
//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!)
237-
// 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)
238217

239-
if (std::is_same<uint16_t, ImageType>::value) {
240-
bspline_offset = 100;
241-
image_temp.copyFromMeshWithUnaryOp(input_image, [=](const auto &a) { return (a + bspline_offset); });
242-
} else if (std::is_same<uint8_t, ImageType>::value){
243-
bspline_offset = 5;
244-
image_temp.copyFromMeshWithUnaryOp(input_image, [=](const auto &a) { return (a + bspline_offset); });
245-
} else {
218+
if (std::is_floating_point<ImageType>::value) {
246219
image_temp.copyFromMesh(input_image);
220+
} else {
221+
bspline_offset = compute_bspline_offset<ImageType>(input_image, par.lambda);
222+
image_temp.copyFromMeshWithUnaryOp(input_image, [=](const auto &a) { return (a + bspline_offset); });
247223
}
248224

249225
fine_grained_timer.stop_timer();
@@ -276,6 +252,8 @@ void APRConverter<ImageType>::applyParameters(APR& aAPR,APRParameters& aprParame
276252
// Apply the main parameters
277253
//
278254

255+
aprParameters.validate_parameters();
256+
279257
fine_grained_timer.start_timer("load_and_apply_mask");
280258
// Apply mask if given
281259
if(par.mask_file != ""){

src/algorithm/APRConverterBatch.hpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,17 +214,14 @@ bool APRConverterBatch<ImageType>::get_apr_method_patch(APR &aAPR, PixelData<T>&
214214
computation_timer.start_timer("Calculations");
215215

216216
fine_grained_timer.start_timer("offset image");
217-
//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!)
218-
// 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)
219-
if (std::is_same<uint16_t, ImageType>::value) {
220-
bspline_offset = 100;
221-
image_temp.copyFromMeshWithUnaryOp(input_image, [=](const auto &a) { return (a + bspline_offset); });
222-
} else if (std::is_same<uint8_t, ImageType>::value){
223-
bspline_offset = 5;
224-
image_temp.copyFromMeshWithUnaryOp(input_image, [=](const auto &a) { return (a + bspline_offset); });
225-
} else {
217+
218+
if (std::is_floating_point<ImageType>::value) {
226219
image_temp.copyFromMesh(input_image);
220+
} else {
221+
bspline_offset = compute_bspline_offset<ImageType>(input_image, par.lambda);
222+
image_temp.copyFromMeshWithUnaryOp(input_image, [=](const auto &a) { return (a + bspline_offset); });
227223
}
224+
228225
fine_grained_timer.stop_timer();
229226

230227
method_timer.start_timer("compute_gradient_magnitude_using_bsplines");
@@ -376,6 +373,8 @@ template<typename ImageType>
376373
void APRConverterBatch<ImageType>::applyParameters(PixelData<ImageType> &grad_temp, PixelData<float> &local_scale_temp,
377374
PixelData<float> &local_scale_temp2, APRParameters& aprParameters) {
378375

376+
aprParameters.validate_parameters();
377+
379378
fine_grained_timer.start_timer("load_and_apply_mask");
380379
// Apply mask if given
381380
if(par.mask_file != ""){

src/algorithm/APRParameters.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ class APRParameters {
6161

6262
return os;
6363
}
64+
65+
void validate_parameters(){
66+
if (sigma_th == 0){
67+
std::cerr << "Warning: sigma_th is set to 0, this may result in unexpected results due to divide by zero errors. Consider setting this to a non-zero small value, if it is not needed." << std::endl;
68+
}
69+
}
6470
};
6571

6672

0 commit comments

Comments
 (0)