Example #1
0
    def test_magickload(self):
        def bmp_valid(im):
            a = im(100, 100)

            assert_almost_equal_objects(a, [227, 216, 201])
            assert im.width == 1419
            assert im.height == 1001

        self.file_loader("magickload", BMP_FILE, bmp_valid)
        self.buffer_loader("magickload_buffer", BMP_FILE, bmp_valid)

        # we should have rgb or rgba for svg files ... different versions of
        # IM handle this differently. GM even gives 1 band.
        im = pyvips.Image.magickload(SVG_FILE)
        assert im.bands == 3 or im.bands == 4 or im.bands == 1

        # density should change size of generated svg
        im = pyvips.Image.magickload(SVG_FILE, density='100')
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(SVG_FILE, density='200')
        # This seems to fail on travis, no idea why, some problem in their IM
        # perhaps
        # assert im.width == width * 2
        # assert im.height == height * 2

        im = pyvips.Image.magickload(GIF_ANIM_FILE)
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(GIF_ANIM_FILE, n=-1)
        assert im.width == width
        assert im.height == height * 5

        # page/n let you pick a range of pages
        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            im = pyvips.Image.magickload(GIF_ANIM_FILE)
            width = im.width
            height = im.height
            im = pyvips.Image.magickload(GIF_ANIM_FILE, page=1, n=2)
            assert im.width == width
            assert im.height == height * 2
            page_height = im.get("page-height")
            assert page_height == height

        # should work for dicom
        im = pyvips.Image.magickload(DICOM_FILE)
        assert im.width == 128
        assert im.height == 128
        # some IMs are 3 bands, some are 1, can't really test
        # assert im.bands == 1

        # libvips has its own sniffer for ICO, test that
        with open(ICO_FILE, 'rb') as f:
            buf = f.read()

        im = pyvips.Image.new_from_buffer(buf, "")
        assert im.width == 16
        assert im.height == 16
Example #2
0
    def test_magickload(self):
        def bmp_valid(im):
            a = im(100, 100)

            assert_almost_equal_objects(a, [227, 216, 201])
            assert im.width == 1419
            assert im.height == 1001

        self.file_loader("magickload", BMP_FILE, bmp_valid)
        self.buffer_loader("magickload_buffer", BMP_FILE, bmp_valid)

        # we should have rgba for svg files
        im = pyvips.Image.magickload(SVG_FILE)
        assert im.bands == 4

        # density should change size of generated svg
        im = pyvips.Image.magickload(SVG_FILE, density='100')
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(SVG_FILE, density='200')
        # This seems to fail on travis, no idea why, some problem in their IM
        # perhaps
        # assert im.width == width * 2
        # assert im.height == height * 2

        # all-frames should load every frame of the animation
        # (though all-frames is deprecated)
        im = pyvips.Image.magickload(GIF_ANIM_FILE)
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(GIF_ANIM_FILE, all_frames=True)
        assert im.width == width
        assert im.height == height * 5

        # page/n let you pick a range of pages
        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            im = pyvips.Image.magickload(GIF_ANIM_FILE)
            width = im.width
            height = im.height
            im = pyvips.Image.magickload(GIF_ANIM_FILE, page=1, n=2)
            assert im.width == width
            assert im.height == height * 2
            page_height = im.get("page-height")
            assert page_height == height

        # should work for dicom
        im = pyvips.Image.magickload(DICOM_FILE)
        assert im.width == 128
        assert im.height == 128
        # some IMs are 3 bands, some are 1, can't really test
        # assert im.bands == 1

        # added in 8.7
        if have("magicksave"):
            self.save_load_file(".bmp", "", self.colour, 0)
            self.save_load_buffer("magicksave_buffer", "magickload_buffer",
                                  self.colour, 0, format="BMP")
            self.save_load("%s.bmp", self.colour)
Example #3
0
    def test_magickload(self):
        if pyvips.type_find("VipsForeign", "magickload") == 0 or \
                not os.path.isfile(GIF_FILE):
            print("no magick support, skipping test")
            return

        def gif_valid(self, im):
            # some libMagick produce an RGB for this image, some a mono, some
            # rgba, some have a valid alpha, some don't :-(
            # therefore ... just test channel 0
            a = im(10, 10)[0]

            self.assertAlmostEqual(a, 33)
            self.assertEqual(im.width, 159)
            self.assertEqual(im.height, 203)

        self.file_loader("magickload", GIF_FILE, gif_valid)
        self.buffer_loader("magickload_buffer", GIF_FILE, gif_valid)

        # we should have rgba for svg files
        im = pyvips.Image.magickload(SVG_FILE)
        self.assertEqual(im.bands, 4)

        # density should change size of generated svg
        im = pyvips.Image.magickload(SVG_FILE, density='100')
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(SVG_FILE, density='200')
        # This seems to fail on travis, no idea why, some problem in their IM
        # perhaps
        # self.assertEqual(im.width, width * 2)
        # self.assertEqual(im.height, height * 2)

        # all-frames should load every frame of the animation
        # (though all-frames is deprecated)
        im = pyvips.Image.magickload(GIF_ANIM_FILE)
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(GIF_ANIM_FILE, all_frames=True)
        self.assertEqual(im.width, width)
        self.assertEqual(im.height, height * 5)

        # page/n let you pick a range of pages
        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            im = pyvips.Image.magickload(GIF_ANIM_FILE)
            width = im.width
            height = im.height
            im = pyvips.Image.magickload(GIF_ANIM_FILE, page=1, n=2)
            self.assertEqual(im.width, width)
            self.assertEqual(im.height, height * 2)
            page_height = im.get_value("page-height")
            self.assertEqual(page_height, height)

        # should work for dicom
        im = pyvips.Image.magickload(DICOM_FILE)
        self.assertEqual(im.width, 128)
        self.assertEqual(im.height, 128)
Example #4
0
    def resize(cls, im, *args, **kwargs):
        kernel = kwargs.pop('kernel', 'cubic')
        if at_least_libvips(8, 3):
            # resize with kernel added in 8.3
            kwargs['kernel'] = kernel
        else:
            kwargs['interpolate'] = Interpolate.new('bi' + kernel)

        return im.resize(*args, **kwargs)
Example #5
0
    def get_args(self):
        args = []

        if at_least_libvips(8, 7):
            p_names = ffi.new('char**[1]')
            p_flags = ffi.new('int*[1]')
            p_n_args = ffi.new('int[1]')

            result = vips_lib.vips_object_get_args(self.object, p_names,
                                                   p_flags, p_n_args)

            if result != 0:
                raise Error('unable to get arguments from operation')

            p_names = p_names[0]
            p_flags = p_flags[0]
            n_args = p_n_args[0]

            for i in range(0, n_args):
                flags = p_flags[i]
                if (flags & _CONSTRUCT) != 0:
                    name = _to_string(p_names[i])

                    # libvips uses '-' to separate parts of arg names, but we
                    # need '_' for Python
                    name = name.replace('-', '_')

                    args.append([name, flags])
        else:

            def add_construct(self, pspec, argument_class, argument_instance,
                              a, b):
                flags = argument_class.flags
                if (flags & _CONSTRUCT) != 0:
                    name = _to_string(pspec.name)

                    # libvips uses '-' to separate parts of arg names, but we
                    # need '_' for Python
                    name = name.replace('-', '_')

                    args.append([name, flags])

                return ffi.NULL

            cb = ffi.callback('VipsArgumentMapFn', add_construct)
            vips_lib.vips_argument_map(self.object, cb, ffi.NULL, ffi.NULL)

        return args
