From 72697f650c0d7890ee9fca2d61225749188aaf3c Mon Sep 17 00:00:00 2001 From: Pavel Krajcevski Date: Thu, 10 Oct 2013 19:36:19 -0400 Subject: [PATCH] Add filtering operation. --- Base/include/Image.h | 7 +++++ Base/src/Image.cpp | 59 +++++++++++++++++++++++++++++++++++++++++ Base/test/TestImage.cpp | 30 +++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/Base/include/Image.h b/Base/include/Image.h index 0c0eaec..0d93ef6 100644 --- a/Base/include/Image.h +++ b/Base/include/Image.h @@ -49,6 +49,8 @@ namespace FasTC { + class IPixel; + template extern double ComputePSNR(Image *img1, Image *img2); @@ -105,6 +107,11 @@ namespace FasTC { // appropriate pixels. 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 &kernel); + private: uint32 m_Width; uint32 m_Height; diff --git a/Base/src/Image.cpp b/Base/src/Image.cpp index fd842d7..43b21cd 100644 --- a/Base/src/Image.cpp +++ b/Base/src/Image.cpp @@ -43,6 +43,7 @@ #include "Image.h" +#include #include #include #include @@ -289,6 +290,64 @@ void Image::SetImageData(uint32 width, uint32 height, PixelType *data } } + template + static inline T Clamp(const T &v, const T &a, const T &b) { + return ::std::min(::std::max(a, v), b); + } + +template +void Image::Filter(const Image &kernel) { + Image 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(k(i, j)); + } + } + + for(uint32 j = 0; j < k.GetHeight(); j++) { + for(uint32 i = 0; i < k.GetWidth(); i++) { + k(i, j) = static_cast(k(i, j)) / sum; + } + } + + int32 ih = static_cast(GetHeight()); + int32 iw = static_cast(GetWidth()); + + int32 kh = static_cast(k.GetHeight()); + int32 kw = static_cast(k.GetWidth()); + + Image 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(x + xoffset, 0, GetWidth() - 1), + Clamp(y + yoffset, 0, GetHeight() - 1))); + Color c; c.Unpack(pixel.Pack()); + Color scaled = c * static_cast(k(x, y)); + newPixel += scaled; + } + } + + filtered(i, j).Unpack(newPixel.Pack()); + } + } + + *this = filtered; +} + template class Image; template class Image; template class Image; diff --git a/Base/test/TestImage.cpp b/Base/test/TestImage.cpp index df1ffc7..1e0c012 100644 --- a/Base/test/TestImage.cpp +++ b/Base/test/TestImage.cpp @@ -52,6 +52,7 @@ #include "gtest/gtest.h" #include "Image.h" +#include "IPixel.h" #include "Pixel.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 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 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(img(i, j)), 0.5f, 0.01); + } + } +}