diff options
author | John Agapiou <jagapiou@google.com> | 2015-11-02 21:13:45 +0300 |
---|---|---|
committer | John Agapiou <jagapiou@google.com> | 2015-11-03 18:27:39 +0300 |
commit | ba54371234e1f2b1d7a1360f5b02a747d0585f27 (patch) | |
tree | fbf76b799c08824cff7d53e61ab9c4a7e0eb76ea | |
parent | d97d20dbbd836a7801804c667fc1e5c56b2e90f2 (diff) |
Fix bug with image.scale 'bicubic' and ByteTensors.
Bicubic interpolation and scaling do not work for ByteTensors due to issues with
numerical precision.
* Add unit tests for ByteTensor scaling.
* Update scaling to use existing cubic interpolation method.
* Update cubic interpolation to use double precision Temporary variables.
* Add function to handle casting from `double` to `real`.
-rwxr-xr-x | generic/image.c | 48 | ||||
-rw-r--r-- | test/test_scale.lua | 45 |
2 files changed, 67 insertions, 26 deletions
diff --git a/generic/image.c b/generic/image.c index 89ba31f..d63e751 100755 --- a/generic/image.c +++ b/generic/image.c @@ -15,6 +15,16 @@ #define M_PI 3.14159265358979323846 #endif + +static inline real image_(FromDouble)(double x) { + #ifdef TH_REAL_IS_BYTE + if( x <= 0 ) return 0; + if( x >= 255 ) return 255; + #endif + return x; +} + + static void image_(Main_op_validate)( lua_State *L, THTensor *Tsrc, THTensor *Tdst){ long src_depth = 1; @@ -117,6 +127,19 @@ static void image_(Main_scaleLinear_rowcol)(THTensor *Tsrc, } } + +static inline real image_(Main_cubicInterpolate)(double p0, double p1, + double p2, double p3, + double x) { + double a0 = p1; + double a1 = p2 - p0; + double a2 = 2 * p0 - 5 * p1 + 4 * p2 - p3; + double a3 = 3 * (p1 - p2) + p3 - p0; + double v = a0 + 0.5 * x * (a1 + x * (a2 + x * a3)); + return image_(FromDouble)(v); +} + + static void image_(Main_scaleCubic_rowcol)(THTensor *Tsrc, THTensor *Tdst, long src_start, @@ -147,28 +170,22 @@ static void image_(Main_scaleCubic_rowcol)(THTensor *Tsrc, long dst_pos = dst_start + di*dst_stride; si_f = di * scale; si_i = (long)si_f; si_f -= si_i; - real p0; - real p1 = src[ src_start + si_i * src_stride ]; - real p2 = src[ src_start + (si_i + 1) * src_stride ]; - real p3; + double p0; + double p1 = src[ src_start + si_i * src_stride ]; + double p2 = src[ src_start + (si_i + 1) * src_stride ]; + double p3; if (si_i > 0) { p0 = src[ src_start + (si_i - 1) * src_stride ]; } else { - p0 = 2*p1 - p2; + p0 = 2 * p1 - p2; } if (si_i + 2 < src_len) { p3 = src[ src_start + (si_i + 2) * src_stride ]; } else { - p3 = 2*p2 - p1; + p3 = 2 * p2 - p1; } - real a0 = p1; - real a1 = -(real)1/(real)2*p0 + (real)1/(real)2*p2; - real a2 = p0 - (real)5/(real)2*p1 + (real)2*p2 - (real)1/(real)2*p3; - real a3 = -(real)1/(real)2*p0 + (real)3/(real)2*p1 - (real)3/(real)2*p2 + - (real)1/(real)2*p3; - - dst[dst_pos] = a0 + si_f * (a1 + si_f * (a2 + a3 * si_f)); + dst[dst_pos] = image_(Main_cubicInterpolate)(p0, p1, p2, p3, si_f); } dst[ dst_start + (dst_len - 1) * dst_stride ] = @@ -1637,11 +1654,6 @@ int image_(Main_flip)(lua_State *L) { return 0; } -static inline real image_(Main_cubicInterpolate)(real p0, real p1, real p2, real p3, real x) -{ - return p1 + 0.5 * x * (p2 - p0 + x * (2 * p0 - 5 * p1 + 4 * p2 - p3 + x * (3 * (p1 - p2) + p3 - p0))); -} - static inline void image_(Main_bicubicInterpolate)( real* src, long* is, long* size, real ix, real iy, real* dst, long *os, diff --git a/test/test_scale.lua b/test/test_scale.lua index f2225a7..02f3410 100644 --- a/test/test_scale.lua +++ b/test/test_scale.lua @@ -14,35 +14,64 @@ local function outerProduct(x) end +local function assertTensorEq(actual, expected) + if torch.type(expected) == 'torch.ByteTensor' then + local areEqual = torch.eq(actual, expected):all() + tester:assert(areEqual) + else + tester:assertTensorEq(actual, expected, 1e-5) + end +end + + function tests.bilinearUpscale() local im = outerProduct{1, 2, 4, 2} local expected = outerProduct{1, 1.5, 2, 3, 4, 3, 2} - local actual = image.scale(im, expected:size(1), expected:size(2), 'bilinear') - tester:assertTensorEq(actual, expected, 1e-5) + local actual = image.scale(im, expected:size(2), expected:size(1), 'bilinear') + assertTensorEq(actual, expected) end function tests.bilinearDownscale() local im = outerProduct{1, 2, 4, 2} local expected = outerProduct{1.25, 3, 2.5} - local actual = image.scale(im, expected:size(1), expected:size(2), 'bilinear') - tester:assertTensorEq(actual, expected, 1e-5) + local actual = image.scale(im, expected:size(2), expected:size(1), 'bilinear') + assertTensorEq(actual, expected) end function tests.bicubicUpscale() local im = outerProduct{1, 2, 4, 2} local expected = outerProduct{1, 1.4375, 2, 3.1875, 4, 3.25, 2} - local actual = image.scale(im, expected:size(1), expected:size(2), 'bicubic') - tester:assertTensorEq(actual, expected, 1e-5) + local actual = image.scale(im, expected:size(2), expected:size(1), 'bicubic') + assertTensorEq(actual, expected) end function tests.bicubicDownscale() local im = outerProduct{1, 2, 4, 2} local expected = outerProduct{1, 3.1875, 2} - local actual = image.scale(im, expected:size(1), expected:size(2), 'bicubic') - tester:assertTensorEq(actual, expected, 1e-5) + local actual = image.scale(im, expected:size(2), expected:size(1), 'bicubic') + assertTensorEq(actual, expected) +end + + +function tests.bicubicUpscale_ByteTensor() + local im = torch.ByteTensor{{0, 1, 32}} + local expected = torch.ByteTensor{{0, 0, 9, 32}} + local actual = image.scale(im, expected:size(2), expected:size(1), 'bicubic') + assertTensorEq(actual, expected) +end + + +function tests.bilinearUpscale_ByteTensor() + local im = torch.ByteTensor{{1, 2}, + {2, 3}} + local expected = torch.ByteTensor{{1, 1, 2}, + {1, 1, 2}, + {2, 2, 3}} + local actual = image.scale(im, expected:size(2), expected:size(1)) + assertTensorEq(actual, expected) end |