diff --git a/libvips/convolution/compass.c b/libvips/convolution/compass.c index 938d8815..3a26a701 100644 --- a/libvips/convolution/compass.c +++ b/libvips/convolution/compass.c @@ -114,16 +114,13 @@ vips_compass_build( VipsObject *object ) break; case VIPS_COMBINE_SUM: - x = abs[0]; - for( i = 1; i < compass->times; i++ ) { - if( vips_add( x, abs[i], &combine[i], NULL ) ) - return( -1 ); - x = combine[i]; - } + if( vips_sum( abs, &combine[0], compass->times, NULL ) ) + return( -1 ); + x = combine[0]; break; default: - /* Silence compiler wrning. + /* Silence compiler warning. */ x = NULL; g_assert( 0 ); diff --git a/test/test_convolution.py b/test/test_convolution.py old mode 100644 new mode 100755 index 36386a2d..73a5637a --- a/test/test_convolution.py +++ b/test/test_convolution.py @@ -1,4 +1,9 @@ -#!/usr/bin/python +#!/usr/bin/python3 + +from __future__ import division +from builtins import zip +from builtins import range +from numbers import Number import unittest import math @@ -8,4 +13,91 @@ import math from gi.repository import Vips +# an expanding zip ... if either of the args is a scalar or a one-element list, +# duplicate it down the other side +def zip_expand(x, y): + # handle singleton list case + if isinstance(x, list) and len(x) == 1: + x = x[0] + if isinstance(y, list) and len(y) == 1: + y = y[0] + if isinstance(x, list) and isinstance(y, list): + return list(zip(x, y)) + elif isinstance(x, list): + return [[i, y] for i in x] + elif isinstance(y, list): + return [[x, j] for j in y] + else: + return [[x, y]] + +# run a 1-ary function on a thing -- loop over elements if the +# thing is a list +def run_fn(fn, x): + if isinstance(x, list): + return [fn(i) for i in x] + else: + return fn(x) + +# run a 2-ary function on two things -- loop over elements pairwise if the +# things are lists +def run_fn2(fn, x, y): + if isinstance(x, Vips.Image) or isinstance(y, Vips.Image): + return fn(x, y) + elif isinstance(x, list) or isinstance(y, list): + return [fn(i, j) for i, j in zip_expand(x, y)] + else: + return fn(x, y) + +# point convolution +def conv(image, mask, x_position, y_position): + s = 0.0 + for x in range(0, mask.width): + for y in range(0, mask.height): + m = mask.getpoint(x, y) + i = image.getpoint(x + x_position, y + y_position) + p = run_fn2(lambda a, b: a * b, m, i) + s = run_fn2(lambda a, b: a + b, s, p) + + return run_fn2(lambda a, b: a / b, s, mask.get_scale()) + +class TestConvolution(unittest.TestCase): + # test a pair of things which can be lists for approx. equality + def assertAlmostEqualObjects(self, a, b, places = 4, msg = ''): + #print 'assertAlmostEqualObjects %s = %s' % (a, b) + for x, y in zip_expand(a, b): + self.assertAlmostEqual(x, y, places = places, msg = msg) + + def setUp(self): + im = Vips.Image.mask_ideal(100, 100, 0.5, reject = True, optical = True) + self.colour = im * [1, 2, 3] + [2, 3, 4] + self.mono = self.colour.extract_band(1) + self.all_images = [self.mono, self.colour] + sharp = Vips.Image.new_from_array([[-1, -1, -1], + [-1, 16, -1], + [-1, -1, -1]], scale = 8) + blur = Vips.Image.new_from_array([[1, 1, 1], + [1, 1, 1], + [1, 1, 1]], scale = 9) + line = Vips.Image.new_from_array([[1, 1, 1], [-2, -2, -2], [1, 1, 1]]) + sobel = Vips.Image.new_from_array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]) + self.all_masks = [sharp, blur, line, sobel] + + def test_conv(self): + for im in self.all_images: + for msk in self.all_masks: + for prec in [Vips.Precision.INTEGER, Vips.Precision.FLOAT]: + convolved = im.conv(msk, precision = prec) + + result = convolved.getpoint(25, 50) + true = conv(im, msk, 24, 49) + self.assertAlmostEqualObjects(result, true) + + result = convolved.getpoint(50, 50) + true = conv(im, msk, 49, 49) + self.assertAlmostEqualObjects(result, true) + + + +if __name__ == '__main__': + unittest.main()