Example #6
0
    def test_hist_local(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        im2 = im.hist_local(10, 10)

        assert im.width == im2.width
        assert im.height == im2.height

        assert im.avg() < im2.avg()
        assert im.deviate() < im2.deviate()

        if pyvips.at_least_libvips(8, 5):
            im3 = im.hist_local(10, 10, max_slope=3)

            assert im.width == im3.width
            assert im.height == im3.height

            assert im3.deviate() < im2.deviate()
Example #7
0
    def test_hist_local(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        im2 = im.hist_local(10, 10)

        self.assertEqual(im.width, im2.width)
        self.assertEqual(im.height, im2.height)

        self.assertTrue(im.avg() < im2.avg())
        self.assertTrue(im.deviate() < im2.deviate())

        if pyvips.at_least_libvips(8, 5):
            im3 = im.hist_local(10, 10, max_slope=3)

            self.assertEqual(im.width, im2.width)
            self.assertEqual(im.height, im2.height)

            self.assertTrue(im3.deviate() < im2.deviate())
Example #8
0
    def test_hist_local(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        im2 = im.hist_local(10, 10)

        assert im.width == im2.width
        assert im.height == im2.height

        assert im.avg() < im2.avg()
        assert im.deviate() < im2.deviate()

        if pyvips.at_least_libvips(8, 5):
            im3 = im.hist_local(10, 10, max_slope=3)

            assert im.width == im3.width
            assert im.height == im3.height

            assert im3.deviate() < im2.deviate()
Example #9
0
    def get_fields(self):
        """Get a list of all the metadata fields on an image.

        Returns:
            [string]

        """

        names = []

        if at_least_libvips(8, 5):
            array = vips_lib.vips_image_get_fields(self.pointer)
            i = 0
            while array[i] != ffi.NULL:
                name = _to_string(array[i])
                names.append(name)
                glib_lib.g_free(array[i])
                i += 1
            glib_lib.g_free(array)

        return names
Example #10
0
    def test_thumbnail(self):
        if not pyvips.at_least_libvips(8, 5):
            return

        im = pyvips.Image.thumbnail(JPEG_FILE, 100)

        self.assertEqual(im.width, 100)
        self.assertEqual(im.bands, 3)
        self.assertEqual(im.bands, 3)

        # the average shouldn't move too much
        im_orig = pyvips.Image.new_from_file(JPEG_FILE)
        self.assertLess(abs(im_orig.avg() - im.avg()), 1)

        # make sure we always get the right width
        for width in range(1000, 1, -13):
            im = pyvips.Image.thumbnail(JPEG_FILE, width)
            self.assertEqual(im.width, width)

        # should fit one of width or height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300)
        self.assertEqual(im.width, 100)
        self.assertNotEqual(im.height, 300)
        im = pyvips.Image.thumbnail(JPEG_FILE, 300, height=100)
        self.assertNotEqual(im.width, 300)
        self.assertEqual(im.height, 100)

        # with @crop, should fit both width and height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100,
                                    height=300, crop=True)
        self.assertEqual(im.width, 100)
        self.assertEqual(im.height, 300)

        im1 = pyvips.Image.thumbnail(JPEG_FILE, 100)
        with open(JPEG_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        self.assertLess(abs(im1.avg() - im2.avg()), 1)
Example #11
0
    def fetch(self, x, y, w, h):
        """Fill a region with pixel data.

        Pixels are filled with data!

        Returns:
            Pixel data.

        Raises:
            :class:`.Error`

        """

        if not at_least_libvips(8, 8):
            raise Error('libvips too old')

        psize = ffi.new('size_t *')
        pointer = vips_lib.vips_region_fetch(self.pointer, x, y, w, h, psize)
        if pointer == ffi.NULL:
            raise Error('unable to fetch from region')

        pointer = ffi.gc(pointer, glib_lib.g_free)
        return ffi.buffer(pointer, psize[0])
Example #12
0
    def test_gifload(self):
        def gif_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [33])
            assert im.width == 159
            assert im.height == 203
            assert im.bands == 1

        self.file_loader("gifload", GIF_FILE, gif_valid)
        self.buffer_loader("gifload_buffer", GIF_FILE, gif_valid)

        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE)
            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=2)
            assert x2.height == 2 * x1.height
            page_height = x2.get("page-height")
            assert page_height == x1.height

            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            assert x2.height == 5 * x1.height

            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, page=1, n=-1)
            assert x2.height == 4 * x1.height
Example #13
0
    def test_gifload(self):
        def gif_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [33])
            assert im.width == 159
            assert im.height == 203
            assert im.bands == 1

        self.file_loader("gifload", GIF_FILE, gif_valid)
        self.buffer_loader("gifload_buffer", GIF_FILE, gif_valid)

        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE)
            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=2)
            assert x2.height == 2 * x1.height
            page_height = x2.get("page-height")
            assert page_height == x1.height

            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            assert x2.height == 5 * x1.height

            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, page=1, n=-1)
            assert x2.height == 4 * x1.height
Example #14
0
    def test_gifload(self):
        def gif_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [33])
            assert im.width == 159
            assert im.height == 203
            assert im.bands == 1

        self.file_loader("gifload", GIF_FILE, gif_valid)
        self.buffer_loader("gifload_buffer", GIF_FILE, gif_valid)

        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE)
            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=2)
            assert x2.height == 2 * x1.height
            page_height = x2.get("page-height")
            assert page_height == x1.height

            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            assert x2.height == 5 * x1.height
            # our test gif has delay 0 for the first frame set in error
            assert x2.get("delay") == [0, 50, 50, 50, 50]

            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, page=1, n=-1)
            assert x2.height == 4 * x1.height

            animation = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            filename = temp_filename(self.tempdir, '.png')
            animation.write_to_file(filename)
            # Uncomment to see output file
            # animation.write_to_file('cogs.png')

            assert filecmp.cmp(GIF_ANIM_EXPECTED_PNG_FILE,
                               filename,
                               shallow=False)
Example #15
0
    def __init__(self, operation_name):
        op = Operation.new_from_name(operation_name)

        self.description = op.get_description()
        self.flags = vips_lib.vips_operation_get_flags(op.pointer)

        # build a list of constructor arg [name, flags] pairs in arg order
        arguments = []

        def add_args(name, flags):
            if (flags & _CONSTRUCT) != 0:
                # libvips uses '-' to separate parts of arg names, but we
                # need '_' for Python
                name = name.replace('-', '_')
                arguments.append([name, flags])

        if at_least_libvips(8, 7):
            p_names = ffi.new('char**[1]')
            p_flags = ffi.new('int*[1]')
            p_n_args = ffi.new('int[1]')
            result = vips_lib.vips_object_get_args(op.vobject, p_names,
                                                   p_flags, p_n_args)
            if result != 0:
                raise Error('unable to get arguments from operation')
            p_names = p_names[0]
            p_flags = p_flags[0]
            n_args = p_n_args[0]

            for i in range(0, n_args):
                add_args(_to_string(p_names[i]), p_flags[i])
        else:

            def add_construct(self, pspec, argument_class, argument_instance,
                              a, b):
                add_args(_to_string(pspec.name), argument_class.flags)
                return ffi.NULL

            cb = ffi.callback('VipsArgumentMapFn', add_construct)
            vips_lib.vips_argument_map(op.vobject, cb, ffi.NULL, ffi.NULL)

        # logger.debug('arguments = %s', self.arguments)

        # build a hash from arg name to detailed arg information
        self.details = {}
        for name, flags in arguments:
            self.details[name] = {
                "name": name,
                "flags": flags,
                "blurb": op.get_blurb(name),
                "type": op.get_typeof(name)
            }

        # lists of arg names by category
        self.required_input = []
        self.optional_input = []
        self.required_output = []
        self.optional_output = []

        for name, flags in arguments:
            if ((flags & _INPUT) != 0 and (flags & _REQUIRED) != 0
                    and (flags & _DEPRECATED) == 0):
                self.required_input.append(name)

                # required inputs which we MODIFY are also required outputs
                if (flags & _MODIFY) != 0:
                    self.required_output.append(name)

            if ((flags & _OUTPUT) != 0 and (flags & _REQUIRED) != 0
                    and (flags & _DEPRECATED) == 0):
                self.required_output.append(name)

            # we let deprecated optional args through, but warn about them
            # if they get used, see below
            if ((flags & _INPUT) != 0 and (flags & _REQUIRED) == 0):
                self.optional_input.append(name)

            if ((flags & _OUTPUT) != 0 and (flags & _REQUIRED) == 0):
                self.optional_output.append(name)

        # find the first required input image arg, if any ... that will be self
        self.member_x = None
        for name in self.required_input:
            details = self.details[name]
            if details['type'] == GValue.image_type:
                self.member_x = name
                break

        # method args are required args, but without the image they are a
        # method on
        if self.member_x is not None:
            self.method_args = list(self.required_input)
            self.method_args.remove(self.member_x)
        else:
            self.method_args = self.required_input
Example #16
0
    def test_tiff(self):
        if pyvips.type_find("VipsForeign", "tiffload") == 0 or \
                not os.path.isfile(TIF_FILE):
            print("no tiff support, skipping test")
            return

        def tiff_valid(self, im):
            a = im(10, 10)
            self.assertAlmostEqualObjects(a, [38671.0, 33914.0, 26762.0])
            self.assertEqual(im.width, 290)
            self.assertEqual(im.height, 442)
            self.assertEqual(im.bands, 3)

        self.file_loader("tiffload", TIF_FILE, tiff_valid)
        self.buffer_loader("tiffload_buffer", TIF_FILE, tiff_valid)
        if pyvips.at_least_libvips(8, 5):
            self.save_load_buffer("tiffsave_buffer",
                                  "tiffload_buffer",
                                  self.colour)
        self.save_load("%s.tif", self.mono)
        self.save_load("%s.tif", self.colour)
        self.save_load("%s.tif", self.cmyk)

        self.save_load("%s.tif", self.onebit)
        self.save_load_file(".tif", "[squash]", self.onebit, 0)
        self.save_load_file(".tif", "[miniswhite]", self.onebit, 0)
        self.save_load_file(".tif", "[squash,miniswhite]", self.onebit, 0)

        self.save_load_file(".tif",
                            "[profile={0}]".format(SRGB_FILE),
                            self.colour, 0)
        self.save_load_file(".tif", "[tile]", self.colour, 0)
        self.save_load_file(".tif", "[tile,pyramid]", self.colour, 0)
        self.save_load_file(".tif",
                            "[tile,pyramid,compression=jpeg]", self.colour, 80)
        self.save_load_file(".tif", "[bigtiff]", self.colour, 0)
        self.save_load_file(".tif", "[compression=jpeg]", self.colour, 80)
        self.save_load_file(".tif",
                            "[tile,tile-width=256]", self.colour, 10)

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set_value("orientation", 2)
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get_value("orientation")
        self.assertEqual(y, 2)

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set_value("orientation", 2)
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get_value("orientation")
        self.assertEqual(y, 2)
        x.remove("orientation")

        filename = temp_filename(self.tempdir, '.tif')
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get_value("orientation")
        self.assertEqual(y, 1)

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set_value("orientation", 6)
        x.write_to_file(filename)
        x1 = pyvips.Image.new_from_file(filename)
        x2 = pyvips.Image.new_from_file(filename, autorotate=True)
        self.assertEqual(x1.width, x2.height)
        self.assertEqual(x1.height, x2.width)

        # OME support in 8.5
        if pyvips.at_least_libvips(8, 5):
            x = pyvips.Image.new_from_file(OME_FILE)
            self.assertEqual(x.width, 439)
            self.assertEqual(x.height, 167)
            page_height = x.height

            x = pyvips.Image.new_from_file(OME_FILE, n=-1)
            self.assertEqual(x.width, 439)
            self.assertEqual(x.height, page_height * 15)

            x = pyvips.Image.new_from_file(OME_FILE, page=1, n=-1)
            self.assertEqual(x.width, 439)
            self.assertEqual(x.height, page_height * 14)

            x = pyvips.Image.new_from_file(OME_FILE, page=1, n=2)
            self.assertEqual(x.width, 439)
            self.assertEqual(x.height, page_height * 2)

            x = pyvips.Image.new_from_file(OME_FILE, n=-1)
            self.assertEqual(x(0, 166)[0], 96)
            self.assertEqual(x(0, 167)[0], 0)
            self.assertEqual(x(0, 168)[0], 1)

            filename = temp_filename(self.tempdir, '.tif')
            x.write_to_file(filename)

            x = pyvips.Image.new_from_file(filename, n=-1)
            self.assertEqual(x.width, 439)
            self.assertEqual(x.height, page_height * 15)
            self.assertEqual(x(0, 166)[0], 96)
            self.assertEqual(x(0, 167)[0], 0)
            self.assertEqual(x(0, 168)[0], 1)

        # pyr save to buffer added in 8.6
        if pyvips.at_least_libvips(8, 6):
            x = pyvips.Image.new_from_file(TIF_FILE)
            buf = x.tiffsave_buffer(tile=True, pyramid=True)
            filename = temp_filename(self.tempdir, '.tif')
            x.tiffsave(filename, tile=True, pyramid=True)
            with open(filename, 'rb') as f:
                buf2 = f.read()
            self.assertEqual(len(buf), len(buf2))

            a = pyvips.Image.new_from_buffer(buf, "", page=2)
            b = pyvips.Image.new_from_buffer(buf2, "", page=2)
            self.assertEqual(a.width, b.width)
            self.assertEqual(a.height, b.height)
            self.assertEqual(a.avg(), b.avg())
