def test_save_cjpeg(self): img = Image.open(TEST_FILE) tempfile = self.tempfile("temp.jpg") JpegImagePlugin._save_cjpeg(img, 0, tempfile) # Default save quality is 75%, so a tiny bit of difference is alright self.assert_image_similar(img, Image.open(tempfile), 17)
def test_jpeg(self): path = os.path.join(TEST_DATA_PATH, "Sam_Hat1.jpg") image = Image.objects.create_from_path(path) # Re-load the image, now that the task is done image = Image.objects.get(id=image.id) self.assertTrue(image.source.path.endswith("Sam_Hat1.jpg")) self.assertEqual(image.width, 3264) self.assertEqual(image.height, 2448) self.assertEqual(image.jpeg_quality, None) self.assertTrue(os.path.exists(image.optimized.path)) self.assertTrue(os.path.exists(image.source.path)) source = PILImage.open(image.source.path) optimized = PILImage.open(image.optimized.path) self.assertEqual( source.quantization, optimized.quantization ) self.assertEqual( JpegImagePlugin.get_sampling(source), JpegImagePlugin.get_sampling(optimized), )
def read_q_table(file_name): jpg = JpegImagePlugin.JpegImageFile(file_name) qtable = JpegImagePlugin.convert_dict_qtables(jpg.quantization) Y_qtable = qtable[0] Y_qtable_2d = np.zeros((8, 8)) qtable_idx = 0 for i in range(0, 8): for j in range(0, 8): Y_qtable_2d[i, j] = Y_qtable[qtable_idx] qtable_idx = qtable_idx + 1 return Y_qtable_2d
def create_image(self, buffer): try: img = Image.open(BytesIO(buffer)) except DECOMPRESSION_BOMB_EXCEPTIONS as error: logger.warning("[PILEngine] create_image failed: %s", error) return None self.icc_profile = img.info.get("icc_profile") self.exif = img.info.get("exif") self.original_mode = img.mode self.subsampling = JpegImagePlugin.get_sampling(img) if self.subsampling == -1: # n/a for this file self.subsampling = None self.qtables = getattr(img, "quantization", None) if (self.context.config.ALLOW_ANIMATED_GIFS and self.extension == ".gif"): frames = [] for frame in ImageSequence.Iterator(img): frames.append(frame.convert("P")) img.seek(0) self.frame_count = len(frames) return frames return img
def optimize_image(image_model, image_buffer, filename): im = PILImage.open(image_buffer) # Let's cache some important stuff format = im.format icc_profile = im.info.get("icc_profile") quantization = getattr(im, "quantization", None) subsampling = None if format == "JPEG": try: subsampling = JpegImagePlugin.get_sampling(im) except IndexError: # Ignore if sampling fails logger.debug('JPEG sampling failed, ignoring') except: # mparent(2016-03-25): Eventually eliminate "catch all", but need to log errors to see # if we're missing any other exception types in the wild logger.exception('JPEG sampling error') if im.size[0] > settings.BETTY_MAX_WIDTH: # If the image is really large, we'll save a more reasonable version as the "original" height = settings.BETTY_MAX_WIDTH * float(im.size[1]) / float(im.size[0]) im = im.resize((settings.BETTY_MAX_WIDTH, int(round(height))), PILImage.ANTIALIAS) out_buffer = io.BytesIO() if format == "JPEG" and im.mode == "RGB": # For JPEG files, we need to make sure that we keep the quantization profile try: im.save( out_buffer, icc_profile=icc_profile, qtables=quantization, subsampling=subsampling, format="JPEG") except ValueError as e: # Maybe the image already had an invalid quant table? if e.args[:1] == ('Invalid quantization table',): out_buffer = io.BytesIO() # Make sure it's empty after failed save attempt im.save( out_buffer, icc_profile=icc_profile, format=format, ) else: raise else: im.save(out_buffer, icc_profile=icc_profile, format=format) image_model.optimized.save(filename, File(out_buffer)) else: # No modifications, just save original as optimized image_buffer.seek(0) image_model.optimized.save(filename, File(image_buffer)) image_model.save()
def save_image(img, fname): print(fname) quantization = getattr(img, 'quantization', None) subsampling = JpegImagePlugin.get_sampling(img) if quantization else None if subsampling: img.save(fname, subsampling=subsampling, qtables=quantization) else: img.save(fname)
def optimize_image(image): im = PILImage.open(image.source.path) # Let's cache some important stuff format = im.format icc_profile = im.info.get("icc_profile") quantization = getattr(im, "quantization", None) subsampling = None if format == "JPEG": try: subsampling = JpegImagePlugin.get_sampling(im) except: pass # Sometimes, crazy images exist. filename = os.path.split(image.source.path)[1] if im.size[0] > settings.BETTY_MAX_WIDTH: # If the image is really large, we'll save a more reasonable version as the "original" height = settings.BETTY_MAX_WIDTH * float(im.size[1]) / float(im.size[0]) im = im.resize((settings.BETTY_MAX_WIDTH, int(round(height))), PILImage.ANTIALIAS) """OK, so this suuuuuucks. When we convert or resize an Image, it is no longer a JPEG. So, in order to reset the quanitzation, etc, we need to save this to a file and then re-read it from the filesystem. Silly, I know. Once my pull request is approved, this can be removed, and we can just pass the qtables into the save method. PR is here: https://github.com/python-imaging/Pillow/pull/677 """ temp = tempfile.NamedTemporaryFile() im.save(temp, format="JPEG") temp.seek(0) im = PILImage.open(temp) im.quantization = quantization image.optimized.name = optimized_upload_to(image, filename) if format == "JPEG" and im.mode == "RGB": # For JPEG files, we need to make sure that we keep the quantization profile try: im.save( image.optimized.name, icc_profile=icc_profile, quality="keep", subsampling=subsampling) except (TypeError, ValueError) as e: # Maybe the image already had an invalid quant table? if e.message.startswith("Not a valid numbers of quantization tables"): im.save( image.optimized.name, icc_profile=icc_profile ) else: raise else: im.save(image.optimized.name, icc_profile=icc_profile) image.save()
def decode_jpeg(encoded, tables=b'', photometric=None, ycbcr_subsampling=None, ycbcr_positioning=None): ''' ycbcr resampling is missing in both tifffile and PIL ''' from StringIO import StringIO from PIL import JpegImagePlugin return JpegImagePlugin.JpegImageFile(StringIO(tables + encoded)).tobytes()
def test_jpeg(): shutil.rmtree(bettysettings.BETTY_IMAGE_ROOT, ignore_errors=True) path = os.path.join(TEST_DATA_PATH, "Sam_Hat1.jpg") image = Image.objects.create_from_path(path) # Re-load the image, now that the task is done image = Image.objects.get(id=image.id) assert image.source.path.endswith("Sam_Hat1.jpg") assert image.width == 3264 assert image.height == 2448 assert image.jpeg_quality is None assert os.path.exists(image.optimized.path) assert os.path.exists(image.source.path) source = PILImage.open(image.source.path) optimized = PILImage.open(image.optimized.path) assert source.quantization == optimized.quantization assert JpegImagePlugin.get_sampling(source) == JpegImagePlugin.get_sampling(optimized)
def test_jpeg(): path = os.path.join(TEST_DATA_PATH, "Sam_Hat1.jpg") image = Image.objects.create_from_path(path) # Re-load the image, now that the task is done image = Image.objects.get(id=image.id) assert image.source.path.endswith("Sam_Hat1.jpg") assert image.width == 3264 assert image.height == 2448 assert image.jpeg_quality is None assert os.path.exists(image.optimized.path) assert os.path.exists(image.source.path) assert not image.animated source = PILImage.open(image.source.path) optimized = PILImage.open(image.optimized.path) assert source.quantization == optimized.quantization assert JpegImagePlugin.get_sampling(source) == JpegImagePlugin.get_sampling(optimized)
def optimize_image(image): im = PILImage.open(image.source.path) # Let's cache some important stuff format = im.format icc_profile = im.info.get("icc_profile") quantization = getattr(im, "quantization", None) subsampling = None if format == "JPEG": try: subsampling = JpegImagePlugin.get_sampling(im) except: pass # Sometimes, crazy images exist. filename = os.path.split(image.source.path)[1] image.optimized.name = optimized_upload_to(image, filename) if im.size[0] > settings.BETTY_MAX_WIDTH: # If the image is really large, we'll save a more reasonable version as the "original" height = settings.BETTY_MAX_WIDTH * float(im.size[1]) / float(im.size[0]) im = im.resize((settings.BETTY_MAX_WIDTH, int(round(height))), PILImage.ANTIALIAS) if format == "JPEG" and im.mode == "RGB": # For JPEG files, we need to make sure that we keep the quantization profile try: im.save( image.optimized.name, icc_profile=icc_profile, qtables=quantization, subsampling=subsampling, format="JPEG") except (TypeError, ValueError) as e: # Maybe the image already had an invalid quant table? if e.message.startswith("Not a valid numbers of quantization tables"): im.save( image.optimized.name, icc_profile=icc_profile ) else: raise else: im.save(image.optimized.name, icc_profile=icc_profile) else: shutil.copy2(image.source.path, image.optimized.name) image.save()
def optimize_image(image): im = PILImage.open(image.source.path) # Let's cache some important stuff format = im.format icc_profile = im.info.get("icc_profile") quantization = getattr(im, "quantization", None) subsampling = None if format == "JPEG": try: subsampling = JpegImagePlugin.get_sampling(im) except: pass # Sometimes, crazy images exist. filename = os.path.split(image.source.path)[1] if im.size[0] > settings.BETTY_MAX_WIDTH: # If the image is really large, we'll save a more reasonable version as the "original" height = settings.BETTY_MAX_WIDTH * float(im.size[1]) / float( im.size[0]) im = im.resize((settings.BETTY_MAX_WIDTH, int(round(height))), PILImage.ANTIALIAS) image.optimized.name = optimized_upload_to(image, filename) if format == "JPEG" and im.mode == "RGB": # For JPEG files, we need to make sure that we keep the quantization profile try: im.save(image.optimized.name, icc_profile=icc_profile, qtables=quantization, subsampling=subsampling, format="JPEG") except (TypeError, ValueError) as e: # Maybe the image already had an invalid quant table? if e.message.startswith( "Not a valid numbers of quantization tables"): im.save(image.optimized.name, icc_profile=icc_profile) else: raise else: im.save(image.optimized.name, icc_profile=icc_profile) image.save()
def create_image(self, buffer): img = Image.open(BytesIO(buffer)) self.icc_profile = img.info.get('icc_profile') self.transparency = img.info.get('transparency') self.exif = img.info.get('exif') self.subsampling = JpegImagePlugin.get_sampling(img) if (self.subsampling == -1): # n/a for this file self.subsampling = None self.qtables = getattr(img, 'quantization', None) if self.context.config.ALLOW_ANIMATED_GIFS and self.extension == '.gif': frames = [] for frame in ImageSequence.Iterator(img): frames.append(frame.convert('P')) img.seek(0) self.frame_count = len(frames) return frames return img
def resize_and_crop(org, dst, size, center=(0.5, 0.5)): from PIL import Image, JpegImagePlugin as JIP img = adjust_rotation(Image.open(org)) org_r = img.size[1] / img.size[0] dst_r = size[1] / size[0] s1 = (size[0], int(size[0] * org_r)) if org_r > dst_r else (int(size[1] / org_r), size[1]) d = [int((s1[i] - size[i]) / 2) for i in (0, 1)] c = [int((0.5 - center[i]) * s1[i]) for i in (0, 1)] i2 = img.resize(s1).crop( (d[0] - c[0], d[1] - c[1], s1[0] - d[0] - c[0], s1[1] - d[1] - c[1])) restore_rotation(i2.resize(size)).save(validate_path(dst), 'JPEG', optimize=True, exif=img.info['exif'], icc_profile=img.info.get( 'icc_profile', ''), subsampling=JIP.get_sampling(img)) return i2
def put_data_to_image(self, data): img = Image.new(self.image.mode, self.image.size) pixels = self.image.load() pixels_new = img.load() size_x, size_y = img.size data_pieces = (data[x:x+2] for x in range(0, len(data), 2)) for x in range(size_x): for y in range(size_y): r, g, b = pixels[x, y] r = bin(r)[2:] g = bin(g)[2:] b = bin(b)[2:] data_r = next(data_pieces, '') data_g = next(data_pieces, '') data_b = next(data_pieces, '') new_r = r[:-len(data_r) or len(r)] + data_r new_g = g[:-len(data_g) or len(g)] + data_g new_b = b[:-len(data_b) or len(b)] + data_b pixels_new[x, y] = (int(new_r, 2), int(new_g, 2), int(new_b, 2)) new_file_name = '.'.join(self.photo_path.split('.')[:-1]) + '_new.' + 'png' img.save(new_file_name, subsampling=JIP.get_sampling(img))
def create_image(self, buffer): try: img = Image.open(BytesIO(buffer)) except Image.DecompressionBombWarning as e: logger.warning("[PILEngine] create_image failed: {0}".format(e)) return None self.icc_profile = img.info.get('icc_profile') self.exif = img.info.get('exif') self.subsampling = JpegImagePlugin.get_sampling(img) if (self.subsampling == -1): # n/a for this file self.subsampling = None self.qtables = getattr(img, 'quantization', None) if self.context.config.ALLOW_ANIMATED_GIFS and self.extension == '.gif': frames = [] for frame in ImageSequence.Iterator(img): frames.append(frame.convert('P')) img.seek(0) self.frame_count = len(frames) return frames return img
def _make_page(self, args, filename): self.img = Image.open(filename) #self.img = change_resolution(self.img, [8.5, 11], 320, False) self.quantization = getattr(self.img, 'quantization', None) self.subsampling = JpegImagePlugin.get_sampling( self.img) if self.quantization else None if args.bg: bg = args.bg if bg.lower() == 'none': bg = None else: bg = detect_background_color(self.img) if args.scale != 1.0: print('Scaling image: {}'.format(args.scale)) self.img = self.img.resize( [int(i * args.scale) for i in self.img.size]) self.landscape = self.img.size[0] > self.img.size[1] if self.landscape: self.img = self.img.rotate(90, expand=True) im = image_to_array(self.img) threshold = args.threshold if not threshold: if Page.threshold is None: #Page.threshold, _ = auto_threshold(im) Page.threshold = np.mean(im) print('auto threshold:', Page.threshold) threshold = Page.threshold #panels = panelize_crop(im, threshold) #im = cv2.convertScaleAbs(im, alpha=2.5) panels, Page.kern_size, Page.iters = panelize_contours( im, threshold, Page.kern_size, Page.iters) return self._set_panels(args, self.img, panels, bg)
def test_pil_palette(): # img = cv2.imread('./img2.jpg') img = Image.open('./featuremaps.png') arr = np.array([ 7, 24, 86, 236, 255, 255, 255, 255, 57, 60, 112, 88, 255, 255, 255, 255, 146, 176, 199, 173, 255, 255, 255, 255, 247, 171, 199, 185, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]) arr2 = np.array([ 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99 ]) arr3 = np.array([ 1, 1, 255, 255, 255, 255, 255, 255, 1, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]) arr = (arr).astype(np.int) qt = {0: arr3} from PIL import JpegImagePlugin qt = JpegImagePlugin.convert_dict_qtables(qt) file = io.BytesIO() img_qt = img.save(file, 'JPEG', qtables=qt) file.seek(0) img_data = file.read() img__ = cv2.imdecode(np.frombuffer(img_data, dtype=np.uint8), 1) cv2.imshow('tmp', img__) cv2.waitKey(0) cv2.destroyAllWindows() qt_n = img__.quantization print(0)
def create_image(self, buffer): try: img = Image.open(BytesIO(buffer)) except DecompressionBombExceptions as e: logger.warning("[PILEngine] create_image failed: {0}".format(e)) return None self.icc_profile = img.info.get('icc_profile') self.exif = img.info.get('exif') self.original_mode = img.mode self.subsampling = JpegImagePlugin.get_sampling(img) if (self.subsampling == -1): # n/a for this file self.subsampling = None self.qtables = getattr(img, 'quantization', None) if self.context.config.ALLOW_ANIMATED_GIFS and self.extension == '.gif': frames = [] for frame in ImageSequence.Iterator(img): frames.append(frame.convert('P')) img.seek(0) self.frame_count = len(frames) return frames return img
def _save(im, fp, filename): # Note that we can only save the current frame at present return JpegImagePlugin._save(im, fp, filename)
def jpeg_image(): return JpegImagePlugin.JpegImageFile(create_test_image('jpeg'))
def getiptcinfo(im): import StringIO data = None if isinstance(im, IptcImageFile): # return info dictionary right away return im.info elif isinstance(im, JpegImagePlugin.JpegImageFile): # extract the IPTC/NAA resource try: app = im.app["APP13"] if app[:14] == "Photoshop 3.0\x00": app = app[14:] # parse the image resource block offset = 0 while app[offset:offset+4] == "8BIM": offset = offset + 4 # resource code code = JpegImagePlugin.i16(app, offset) offset = offset + 2 # resource name (usually empty) name_len = ord(app[offset]) name = app[offset+1:offset+1+name_len] offset = 1 + offset + name_len if offset & 1: offset = offset + 1 # resource data block size = JpegImagePlugin.i32(app, offset) offset = offset + 4 if code == 0x0404: # 0x0404 contains IPTC/NAA data data = app[offset:offset+size] break offset = offset + size if offset & 1: offset = offset + 1 except (AttributeError, KeyError): pass elif isinstance(im, TiffImagePlugin.TiffImageFile): # get raw data from the IPTC/NAA tag (PhotoShop tags the data # as 4-byte integers, so we cannot use the get method...) try: type, data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] except (AttributeError, KeyError): pass if data is None: return None # no properties # create an IptcImagePlugin object without initializing it class FakeImage: pass im = FakeImage() im.__class__ = IptcImageFile # parse the IPTC information chunk im.info = {} im.fp = StringIO.StringIO(data) try: im._open() except (IndexError, KeyError): pass # expected failure return im.info
def _save(im, fp, filename): return JpegImagePlugin._save(im, fp, filename)
def _accept(prefix): return JpegImagePlugin._accept(prefix)
def test_save_cjpeg(self, tmp_path): with Image.open(TEST_FILE) as img: tempfile = str(tmp_path / "temp.jpg") JpegImagePlugin._save_cjpeg(img, 0, tempfile) # Default save quality is 75%, so a tiny bit of difference is alright assert_image_similar(img, Image.open(tempfile), 17)
def getiptcinfo(im): from PIL import TiffImagePlugin, JpegImagePlugin import io data = None if isinstance(im, IptcImageFile): # return info dictionary right away return im.info elif isinstance(im, JpegImagePlugin.JpegImageFile): # extract the IPTC/NAA resource try: app = im.app["APP13"] if app[:14] == b"Photoshop 3.0\x00": app = app[14:] # parse the image resource block offset = 0 while app[offset:offset + 4] == b"8BIM": offset += 4 # resource code code = JpegImagePlugin.i16(app, offset) offset += 2 # resource name (usually empty) name_len = i8(app[offset]) # name = app[offset+1:offset+1+name_len] offset = 1 + offset + name_len if offset & 1: offset += 1 # resource data block size = JpegImagePlugin.i32(app, offset) offset += 4 if code == 0x0404: # 0x0404 contains IPTC/NAA data data = app[offset:offset + size] break offset = offset + size if offset & 1: offset += 1 except (AttributeError, KeyError): pass elif isinstance(im, TiffImagePlugin.TiffImageFile): # get raw data from the IPTC/NAA tag (PhotoShop tags the data # as 4-byte integers, so we cannot use the get method...) try: data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] except (AttributeError, KeyError): pass if data is None: return None # no properties # create an IptcImagePlugin object without initializing it class FakeImage(object): pass im = FakeImage() im.__class__ = IptcImageFile # parse the IPTC information chunk im.info = {} im.fp = io.BytesIO(data) try: im._open() except (IndexError, KeyError): pass # expected failure return im.info
def one_image(path): if os.path.splitext(path.lower())[1] not in (".jpg", ".jpeg"): return 0 # a raw image may or may not exist, but we generally would want to keep any raw sync-ed up with its jpeg path_raw = os.path.splitext(path)[0] + ".cr2" if args.edit_in_place or args.dry_run: outpath = path outpath_raw = path_raw elif args.output_path: _, filepart = os.path.split(path) outpath = os.path.join(args.output_path, filepart) _, filepart = os.path.split(path_raw) outpath_raw = os.path.join(args.output_path, filepart) with Image.open(path) as imgobj: if 'exif' in imgobj.info: exif_dict = piexif.load(imgobj.info['exif']) else: print("Keys in image info: %s" % sorted(imgobj.info.keys())) raise Exception( "The file '%s' does not appear to contain exif data." % path) if args.filter_min_size and ( imgobj.size[0] <= args.filter_min_size[0] or imgobj.size[1] <= args.filter_min_size[1]): return 0 if args.filter_min_pixels and ( imgobj.size[0] * imgobj.size[1] <= args.filter_min_pixels[0] * args.filter_min_pixels[1]): return 0 if args.print_some_tags: print("Dims 1: %s x %s" % (exif_dict["0th"].get(piexif.ImageIFD.ImageWidth), exif_dict["0th"].get(piexif.ImageIFD.ImageLength))) print("Dims 2: %s x %s" % (exif_dict["Exif"].get(piexif.ExifIFD.PixelXDimension), exif_dict["Exif"].get(piexif.ExifIFD.PixelYDimension))) print("Date 1: %s" % parsedate2(exif_dict["0th"][piexif.ImageIFD.DateTime])) print( "Date 2: %s" % parsedate2(exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal])) print("Date 3: %s" % parsedate2( exif_dict["Exif"][piexif.ExifIFD.DateTimeDigitized])) if args.print_all_tags: for ifd in ("0th", "Exif", "GPS", "1st"): for tag in exif_dict[ifd]: val = exif_dict[ifd][tag] try: if len(val) > 100: val = "%s len %s" % (str(type(val)), len(val)) except TypeError: pass print("(%s) (%s) %s = %s" % (ifd, tag, piexif.TAGS[ifd][tag]["name"], val)) if args.adjust_date: changeto = parsedate(exif_dict["Exif"][ piexif.ExifIFD.DateTimeDigitized]) + datetime.timedelta( minutes=args.adjust_date) exif_dict["0th"][piexif.ImageIFD.DateTime] = changeto.strftime( "%Y:%m:%d %H:%M:%S") exif_dict["Exif"][ piexif.ExifIFD.DateTimeOriginal] = changeto.strftime( "%Y:%m:%d %H:%M:%S") exif_bytes = piexif.dump(exif_dict) shutil.copy2(path, args.temporary_file) compare_files(path, args.temporary_file, True) compare_images(path, args.temporary_file, True) piexif.insert(exif_bytes, args.temporary_file) compare_files(path, args.temporary_file, False) compare_images(path, args.temporary_file, True) if args.scale_percent: newdims = (int(imgobj.size[0] * args.scale_percent), int(imgobj.size[1] * args.scale_percent)) exif_dict["0th"][piexif.ImageIFD.ImageWidth] = newdims[0] exif_dict["0th"][piexif.ImageIFD.ImageLength] = newdims[ 1] # fix dims or they are wrong in exif exif_bytes = piexif.dump(exif_dict) quantization = getattr(imgobj, 'quantization', None) subsampling = JpegImagePlugin.get_sampling(imgobj) quality = 100 if quantization is None else 0 imgobj2 = imgobj.resize(newdims, resample=Image.LANCZOS) # include exif or else it is lost # also attempt to compress with the settings that were used previously imgobj2.save(args.temporary_file, exif=exif_bytes, format='jpeg', subsampling=subsampling, qtables=quantization, quality=quality) compare_files(path, args.temporary_file, False) compare_images(path, args.temporary_file, False) # the image object is closed here, now we can replace or delete the original jpeg if args.adjust_date or args.scale_percent: # we have theoretically made a temporary file with the contents we want, now we can get it where it needs to go if args.dry_run: print("Edit '%s'." % path) else: shutil.move(args.temporary_file, outpath) if os.path.exists(path_raw) and path_raw != outpath_raw: # the optional raw file is handled differently, as there is no modification and no temporary raw file if args.edit_in_place: shutil.move(path_raw, outpath_raw) elif args.output_path: shutil.copy2(path_raw, outpath_raw) else: print("Edit '%s'." % path_raw) if args.rename_images: changeto = parsedate(exif_dict["Exif"][ piexif.ExifIFD.DateTimeOriginal]).strftime("img_%Y%m%d_%H%M%S") outpath, filepart = os.path.split(outpath) extra = 0 finaloutpath = os.path.join(outpath, changeto + ".jpg") # deal with name conflicts finaloutpath_raw = os.path.join( outpath, changeto + ".cr2") # raw follows along if it exists while os.path.exists(finaloutpath) and path != finaloutpath: extra += 1 finaloutpath = os.path.join(outpath, "%s.%d.jpg" % (changeto, extra)) finaloutpath_raw = os.path.join(outpath, "%s.%d.cr2" % (changeto, extra)) if extra > 100: # because I don't trust unbounded loops raise Exception("Apparent runaway extra for %s." % path) if path == finaloutpath: return 0 if args.edit_in_place: func = shutil.move elif args.output_path: func = shutil.copy2 else: func = lambda x, y: print("Move '%s' to '%s'." % (x, y)) func(path, finaloutpath) if os.path.exists(path_raw): func(path_raw, finaloutpath_raw) return 1
def test_convert_dict_qtables_deprecation(self): with pytest.warns(DeprecationWarning): qtable = {0: [1, 2, 3, 4]} qtable2 = JpegImagePlugin.convert_dict_qtables(qtable) assert qtable == qtable2
from PIL import JpegImagePlugin from PIL import Image, ImageFile, _binary import struct, binascii fd = open("124.jpg") fd2 = open("125.jpg", "wr") fdp = fd.read() fd.close() msg1 = "hello xxsfsdf" msg = struct.pack(">BBH%ds" % len(msg1), 255, 254, len(msg1) + 2, msg1) print binascii.hexlify(msg) fd2.write(fdp[:2]) fd2.write(msg) fd2.write(fdp[2:]) fd2.close() img = Image.open("125.jpg") info1 = JpegImagePlugin(img) print info1