def testGetAndConvertAllowTruncatedImage(self): # Should return a converted image if we tolerate truncated images. # To test image truncation, we actually need to write a file to disk. img_filepath = os.path.join(self.testdata_dir, 'test_img.png') try: orig_img = Image.new('RGBA', (500, 500)) orig_img.save(img_filepath) filesize = os.path.getsize(img_filepath) with open(img_filepath, 'r+') as img_on_disk: img_on_disk.truncate(filesize - 100) image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) output_image_with_status = parallelize.get_and_convert_image( img_filepath, image_convert_settings, allow_truncated_images=True) self.assertEqual(output_image_with_status[0].size, (100, 100)) except: raise finally: # Cleanup. if os.path.isfile(img_filepath): os.remove(img_filepath)
def testConvertDefaultImageFails(self): testfile_path = os.path.join(self.testdata_dir, 'attributions.txt') image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) with self.assertRaises(IOError): parallelize.convert_default_image(testfile_path, image_convert_settings)
def testGetAndConvertTruncatedImageFail(self): # Should note fail but return None when PIL fails on truncated image. # To test image truncation, we actually need to write a file to disk. img_filepath = os.path.join(self.testdata_dir, 'test_img.png') try: orig_img = Image.new('RGBA', (500, 500)) orig_img.save(img_filepath) filesize = os.path.getsize(img_filepath) with open(img_filepath, 'r+') as img_on_disk: img_on_disk.truncate(filesize - 100) image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) output_image_with_status = parallelize.get_and_convert_image( img_filepath, image_convert_settings) self.assertIsNone(output_image_with_status[0]) self.assertTrue(output_image_with_status[1]) # Has error message. except: raise finally: # Cleanup. if os.path.isfile(img_filepath): os.remove(img_filepath)
def testConvertSmallerIgnoreRatioCropTopLeftNoAspectRatio(self): # Verify that we get the desired output image when cropping from top left. # In this case, the aspect ratio of the output image is different than # that of the orig image so cropping occurs. # Orig image is a 4 quadrant (200, 200) pixel image, with colors # (orange, red, green, yellow) starting from top left and going clockwise. orig_img = Image.new('RGBA', (200, 200), self.yellow_rgb) orig_img.paste(Image.new('RGBA', (100, 100), self.orange_rgb), (0, 0)) orig_img.paste(Image.new('RGBA', (100, 100), self.red_rgb), (100, 0)) orig_img.paste(Image.new('RGBA', (100, 100), self.green_rgb), (100, 100)) # Note that in this case, the aspect ratio has changed. conversion_settings = convert.ImageConvertSettings( 'png', width=20, height=200, position=(0, 0), preserve_aspect_ratio=False) # Output image should be 20x200 that only contains the orange and yellow # sections since we crop from the left. image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() # Top left of output image should be orange. self.assertEqual(converted_img.getpixel((0, 0))[0:3], self.orange_rgb) # Top right should be orange self.assertEqual(converted_img.getpixel((19, 0))[0:3], self.orange_rgb) # Bottom right should be yellow self.assertEqual( converted_img.getpixel((19, 199))[0:3], self.yellow_rgb) # Bottom left should be yellow self.assertEqual( converted_img.getpixel((0, 199))[0:3], self.yellow_rgb)
def testConvertSmallerRightPosKeepRatio(self): # Same as above but we specify that the position remains in the right. # Verify that for an original red rectangular (vertically long image), # after conversion to a square size with blue background the output image # looks correct based on sampling color at several locations. orig_img = Image.new('RGBA', (50, 500), self.red_rgb) conversion_settings = convert.ImageConvertSettings( 'png', 100, 100, position=(1, 0.5), bg_color_rgb=self.blue_rgb) # Final image should be a 10px wide by 100px tall rectangle flush with # the right edge of the output size. I.e., rectangle with center at pixel # (94, 49) and the blue background staring at x position 9. image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() # Just compare the RGB values, not opacity # Center of image should be blue self.assertEqual(converted_img.getpixel((45, 45))[0:3], self.blue_rgb) # Top left corner should be blue background. self.assertEqual(converted_img.getpixel((0, 0))[0:3], self.blue_rgb) # Center of rectangle should be red self.assertEqual(converted_img.getpixel((94, 49))[0:3], self.red_rgb) # Bottom left edge of rectangle and adjacent background self.assertEqual(converted_img.getpixel((90, 99))[0:3], self.red_rgb) self.assertEqual(converted_img.getpixel((89, 99))[0:3], self.blue_rgb) # Top left edge of rectangle and adjacent background self.assertEqual(converted_img.getpixel((90, 0))[0:3], self.red_rgb) self.assertEqual(converted_img.getpixel((89, 0))[0:3], self.blue_rgb)
def testConvertResizedSmallKeepRatio(self): # Verify that for an original red rectangular (vertically long image), # after conversion to a square size with blue background the output image # looks correct based on sampling color at several locations. orig_img = Image.new('RGBA', (50, 500), self.red_rgb) conversion_settings = convert.ImageConvertSettings( 'png', 100, 100, bg_color_rgb=self.blue_rgb) # Final image should be a 10px wide by 100px tall rectangle centered # within the square, i.e., with center at pixel (49, 49) and borders of # 45 pixels on each side. image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() # Just compare the RGB values, not opacity # Center should be red self.assertEqual(converted_img.getpixel((45, 45))[0:3], self.red_rgb) # Top Center should be red self.assertEqual(converted_img.getpixel((45, 0))[0:3], self.red_rgb) # Bottom Center should be red self.assertEqual(converted_img.getpixel((45, 99))[0:3], self.red_rgb) # Top left edge border should be blue background, then red rect. self.assertEqual(converted_img.getpixel((44, 0))[0:3], self.blue_rgb) self.assertEqual(converted_img.getpixel((45, 0))[0:3], self.red_rgb) # Bottom right edge border should be red, then blue background. self.assertEqual(converted_img.getpixel((54, 99))[0:3], self.red_rgb) self.assertEqual(converted_img.getpixel((55, 99))[0:3], self.blue_rgb)
def testConvertDefaultImageSucceeds(self): testfile_path = os.path.join(self.testdata_dir, 'Googleplex-Patio-Aug-2014.JPG') image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) img = parallelize.convert_default_image(testfile_path, image_convert_settings) self.assertEqual(img.size, (100, 100))
def testGetAndConvertOneImageBadUrl(self): # Returns None if can't open file. testfile_path = 'http://www.google.com' image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) output_image, status = parallelize.get_and_convert_image( testfile_path, image_convert_settings) del status # linter self.assertEqual(output_image, None)
def testGetAndConvertOneImageNotImageFile(self): # Returns None if can't open file. testfile_path = os.path.join(self.testdata_dir, 'attributions.txt') image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) output_image, status = parallelize.get_and_convert_image( testfile_path, image_convert_settings) del status # linter self.assertEqual(output_image, None)
def testGetAndConvertOneImageFromLocalFile(self): # Test parallelization of a single image succeeds. testfile_path = os.path.join(self.testdata_dir, 'Googleplex-Patio-Aug-2014.JPG') expected_output_image_size = (100, 100) image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) output_image, status = parallelize.get_and_convert_image( testfile_path, image_convert_settings) del status # linter self.assertSameElements(output_image.size, expected_output_image_size)
def test_initializeWithAllSettings(self): # Simply verifies we can create an instance and return some properties. settings = convert.ImageConvertSettings('png', 100, 100, position=(0.1, 0.1), bg_color_rgb=(100, 100, 100), opacity=100, resize_if_larger=True, preserve_aspect_ratio=True) self.assertEqual(settings.format, 'png') self.assertEqual(settings.height, 100) self.assertEqual(settings.width, 100)
def setUp(self): self.testdata_dir = os.path.join(os.getcwd(), TESTDATA_DIR) self.desired_width = 100 self.desired_height = 100 self.conversion_settings = convert.ImageConvertSettings( 'png', self.desired_width, self.desired_height) # Color settings used to verifying image output is correct self.red_rgb = ImageColor.getrgb('red') self.blue_rgb = ImageColor.getrgb('blue') self.orange_rgb = ImageColor.getrgb('orange') self.green_rgb = ImageColor.getrgb('green') self.yellow_rgb = ImageColor.getrgb('yellow')
def testConvertImagePaddedLargerCorrectSize(self): # Input image is smaller than desired, but we just pad it to fit new size. conversion_settings = convert.ImageConvertSettings( 'png', self.desired_width, self.desired_height, resize_if_larger=True) orig_img = Image.new('RGBA', (10, 10)) image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() self.assertEqual(converted_img.size, (self.desired_width, self.desired_height))
def testConvertSmallerIgnoreRatio(self): # Test resize image to smaller sprite without retaining aspect ratio. # Simply verifies correct size output. conversion_settings = convert.ImageConvertSettings( 'png', self.desired_width, self.desired_height, preserve_aspect_ratio=False) orig_img = Image.new('RGBA', (1000, 500)) image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() self.assertEqual(converted_img.size, (self.desired_width, self.desired_height))
def testConvertResizedLargerKeepAspectRatio(self): conversion_settings = convert.ImageConvertSettings( 'png', self.desired_width, self.desired_height, resize_if_larger=True, preserve_aspect_ratio=False) orig_img = Image.new('RGBA', (10, 10)) image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() self.assertEqual(converted_img.size, (self.desired_width, self.desired_height))
def testConvertSmallerKeepRatioHasCorrectSize(self): # Larger image is resized smaller, keeping aspect ratio. # This test simply verifies the size # TODO: delete and merge with following tests. conversion_settings = convert.ImageConvertSettings( 'png', self.desired_width, self.desired_height, preserve_aspect_ratio=True) orig_img = Image.new('RGBA', (1000, 500)) image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() self.assertEqual(converted_img.size, (self.desired_width, self.desired_height))
def testParallelizeConvertImagesFromLocalfile(self): # Test parallelization with multiple reads of the same image file produces # expected output images with correct sizes. testfile_path = os.path.join(self.testdata_dir, 'Googleplex-Patio-Aug-2014.JPG') testfile_locations = [testfile_path] * 3 expected_output_image_sizes = [(100, 100)] * 3 image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) output_imgs_with_status = parallelize.get_and_convert_images_parallel( testfile_locations, image_convert_settings, verbose=1) resulting_image_sizes = [] for (image, status) in output_imgs_with_status: del status # linter resulting_image_sizes.append(image.size) self.assertSameElements(resulting_image_sizes, expected_output_image_sizes)
def testParallelizeConvertWithFailures(self): # 3 images are attempted, the last one should fail. testfile_path = os.path.join(self.testdata_dir, 'Googleplex-Patio-Aug-2014.JPG') bad_testfile_path = os.path.join(self.testdata_dir, 'attributions.txt') testfile_locations = [testfile_path, testfile_path, bad_testfile_path] expected_image_size = (100, 100) image_convert_settings = convert.ImageConvertSettings( img_format='png', width=100, height=100) output_images = parallelize.get_and_convert_images_parallel( testfile_locations, image_convert_settings, verbose=1) first_converted_img = output_images[0][0] second_converted_img = output_images[1][0] third_converted_img = output_images[2][0] self.assertSameElements(first_converted_img.size, expected_image_size) self.assertSameElements(second_converted_img.size, expected_image_size) self.assertEqual(third_converted_img, None) # Failed conversion.
def testConvertSmallerIgnoreRatioCropTopLeftSameAspectRatio(self): # Verify that we get the desired output image when cropping from left. # In this case, since the output image's aspect ratio is same as the input # image's aspect ratio, we're able to crop it correctly and keep the same # pattern. # Orig image is a 4 quadrant (200, 200) pixel image, with colors # (orange, red, green, yellow) starting from top left and going clockwise. orig_img = Image.new('RGBA', (200, 200), self.yellow_rgb) orig_img.paste(Image.new('RGBA', (100, 100), self.orange_rgb), (0, 0)) orig_img.paste(Image.new('RGBA', (100, 100), self.red_rgb), (100, 0)) orig_img.paste(Image.new('RGBA', (100, 100), self.green_rgb), (100, 100)) conversion_settings = convert.ImageConvertSettings( 'png', self.desired_width, self.desired_height, position=(0, 0), preserve_aspect_ratio=False) # Output image should be 100x100 cropped from the center. image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() # Top left of output image should be orange. self.assertEqual(converted_img.getpixel((0, 0))[0:3], self.orange_rgb) # Top right should be red self.assertEqual(converted_img.getpixel((99, 0))[0:3], self.red_rgb) # Bottom right should be green self.assertEqual(converted_img.getpixel((99, 99))[0:3], self.green_rgb) # Bottom left should be yellow self.assertEqual(converted_img.getpixel((0, 99))[0:3], self.yellow_rgb) # Center section should comprise the same set of colors clockwise. self.assertEqual( converted_img.getpixel((49, 49))[0:3], self.orange_rgb) self.assertEqual(converted_img.getpixel((50, 49))[0:3], self.red_rgb) self.assertEqual(converted_img.getpixel((50, 50))[0:3], self.green_rgb) self.assertEqual( converted_img.getpixel((49, 50))[0:3], self.yellow_rgb)
def testConvertImageResizedLargerIgnoreRatio(self): # Verifies that output size and image (based on pixel samples) are correct. conversion_settings = convert.ImageConvertSettings( 'png', self.desired_width, self.desired_height, bg_color_rgb=self.blue_rgb, resize_if_larger=True, preserve_aspect_ratio=False) orig_img = Image.new('RGBA', (20, 10), self.red_rgb) image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() # Verify size. self.assertEqual(converted_img.size, (self.desired_width, self.desired_height)) # Image should be stretched to fit desired output size, so entire output # should be red. Verify center, and top left / bottom right edges. self.assertEqual(converted_img.getpixel((45, 45))[0:3], self.red_rgb) self.assertEqual(converted_img.getpixel((0, 0))[0:3], self.red_rgb) self.assertEqual(converted_img.getpixel((99, 99))[0:3], self.red_rgb)
def testConvertResizedSmallDontKeepRatioCenterCrop(self): # Verify that we get the desired output image when cropping from center. # Orig image is a 4 quadrant (200, 200) pixel image, with colors # (orange, red, green, yellow) starting from top left and going clockwise. orig_img = Image.new('RGBA', (200, 200), self.yellow_rgb) orig_img.paste(Image.new('RGBA', (100, 100), self.orange_rgb), (0, 0)) orig_img.paste(Image.new('RGBA', (100, 100), self.red_rgb), (100, 0)) orig_img.paste(Image.new('RGBA', (100, 100), self.green_rgb), (100, 100)) conversion_settings = convert.ImageConvertSettings( 'png', self.desired_width, self.desired_height, preserve_aspect_ratio=False) # Output image should be 100x100 cropped from the center. image_converter = convert.ImageConverter(orig_img, conversion_settings) converted_img = image_converter.convert() # Top left of output image should be orange. self.assertEqual(converted_img.getpixel((0, 0))[0:3], self.orange_rgb) # Top right should be red self.assertEqual(converted_img.getpixel((99, 0))[0:3], self.red_rgb) # Bottom right should be green self.assertEqual(converted_img.getpixel((99, 99))[0:3], self.green_rgb) # Bottom left should be yellow self.assertEqual(converted_img.getpixel((0, 99))[0:3], self.yellow_rgb) # Center section should comprise the same set of colors clockwise. self.assertEqual( converted_img.getpixel((49, 49))[0:3], self.orange_rgb) self.assertEqual(converted_img.getpixel((50, 49))[0:3], self.red_rgb) self.assertEqual(converted_img.getpixel((50, 50))[0:3], self.green_rgb) self.assertEqual( converted_img.getpixel((49, 50))[0:3], self.yellow_rgb)
def test_invalidOpacity(self): with self.assertRaises(ValueError): convert.ImageConvertSettings('png', 10, 10, opacity=300)
def test_invalidBGColorRGB(self): with self.assertRaises(ValueError): convert.ImageConvertSettings('png', 10, 10, bg_color_rgb=(-1, 500, 255))
def test_invalidPosition(self): with self.assertRaises(ValueError): convert.ImageConvertSettings('png', 10, 10, position=(-1, 0))
def test_invalidHeight(self): with self.assertRaises(ValueError): convert.ImageConvertSettings('png', 10, 0)
def testCreateDefaultImage(self): conversion_settings = convert.ImageConvertSettings( 'png', 20, 20, resize_if_larger=True) output_img = convert.create_default_image(conversion_settings) self.assertEqual(output_img.size, (20, 20))
def test_invalidWidth(self): with self.assertRaises(ValueError): convert.ImageConvertSettings('png', 0, 10)
def main(argv): del argv # Unused. # TODO: Add more flag validations. if FLAGS.max_failures is not None and FLAGS.max_failures > 0: raise NotImplementedError( 'Does not yet handle image retrieval/conversion ' 'failures') if FLAGS.atlas_width is not None or FLAGS.atlas_height is not None: print(FLAGS.atlas_width, FLAGS.atlas_height) # raise NotImplementedError( # 'Does not yet support specifying an atlas size.') if FLAGS.sourcelist is None: raise flags.ValidationError( 'You must specify a list of image sources.') bg_color_rgb = _determine_bg_rgb() outputdir = FLAGS.output_dir if outputdir is None: outputdir = os.path.join(os.getcwd()) image_source_list = atlasmaker_io.read_src_list_csvfile( FLAGS.sourcelist, FLAGS.sourcelist_dups_handling) # Provide some useful confirmation info about settings to user. logging.info( 'Desired output size in pixels width, height for each image is: ' '(%d, %d)' % (FLAGS.image_width, FLAGS.image_height)) logging.info('Image format for Atlas is: %s' % FLAGS.image_format) logging.info('Background RGB is set to %s' % str(bg_color_rgb)) logging.info('Background opacity is set to %d' % FLAGS.image_opacity) logging.info( 'Should we preserve image aspect ratio during conversion? %s' % FLAGS.keep_aspect_ratio) image_convert_settings = convert.ImageConvertSettings( img_format=FLAGS.image_format, width=FLAGS.image_width, height=FLAGS.image_height, bg_color_rgb=bg_color_rgb, opacity=FLAGS.image_opacity, preserve_aspect_ratio=FLAGS.keep_aspect_ratio, resize_if_larger=FLAGS.resize_if_larger) # Ensure we can write to the output dir or fail fast. atlasmaker_io.create_output_dir_if_not_exist(FLAGS.output_dir) # Create default image to be used for images that we can't get or convert. if FLAGS.default_image_path is not None: logging.info('Using image %s as default image when a specified image ' 'can\'t be fetched or converted' % FLAGS.default_image_path) default_img = parallelize.convert_default_image( FLAGS.default_image_path, image_convert_settings) else: logging.info( 'No default image for failures specified by user, so just ' 'using the background as the default image.') default_img = convert.create_default_image(image_convert_settings) # Verify we can write the specified output format, or fail fast. try: testimage_file_name = '{}.{}'.format('testimage', str(FLAGS.image_format).lower()) atlasmaker_io.save_image(default_img, os.path.join(FLAGS.output_dir, testimage_file_name), delete_after_write=True) logging.info('Confirmed we can output images in %s format' % FLAGS.image_format) except: logging.error('Unable to write test image in desired output format. ' 'Please confirm that \'%s\' is a supported PIL output ' 'format.' % FLAGS.image_format) raise # Convert images in parallel. logging.info('Scheduling %d tasks.' % len(image_source_list)) converted_images_with_statuses = parallelize.get_and_convert_images_parallel( image_source_list, image_convert_settings, n_jobs=FLAGS.num_parallel_jobs, verbose=FLAGS.parallelization_verbosity, allow_truncated_images=FLAGS.use_truncated_images, request_timeout=FLAGS.http_request_timeout, http_max_retries=FLAGS.http_max_retries) sprite_atlas_settings = montage.SpriteAtlasSettings( img_format=FLAGS.image_format, height=FLAGS.atlas_height, width=FLAGS.atlas_width, filename=FLAGS.filename) # width=FLAGS.atlas_width) # Generate the atlas from converted images. sprite_atlas_generator = montage.SpriteAtlasGenerator( images_with_statuses=converted_images_with_statuses, img_src_paths=image_source_list, atlas_settings=sprite_atlas_settings, default_img=default_img) atlases, manifests = sprite_atlas_generator.create_atlas() atlasmaker_io.save_atlas_and_manifests( outdir=outputdir, atlases=atlases, manifests=manifests, sprite_atlas_settings=sprite_atlas_settings)
def test_initializeWithDefaultSettings(self): # Simply verifies we can create an instance and return some properties. settings = convert.ImageConvertSettings('png', 100, 100) self.assertEqual(settings.format, 'png') self.assertEqual(settings.height, 100) self.assertEqual(settings.width, 100)