Example #17
0
    def height(self):
        """Height of pixels held by region."""
        if not at_least_libvips(8, 8):
            raise Error('libvips too old')

        return vips_lib.vips_region_height(self.pointer)
Example #18
0
    def width(self):
        """Width of pixels held by region."""
        if not at_least_libvips(8, 8):
            raise Error('libvips too old')

        return vips_lib.vips_region_width(self.pointer)
Example #19
0
class TestConnections:
    @classmethod
    def setup_class(cls):
        cls.tempdir = tempfile.mkdtemp()

    @skip_if_no('jpegload')
    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 9),
                        reason="requires libvips >= 8.9")
    def test_connection(self):
        source = pyvips.Source.new_from_file(JPEG_FILE)
        image = pyvips.Image.new_from_source(source, '', access='sequential')
        filename = temp_filename(self.tempdir, '.png')
        target = pyvips.Target.new_to_file(filename)
        image.write_to_target(target, '.png')

        image = pyvips.Image.new_from_file(JPEG_FILE, access='sequential')
        image2 = pyvips.Image.new_from_file(filename, access='sequential')

        assert (image - image2).abs().max() < 10

    @skip_if_no('jpegload')
    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 9),
                        reason="requires libvips >= 8.9")
    def test_source_custom_no_seek(self):
        input_file = open(JPEG_FILE, "rb")

        def read_handler(size):
            return input_file.read(size)

        source = pyvips.SourceCustom()
        source.on_read(read_handler)

        image = pyvips.Image.new_from_source(source, '', access='sequential')
        image2 = pyvips.Image.new_from_file(JPEG_FILE, access='sequential')

        assert (image - image2).abs().max() == 0

    @skip_if_no('jpegload')
    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 9),
                        reason="requires libvips >= 8.9")
    def test_source_custom(self):
        input_file = open(JPEG_FILE, "rb")

        def read_handler(size):
            return input_file.read(size)

        def seek_handler(offset, whence):
            input_file.seek(offset, whence)
            return input_file.tell()

        source = pyvips.SourceCustom()
        source.on_read(read_handler)
        source.on_seek(seek_handler)

        image = pyvips.Image.new_from_source(source, '', access='sequential')
        image2 = pyvips.Image.new_from_file(JPEG_FILE, access='sequential')

        assert (image - image2).abs().max() == 0

    @skip_if_no('jpegload')
    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 9),
                        reason="requires libvips >= 8.9")
    def test_target_custom(self):
        filename = temp_filename(self.tempdir, '.png')
        output_file = open(filename, "wb")

        def write_handler(chunk):
            return output_file.write(chunk)

        def finish_handler():
            output_file.close()

        target = pyvips.TargetCustom()
        target.on_write(write_handler)
        target.on_finish(finish_handler)

        image = pyvips.Image.new_from_file(JPEG_FILE, access='sequential')
        image.write_to_target(target, '.png')

        image = pyvips.Image.new_from_file(JPEG_FILE, access='sequential')
        image2 = pyvips.Image.new_from_file(filename, access='sequential')

        assert (image - image2).abs().max() == 0

    # test webp as well, since that maps the stream rather than using read

    @skip_if_no('webpload')
    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 9),
                        reason="requires libvips >= 8.9")
    def test_source_custom_webp_no_seek(self):
        input_file = open(WEBP_FILE, "rb")

        def read_handler(size):
            return input_file.read(size)

        source = pyvips.SourceCustom()
        source.on_read(read_handler)

        image = pyvips.Image.new_from_source(source, '', access='sequential')
        image2 = pyvips.Image.new_from_file(WEBP_FILE, access='sequential')

        assert (image - image2).abs().max() == 0

    @skip_if_no('webpload')
    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 9),
                        reason="requires libvips >= 8.9")
    def test_source_custom_webp(self):
        input_file = open(WEBP_FILE, "rb")

        def read_handler(size):
            return input_file.read(size)

        def seek_handler(offset, whence):
            input_file.seek(offset, whence)
            return input_file.tell()

        source = pyvips.SourceCustom()
        source.on_read(read_handler)
        source.on_seek(seek_handler)

        image = pyvips.Image.new_from_source(source, '', access='sequential')
        image2 = pyvips.Image.new_from_file(WEBP_FILE, access='sequential')

        assert (image - image2).abs().max() == 0
Example #20
0
    def test_magickload(self):
        def bmp_valid(im):
            a = im(100, 100)

            assert_almost_equal_objects(a, [227, 216, 201])
            assert im.width == 1419
            assert im.height == 1001

        self.file_loader("magickload", BMP_FILE, bmp_valid)
        self.buffer_loader("magickload_buffer", BMP_FILE, bmp_valid)

        # we should have rgb or rgba for svg files ... different versions of
        # IM handle this differently
        im = pyvips.Image.magickload(SVG_FILE)
        assert im.bands == 3 or im.bands == 4

        # density should change size of generated svg
        im = pyvips.Image.magickload(SVG_FILE, density='100')
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(SVG_FILE, density='200')
        # This seems to fail on travis, no idea why, some problem in their IM
        # perhaps
        # assert im.width == width * 2
        # assert im.height == height * 2

        # all-frames should load every frame of the animation
        # (though all-frames is deprecated)
        im = pyvips.Image.magickload(GIF_ANIM_FILE)
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(GIF_ANIM_FILE, all_frames=True)
        assert im.width == width
        assert im.height == height * 5

        # page/n let you pick a range of pages
        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            im = pyvips.Image.magickload(GIF_ANIM_FILE)
            width = im.width
            height = im.height
            im = pyvips.Image.magickload(GIF_ANIM_FILE, page=1, n=2)
            assert im.width == width
            assert im.height == height * 2
            page_height = im.get("page-height")
            assert page_height == height

        # should work for dicom
        im = pyvips.Image.magickload(DICOM_FILE)
        assert im.width == 128
        assert im.height == 128
        # some IMs are 3 bands, some are 1, can't really test
        # assert im.bands == 1

        # added in 8.7
        self.save_load_file(".bmp", "", self.colour, 0)
        self.save_load_buffer("magicksave_buffer", "magickload_buffer",
                              self.colour, 0, format="BMP")
        self.save_load("%s.bmp", self.colour)

        # libvips has its own sniffer for ICO, test that
        with open(ICO_FILE, 'rb') as f:
            buf = f.read()

        im = pyvips.Image.new_from_buffer(buf, "")
        assert im.width == 16
        assert im.height == 16
