예제 #1
0
    def test_16_image_process_format(self):
        """Test the format parameter of image_process."""

        image = tools.base64_to_image(
            tools.image_process(self.base64_1920x1080_jpeg,
                                output_format='PNG'))
        self.assertEqual(image.format, 'PNG', "change format to PNG")

        image = tools.base64_to_image(
            tools.image_process(self.base64_1x1_png, output_format='JpEg'))
        self.assertEqual(image.format, 'JPEG',
                         "change format to JPEG (case insensitive)")

        image = tools.base64_to_image(
            tools.image_process(self.base64_1920x1080_jpeg,
                                output_format='BMP'))
        self.assertEqual(image.format, 'PNG',
                         "change format to BMP converted to PNG")

        self.base64_image_1080_1920_rgba = tools.image_to_base64(
            Image.new('RGBA', (108, 192)), 'PNG')
        image = tools.base64_to_image(
            tools.image_process(self.base64_image_1080_1920_rgba,
                                output_format='jpeg'))
        self.assertEqual(image.format, 'JPEG',
                         "change format PNG with RGBA to JPEG")

        # pass quality to force the image to be processed
        self.base64_image_1080_1920_tiff = tools.image_to_base64(
            Image.new('RGB', (108, 192)), 'TIFF')
        image = tools.base64_to_image(
            tools.image_process(self.base64_image_1080_1920_tiff, quality=95))
        self.assertEqual(image.format, 'JPEG', "unsupported format to JPEG")
예제 #2
0
    def test_00_base64_to_image(self):
        """Test that base64 is correctly opened as a PIL image."""
        image = tools.base64_to_image(self.base64_1x1_png)
        self.assertEqual(type(image), PngImagePlugin.PngImageFile,
                         "base64 as bytes, correct format")
        self.assertEqual(image.size, (1, 1), "base64 as bytes, correct size")

        image = tools.base64_to_image(self.base64_1x1_png.decode('ASCII'))
        self.assertEqual(type(image), PngImagePlugin.PngImageFile,
                         "base64 as string, correct format")
        self.assertEqual(image.size, (1, 1), "base64 as string, correct size")

        with self.assertRaises(
                UserError,
                msg=
                "This file could not be decoded as an image file. Please try with a different file."
        ):
            image = tools.base64_to_image(b'oazdazpodazdpok')

        with self.assertRaises(
                UserError,
                msg=
                "This file could not be decoded as an image file. Please try with a different file."
        ):
            image = tools.base64_to_image(b'oazdazpodazdpokd')
예제 #3
0
    def test_11_image_process_size(self):
        """Test the size parameter of image_process."""

        # Format of `tests`: (original base64 image, size parameter, expected result, text)
        tests = [
            (self.base64_1920x1080_jpeg, (192, 108), (192, 108),
             "resize to given size"),
            (self.base64_1920x1080_jpeg, (1920, 1080), (1920, 1080),
             "same size, no change"),
            (self.base64_1920x1080_jpeg, (192, None), (192, 108),
             "set height from ratio"),
            (self.base64_1920x1080_jpeg, (0, 108), (192, 108),
             "set width from ratio"),
            (self.base64_1920x1080_jpeg, (192, 200), (192, 108),
             "adapt to width"),
            (self.base64_1920x1080_jpeg, (400, 108), (192, 108),
             "adapt to height"),
            (self.base64_1920x1080_jpeg, (3000, 2000), (1920, 1080),
             "don't resize above original, both set"),
            (self.base64_1920x1080_jpeg, (3000, False), (1920, 1080),
             "don't resize above original, width set"),
            (self.base64_1920x1080_jpeg, (None, 2000), (1920, 1080),
             "don't resize above original, height set"),
            (self.base64_1080x1920_png, (3000, 192), (108, 192),
             "vertical image, resize if below"),
        ]

        count = 0
        for test in tests:
            image = tools.base64_to_image(
                tools.image_process(test[0], size=test[1]))
            self.assertEqual(image.size, test[2], test[3])
            count = count + 1
        self.assertEqual(count, 10, "ensure the loop is ran")
