Add filtering operation.

This commit is contained in:
Pavel Krajcevski 2013-10-10 19:36:19 -04:00
parent 076ca07bcf
commit 72697f650c
3 changed files with 96 additions and 0 deletions

View file

@ -49,6 +49,8 @@
namespace FasTC { namespace FasTC {
class IPixel;
template<typename U, typename V> template<typename U, typename V>
extern double ComputePSNR(Image<U> *img1, Image<V> *img2); extern double ComputePSNR(Image<U> *img1, Image<V> *img2);
@ -105,6 +107,11 @@ namespace FasTC {
// appropriate pixels. // appropriate pixels.
virtual void ComputePixels() { } virtual void ComputePixels() { }
// Filters the image with a given set of kernel values. The values
// are normalized before they are used (i.e. we make sure that they
// sum up to one).
void Filter(const Image<IPixel> &kernel);
private: private:
uint32 m_Width; uint32 m_Width;
uint32 m_Height; uint32 m_Height;

View file

@ -43,6 +43,7 @@
#include "Image.h" #include "Image.h"
#include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
@ -289,6 +290,64 @@ void Image<PixelType>::SetImageData(uint32 width, uint32 height, PixelType *data
} }
} }
template<typename T>
static inline T Clamp(const T &v, const T &a, const T &b) {
return ::std::min(::std::max(a, v), b);
}
template<typename PixelType>
void Image<PixelType>::Filter(const Image<IPixel> &kernel) {
Image<IPixel> k(kernel);
// Only odd sized filters make sense....
assert(k.GetWidth() % 2);
assert(k.GetHeight() % 2);
double sum = 0.0;
for(uint32 j = 0; j < k.GetHeight(); j++) {
for(uint32 i = 0; i < k.GetWidth(); i++) {
sum += static_cast<float>(k(i, j));
}
}
for(uint32 j = 0; j < k.GetHeight(); j++) {
for(uint32 i = 0; i < k.GetWidth(); i++) {
k(i, j) = static_cast<float>(k(i, j)) / sum;
}
}
int32 ih = static_cast<int32>(GetHeight());
int32 iw = static_cast<int32>(GetWidth());
int32 kh = static_cast<int32>(k.GetHeight());
int32 kw = static_cast<int32>(k.GetWidth());
Image<PixelType> filtered(iw, ih);
for(int32 j = 0; j < ih; j++) {
for(int32 i = 0; i < iw; i++) {
int32 yoffset = j - (k.GetHeight() / 2);
int32 xoffset = i - (k.GetWidth() / 2);
Color newPixel;
for(int32 y = 0; y < kh; y++) {
for(int32 x = 0; x < kw; x++) {
PixelType pixel = ((*this)(
Clamp<int32>(x + xoffset, 0, GetWidth() - 1),
Clamp<int32>(y + yoffset, 0, GetHeight() - 1)));
Color c; c.Unpack(pixel.Pack());
Color scaled = c * static_cast<float>(k(x, y));
newPixel += scaled;
}
}
filtered(i, j).Unpack(newPixel.Pack());
}
}
*this = filtered;
}
template class Image<Pixel>; template class Image<Pixel>;
template class Image<IPixel>; template class Image<IPixel>;
template class Image<Color>; template class Image<Color>;

View file

@ -52,6 +52,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "Image.h" #include "Image.h"
#include "IPixel.h"
#include "Pixel.h" #include "Pixel.h"
#include "Utils.h" #include "Utils.h"
@ -119,3 +120,32 @@ TEST(Image, AssignmentOperator) {
} }
} }
} }
TEST(Image, Filter) {
const uint32 w = 16;
const uint32 h = 16;
// Make a black and white image...
FasTC::Image<FasTC::IPixel> img(w, h);
for(uint32 j = 0; j < h; j++) {
for(uint32 i = 0; i < w; i++) {
if((i ^ j) % 2)
img(i, j) = 1.0f;
else
img(i, j) = 0.0f;
}
}
// Make a weird averaging kernel...
FasTC::Image<FasTC::IPixel> kernel(3, 3);
kernel(0, 1) = kernel(1, 0) = kernel(1, 2) = kernel(2, 1) = 0.125f;
kernel(1, 1) = 0.5f;
img.Filter(kernel);
for(uint32 j = 1; j < h-1; j++) {
for(uint32 i = 1; i < w-1; i++) {
EXPECT_NEAR(static_cast<float>(img(i, j)), 0.5f, 0.01);
}
}
}