Example #21
0
class GValue(object):
    """Wrap GValue in a Python class.

    This class wraps :class:`.GValue` in a convenient interface. You can use
    instances of this class to get and set :class:`.GObject` properties.

    On construction, :class:`.GValue` is all zero (empty). You can pass it to
    a get function to have it filled by :class:`.GObject`, or use init to
    set a type, set to set a value, then use it to set an object property.

    GValue lifetime is managed automatically.

    """

    # look up some common gtypes at init for speed
    gbool_type = type_from_name('gboolean')
    gint_type = type_from_name('gint')
    gdouble_type = type_from_name('gdouble')
    gstr_type = type_from_name('gchararray')
    genum_type = type_from_name('GEnum')
    gflags_type = type_from_name('GFlags')
    gobject_type = type_from_name('GObject')
    image_type = type_from_name('VipsImage')
    array_int_type = type_from_name('VipsArrayInt')
    array_double_type = type_from_name('VipsArrayDouble')
    array_image_type = type_from_name('VipsArrayImage')
    refstr_type = type_from_name('VipsRefString')
    blob_type = type_from_name('VipsBlob')

    pyvips.vips_lib.vips_band_format_get_type()
    format_type = type_from_name('VipsBandFormat')

    if at_least_libvips(8, 6):
        pyvips.vips_lib.vips_blend_mode_get_type()
    blend_mode_type = type_from_name('VipsBlendMode')

    # map a gtype to the name of the corresponding Python type
    _gtype_to_python = {
        gbool_type: 'bool',
        gint_type: 'int',
        gdouble_type: 'float',
        gstr_type: 'str',
        refstr_type: 'str',
        genum_type: 'str',
        gflags_type: 'int',
        gobject_type: 'GObject',
        image_type: 'Image',
        array_int_type: 'list[int]',
        array_double_type: 'list[float]',
        array_image_type: 'list[Image]',
        blob_type: 'str'
    }

    @staticmethod
    def gtype_to_python(gtype):
        """Map a gtype to the name of the Python type we use to represent it.

        """

        fundamental = gobject_lib.g_type_fundamental(gtype)

        if gtype in GValue._gtype_to_python:
            return GValue._gtype_to_python[gtype]
        if fundamental in GValue._gtype_to_python:
            return GValue._gtype_to_python[fundamental]
        return '<unknown type>'

    @staticmethod
    def to_enum(gtype, value):
        """Turn a string into an enum value ready to be passed into libvips.

        """

        if isinstance(value, basestring if _is_PY2 else str):
            enum_value = vips_lib.vips_enum_from_nick(b'pyvips', gtype,
                                                      _to_bytes(value))
            if enum_value < 0:
                raise Error('no value {0} in gtype {1} ({2})'.format(
                    value, type_name(gtype), gtype))
        else:
            enum_value = value

        return enum_value

    @staticmethod
    def from_enum(gtype, enum_value):
        """Turn an int back into an enum string.

        """

        pointer = vips_lib.vips_enum_nick(gtype, enum_value)
        if pointer == ffi.NULL:
            raise Error('value not in enum')

        return _to_string(pointer)

    def __init__(self):
        # allocate memory for the gvalue which will be freed on GC
        self.pointer = ffi.new('GValue *')
        # logger.debug('GValue.__init__: pointer = %s', self.pointer)

        # and tag it to be unset on GC as well
        self.gvalue = ffi.gc(self.pointer, gobject_lib.g_value_unset)
        # logger.debug('GValue.__init__: gvalue = %s', self.gvalue)

    def set_type(self, gtype):
        """Set the type of a GValue.

        GValues have a set type, fixed at creation time. Use set_type to set
        the type of a GValue before assigning to it.

        GTypes are 32 or 64-bit integers (depending on the platform). See
        type_find.

        """

        gobject_lib.g_value_init(self.gvalue, gtype)

    def set(self, value):
        """Set a GValue.

        The value is converted to the type of the GValue, if possible, and
        assigned.

        """

        # logger.debug('GValue.set: value = %s', value)

        gtype = self.gvalue.g_type
        fundamental = gobject_lib.g_type_fundamental(gtype)

        if gtype == GValue.gbool_type:
            gobject_lib.g_value_set_boolean(self.gvalue, value)
        elif gtype == GValue.gint_type:
            gobject_lib.g_value_set_int(self.gvalue, int(value))
        elif gtype == GValue.gdouble_type:
            gobject_lib.g_value_set_double(self.gvalue, value)
        elif fundamental == GValue.genum_type:
            gobject_lib.g_value_set_enum(self.gvalue,
                                         GValue.to_enum(gtype, value))
        elif fundamental == GValue.gflags_type:
            gobject_lib.g_value_set_flags(self.gvalue, value)
        elif gtype == GValue.gstr_type:
            gobject_lib.g_value_set_string(self.gvalue, _to_bytes(value))
        elif gtype == GValue.refstr_type:
            vips_lib.vips_value_set_ref_string(self.gvalue, _to_bytes(value))
        elif fundamental == GValue.gobject_type:
            gobject_lib.g_value_set_object(self.gvalue, value.pointer)
        elif gtype == GValue.array_int_type:
            if isinstance(value, numbers.Number):
                value = [value]

            array = ffi.new('int[]', value)
            vips_lib.vips_value_set_array_int(self.gvalue, array, len(value))
        elif gtype == GValue.array_double_type:
            if isinstance(value, numbers.Number):
                value = [value]

            array = ffi.new('double[]', value)
            vips_lib.vips_value_set_array_double(self.gvalue, array,
                                                 len(value))
        elif gtype == GValue.array_image_type:
            if isinstance(value, pyvips.Image):
                value = [value]

            vips_lib.vips_value_set_array_image(self.gvalue, len(value))
            array = vips_lib.vips_value_get_array_image(self.gvalue, ffi.NULL)
            for i, image in enumerate(value):
                gobject_lib.g_object_ref(image.pointer)
                array[i] = image.pointer
        elif gtype == GValue.blob_type:
            # we need to set the blob to a copy of the string that vips_lib
            # can own
            memory = glib_lib.g_malloc(len(value))
            ffi.memmove(memory, value, len(value))

            # this is horrible!
            #
            # * in API mode, we must have 8.6+ and use set_blob_free to
            #   attach the metadata to avoid leaks
            # * pre-8.6, we just pass a NULL free pointer and live with the
            #   leak
            #
            # this is because in API mode you can't pass a builtin (what
            # vips_lib.g_free() becomes) as a parameter to ffi.callback(), and
            # vips_value_set_blob() needs a callback for arg 2
            #
            # additionally, you can't make a py def which calls g_free() and
            # then use the py def in the callback, since libvips will trigger
            # these functions during cleanup, and py will have shut down by
            # then and you'll get a segv

            if at_least_libvips(8, 6):
                vips_lib.vips_value_set_blob_free(self.gvalue, memory,
                                                  len(value))
            else:
                if pyvips.API_mode:
                    vips_lib.vips_value_set_blob(self.gvalue, ffi.NULL, memory,
                                                 len(value))
                else:
                    vips_lib.vips_value_set_blob(self.gvalue, glib_lib.g_free,
                                                 memory, len(value))
        else:
            raise Error(
                'unsupported gtype for set {0}, fundamental {1}'.format(
                    type_name(gtype), type_name(fundamental)))

    def get(self):
        """Get the contents of a GValue.

        The contents of the GValue are read out as a Python type.
        """

        # logger.debug('GValue.get: self = %s', self)

        gtype = self.gvalue.g_type
        fundamental = gobject_lib.g_type_fundamental(gtype)

        result = None

        if gtype == GValue.gbool_type:
            result = bool(gobject_lib.g_value_get_boolean(self.gvalue))
        elif gtype == GValue.gint_type:
            result = gobject_lib.g_value_get_int(self.gvalue)
        elif gtype == GValue.gdouble_type:
            result = gobject_lib.g_value_get_double(self.gvalue)
        elif fundamental == GValue.genum_type:
            return GValue.from_enum(gtype,
                                    gobject_lib.g_value_get_enum(self.gvalue))
        elif fundamental == GValue.gflags_type:
            result = gobject_lib.g_value_get_flags(self.gvalue)
        elif gtype == GValue.gstr_type:
            pointer = gobject_lib.g_value_get_string(self.gvalue)

            if pointer != ffi.NULL:
                result = _to_string(pointer)
        elif gtype == GValue.refstr_type:
            psize = ffi.new('size_t *')
            pointer = vips_lib.vips_value_get_ref_string(self.gvalue, psize)

            # psize[0] will be number of bytes in string, but just assume it's
            # NULL-terminated
            result = _to_string(pointer)
        elif gtype == GValue.image_type:
            # g_value_get_object() will not add a ref ... that is
            # held by the gvalue
            go = gobject_lib.g_value_get_object(self.gvalue)
            vi = ffi.cast('VipsImage *', go)

            # we want a ref that will last with the life of the vimage:
            # this ref is matched by the unref that's attached to finalize
            # by Image()
            gobject_lib.g_object_ref(go)

            result = pyvips.Image(vi)
        elif gtype == GValue.array_int_type:
            pint = ffi.new('int *')
            array = vips_lib.vips_value_get_array_int(self.gvalue, pint)

            result = []
            for i in range(0, pint[0]):
                result.append(array[i])
        elif gtype == GValue.array_double_type:
            pint = ffi.new('int *')
            array = vips_lib.vips_value_get_array_double(self.gvalue, pint)

            result = []
            for i in range(0, pint[0]):
                result.append(array[i])
        elif gtype == GValue.array_image_type:
            pint = ffi.new('int *')
            array = vips_lib.vips_value_get_array_image(self.gvalue, pint)

            result = []
            for i in range(0, pint[0]):
                vi = array[i]
                gobject_lib.g_object_ref(vi)
                image = pyvips.Image(vi)
                result.append(image)
        elif gtype == GValue.blob_type:
            psize = ffi.new('size_t *')
            array = vips_lib.vips_value_get_blob(self.gvalue, psize)
            buf = ffi.cast('char*', array)

            result = ffi.unpack(buf, psize[0])
        else:
            raise Error('unsupported gtype for get {0}'.format(
                type_name(gtype)))

        return result
