diff --git a/Base/include/FasTC/Image.h b/Base/include/FasTC/Image.h index f39ff2e..b09b6cb 100644 --- a/Base/include/FasTC/Image.h +++ b/Base/include/FasTC/Image.h @@ -132,6 +132,7 @@ namespace FasTC { Image *channelThree); extern void DiscreteCosineXForm(Image *img, int blockSize); + extern void InvDiscreteCosineXForm(Image *img, int blockSize); } // namespace FasTC #endif // __TEXCOMP_IMAGE_H__ diff --git a/Base/src/Image.cpp b/Base/src/Image.cpp index 7d30edd..bab3457 100644 --- a/Base/src/Image.cpp +++ b/Base/src/Image.cpp @@ -48,6 +48,8 @@ #include #include #include + +#define _USE_MATH_DEFINES #include #include "FasTC/Color.h" @@ -616,6 +618,8 @@ void SplitChannels(const Image &in, // //////////////////////////////////////////////////////////////////////////////// +typedef void (*DCTBlockFn)(Image *); + static void DCT(Image *img) { Image new_img = *img; @@ -625,29 +629,74 @@ static void DCT(Image *img) { for (unsigned int v = 0; v < img->GetHeight(); ++v) { for (unsigned int u = 0; u < img->GetWidth(); ++u) { new_img(u, v) = 0.0f; + float fu = static_cast(u); + float fv = static_cast(v); for (unsigned int y = 0; y < img->GetHeight(); ++y) { for (unsigned int x = 0; x < img->GetWidth(); ++x) { float fx = static_cast(x); float fy = static_cast(y); new_img(u, v) += (*img)(x, y) - * cos((2*fx + 1) / (2 * N)) - * cos((2*fy + 1) / (2 * M)); + * cos(((2*fx + 1) * fu * M_PI) / (2 * N)) + * cos(((2*fy + 1) * fv * M_PI) / (2 * M)); } } - + if (u == 0 && v == 0) { - new_img(u, v) *= 0.5; + new_img(u, v) /= N; } else if (u == 0 || v == 0) { - new_img(u, v) /= sqrt(2); + new_img(u, v) *= sqrt(2) / N; + } else { + new_img(u, v) *= 2 / N; } - new_img(u, v) *= 0.25; } } *img = new_img; } -extern void DiscreteCosineXForm(Image *img, int blockSize) { +static void IDCT(Image *img) { + Image new_img = *img; + + float N = static_cast(img->GetWidth()); + float M = static_cast(img->GetHeight()); + + assert (N == M); + + for (unsigned int y = 0; y < img->GetHeight(); ++y) { + for (unsigned int x = 0; x < img->GetWidth(); ++x) { + new_img(x, y) = 0.0f; + float fx = static_cast(x); + float fy = static_cast(y); + + for (unsigned int v = 0; v < img->GetHeight(); ++v) { + for (unsigned int u = 0; u < img->GetWidth(); ++u) { + float fu = static_cast(u); + float fv = static_cast(v); + new_img(x, y) += (*img)(u, v) + * cos(((2*fx + 1) * fu * M_PI) / (2 * N)) + * cos(((2*fy + 1) * fv * M_PI) / (2 * M)); + + if (u == 0 && v == 0) { + new_img(x, y) /= N; + } else if (u == 0 || v == 0) { + new_img(x, y) /= sqrt(2) / N; + } else { + new_img(x, y) *= 2 / N; + } + } + } + } + } + + *img = new_img; +} + +static void RunDCTBlockFn(Image *img, int blockSize, DCTBlockFn fn) { + assert (NULL != fn); + assert (0 < blockSize); + assert (static_cast(blockSize) < img->GetWidth()); + assert (static_cast(blockSize) < img->GetHeight()); + Image block(blockSize, blockSize); for (unsigned int j = 0; j < img->GetHeight(); j += blockSize) { for (unsigned int i = 0; i < img->GetWidth(); i += blockSize) { @@ -660,8 +709,8 @@ extern void DiscreteCosineXForm(Image *img, int blockSize) { } } - // Transform it - DCT(&block); + // Run the function + fn(&block); // Put it back in the original image for (int y = 0; y < blockSize; ++y) { @@ -684,5 +733,12 @@ extern void DiscreteCosineXForm(Image *img, int blockSize) { } } +void DiscreteCosineXForm(Image *img, int blockSize) { + RunDCTBlockFn(img, blockSize, DCT); +} + +void InvDiscreteCosineXForm(Image *img, int blockSize) { + RunDCTBlockFn(img, blockSize, IDCT); +} } // namespace FasTC diff --git a/Base/test/TestImage.cpp b/Base/test/TestImage.cpp index 3890a4a..70512eb 100644 --- a/Base/test/TestImage.cpp +++ b/Base/test/TestImage.cpp @@ -211,3 +211,38 @@ TEST(Image, SplitImage) { } } } + +TEST(Image, DCT) { + + const uint32 w = 32; + const uint32 h = 32; + + FasTC::Image img(w, h); + for (uint32 j = 0; j < h; ++j) { + for (uint32 i = 0; i < w; ++i) { + img(i, j) = static_cast(i + j); +// img(i, j) = static_cast(1); + } + } + + FasTC::Image orig(img); + + // Make sure that taking the DCT and inverse DCT returns + // the same image... + FasTC::DiscreteCosineXForm(&img, 8); + + // First make sure they're different + for (uint32 j = 0; j < h; ++j) { + for (uint32 i = 0; i < w; ++i) { + EXPECT_NE(img(i, j), orig(i, j)); + } + } + + FasTC::InvDiscreteCosineXForm(&img, 8); + + for (uint32 j = 0; j < h; ++j) { + for (uint32 i = 0; i < w; ++i) { + EXPECT_NEAR(img(i, j), orig(i, j), 1e-5); + } + } +}