예제 #4
0
    def test_10_image_process_base64_source(self):
        """Test the base64_source parameter of image_process."""
        wrong_base64 = b'oazdazpodazdpok'

        self.assertFalse(tools.image_process(False),
                         "return False if base64_source is falsy")
        self.assertEqual(tools.image_process(self.base64_svg), self.base64_svg,
                         "return base64_source if format is SVG")

        # in the following tests, pass `quality` to force the processing
        with self.assertRaises(
                UserError,
                msg=
                "This file could not be decoded as an image file. Please try with a different file."
        ):
            tools.image_process(wrong_base64, quality=95)

        with self.assertRaises(
                UserError,
                msg=
                "This file could not be decoded as an image file. Please try with a different file."
        ):
            tools.image_process(b'oazdazpodazdpokd', quality=95)

        image = tools.base64_to_image(
            tools.image_process(self.base64_1920x1080_jpeg, quality=95))
        self.assertEqual(image.size, (1920, 1080), "OK return the image")

        # test that nothing happens if no operation has been requested
        # (otherwise those would raise because of wrong base64)
        self.assertEqual(tools.image_process(wrong_base64), wrong_base64)
        self.assertEqual(tools.image_process(wrong_base64, size=False),
                         wrong_base64)
예제 #5
0
 def _compute_image_size(self):
     for attachment in self:
         try:
             image = tools.base64_to_image(attachment.datas)
             attachment.image_width = image.width
             attachment.image_height = image.height
         except Exception:
             attachment.image_width = 0
             attachment.image_height = 0
예제 #6
0
    def test_15_image_process_colorize(self):
        """Test the colorize parameter of image_process."""

        # verify initial condition
        image_rgba = Image.new('RGBA', (1, 1))
        self.assertEqual(image_rgba.mode, 'RGBA')
        self.assertEqual(image_rgba.getpixel((0, 0)), (0, 0, 0, 0))
        base64_rgba = tools.image_to_base64(image_rgba, 'PNG')

        # CASE: color random, color has changed
        image = tools.base64_to_image(
            tools.image_process(base64_rgba, colorize=True))
        self.assertEqual(image.mode, 'RGB')
        self.assertNotEqual(image.getpixel((0, 0)), (0, 0, 0))
예제 #7
0
 def _orientation_test(self, orientation, colors, size, expected):
     # Generate the test image based on orientation and order of colors.
     b64_image = self._get_exif_colored_square_b64(orientation, colors,
                                                   size)
     # The image is read again now that it has orientation added.
     fixed_image = tools.image_fix_orientation(
         tools.base64_to_image(b64_image))
     # Ensure colors are in the right order (blue, yellow, green, pink).
     self._assertAlmostEqualSequence(fixed_image.getpixel((0, 0)),
                                     expected[0])  # top/left
     self._assertAlmostEqualSequence(fixed_image.getpixel((size - 1, 0)),
                                     expected[1])  # top/right
     self._assertAlmostEqualSequence(fixed_image.getpixel((0, size - 1)),
                                     expected[2])  # bottom/left
     self._assertAlmostEqualSequence(
         fixed_image.getpixel((size - 1, size - 1)),
         expected[3])  # bottom/right
