Commit 86dfa80bf728ed221ec9acc53c1b5d406248bd51
1 parent
dbeb83f2
added 2D gaussian filter (CPU implementation), and added the ability to split an…
…d merge stim::image by channel
Showing
2 changed files
with
67 additions
and
6 deletions
Show diff stats
stim/image/image.h
... | ... | @@ -54,7 +54,7 @@ class image{ |
54 | 54 | |
55 | 55 | size_t bytes(){ return size() * sizeof(T); } |
56 | 56 | |
57 | - inline size_t idx(size_t x, size_t y, size_t c = 0){ | |
57 | + inline size_t idx(size_t x, size_t y, size_t c = 0) const { | |
58 | 58 | return y * R[0] * R[1] + x * R[0] + c; |
59 | 59 | } |
60 | 60 | |
... | ... | @@ -125,6 +125,11 @@ public: |
125 | 125 | free(img); |
126 | 126 | } |
127 | 127 | |
128 | + ///Resize an image | |
129 | + void resize(size_t x, size_t y, size_t c = 1) { | |
130 | + allocate(x, y, c); | |
131 | + } | |
132 | + | |
128 | 133 | stim::image<T>& operator=(const stim::image<T>& I){ |
129 | 134 | init(); |
130 | 135 | if(&I == this) //handle self-assignment |
... | ... | @@ -340,7 +345,7 @@ public: |
340 | 345 | |
341 | 346 | /// Return an image representing a specified channel |
342 | 347 | /// @param c is the channel to be returned |
343 | - image<T> channel(size_t c){ | |
348 | + image<T> channel(size_t c) const { | |
344 | 349 | image<T> r(X(), Y(), 1); //create a new image |
345 | 350 | for(size_t x = 0; x < X(); x++){ |
346 | 351 | for(size_t y = 0; y < Y(); y++){ |
... | ... | @@ -351,7 +356,7 @@ public: |
351 | 356 | } |
352 | 357 | |
353 | 358 | /// Returns an std::vector containing each channel as a separate image |
354 | - std::vector<image<T>> split() { | |
359 | + std::vector<image<T>> split() const { | |
355 | 360 | std::vector<image<T>> r; //create an image array |
356 | 361 | r.resize(C()); //create images for each channel |
357 | 362 | |
... | ... | @@ -361,6 +366,15 @@ public: |
361 | 366 | return r; |
362 | 367 | } |
363 | 368 | |
369 | + /// Merge a series of single-channel images into a multi-channel image | |
370 | + void merge(std::vector<image<T>>& list) { | |
371 | + size_t x = list[0].width(); //calculate the size of the image | |
372 | + size_t y = list[0].height(); | |
373 | + allocate(x, y, list.size()); //re-allocate the image | |
374 | + for (size_t c = 0; c < list.size(); c++) //for each channel | |
375 | + set_channel(list[c].channel(0).data(), c); //insert the channel into the output image | |
376 | + } | |
377 | + | |
364 | 378 | T& operator()(size_t x, size_t y, size_t c = 0){ |
365 | 379 | return img[idx(x, y, c)]; |
366 | 380 | } |
... | ... | @@ -394,15 +408,15 @@ public: |
394 | 408 | } |
395 | 409 | } |
396 | 410 | |
397 | - size_t channels(){ | |
411 | + size_t channels() const{ | |
398 | 412 | return C(); |
399 | 413 | } |
400 | 414 | |
401 | - size_t width(){ | |
415 | + size_t width() const{ | |
402 | 416 | return X(); |
403 | 417 | } |
404 | 418 | |
405 | - size_t height(){ | |
419 | + size_t height() const{ | |
406 | 420 | return Y(); |
407 | 421 | } |
408 | 422 | ... | ... |
1 | +#ifndef STIM_CUDA_GAUSS2_H | |
2 | +#define STIM_CUDA_GAUSS2_H | |
3 | + | |
4 | +#include <stim/image/image.h> | |
5 | +#include <stim/math/filters/sepconv2.h> | |
6 | +#include <stim/math/constants.h> | |
7 | + | |
8 | +namespace stim { | |
9 | + | |
10 | + template<typename T> | |
11 | + T gauss1d(T x, T mu, T std) { | |
12 | + return ((T)1 / (T)sqrt(stim::TAU * std * std) * exp(-(x - mu)*(x - mu) / (2 * std*std))); | |
13 | + } | |
14 | + ///Perform a 2D gaussian convolution on an input image. | |
15 | + ///@param in is a pointer to the input image | |
16 | + ///@param stdx is the standard deviation (in pixels) along the x axis | |
17 | + ///@param stdy is the standard deviation (in pixels) along the y axis | |
18 | + ///@param nstds specifies the number of standard deviations of the Gaussian that will be kept in the kernel | |
19 | + template<typename T, typename K = T> | |
20 | + stim::image<T> cpu_gauss2(const stim::image<T>& in, K stdx, K stdy, size_t nstds = 3) { | |
21 | + size_t kx = stdx * nstds * 2; //calculate the kernel sizes | |
22 | + size_t ky = stdy * nstds * 2; | |
23 | + size_t X = in.width() - kx + 1; //calculate the size of the output image | |
24 | + size_t Y = in.height() - ky + 1; | |
25 | + stim::image<T> r(X, Y, in.channels()); //create an output image | |
26 | + | |
27 | + K* gx = (K*)malloc(kx * sizeof(K)); //allocate space for the gaussian kernels | |
28 | + K* gy = (K*)malloc(ky * sizeof(K)); | |
29 | + K mux = (K)kx / (K)2; //calculate the mean of the gaussian (center of the kernel) | |
30 | + K muy = (K)ky / (K)2; | |
31 | + for (size_t xi = 0; xi < kx; xi++) //evaluate the kernels | |
32 | + gx[xi] = gauss1d((K)xi, mux, stdx); | |
33 | + for (size_t yi = 0; yi < ky; yi++) | |
34 | + gy[yi] = gauss1d((K)yi, muy, stdy); | |
35 | + | |
36 | + std::vector<stim::image<T>> IN = in.split(); //split the input image into channels | |
37 | + std::vector<stim::image<T>> R = r.split(); //split the output image into channels | |
38 | + for (size_t c = 0; c < in.channels(); c++) //for each channel | |
39 | + cpu_sepconv2(R[c].data(), IN[c].data(), gx, gy, IN[c].width(), IN[c].height(), kx, ky); | |
40 | + | |
41 | + r.merge(R); //merge the blurred channels into the final image | |
42 | + return r; | |
43 | + } | |
44 | +} | |
45 | + | |
46 | + | |
47 | +#endif | |
0 | 48 | \ No newline at end of file | ... | ... |