diff options
author | John Agapiou <jagapiou@google.com> | 2015-12-09 16:48:43 +0300 |
---|---|---|
committer | John Agapiou <jagapiou@google.com> | 2015-12-21 13:05:20 +0300 |
commit | c98cba9e50d8e8d1499b614a8e7d8df4969691e3 (patch) | |
tree | 803c0b0cd56120f35b5783bb7806f5a32816b087 /test | |
parent | 045e9d443fd947880e4ce2e949abb2de1247d69f (diff) |
Fix issues with ByteTensors.
Summary
=======
In generic/image.c many of the intermediate calculations are done using the
datatype `real`. For ByteTensors this is an integral type meaning that undesired
results are obtained for ByteTensors.
The following methods are fixed for ByteTensors:
* y2jet
* rgb2y
* rgb2hsv
* hsv2rgb
* rgb2hsl
* hsl2rgb
* gaussian
The following methods are disabled for ByteTensors:
* rgb2lab
* lab2rgb
The following methods are believed to be fixed for ByteTensors, but have not
been tested:
* warp
* rotate
* polar
The following methods have not been functionally modified, are believed to
already work for ByteTensors, but have not been tested:
* crop
* translate
* vflip
* hflip
* flip
Changes
=======
generic/image.c
---------------
* Define a `temp_t` type to control the precision of intermediate
calculations. This avoids hard-wiring 64-bit precision for 32-bit
calculations.
* Use `temp_t` in place of `real` for intermediate variables. If the function
already uses `float` for intermediate calculations then continue to use
`float`.
* Rename `image_(FromDouble)` to `image_(FromIntermediate)`.
* Use `image_(FromIntermediate)` wherever assigning a non-`real` to a `real`.
For ByteTensors, this ensures rounding and clipping to the range [0, 255].
* For color conversion, use the ranges [0, 255] rather than [0, 1] for
ByteTensors.
* Remove `rgb2lab` and `lab2rgb` for ByteTensors. Lab is not constrained to
[0, 1] and does not fit well into a 1 byte quantization depth. It makes no
sense to have this for ByteTensors.
* Make `saturate` a noop for ByteTensors, since they are already constrained
to [0, 255].
* In `colorize` randomize a color value only if the colormap has not been set.
There was a bug that meant that for ByteTensors any color with R=255 would
be randomized.
test/test.lua
-------------
* Add gaussian test cases.
* Modify bilinearUpscale_ByteTensor test to reflect change to rounding
behavior.
* Update test cases so all color conversion functions are covered.
init.lua
--------
* Allow y2jet to operate on FloatTensors and ByteTensors.
image.c
-------
* Move sRGB conversion functions to generic/image.c.
Diffstat (limited to 'test')
-rw-r--r-- | test/test.lua | 151 |
1 files changed, 131 insertions, 20 deletions
diff --git a/test/test.lua b/test/test.lua index 8ba2ced..a878208 100644 --- a/test/test.lua +++ b/test/test.lua @@ -2,20 +2,43 @@ local test = {} local precision = 1e-4 local precision_mean = 1e-3 local precision_std = 1e-1 --- Specific precision for Lab conversion -local Lab_precision = 1e-4 + local function getTestImagePath(name) return paths.concat(sys.fpath(), 'assets', name) end + local function assertByteTensorEq(actual, expected, rcond, msg) rcond = rcond or 1e-5 tester:assertTensorEq(actual:double(), expected:double(), rcond, msg) end + +local function toByteTensor(x) + local y = torch.round(x):byte() + y[torch.le(x, 0)] = 0 + y[torch.ge(x, 255)] = 255 + return y +end + + +local function toByteImage(x) + return toByteTensor(torch.mul(x, 255)) +end + + +local function testFunctionOnByteTensor(f, msg) + local lena = image.lena():float() + local expected = toByteImage(f(lena)) + local actual = f(toByteImage(lena)) + assertByteTensorEq(actual, expected, nil, msg) +end + + local unpack = unpack and unpack or table.unpack -- lua52 compatibility + ---------------------------------------------------------------------- -- Flip test -- @@ -142,6 +165,20 @@ function test.gaussian() end end + +function test.byteGaussian() + local expected = toByteTensor(image.gaussian{ + amplitude = 1000, + tensor = torch.FloatTensor(5, 5), + }) + local actual = image.gaussian{ + amplitude = 1000, + tensor = torch.ByteTensor(5, 5), + } + assertByteTensorEq(actual, expected) +end + + ---------------------------------------------------------------------- -- Gaussian pyramid test -- @@ -210,13 +247,14 @@ end function test.bilinearUpscale_ByteTensor() local im = torch.ByteTensor{{1, 2}, {2, 3}} - local expected = torch.ByteTensor{{1, 1, 2}, - {1, 1, 2}, - {2, 2, 3}} + local expected = torch.ByteTensor{{1, 2, 2}, + {2, 3, 3}, + {2, 3, 3}} local actual = image.scale(im, expected:size(2), expected:size(1)) assertByteTensorEq(actual, expected) end + ---------------------------------------------------------------------- -- Scale test -- @@ -378,25 +416,98 @@ end ---------------------------------------------------------------------- -- Lab conversion test --- -function test.TestLabConversionBackAndForth() - -- This test breaks if someone removes lena from the repo - local imfile = getTestImagePath('lena.jpg') - if not paths.filep(imfile) then - error(imfile .. ' is missing!') - end +-- These tests break if someone removes lena from the repo + + +local function testRoundtrip(forward, backward) + local expected = image.lena() + local actual = backward(forward(expected)) + tester:assertTensorEq(actual, expected, 1e-4) +end + + +function test.rgb2lab() + testRoundtrip(image.rgb2lab, image.lab2rgb) +end - -- Load lena directly from the filename - local img = image.loadJPG(imfile) - -- Convert to LAB and back to RGB - local lab = image.rgb2lab(img) - local img2 = image.lab2rgb(lab) - -- Compare RGB images - tester:assertlt((img - img2):abs():max(), Lab_precision, - 'RGB <-> LAB conversion produces wrong results! ') +function test.rgb2hsv() + testRoundtrip(image.rgb2hsv, image.hsv2rgb) end + +function test.rgb2hsl() + testRoundtrip(image.rgb2hsl, image.hsl2rgb) +end + + +function test.rgb2y() + local x = torch.FloatTensor{{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}}:transpose(1, 3) + local actual = image.rgb2y(x) + local expected = torch.FloatTensor{{0.299, 0.587, 0.114}} + tester:assertTensorEq(actual, expected, 1e-5) +end + + +function test.y2jet() + local levels = torch.Tensor{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + local expected = image.jetColormap(10) + local actual = image.y2jet(levels)[{{}, 1, {}}]:t() + tester:assertTensorEq(actual, expected, 1e-5) +end + + +function test.rgb2labByteTensor() + local lena = image.lena():byte() + tester:assertError(function () image.rgb2lab(lena) end) + tester:assertError(function () image.lab2rgb(lena) end) +end + + +local function testByteTensorRoundtrip(forward, backward, cond, msg) + local lena = toByteImage(image.lena()) + local expected = lena + local actual = backward(forward(expected)) + assertByteTensorEq(actual, expected, cond, msg) +end + + +function test.toFromByteTensor() + local expected = toByteImage(image.lena():float()) + local actual = toByteImage(expected:float():div(255)) + assertByteTensorEq(actual, expected, nil, msg) +end + + +function test.rgb2hsvByteTensor() + testFunctionOnByteTensor(image.rgb2hsv, 'image.rgb2hsv error for ByteTensor') + testFunctionOnByteTensor(image.hsv2rgb, 'image.hsv2rgb error for ByteTensor') + testByteTensorRoundtrip(image.rgb2hsv, image.hsv2rgb, 2, + 'image.rgb2hsv roundtrip error for ByteTensor') +end + + +function test.rgb2hslByteTensor() + testFunctionOnByteTensor(image.rgb2hsl, 'image.hsl2rgb error for ByteTensor') + testFunctionOnByteTensor(image.hsl2rgb, 'image.rgb2hsl error for ByteTensor') + testByteTensorRoundtrip(image.rgb2hsl, image.hsl2rgb, 3, + 'image.rgb2hsl roundtrip error for ByteTensor') +end + + +function test.rgb2yByteTensor() + testFunctionOnByteTensor(image.rgb2y, 'image.rgb2y error for ByteTensor') +end + + +function test.y2jetByteTensor() + local levels = torch.Tensor{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + local expected = toByteImage(image.y2jet(levels)) + local actual = image.y2jet(levels:byte()) + assertByteTensorEq(actual, expected, nil) +end + + ---------------------------------------------------------------------- -- PNG test -- |