Example #22
0
    def _marshal_image_progress(vi, pointer, handle):
        gobject_lib.g_object_ref(vi)
        image = pyvips.Image(vi)
        callback = ffi.from_handle(handle)
        progress = ffi.cast('VipsProgress*', pointer)
        callback(image, progress)
    _marshal_image_progress_cb = \
        ffi.cast('GCallback', _marshal_image_progress)

_marshalers = {
    'preeval': _marshal_image_progress_cb,
    'eval': _marshal_image_progress_cb,
    'posteval': _marshal_image_progress_cb,
}

if at_least_libvips(8, 9):
    if pyvips.API_mode:

        @ffi.def_extern()
        def _marshal_read(source_custom, pointer, length, handle):
            buf = ffi.buffer(pointer, length)
            callback = ffi.from_handle(handle)
            return callback(buf)

        _marshal_read_cb = ffi.cast('GCallback', gobject_lib._marshal_read)
    else:

        @ffi.callback('gint64(VipsSourceCustom*, void*, gint64, void*)')
        def _marshal_read(source_custom, pointer, length, handle):
            buf = ffi.buffer(pointer, length)
            callback = ffi.from_handle(handle)
Example #23
0
class TestResample:
    def test_affine(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        # vsqbs is non-interpolatory, don't test this way
        for name in ["nearest", "bicubic", "bilinear", "nohalo", "lbb"]:
            x = im
            interpolate = pyvips.Interpolate.new(name)
            for i in range(4):
                x = x.affine([0, 1, 1, 0], interpolate=interpolate)

            assert (x - im).abs().max() == 0

    def test_reduce(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        # cast down to 0-127, the smallest range, so we aren't messed up by
        # clipping
        im = im.cast(pyvips.BandFormat.CHAR)

        for fac in [1, 1.1, 1.5, 1.999]:
            for fmt in all_formats:
                for kernel in [
                        "nearest", "linear", "cubic", "lanczos2", "lanczos3"
                ]:
                    x = im.cast(fmt)
                    r = x.reduce(fac, fac, kernel=kernel)
                    d = abs(r.avg() - im.avg())
                    assert d < 2

        # try constant images ... should not change the constant
        for const in [0, 1, 2, 254, 255]:
            im = (pyvips.Image.black(10, 10) + const).cast("uchar")
            for kernel in [
                    "nearest", "linear", "cubic", "lanczos2", "lanczos3"
            ]:
                # print "testing kernel =", kernel
                # print "testing const =", const
                shr = im.reduce(2, 2, kernel=kernel)
                d = abs(shr.avg() - im.avg())
                assert d == 0

    def test_resize(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.resize(0.25)
        # in py3, round() does not round to nearest in the obvious way, so we
        # have to do it by hand
        assert im2.width == int(im.width / 4.0 + 0.5)
        assert im2.height == int(im.height / 4.0 + 0.5)

        # test geometry rounding corner case
        im = pyvips.Image.black(100, 1)
        x = im.resize(0.5)
        assert x.width == 50
        assert x.height == 1

    def test_resize_logo3__lanczos3(self):
        # 2382x711 -> 328x711
        # 13.77%x100%
        # self.resize_and_sharpen(IMAGES + '/logo3.png', 328.0, 711.0)
        self.resize_and_sharpen(IMAGES + '/logo3.png', 328.0)

    def test_resize_and_sharpen_zetta(self):
        self.resize_and_sharpen(IMAGES + '/zetta.png', 436.0)

    def test_sharpen_resized_by_magick_olhos(self):
        # 828 × 322 -> 402 x 156
        self.sharpen(IMAGES + '/olhos-resized-by-magick.png')

    def test_resize_and_sharpen_world_leaders(self):
        # w_640,h_416
        self.resize_and_sharpen(IMAGES + '/world-leaders.jpg', 640.0, 416.0)

    def test_resize_and_sharpen_olhos(self):
        # 828 × 322 -> 402 x 156
        self.resize_and_sharpen(IMAGES + '/olhos.png', 402.0, 156.0)

    def test_resize_and_sharpen_olhos__only_horizontal(self):
        # 828 × 322 -> 402 x 322
        self.resize_and_sharpen(IMAGES + '/olhos-h.png', 402.0, 322.0)

    def test_resize_and_sharpen_olhos__only_vertical(self):
        # 828 × 322 -> 828 × 156
        self.resize_and_sharpen(IMAGES + '/olhos-v.png', 828.0, 156.0)

    def test_resize_and_sharpen_40x40(self):
        # 828 × 322 -> 402 x 156
        self.resize_and_sharpen(IMAGES + '/40x40.png', 39.0)

    def test_resize_and_sharpen_40x40__only_horizontal(self):
        # 828 × 322 -> 402 x 322
        self.resize_and_sharpen(IMAGES + '/40x40-h.png', 39.0, 40.0)

    def test_resize_and_sharpen_40x40__only_vertical(self):
        # 828 × 322 -> 828 × 156
        self.resize_and_sharpen(IMAGES + '/40x40-v.png', 40.0, 39.0)

    def test_resize_and_sharpen_tiny(self):
        self.resize_and_sharpen(IMAGES + '/4x4.png', 3.0)

    def test_resize_and_sharpen_two_strip(self):
        self.resize_and_sharpen(IMAGES + '/two-strip.png', 16.0)

    def test_resize_and_sharpen_1024_to_16(self):
        for i in range(1):
            print('----- Iteration %s' % i)
            self.resize_and_sharpen(IMAGES + '/1024x1024.png', 16.0)

    @staticmethod
    def resize_and_sharpen(filename, new_width, new_height=None):
        _, ext = os.path.splitext(filename)
        im = pyvips.Image.new_from_file(filename)
        new_height = new_height or im.height * new_width / im.width
        print('new height %s' % new_height)
        # im = im.colourspace('rgb16')

        print('new_width / im.width=', new_width / im.width)
        im = im.resize(new_width / im.width, vscale=new_height / im.height)

        print('Writing resized')
        im.write_to_file('%s.resized-lanczos%s' % (filename, ext))
        im = im.sharpen(mode='rgb', sigma=0.66, m2=1.0, x1=1.0)
        im.write_to_file('%s.resized-lanczos-sharpened%s' % (filename, ext))

    @staticmethod
    def sharpen(filename):
        im = pyvips.Image.new_from_file(filename)
        im = im.colourspace('rgb16')
        im = im.sharpen(mode='rgb', sigma=0.66, m2=1.0, x1=1.0)
        im.write_to_file('%s-sharpened.png' % filename)

    def test_thumbnail_logo3(self):
        filename = IMAGES + '/logo3.png'
        im = pyvips.Image.new_from_file(filename)
        im = im.thumbnail_image(328, linear=True)
        im.write_to_file('%s.thumbnail-linear.png' % filename)
        im = im.sharpen(mode='rgb', sigma=0.66, m2=1.0, x1=1.0)
        im.write_to_file('%s.thumbnail-linear-sharpened.png' % filename)

    def test_shrink(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.shrink(4, 4)
        # in py3, round() does not round to nearest in the obvious way, so we
        # have to do it by hand
        assert im2.width == int(im.width / 4.0 + 0.5)
        assert im2.height == int(im.height / 4.0 + 0.5)
        assert abs(im.avg() - im2.avg()) < 1

        im2 = im.shrink(2.5, 2.5)
        assert im2.width == int(im.width / 2.5 + 0.5)
        assert im2.height == int(im.height / 2.5 + 0.5)
        assert abs(im.avg() - im2.avg()) < 1

    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 5),
                        reason="requires libvips >= 8.5")
    def test_thumbnail(self):
        im = pyvips.Image.thumbnail(JPEG_FILE, 100)

        assert im.height == 100
        assert im.bands == 3
        assert im.bands == 3

        # the average shouldn't move too much
        im_orig = pyvips.Image.new_from_file(JPEG_FILE)
        assert abs(im_orig.avg() - im.avg()) < 1

        # make sure we always get the right width
        for height in range(440, 1, -13):
            im = pyvips.Image.thumbnail(JPEG_FILE, height)
            assert im.height == height

        # should fit one of width or height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300)
        assert im.width == 100
        assert im.height != 300
        im = pyvips.Image.thumbnail(JPEG_FILE, 300, height=100)
        assert im.width != 300
        assert im.height == 100

        # with @crop, should fit both width and height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300, crop=True)
        assert im.width == 100
        assert im.height == 300

        im1 = pyvips.Image.thumbnail(JPEG_FILE, 100)
        with open(JPEG_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        assert abs(im1.avg() - im2.avg()) < 1

        # should be able to thumbnail many-page tiff
        im = pyvips.Image.thumbnail(OME_FILE, 100)
        assert im.width == 100
        assert im.height == 38

        # should be able to thumbnail individual pages from many-page tiff
        im1 = pyvips.Image.thumbnail(OME_FILE + "[page=0]", 100)
        assert im1.width == 100
        assert im1.height == 38
        im2 = pyvips.Image.thumbnail(OME_FILE + "[page=1]", 100)
        assert im2.width == 100
        assert im2.height == 38
        assert (im1 - im2).abs().max() != 0

        # should be able to thumbnail entire many-page tiff as a toilet-roll
        # image
        im = pyvips.Image.thumbnail(OME_FILE + "[n=-1]", 100)
        assert im.width == 100
        assert im.height == 570

        # should be able to thumbnail a single-page tiff in a buffer
        im1 = pyvips.Image.thumbnail(TIF_FILE, 100)
        with open(TIF_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        assert abs(im1.avg() - im2.avg()) < 1

        if have("heifload"):
            # this image is orientation 6 ... thumbnail should flip it
            im = pyvips.Image.new_from_file(HEIC_FILE)
            thumb = pyvips.Image.thumbnail(HEIC_FILE, 100)

            # thumb should be portrait
            assert thumb.width < thumb.height
            assert thumb.height == 100

    def test_similarity(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.similarity(angle=90)
        im3 = im.affine([0, -1, 1, 0])
        # rounding in calculating the affine transform from the angle stops
        # this being exactly true
        assert (im2 - im3).abs().max() < 50

    def test_similarity_scale(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.similarity(scale=2)
        im3 = im.affine([2, 0, 0, 2])
        assert (im2 - im3).abs().max() == 0

    # added in 8.7
    def test_rotate(self):
        if have("rotate"):
            im = pyvips.Image.new_from_file(JPEG_FILE)
            im2 = im.rotate(90)
            im3 = im.affine([0, -1, 1, 0])
            # rounding in calculating the affine transform from the angle stops
            # this being exactly true
            assert (im2 - im3).abs().max() < 50

    def test_mapim(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        p = to_polar(im)
        r = to_rectangular(p)

        # the left edge (which is squashed to the origin) will be badly
        # distorted, but the rest should not be too bad
        a = r.crop(50, 0, im.width - 50, im.height).gaussblur(2)
        b = im.crop(50, 0, im.width - 50, im.height).gaussblur(2)
        assert (a - b).abs().max() < 40

        # this was a bug at one point, strangely, if executed with debug
        # enabled
        mp = pyvips.Image.xyz(im.width, im.height)
        interp = pyvips.Interpolate.new('bicubic')
        assert im.mapim(mp, interpolate=interp).avg() == im.avg()
Example #24
0
class TestResample:
    def test_affine(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        # vsqbs is non-interpolatory, don't test this way
        for name in ["nearest", "bicubic", "bilinear", "nohalo", "lbb"]:
            x = im
            interpolate = pyvips.Interpolate.new(name)
            for i in range(4):
                x = x.affine([0, 1, 1, 0], interpolate=interpolate)

            assert (x - im).abs().max() == 0

    def test_reduce(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        # cast down to 0-127, the smallest range, so we aren't messed up by
        # clipping
        im = im.cast(pyvips.BandFormat.CHAR)

        for fac in [1, 1.1, 1.5, 1.999]:
            for fmt in all_formats:
                for kernel in [
                        "nearest", "linear", "cubic", "lanczos2", "lanczos3"
                ]:
                    x = im.cast(fmt)
                    r = x.reduce(fac, fac, kernel=kernel)
                    d = abs(r.avg() - im.avg())
                    assert d < 2

        # try constant images ... should not change the constant
        for const in [0, 1, 2, 254, 255]:
            im = (pyvips.Image.black(10, 10) + const).cast("uchar")
            for kernel in [
                    "nearest", "linear", "cubic", "lanczos2", "lanczos3"
            ]:
                # print "testing kernel =", kernel
                # print "testing const =", const
                shr = im.reduce(2, 2, kernel=kernel)
                d = abs(shr.avg() - im.avg())
                assert d == 0

    def test_resize(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.resize(0.25)
        assert im2.width == round(im.width / 4.0)
        assert im2.height == round(im.height / 4.0)

        # test geometry rounding corner case
        im = pyvips.Image.black(100, 1)
        x = im.resize(0.5)
        assert x.width == 50
        assert x.height == 1

    def test_shrink(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.shrink(4, 4)
        assert im2.width == round(im.width / 4.0)
        assert im2.height == round(im.height / 4.0)
        assert abs(im.avg() - im2.avg()) < 1

        im2 = im.shrink(2.5, 2.5)
        assert im2.width == round(im.width / 2.5)
        assert im2.height == round(im.height / 2.5)
        assert abs(im.avg() - im2.avg()) < 1

    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 5),
                        reason="requires libvips >= 8.5")
    def test_thumbnail(self):
        im = pyvips.Image.thumbnail(JPEG_FILE, 100)

        assert im.width == 100
        assert im.bands == 3
        assert im.bands == 3

        # the average shouldn't move too much
        im_orig = pyvips.Image.new_from_file(JPEG_FILE)
        assert abs(im_orig.avg() - im.avg()) < 1

        # make sure we always get the right width
        for width in range(1000, 1, -13):
            im = pyvips.Image.thumbnail(JPEG_FILE, width)
            assert im.width == width

        # should fit one of width or height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300)
        assert im.width == 100
        assert im.height != 300
        im = pyvips.Image.thumbnail(JPEG_FILE, 300, height=100)
        assert im.width != 300
        assert im.height == 100

        # with @crop, should fit both width and height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300, crop=True)
        assert im.width == 100
        assert im.height == 300

        im1 = pyvips.Image.thumbnail(JPEG_FILE, 100)
        with open(JPEG_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        assert abs(im1.avg() - im2.avg()) < 1

    def test_similarity(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.similarity(angle=90)
        im3 = im.affine([0, -1, 1, 0])
        # rounding in calculating the affine transform from the angle stops
        # this being exactly true
        assert (im2 - im3).abs().max() < 50

    def test_similarity_scale(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.similarity(scale=2)
        im3 = im.affine([2, 0, 0, 2])
        assert (im2 - im3).abs().max() == 0

    # added in 8.7
    def test_rotate(self):
        if have("rotate"):
            im = pyvips.Image.new_from_file(JPEG_FILE)
            im2 = im.rotate(90)
            im3 = im.affine([0, -1, 1, 0])
            # rounding in calculating the affine transform from the angle stops
            # this being exactly true
            assert (im2 - im3).abs().max() < 50

    def test_mapim(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        p = to_polar(im)
        r = to_rectangular(p)

        # the left edge (which is squashed to the origin) will be badly
        # distorted, but the rest should not be too bad
        a = r.crop(50, 0, im.width - 50, im.height).gaussblur(2)
        b = im.crop(50, 0, im.width - 50, im.height).gaussblur(2)
        assert (a - b).abs().max() < 20
Example #25
0
class TestIofuncs:
    # test the vips7 filename splitter ... this is very fragile and annoying
    # code with lots of cases
    def test_split7(self):
        def split(path):
            filename7 = pyvips.path_filename7(path)
            mode7 = pyvips.path_mode7(path)

            return [filename7, mode7]

        cases = [[
            "c:\\silly:dir:name\\fr:ed.tif:jpeg:95,,,,c:\\icc\\srgb.icc",
            ["c:\\silly:dir:name\\fr:ed.tif", "jpeg:95,,,,c:\\icc\\srgb.icc"]
        ], ["I180:", ["I180", ""]], ["c:\\silly:", ["c:\\silly", ""]],
                 [
                     "c:\\program files\\x:hello",
                     ["c:\\program files\\x", "hello"]
                 ],
                 [
                     "C:\\fixtures\\2569067123_aca715a2ee_o.jpg",
                     ["C:\\fixtures\\2569067123_aca715a2ee_o.jpg", ""]
                 ]]

        for case in cases:
            assert_equal_objects(split(case[0]), case[1])

    def test_new_from_image(self):
        im = pyvips.Image.mask_ideal(100, 100, 0.5, reject=True, optical=True)

        im2 = im.new_from_image(12)

        assert im2.width == im.width
        assert im2.height == im.height
        assert im2.interpretation == im.interpretation
        assert im2.format == im.format
        assert im2.xres == im.xres
        assert im2.yres == im.yres
        assert im2.xoffset == im.xoffset
        assert im2.yoffset == im.yoffset
        assert im2.bands == 1
        assert im2.avg() == 12

        im2 = im.new_from_image([1, 2, 3])

        assert im2.bands == 3
        assert im2.avg() == 2

    def test_new_from_memory(self):
        s = bytearray(200)
        im = pyvips.Image.new_from_memory(s, 20, 10, 1, 'uchar')

        assert im.width == 20
        assert im.height == 10
        assert im.format == 'uchar'
        assert im.bands == 1
        assert im.avg() == 0

        im += 10

        assert im.avg() == 10

    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 5),
                        reason="requires libvips >= 8.5")
    def test_get_fields(self):
        im = pyvips.Image.black(10, 10)
        fields = im.get_fields()
        # we might add more fields later
        assert len(fields) > 10
        assert fields[0] == 'width'

    def test_write_to_memory(self):
        s = bytearray(200)
        im = pyvips.Image.new_from_memory(s, 20, 10, 1, 'uchar')
        t = im.write_to_memory()

        assert s == t
Example #26
0
    def test_tiff(self):
        def tiff_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [38671.0, 33914.0, 26762.0])
            assert im.width == 290
            assert im.height == 442
            assert im.bands == 3

        self.file_loader("tiffload", TIF_FILE, tiff_valid)
        self.buffer_loader("tiffload_buffer", TIF_FILE, tiff_valid)
        if pyvips.at_least_libvips(8, 5):
            self.save_load_buffer("tiffsave_buffer",
                                  "tiffload_buffer",
                                  self.colour)
        self.save_load("%s.tif", self.mono)
        self.save_load("%s.tif", self.colour)
        self.save_load("%s.tif", self.cmyk)

        self.save_load("%s.tif", self.onebit)
        self.save_load_file(".tif", "[squash]", self.onebit, 0)
        self.save_load_file(".tif", "[miniswhite]", self.onebit, 0)
        self.save_load_file(".tif", "[squash,miniswhite]", self.onebit, 0)

        self.save_load_file(".tif",
                            "[profile={0}]".format(SRGB_FILE),
                            self.colour, 0)
        self.save_load_file(".tif", "[tile]", self.colour, 0)
        self.save_load_file(".tif", "[tile,pyramid]", self.colour, 0)
        self.save_load_file(".tif",
                            "[tile,pyramid,compression=jpeg]", self.colour, 80)
        self.save_load_file(".tif", "[bigtiff]", self.colour, 0)
        self.save_load_file(".tif", "[compression=jpeg]", self.colour, 80)
        self.save_load_file(".tif",
                            "[tile,tile-width=256]", self.colour, 10)

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 2)
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 2

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 2)
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 2
        x.remove("orientation")

        filename = temp_filename(self.tempdir, '.tif')
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 1

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 6)
        x.write_to_file(filename)
        x1 = pyvips.Image.new_from_file(filename)
        x2 = pyvips.Image.new_from_file(filename, autorotate=True)
        assert x1.width == x2.height
        assert x1.height == x2.width

        # OME support in 8.5
        x = pyvips.Image.new_from_file(OME_FILE)
        assert x.width == 439
        assert x.height == 167
        page_height = x.height

        x = pyvips.Image.new_from_file(OME_FILE, n=-1)
        assert x.width == 439
        assert x.height == page_height * 15

        x = pyvips.Image.new_from_file(OME_FILE, page=1, n=-1)
        assert x.width == 439
        assert x.height == page_height * 14

        x = pyvips.Image.new_from_file(OME_FILE, page=1, n=2)
        assert x.width == 439
        assert x.height == page_height * 2

        x = pyvips.Image.new_from_file(OME_FILE, n=-1)
        assert x(0, 166)[0] == 96
        assert x(0, 167)[0] == 0
        assert x(0, 168)[0] == 1

        filename = temp_filename(self.tempdir, '.tif')
        x.write_to_file(filename)

        x = pyvips.Image.new_from_file(filename, n=-1)
        assert x.width == 439
        assert x.height == page_height * 15
        assert x(0, 166)[0] == 96
        assert x(0, 167)[0] == 0
        assert x(0, 168)[0] == 1

        # pyr save to buffer added in 8.6
        x = pyvips.Image.new_from_file(TIF_FILE)
        buf = x.tiffsave_buffer(tile=True, pyramid=True)
        filename = temp_filename(self.tempdir, '.tif')
        x.tiffsave(filename, tile=True, pyramid=True)
        with open(filename, 'rb') as f:
            buf2 = f.read()
        assert len(buf) == len(buf2)

        a = pyvips.Image.new_from_buffer(buf, "", page=2)
        b = pyvips.Image.new_from_buffer(buf2, "", page=2)
        assert a.width == b.width
        assert a.height == b.height
        assert a.avg() == b.avg()

        # region-shrink added in 8.7
        x = pyvips.Image.new_from_file(TIF_FILE)
        buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="mean")
        buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="mode")
        buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="median")
Example #27
0
    def test_tiff(self):
        def tiff_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [38671.0, 33914.0, 26762.0])
            assert im.width == 290
            assert im.height == 442
            assert im.bands == 3

        self.file_loader("tiffload", TIF_FILE, tiff_valid)
        self.buffer_loader("tiffload_buffer", TIF_FILE, tiff_valid)
        if pyvips.at_least_libvips(8, 5):
            self.save_load_buffer("tiffsave_buffer", "tiffload_buffer",
                                  self.colour)
        self.save_load("%s.tif", self.mono)
        self.save_load("%s.tif", self.colour)
        self.save_load("%s.tif", self.cmyk)

        self.save_load("%s.tif", self.onebit)
        self.save_load_file(".tif", "[squash]", self.onebit, 0)
        self.save_load_file(".tif", "[miniswhite]", self.onebit, 0)
        self.save_load_file(".tif", "[squash,miniswhite]", self.onebit, 0)

        self.save_load_file(".tif", "[profile={0}]".format(SRGB_FILE),
                            self.colour, 0)
        self.save_load_file(".tif", "[tile]", self.colour, 0)
        self.save_load_file(".tif", "[tile,pyramid]", self.colour, 0)
        self.save_load_file(".tif", "[tile,pyramid,compression=jpeg]",
                            self.colour, 80)
        self.save_load_file(".tif", "[bigtiff]", self.colour, 0)
        self.save_load_file(".tif", "[compression=jpeg]", self.colour, 80)
        self.save_load_file(".tif", "[tile,tile-width=256]", self.colour, 10)

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 2)
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 2

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 2)
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 2
        x.remove("orientation")

        filename = temp_filename(self.tempdir, '.tif')
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 1

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 6)
        x.write_to_file(filename)
        x1 = pyvips.Image.new_from_file(filename)
        x2 = pyvips.Image.new_from_file(filename, autorotate=True)
        assert x1.width == x2.height
        assert x1.height == x2.width

        # OME support in 8.5
        x = pyvips.Image.new_from_file(OME_FILE)
        assert x.width == 439
        assert x.height == 167
        page_height = x.height

        x = pyvips.Image.new_from_file(OME_FILE, n=-1)
        assert x.width == 439
        assert x.height == page_height * 15

        x = pyvips.Image.new_from_file(OME_FILE, page=1, n=-1)
        assert x.width == 439
        assert x.height == page_height * 14

        x = pyvips.Image.new_from_file(OME_FILE, page=1, n=2)
        assert x.width == 439
        assert x.height == page_height * 2

        x = pyvips.Image.new_from_file(OME_FILE, n=-1)
        assert x(0, 166)[0] == 96
        assert x(0, 167)[0] == 0
        assert x(0, 168)[0] == 1

        filename = temp_filename(self.tempdir, '.tif')
        x.write_to_file(filename)

        x = pyvips.Image.new_from_file(filename, n=-1)
        assert x.width == 439
        assert x.height == page_height * 15
        assert x(0, 166)[0] == 96
        assert x(0, 167)[0] == 0
        assert x(0, 168)[0] == 1

        # pyr save to buffer added in 8.6
        x = pyvips.Image.new_from_file(TIF_FILE)
        buf = x.tiffsave_buffer(tile=True, pyramid=True)
        filename = temp_filename(self.tempdir, '.tif')
        x.tiffsave(filename, tile=True, pyramid=True)
        with open(filename, 'rb') as f:
            buf2 = f.read()
        assert len(buf) == len(buf2)

        a = pyvips.Image.new_from_buffer(buf, "", page=2)
        b = pyvips.Image.new_from_buffer(buf2, "", page=2)
        assert a.width == b.width
        assert a.height == b.height
        assert a.avg() == b.avg()

        # region-shrink added in 8.7
        x = pyvips.Image.new_from_file(TIF_FILE)
        buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="mean")
        buf = x.tiffsave_buffer(tile=True, pyramid=True, region_shrink="mode")
        buf = x.tiffsave_buffer(tile=True,
                                pyramid=True,
                                region_shrink="median")
