def test_that_pixelate_returns_pixelated_photo(self):
     pixelated_wolf = self.creator.pixelate(nr_pixels_in_x=50,
                                            nr_pixels_in_y=35)
     output_file = Path.to_testphoto('wolf_pixelated_50_35.bmp')
     if self.RESET_EXPECTED_OUTPUT:
         pixelated_wolf.save(output_file)
     # Expected image is a bitmap, to prevent jpeg artefacts in comparison
     expected_pixelated_wolf = Photo.open(output_file)
     self.assertEqual(expected_pixelated_wolf, pixelated_wolf)
    def test_that_resize_images_resizes_all_images(self):
        self.setup_photo_dir_structure()
        collector = PhotoCollector(self.dirname)
        collector._split_by_size_ratio()
        collector._select_images(desired_size_ratio=100)

        # Crop the first image, such that the original is not square anymore
        mosaic_dir = os.path.join(self.photos_dir, 'mosaic')
        first_cat = os.path.join(mosaic_dir, '00000001.jpg')
        img = Photo.open(first_cat)
        img = img.crop((0, 0, 400, 200))
        img.save(first_cat)

        collector._resize_images(desired_size_ratio=100, desired_width=100)

        for file in os.scandir(mosaic_dir):
            img = Photo.open(file.path)
            desired_size = (100, 100)
            self.assertTupleEqual(desired_size, img.size)
Exemple #3
0
 def photos(self) -> Dict[str, Photo]:
     if not self._photos:
         self._photos = {
             filename: Photo.open(os.path.join(self.src_dir, filename))
             for filename in sorted(os.listdir(self.src_dir))
             if self._is_image(filename)
         }
         if not self._photos:
             msg = f'No photos found in directory {self.src_dir}'
             raise FileNotFoundError(msg)
     return self._photos
 def test_that_photo_pixelate_returns_photo_pixelated_photo(self):
     random.seed(1)
     pixelated_wolf = self.creator.photo_pixelate(src_dir=os.path.join(
         Path.testdata, 'cats'),
                                                  nr_pixels_in_x=30,
                                                  nr_pixels_in_y=30)
     output_file = Path.to_testphoto('wolf_photo_pixelated_30_30.bmp')
     if self.RESET_EXPECTED_OUTPUT:
         pixelated_wolf.save(output_file)
     # Expected image is a bitmap, to prevent jpeg artefacts in comparison
     expected_pixelated_wolf = Photo.open(output_file)
     self.assertEqual(expected_pixelated_wolf, pixelated_wolf)
    def __init__(self,
                 filepath: str,
                 max_output_size: int = DEFAULT_MAX_OUTPUT_SIZE,
                 cheat_parameter: int = DEFAULT_CHEAT_PARAMETER):
        """
        :param filepath: Path to the file with the photo to recreate
        :param max_output_size: Maximum width or height of the output image
        :param cheat_parameter: Value between 0 (no cheat) and 255 (full cheat)
                                to additionally color the photos in the original pixel's color
        """

        assert 0 <= cheat_parameter <= 255

        self.original_photo = Photo.open(filepath)
        self.original_size = self.original_photo.size
        self.pixels = list(self.original_photo.getdata())
        self.max_output_size = max_output_size
        self.cheat_parameter = cheat_parameter
        self.output_size = self._determine_output_size()
Exemple #6
0
    def _split_by_size_ratio(self) -> int:
        """
        Split the flat directory structure of images in `photos/<dirname>` into a directory per size ratio

        Size ratio is defined as width/height, multiplied by 100 and rounded to an integer
        In other words: 100 means 1:1, 160 means 8:5, etc.

        As a side effect, we write a file called `size_ratio.json` with per size ratio the
        amount of images with that size ratio. This can be used to manually inspect the
        various size ratios incase you receive surprising results.

        :return: The most common size ratio
        """

        # Exhaust the iterator in order to not iterate over directories created in the for-loop
        files = list(os.scandir(self.photos_dir))
        size_ratio_counter = defaultdict(int)
        for file in files:
            img = Photo.open(file.path)
            w, h = img.size
            resolution = int(round(w / h * 100))
            dst_dirname = str(resolution)
            dst_dir = os.path.join(self.photos_dir, dst_dirname)
            if not os.path.isdir(dst_dir):
                os.mkdir(dst_dir)
            dst_full_path = os.path.join(dst_dir, file.name)
            size_ratio_counter[resolution] += 1
            shutil.move(file.path, dst_full_path)

        # Sort by number of occurrences
        size_ratio_counter = Counter(size_ratio_counter).most_common()
        most_common = size_ratio_counter[0][0]
        size_ratio_counter = dict(size_ratio_counter)

        resolution_file = os.path.join(self.photos_dir, 'size_ratio.json')
        with open(resolution_file, 'w') as f:
            f.write(json.dumps(size_ratio_counter, indent=4))

        return most_common
Exemple #7
0
    def _resize_images(self, desired_size_ratio: int,
                       desired_width: int) -> None:
        """
        Resize all images from photos/<dirname>/mosaic to the desired width

        :param desired_size_ratio: The desired size ratio for the final images (as defined in split_by_size_ratio)
        :param desired_width: The width of the final images to use in the MosaicCreator

        Rationale why we do not have desired_height as input: it needs to be calculated in a rather
        tedious way, and we don't want the client to take care of that. The desired_
        """

        # TODO: I have the feeling that this should not be done here.

        desired_height = int(round(desired_width / desired_size_ratio * 100))
        desired_size: Size = (desired_width, desired_height)

        mosaic_dir = os.path.join(self.photos_dir, 'mosaic')

        files = list(os.scandir(mosaic_dir))
        for file in files:
            img = Photo.open(file.path)
            img = img.resize(desired_size)
            img.save(file.path)
Exemple #8
0
 def test_that_real_photo_returns_correct_color(self):
     photo = Photo.open(Path.to_testphoto('wolf_low_res'))
     expected_avg_color = (127, 111, 102)
     self.assertTupleEqual(expected_avg_color, photo.avg_color)