Skip to content

Commit 4e05865

Browse files
authored
Merge pull request #41 from ebertin/feature/dft_background
Use DFT convolution for kernel > 5x5
2 parents 9661fcf + 495f27a commit 4e05865

File tree

25 files changed

+662
-103
lines changed

25 files changed

+662
-103
lines changed

SEBenchmarks/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ elements_subdir(SEBenchmarks)
1515
#===============================================================================
1616
elements_depends_on_subdirs(ModelFitting)
1717
elements_depends_on_subdirs(SEFramework)
18+
elements_depends_on_subdirs(SEImplementation)
1819

1920
#===============================================================================
2021
# Add the find_package macro (a pure CMake command) here to locate the
@@ -48,6 +49,8 @@ endif ()
4849
#===============================================================================
4950
elements_add_executable(BenchConvolution src/program/BenchConvolution.cpp
5051
LINK_LIBRARIES ModelFitting SEFramework ${Boost_LIBRARIES} ${OpenCV_LIBS})
52+
elements_add_executable(BenchBackgroundConvolution src/program/BenchBackgroundConvolution.cpp
53+
LINK_LIBRARIES SEFramework SEImplementation ${Boost_LIBRARIES})
5154

5255
#===============================================================================
5356
# Declare the Boost tests here
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright (C) 2012-2020 Euclid Science Ground Segment
3+
*
4+
* This library is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU Lesser General Public License as published by the Free
6+
* Software Foundation; either version 3.0 of the License, or (at your option)
7+
* any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11+
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12+
* details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this library; if not, write to the Free Software Foundation, Inc.,
16+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
/**
20+
* @file src/program/BenchBackgroundConvolution.cpp
21+
* @date 27/03/19
22+
* @author aalvarez
23+
*/
24+
25+
#include <map>
26+
#include <string>
27+
28+
#include <boost/program_options.hpp>
29+
#include <boost/timer/timer.hpp>
30+
#include <random>
31+
#include "ElementsKernel/ProgramHeaders.h"
32+
#include "ElementsKernel/Real.h"
33+
#include "SEImplementation/Segmentation/BackgroundConvolution.h"
34+
#include "SEFramework/Image/VectorImage.h"
35+
#include "SEUtils/IsClose.h"
36+
37+
#if BOOST_VERSION < 105600
38+
#include <boost/units/detail/utility.hpp>
39+
using boost::units::detail::demangle;
40+
#else
41+
using boost::core::demangle;
42+
#endif
43+
44+
namespace po = boost::program_options;
45+
namespace timer = boost::timer;
46+
using namespace SExtractor;
47+
48+
static Elements::Logging logger = Elements::Logging::getLogger("BenchBackgroundConvolution");
49+
50+
class BenchBackgroundConvolution : public Elements::Program {
51+
private:
52+
std::default_random_engine random_generator;
53+
std::uniform_real_distribution<SeFloat> random_dist{0, 1};
54+
55+
public:
56+
57+
po::options_description defineSpecificProgramOptions() override {
58+
po::options_description options{};
59+
options.add_options()
60+
("image-start", po::value<int>()->default_value(100), "Image start size")
61+
("image-step-size", po::value<int>()->default_value(3), "Image step size")
62+
("image-nsteps", po::value<int>()->default_value(1), "Number of steps for the image")
63+
("kernel-start", po::value<int>()->default_value(3), "Kernel start size")
64+
("kernel-step-size", po::value<int>()->default_value(4), "Kernel step size")
65+
("kernel-nsteps", po::value<int>()->default_value(2), "Number of steps for the kernel")
66+
("repeat", po::value<int>()->default_value(5), "Repeat")
67+
("measures", po::value<int>()->default_value(10), "Number of measures");
68+
return options;
69+
}
70+
71+
std::shared_ptr<VectorImage<SeFloat>> generateImage(int size) {
72+
auto img = VectorImage<SeFloat>::create(size, size);
73+
for (int x = 0; x < size; ++x) {
74+
for (int y = 0; y < size; ++y) {
75+
img->setValue(x, y, random_dist(random_generator));
76+
}
77+
}
78+
return img;
79+
}
80+
81+
Elements::ExitCode mainMethod(std::map<std::string, po::variable_value>& args) override {
82+
83+
auto img_start = args["image-start"].as<int>();
84+
auto img_step_size = args["image-step-size"].as<int>();
85+
auto img_nsteps = args["image-nsteps"].as<int>();
86+
auto krn_start = args["kernel-start"].as<int>();
87+
auto krn_step_size = args["kernel-step-size"].as<int>();
88+
auto krn_nsteps = args["kernel-nsteps"].as<int>();
89+
auto repeat = args["repeat"].as<int>();
90+
auto measures = args["measures"].as<int>();
91+
92+
std::cout << "Image,Kernel,Implementation,Time" << std::endl;
93+
94+
for (int img_step = 0; img_step < img_nsteps; ++img_step) {
95+
auto img_size = img_start + img_step * img_step_size;
96+
auto image = generateImage(img_size);
97+
auto variance = generateImage(img_size);
98+
99+
for (int krn_step = 0; krn_step < krn_nsteps; ++krn_step) {
100+
auto krn_size = krn_start + krn_step * krn_step_size;
101+
102+
logger.info() << "Using an image of " << img_size << "x" << img_size;
103+
logger.info() << "Using a kernel of " << krn_size << "x" << krn_size;
104+
105+
auto kernel = generateImage(krn_size);
106+
107+
logger.info() << "Timing Direct implementation";
108+
auto direct_result = benchmark<BgConvolutionImageSource>(image, variance, kernel, repeat, measures);
109+
110+
logger.info() << "Timing DFT implementation";
111+
auto dft_result = benchmark<BgDFTConvolutionImageSource>(image, variance, kernel, repeat, measures);
112+
113+
logger.info() << "Comparing results";
114+
verifyResults(direct_result, dft_result);
115+
}
116+
}
117+
118+
return Elements::ExitCode::OK;
119+
}
120+
121+
template<typename BackgroundConvolution>
122+
std::shared_ptr<VectorImage<SeFloat>>
123+
benchmark(std::shared_ptr<VectorImage<SeFloat>>& image, std::shared_ptr<VectorImage<SeFloat>>& variance,
124+
std::shared_ptr<VectorImage<SeFloat>>& kernel, int repeat, int measures) {
125+
auto conv_name = demangle(typeid(BackgroundConvolution).name());
126+
127+
auto bg_convolution = std::make_shared<BackgroundConvolution>(image, variance, 0.5, kernel);
128+
129+
std::shared_ptr<VectorImage<SeFloat>> result;
130+
131+
for (int m = 0; m < measures; ++m) {
132+
logger.info() << conv_name << " " << m + 1 << "/" << measures;
133+
timer::cpu_timer timer;
134+
timer.stop();
135+
136+
for (int r = 0; r < repeat; ++r) {
137+
timer.start();
138+
result = bg_convolution->getImageTile(0, 0, image->getWidth(), image->getHeight())->getImage();
139+
timer.stop();
140+
}
141+
142+
std::cout << image->getWidth() << ',' << kernel->getWidth() << ",\"" << conv_name << "\"," << timer.elapsed().wall
143+
<< std::endl;
144+
}
145+
146+
return result;
147+
}
148+
149+
void verifyResults(std::shared_ptr<VectorImage<SeFloat>> a, std::shared_ptr<VectorImage<SeFloat>> b) {
150+
bool all_equal = true;
151+
for (int x = 0; x < a->getWidth(); ++x) {
152+
for (int y = 0; y < a->getHeight(); ++y) {
153+
auto av = a->getValue(x, y);
154+
auto bv = b->getValue(x, y);
155+
if (!isClose(av, bv, 1e-3, 1e-3)) {
156+
logger.info() << "Mismatch at " << x << ',' << y << ": "
157+
<< av << " != " << bv;
158+
all_equal = false;
159+
}
160+
}
161+
}
162+
if (all_equal) {
163+
logger.info() << "All elements are equal!";
164+
} else {
165+
logger.warn() << "Convoluted images are not equal!";
166+
}
167+
}
168+
};
169+
170+
MAIN_FOR(BenchBackgroundConvolution)