예제 #8
0
    def test_14_image_process_crop(self):
        """Test the crop parameter of image_process."""

        # Optimized PNG use palette, getpixel below will return palette value.
        fill = 0
        bg = 1

        # Format of `tests`: (original base64 image, size parameter, crop parameter, res size, res color (top, bottom, left, right), text)
        tests = [
            (self.base64_1920x1080_png, None, None, (1920, 1080),
             (fill, fill, bg, bg), "horizontal, verify initial"),
            (self.base64_1920x1080_png, (2000, 2000), 'center', (1080, 1080),
             (fill, fill, fill, fill), "horizontal, crop biggest possible"),
            (self.base64_1920x1080_png, (2000, 4000), 'center', (540, 1080),
             (fill, fill, fill,
              fill), "horizontal, size vertical, limit height"),
            (self.base64_1920x1080_png, (4000, 2000), 'center', (1920, 960),
             (fill, fill, bg, bg), "horizontal, size horizontal, limit width"),
            (self.base64_1920x1080_png, (512, 512), 'center', (512, 512),
             (fill, fill, fill, fill), "horizontal, type center"),
            (self.base64_1920x1080_png, (512, 512), 'top', (512, 512),
             (fill, fill, fill, fill), "horizontal, type top"),
            (self.base64_1920x1080_png, (512, 512), 'bottom', (512, 512),
             (fill, fill, fill, fill), "horizontal, type bottom"),
            (self.base64_1920x1080_png, (512, 512), 'wrong', (512, 512),
             (fill, fill, fill,
              fill), "horizontal, wrong crop value, use center"),
            (self.base64_1920x1080_png, (192, 0), None, (192, 108),
             (fill, fill, bg, bg), "horizontal, not cropped, just do resize"),
            (self.base64_1080x1920_png, None, None, (1080, 1920),
             (bg, bg, fill, fill), "vertical, verify initial"),
            (self.base64_1080x1920_png, (2000, 2000), 'center', (1080, 1080),
             (fill, fill, fill, fill), "vertical, crop biggest possible"),
            (self.base64_1080x1920_png, (2000, 4000), 'center', (960, 1920),
             (bg, bg, fill, fill), "vertical, size vertical, limit height"),
            (self.base64_1080x1920_png, (4000, 2000), 'center', (1080, 540),
             (fill, fill, fill,
              fill), "vertical, size horizontal, limit width"),
            (self.base64_1080x1920_png, (512, 512), 'center', (512, 512),
             (fill, fill, fill, fill), "vertical, type center"),
            (self.base64_1080x1920_png, (512, 512), 'top', (512, 512),
             (bg, fill, fill, fill), "vertical, type top"),
            (self.base64_1080x1920_png, (512, 512), 'bottom', (512, 512),
             (fill, bg, fill, fill), "vertical, type bottom"),
            (self.base64_1080x1920_png, (512, 512), 'wrong', (512, 512),
             (fill, fill, fill,
              fill), "vertical, wrong crop value, use center"),
            (self.base64_1080x1920_png, (108, 0), None, (108, 192),
             (bg, bg, fill, fill), "vertical, not cropped, just do resize"),
        ]

        count = 0
        for test in tests:
            count = count + 1
            # process the image, pass quality to make sure the result is palette
            image = tools.base64_to_image(
                tools.image_process(test[0],
                                    size=test[1],
                                    crop=test[2],
                                    quality=95))
            # verify size
            self.assertEqual(image.size, test[3],
                             "%s - correct size" % test[5])

            half_width, half_height = image.size[0] / 2, image.size[1] / 2
            top, bottom, left, right = 0, image.size[1] - 1, 0, image.size[
                0] - 1
            # verify top
            px = (half_width, top)
            self.assertEqual(
                image.getpixel(px), test[4][0],
                "%s - color top (%s, %s)" % (test[5], px[0], px[1]))
            # verify bottom
            px = (half_width, bottom)
            self.assertEqual(
                image.getpixel(px), test[4][1],
                "%s - color bottom (%s, %s)" % (test[5], px[0], px[1]))
            # verify left
            px = (left, half_height)
            self.assertEqual(
                image.getpixel(px), test[4][2],
                "%s - color left (%s, %s)" % (test[5], px[0], px[1]))
            # verify right
            px = (right, half_height)
            self.assertEqual(
                image.getpixel(px), test[4][3],
                "%s - color right (%s, %s)" % (test[5], px[0], px[1]))

        self.assertEqual(count, 2 * 9, "ensure the loop is ran")
예제 #9
0
    def _parse_logo_colors(self, logo=None, white_threshold=225):
        """
        Identifies dominant colors

        First resizes the original image to improve performance, then discards
        transparent colors and white-ish colors, then calls the averaging
        method twice to evaluate both primary and secondary colors.

        :param logo: alternate logo to process
        :param white_threshold: arbitrary value defining the maximum value a color can reach

        :return colors: hex values of primary and secondary colors
        """
        self.ensure_one()
        logo = logo or self.logo
        if not logo:
            return False, False

        # The "===" gives different base64 encoding a correct padding
        logo += b'===' if type(logo) == bytes else '==='
        try:
            # Catches exceptions caused by logo not being an image
            image = tools.image_fix_orientation(tools.base64_to_image(logo))
        except Exception:
            return False, False

        base_w, base_h = image.size
        w = int(50 * base_w / base_h)
        h = 50

        # Converts to RGBA if no alpha detected
        image_converted = image.convert(
            'RGBA') if 'A' not in image.getbands() else image
        image_resized = image_converted.resize((w, h))

        colors = []
        for color in image_resized.getcolors(w * h):
            if not (color[1][0] > white_threshold
                    and color[1][1] > white_threshold
                    and color[1][2] > white_threshold) and color[1][3] > 0:
                colors.append(color)

        if not colors:  # May happen when the whole image is white
            return False, False
        primary, remaining = tools.average_dominant_color(colors)
        secondary = tools.average_dominant_color(
            remaining)[0] if len(remaining) > 0 else primary

        # Lightness and saturation are calculated here.
        # - If both colors have a similar lightness, the most colorful becomes primary
        # - When the difference in lightness is too great, the brightest color becomes primary
        l_primary = tools.get_lightness(primary)
        l_secondary = tools.get_lightness(secondary)
        if (l_primary < 0.2 and l_secondary < 0.2) or (l_primary >= 0.2
                                                       and l_secondary >= 0.2):
            s_primary = tools.get_saturation(primary)
            s_secondary = tools.get_saturation(secondary)
            if s_primary < s_secondary:
                primary, secondary = secondary, primary
        elif l_secondary > l_primary:
            primary, secondary = secondary, primary

        return tools.rgb_to_hex(primary), tools.rgb_to_hex(secondary)