Re-implement nearest-neighbor interpolation

Fix for issue 295, there was, what amounts to an off by one error in the
original code, which was causing the problem in issue 295.

This error was probably not very noticable in larger images, but as
pointed out by the bug report smaller images may contain more distortion
than necessary.

Wasn't entirely sure how to correct the original code, so replaced it with
entirely new code.

Also added unit test for resize methods, however the bilinear test only
checks using RGB, and not the other ImageTrait types.
This commit is contained in:
Joel Madigan 2013-12-02 00:23:05 -05:00
parent 12a46f5ca3
commit 9d281793c8
2 changed files with 125 additions and 15 deletions

View File

@ -35,23 +35,22 @@ void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palet
switch (method) {
// TODO optimize this
case RESIZE_METHOD_NEAREST_NEIGHBOR: {
uint32_t color;
double u, v, du, dv;
int x, y;
case RESIZE_METHOD_NEAREST_NEIGHBOR:
{
int o_width = src->getWidth(), o_height = src->getHeight();
int n_width = dst->getWidth(), n_height = dst->getHeight();
double x_ratio = o_width / (double)n_width;
double y_ratio = o_height / (double)n_height;
double px, py;
int i;
u = v = 0.0;
du = src->getWidth() * 1.0 / dst->getWidth();
dv = src->getHeight() * 1.0 / dst->getHeight();
for (y=0; y<dst->getHeight(); ++y) {
for (x=0; x<dst->getWidth(); ++x) {
color = src->getPixel(MID(0, u, src->getWidth()-1),
MID(0, v, src->getHeight()-1));
dst->putPixel(x, y, color);
u += du;
for (int y = 0; y < n_height; y++) {
for (int x = 0; x < n_width; x++) {
px = floor(x * x_ratio);
py = floor(y * y_ratio);
i = (int)(py * o_width + px);
dst->putPixel(x, y, src->getPixel(i % o_width, i / o_width));
}
u = 0.0;
v += dv;
}
break;
}

View File

@ -0,0 +1,111 @@
// Aseprite Gfx Library
// Copyright (C) 2001-2013 David Capello
//
// This source file is distributed under MIT license,
// please read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gtest/gtest.h>
#include "raster/color.h"
#include "raster/image.h"
#include "raster/algorithm/resize_image.h"
using namespace std;
using namespace raster;
/***************************
* Test dat
*/
// Base image
color_t test_image_base_3x3[9] =
{
0x000000, 0xffffff, 0x000000,
0xffffff, 0xffffff, 0xffffff,
0x000000, 0xffffff, 0x000000
};
// Base image scaled to 9x9 with nearest neighbor interpolation
color_t test_image_scaled_9x9_nearest[81] =
{
0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000,
0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff,
0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff,
0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff,
0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000,
0x000000, 0x000000, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000
};
// Base image scalled to 9x9 with bilinear interpolation
color_t test_image_scaled_9x9_bilinear[81] =
{
0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000,
0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000,
0x565656, 0x565656, 0x8f8f8f, 0xc6c6c6, 0xffffff, 0xc6c6c6, 0x8f8f8f, 0x565656, 0x565656,
0xa9a9a9, 0xa9a9a9, 0xc6c6c6, 0xe2e2e2, 0xffffff, 0xe2e2e2, 0xc6c6c6, 0xa9a9a9, 0xa9a9a9,
0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff,
0xa9a9a9, 0xa9a9a9, 0xc6c6c6, 0xe2e2e2, 0xffffff, 0xe2e2e2, 0xc6c6c6, 0xa9a9a9, 0xa9a9a9,
0x565656, 0x565656, 0x8f8f8f, 0xc6c6c6, 0xffffff, 0xc6c6c6, 0x8f8f8f, 0x565656, 0x565656,
0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000,
0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000
};
Image* create_image_from_data(PixelFormat format, color_t* data, int width, int height)
{
Image* new_image = Image::create(format, width, height);
for (int i = 0; i < width * height; i++) {
new_image->putPixel(i % width, i / width, data[i]);
}
return new_image;
}
// Simple pixel to pixel image comparison
bool compare_images(Image* a, Image* b)
{
for (int y = 0; y < a->getHeight(); y++) {
for (int x = 0; x < a->getWidth(); x++) {
if (!(a->getPixel(x, y) == b->getPixel(x, y)))
return false;
}
}
return true;
}
TEST(ResizeImage, NearestNeighborInterp)
{
Image* src = create_image_from_data(IMAGE_RGB, test_image_base_3x3, 3, 3);
Image* dst = Image::create(IMAGE_RGB, 9, 9);
// Pre-rendered test image for comparison
Image* test_dst = create_image_from_data(IMAGE_RGB, test_image_scaled_9x9_nearest, 9, 9);
algorithm::resize_image(src, dst, algorithm::ResizeMethod::RESIZE_METHOD_NEAREST_NEIGHBOR, NULL, NULL);
ASSERT_TRUE(compare_images(dst, test_dst)) << "resize_image() result does not match test image!";
}
TEST(ResizeImage, BilinearInterpRGBType)
{
Image* src = create_image_from_data(IMAGE_RGB, test_image_base_3x3, 3, 3);
Image* dst = Image::create(IMAGE_RGB, 9, 9);
// Pre-rendered test image for comparison
Image* test_dst = create_image_from_data(IMAGE_RGB, test_image_scaled_9x9_bilinear, 9, 9);
algorithm::resize_image(src, dst, algorithm::ResizeMethod::RESIZE_METHOD_BILINEAR, NULL, NULL);
ASSERT_TRUE(compare_images(dst, test_dst)) << "resize_image() result does not match test image!";
}
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}