SEBenchmarks/src/program/BenchConvolution.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <random>
3131
#include "ElementsKernel/ProgramHeaders.h"
3232
#include "ElementsKernel/Real.h"
33+
#include "SEUtils/IsClose.h"
3334
#include "SEFramework/Image/VectorImage.h"
3435
#include "SEFramework/Convolution/DirectConvolution.h"
3536
#include "SEFramework/Convolution/DFT.h"
@@ -180,11 +181,6 @@ class BenchConvolution : public Elements::Program {
180181
logger.warn() << "Convoluted images are not equal!";
181182
}
182183
}
183-
184-
bool isClose(SeFloat a, SeFloat b, SeFloat atol=1e-8, SeFloat rtol=1e-5) const {
185-
return std::abs(a - b) <= (atol + rtol * std::abs(b));
186-
}
187-
188184
};
189185

190186
MAIN_FOR(BenchConvolution)

SEFramework/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ elements_add_unit_test(RecenterImage_test tests/src/Image/RecenterImage_test.cpp
133133
elements_add_unit_test(MirrorImage_test tests/src/Image/MirrorImage_test.cpp
134134
LINK_LIBRARIES SEFramework
135135
TYPE Boost)
136+
elements_add_unit_test(FunctionalImage_test tests/src/Image/FunctionalImage_test.cpp
137+
LINK_LIBRARIES SEFramework
138+
TYPE Boost)
136139
elements_add_unit_test(FFT_test tests/src/FFT/FFT_test.cpp
137140
LINK_LIBRARIES SEFramework
138141
TYPE Boost)

SEFramework/SEFramework/Convolution/DFT.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ class DFTConvolution {
3939
return m_kernel->getHeight();
4040
}
4141

42-
void convolve(std::shared_ptr<WriteableImage<T>> image_ptr) const {
42+
template <typename ...Args>
43+
void convolve(std::shared_ptr<WriteableImage<T>> image_ptr, Args... padding_args) const {
4344
// Dimension of the working padded images
4445
auto padded_width = image_ptr->getWidth() + m_kernel->getWidth() - 1;
4546
auto padded_height = image_ptr->getHeight() + m_kernel->getHeight() - 1;
@@ -51,7 +52,7 @@ class DFTConvolution {
5152
auto total_size = padded_height * padded_width;
5253

5354
// Padded image
54-
auto padded = TPadding::create(image_ptr, padded_width, padded_height);
55+
auto padded = TPadding::create(image_ptr, padded_width, padded_height, std::forward<Args>(padding_args)...);
5556

5657
// Buffers
5758
std::vector<real_t> real_buffer(total_size * 2);

SEFramework/SEFramework/Convolution/DirectConvolution.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ class DirectConvolution {
2121

2222
virtual ~DirectConvolution() = default;
2323

24-
void convolve(std::shared_ptr<WriteableImage<T>> image) const {
24+
template <typename ...Args>
25+
void convolve(std::shared_ptr<WriteableImage<T>> image, Args... padding_args) const {
2526
auto padded_width = image->getWidth() + m_kernel->getWidth() - 1;
2627
auto padded_height = image->getHeight() + m_kernel->getHeight() - 1;
2728
auto lpad = m_kernel->getWidth() / 2;
2829
auto tpad = m_kernel->getHeight() / 2;
2930

30-
auto padded = VectorImage<T>::create(TPadding::create(image, padded_width, padded_height));
31+
auto padded = VectorImage<T>::create(
32+
TPadding::create(image, padded_width, padded_height, std::forward<Args>(padding_args)...)
33+
);
3134

3235
for (int iy = tpad; iy < padded->getHeight() - tpad; ++iy) {
3336
for (int ix = lpad; ix < padded->getWidth() - lpad; ++ix) {

SEFramework/SEFramework/Image/BufferedImage.h

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,13 @@ class BufferedImage : public ImageBase<T> {
3939

4040
/// Returns the value of the pixel with the coordinates (x,y)
4141
virtual T getValue(int x, int y) const override {
42-
//std::cout << "BufferedImage::getValue() " << x << " " << y << "\n";
4342
assert(x >= 0 && y >=0 && x < m_source->getWidth() && y < m_source->getHeight());
4443

45-
auto& current_tile = getEntryForThisThread();
46-
47-
if (current_tile == nullptr || !current_tile->isPixelInTile(x, y)) {
48-
current_tile = m_tile_manager->getTileForPixel(x, y, m_source);
44+
if (m_current_tile == nullptr || !m_current_tile->isPixelInTile(x, y)) {
45+
m_current_tile = m_tile_manager->getTileForPixel(x, y, m_source);
4946
}
5047

51-
return current_tile->getValue(x, y);
48+
return m_current_tile->getValue(x, y);
5249
}
5350

5451
/// Returns the width of the image in pixels
@@ -80,16 +77,7 @@ class BufferedImage : public ImageBase<T> {
8077
protected:
8178
std::shared_ptr<const ImageSource<T>> m_source;
8279
std::shared_ptr<TileManager> m_tile_manager;
83-
84-
std::shared_ptr<ImageTile<T>> &getEntryForThisThread() const {
85-
if (!m_current_tile.get()) {
86-
m_current_tile.reset(new std::shared_ptr<ImageTile<T>>{nullptr});
87-
}
88-
return *m_current_tile;
89-
}
90-
91-
private:
92-
mutable boost::thread_specific_ptr<std::shared_ptr<ImageTile<T>>> m_current_tile;
80+
mutable std::shared_ptr<ImageTile<T>> m_current_tile;
9381
};
9482

9583
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* @file SEFramework/Image/FunctionalImage.h
3+
* @date 27/03/19
4+
* @author Alejandro Alvarez Ayllon
5+
*/
6+
7+
#ifndef _SEFRAMEWORK_IMAGE_FUNCTIONALIMAGE_H
8+
#define _SEFRAMEWORK_IMAGE_FUNCTIONALIMAGE_H
9+
10+
#include "SEFramework/Image/ImageBase.h"
11+
12+
namespace SExtractor {
13+
14+
template<typename T>
15+
class FunctionalImage : public ImageBase<T> {
16+
public:
17+
using FunctorType = std::function<T(int x, int y)>;
18+
19+
protected:
20+
FunctionalImage(int width, int height, FunctorType functor)
21+
: m_width{width}, m_height{height}, m_functor{functor} {
22+
}
23+
24+
public:
25+
virtual ~FunctionalImage() = default;
26+
27+
template<typename ...Args>
28+
static std::shared_ptr<ImageBase<T>> create(Args&& ... args) {
29+
return std::shared_ptr<FunctionalImage<T>>(new FunctionalImage{std::forward<Args>(args)...});
30+
}
31+
32+
T getValue(int x, int y) const override {
33+
return m_functor(x, y);
34+
}
35+
36+
int getWidth() const override {
37+
return m_width;
38+
}
39+
40+
int getHeight() const override {
41+
return m_height;
42+
}
43+
44+
private:
45+
int m_width, m_height;
46+
FunctorType m_functor;
47+
};
48+
49+
} // end SExtractor
50+
51+
#endif // _SEFRAMEWORK_IMAGE_FUNCTIONALIMAGE_H

SEFramework/SEFramework/Image/WriteableBufferedImage.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class WriteableBufferedImage : public BufferedImage<T>, public WriteableImage<T>
2121
WriteableBufferedImage(std::shared_ptr<const ImageSource<T>> source, std::shared_ptr<TileManager> tile_manager)
2222
: BufferedImage<T>(source, tile_manager) {}
2323

24-
using BufferedImage<T>::getEntryForThisThread;
24+
using BufferedImage<T>::m_current_tile;
2525

2626
public:
2727

@@ -35,15 +35,12 @@ class WriteableBufferedImage : public BufferedImage<T>, public WriteableImage<T>
3535
virtual void setValue(int x, int y, T value) override {
3636
assert(x >= 0 && y >=0 && x < BufferedImage<T>::m_source->getWidth() && y < BufferedImage<T>::m_source->getHeight());
3737

38-
auto& current_tile = getEntryForThisThread();
39-
40-
if (current_tile == nullptr || !current_tile->isPixelInTile(x, y)) {
41-
current_tile =
42-
BufferedImage<T>::m_tile_manager->getTileForPixel(x, y, BufferedImage<T>::m_source);
38+
if (m_current_tile == nullptr || !m_current_tile->isPixelInTile(x, y)) {
39+
m_current_tile = BufferedImage<T>::m_tile_manager->getTileForPixel(x, y, BufferedImage<T>::m_source);
4340
}
4441

45-
current_tile->setModified(true);
46-
current_tile->setValue(x, y, value);
42+
m_current_tile->setModified(true);
43+
m_current_tile->setValue(x, y, value);
4744
}
4845

4946
};

0 commit comments

Comments
 (0)