Example #28
0
    def set(self, value):
        """Set a GValue.

        The value is converted to the type of the GValue, if possible, and
        assigned.

        """

        # logger.debug('GValue.set: value = %s', value)

        gtype = self.gvalue.g_type
        fundamental = gobject_lib.g_type_fundamental(gtype)

        if gtype == GValue.gbool_type:
            gobject_lib.g_value_set_boolean(self.gvalue, value)
        elif gtype == GValue.gint_type:
            gobject_lib.g_value_set_int(self.gvalue, int(value))
        elif gtype == GValue.gdouble_type:
            gobject_lib.g_value_set_double(self.gvalue, value)
        elif fundamental == GValue.genum_type:
            gobject_lib.g_value_set_enum(self.gvalue,
                                         GValue.to_enum(gtype, value))
        elif fundamental == GValue.gflags_type:
            gobject_lib.g_value_set_flags(self.gvalue, value)
        elif gtype == GValue.gstr_type:
            gobject_lib.g_value_set_string(self.gvalue, _to_bytes(value))
        elif gtype == GValue.refstr_type:
            vips_lib.vips_value_set_ref_string(self.gvalue, _to_bytes(value))
        elif fundamental == GValue.gobject_type:
            gobject_lib.g_value_set_object(self.gvalue, value.pointer)
        elif gtype == GValue.array_int_type:
            if isinstance(value, numbers.Number):
                value = [value]

            array = ffi.new('int[]', value)
            vips_lib.vips_value_set_array_int(self.gvalue, array, len(value))
        elif gtype == GValue.array_double_type:
            if isinstance(value, numbers.Number):
                value = [value]

            array = ffi.new('double[]', value)
            vips_lib.vips_value_set_array_double(self.gvalue, array,
                                                 len(value))
        elif gtype == GValue.array_image_type:
            if isinstance(value, pyvips.Image):
                value = [value]

            vips_lib.vips_value_set_array_image(self.gvalue, len(value))
            array = vips_lib.vips_value_get_array_image(self.gvalue, ffi.NULL)
            for i, image in enumerate(value):
                gobject_lib.g_object_ref(image.pointer)
                array[i] = image.pointer
        elif gtype == GValue.blob_type:
            # we need to set the blob to a copy of the string that vips_lib
            # can own
            memory = glib_lib.g_malloc(len(value))
            ffi.memmove(memory, value, len(value))

            # this is horrible!
            #
            # * in API mode, we must have 8.6+ and use set_blob_free to
            #   attach the metadata to avoid leaks
            # * pre-8.6, we just pass a NULL free pointer and live with the
            #   leak
            #
            # this is because in API mode you can't pass a builtin (what
            # vips_lib.g_free() becomes) as a parameter to ffi.callback(), and
            # vips_value_set_blob() needs a callback for arg 2
            #
            # additionally, you can't make a py def which calls g_free() and
            # then use the py def in the callback, since libvips will trigger
            # these functions during cleanup, and py will have shut down by
            # then and you'll get a segv

            if at_least_libvips(8, 6):
                vips_lib.vips_value_set_blob_free(self.gvalue, memory,
                                                  len(value))
            else:
                if pyvips.API_mode:
                    vips_lib.vips_value_set_blob(self.gvalue, ffi.NULL, memory,
                                                 len(value))
                else:
                    vips_lib.vips_value_set_blob(self.gvalue, glib_lib.g_free,
                                                 memory, len(value))
        else:
            raise Error(
                'unsupported gtype for set {0}, fundamental {1}'.format(
                    type_name(gtype), type_name(fundamental)))
Example #29
0
class TestResample:
    def test_affine(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        # vsqbs is non-interpolatory, don't test this way
        for name in ["nearest", "bicubic", "bilinear", "nohalo", "lbb"]:
            x = im
            interpolate = pyvips.Interpolate.new(name)
            for i in range(4):
                x = x.affine([0, 1, 1, 0], interpolate=interpolate)

            assert (x - im).abs().max() == 0

    def test_reduce(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        # cast down to 0-127, the smallest range, so we aren't messed up by
        # clipping
        im = im.cast(pyvips.BandFormat.CHAR)

        for fac in [1, 1.1, 1.5, 1.999]:
            for fmt in all_formats:
                for kernel in [
                        "nearest", "linear", "cubic", "lanczos2", "lanczos3"
                ]:
                    x = im.cast(fmt)
                    r = x.reduce(fac, fac, kernel=kernel)
                    d = abs(r.avg() - im.avg())
                    assert d < 2

        # try constant images ... should not change the constant
        for const in [0, 1, 2, 254, 255]:
            im = (pyvips.Image.black(10, 10) + const).cast("uchar")
            for kernel in [
                    "nearest", "linear", "cubic", "lanczos2", "lanczos3"
            ]:
                # print "testing kernel =", kernel
                # print "testing const =", const
                shr = im.reduce(2, 2, kernel=kernel)
                d = abs(shr.avg() - im.avg())
                assert d == 0

    def test_resize(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.resize(0.25)
        # in py3, round() does not round to nearest in the obvious way, so we
        # have to do it by hand
        assert im2.width == int(im.width / 4.0 + 0.5)
        assert im2.height == int(im.height / 4.0 + 0.5)

        # test geometry rounding corner case
        im = pyvips.Image.black(100, 1)
        x = im.resize(0.5)
        assert x.width == 50
        assert x.height == 1

        # test whether we use double-precision calculations in reduce{h,v}
        im = pyvips.Image.black(1600, 1000)
        x = im.resize(10.0 / im.width)
        assert x.width == 10
        assert x.height == 6

        # test round-up option of shrink
        im = pyvips.Image.black(2049 - 2, 2047 - 2, bands=3)
        im = im.embed(1,
                      1,
                      2049,
                      2047,
                      extend=pyvips.Extend.BACKGROUND,
                      background=[255, 0, 0])
        for scale in [8, 9.4, 16]:
            x = im.resize(1 / scale, vscale=1 / scale)

            for point in ([(round(x.width / 2), 0),
                           (x.width - 1, round(x.height / 2)),
                           (round(x.width / 2), x.height - 1),
                           (0, round(x.height / 2))]):
                y = x(*point)[0]
                assert y != 0

    def test_shrink(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.shrink(4, 4)
        # in py3, round() does not round to nearest in the obvious way, so we
        # have to do it by hand
        assert im2.width == int(im.width / 4.0 + 0.5)
        assert im2.height == int(im.height / 4.0 + 0.5)
        assert abs(im.avg() - im2.avg()) < 1

        im2 = im.shrink(2.5, 2.5)
        assert im2.width == int(im.width / 2.5 + 0.5)
        assert im2.height == int(im.height / 2.5 + 0.5)
        assert abs(im.avg() - im2.avg()) < 1

    @pytest.mark.skipif(not pyvips.at_least_libvips(8, 5),
                        reason="requires libvips >= 8.5")
    def test_thumbnail(self):
        im = pyvips.Image.thumbnail(JPEG_FILE, 100)

        assert im.height == 100
        assert im.bands == 3
        assert im.bands == 3

        # the average shouldn't move too much
        im_orig = pyvips.Image.new_from_file(JPEG_FILE)
        assert abs(im_orig.avg() - im.avg()) < 1

        # make sure we always get the right width
        for height in range(440, 1, -13):
            im = pyvips.Image.thumbnail(JPEG_FILE, height)
            assert im.height == height

        # should fit one of width or height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300)
        assert im.width == 100
        assert im.height != 300
        im = pyvips.Image.thumbnail(JPEG_FILE, 300, height=100)
        assert im.width != 300
        assert im.height == 100

        # with @crop, should fit both width and height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300, crop=True)
        assert im.width == 100
        assert im.height == 300

        im1 = pyvips.Image.thumbnail(JPEG_FILE, 100)
        with open(JPEG_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        assert abs(im1.avg() - im2.avg()) < 1

        # should be able to thumbnail many-page tiff
        im = pyvips.Image.thumbnail(OME_FILE, 100)
        assert im.width == 100
        assert im.height == 38

        # should be able to thumbnail individual pages from many-page tiff
        im1 = pyvips.Image.thumbnail(OME_FILE + "[page=0]", 100)
        assert im1.width == 100
        assert im1.height == 38
        im2 = pyvips.Image.thumbnail(OME_FILE + "[page=1]", 100)
        assert im2.width == 100
        assert im2.height == 38
        assert (im1 - im2).abs().max() != 0

        # should be able to thumbnail entire many-page tiff as a toilet-roll
        # image
        im = pyvips.Image.thumbnail(OME_FILE + "[n=-1]", 100)
        assert im.width == 100
        assert im.height == 570

        # should be able to thumbnail a single-page tiff in a buffer
        im1 = pyvips.Image.thumbnail(TIF_FILE, 100)
        with open(TIF_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        assert abs(im1.avg() - im2.avg()) < 1

        # linear shrink should work on rgba images
        im1 = pyvips.Image.thumbnail(RGBA_FILE, 64, linear=True)
        im2 = pyvips.Image.new_from_file(RGBA_CORRECT_FILE)
        assert abs(im1.flatten(background=255).avg() - im2.avg()) < 1

        if have("heifload"):
            # this image is orientation 6 ... thumbnail should flip it
            im = pyvips.Image.new_from_file(AVIF_FILE)
            thumb = pyvips.Image.thumbnail(AVIF_FILE, 100)

            # thumb should be portrait
            assert thumb.width < thumb.height
            assert thumb.height == 100

    def test_similarity(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.similarity(angle=90)
        im3 = im.affine([0, -1, 1, 0])
        # rounding in calculating the affine transform from the angle stops
        # this being exactly true
        assert (im2 - im3).abs().max() < 50

    def test_similarity_scale(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)
        im2 = im.similarity(scale=2)
        im3 = im.affine([2, 0, 0, 2])
        assert (im2 - im3).abs().max() == 0

    # added in 8.7
    def test_rotate(self):
        if have("rotate"):
            im = pyvips.Image.new_from_file(JPEG_FILE)
            im2 = im.rotate(90)
            im3 = im.affine([0, -1, 1, 0])
            # rounding in calculating the affine transform from the angle stops
            # this being exactly true
            assert (im2 - im3).abs().max() < 50

    def test_mapim(self):
        im = pyvips.Image.new_from_file(JPEG_FILE)

        p = to_polar(im)
        r = to_rectangular(p)

        # the left edge (which is squashed to the origin) will be badly
        # distorted, but the rest should not be too bad
        a = r.crop(50, 0, im.width - 50, im.height).gaussblur(2)
        b = im.crop(50, 0, im.width - 50, im.height).gaussblur(2)
        assert (a - b).abs().max() < 50

        # this was a bug at one point, strangely, if executed with debug
        # enabled
        mp = pyvips.Image.xyz(im.width, im.height)
        interp = pyvips.Interpolate.new('bicubic')
        assert im.mapim(mp, interpolate=interp).avg() == im.avg()
Example #30
0
    const char* vips_value_get_ref_string (const GValue* value,
        size_t* length);
    void* g_value_get_object (GValue* value);
    double* vips_value_get_array_double (const GValue* value, int* n);
    int* vips_value_get_array_int (const GValue* value, int* n);
    VipsImage** vips_value_get_array_image (const GValue* value, int* n);
    void* vips_value_get_blob (const GValue* value, size_t* length);

    // need to make some of these by hand
    GType vips_interpretation_get_type (void);
    GType vips_operation_flags_get_type (void);
    GType vips_band_format_get_type (void);

''')

if at_least_libvips(8, 6):
    ffi.cdef('''
        GType vips_blend_mode_get_type (void);

    ''')


class GValue(object):
    """Wrap GValue in a Python class.

    This class wraps :class:`.GValue` in a convenient interface. You can use
    instances of this class to get and set :class:`.GObject` properties.

    On construction, :class:`.GValue` is all zero (empty). You can pass it to
    a get function to have it filled by :class:`.GObject`, or use init to
    set a type, set to set a value, then use it to set an object property.