Skip to content

Instantly share code, notes, and snippets.

@cceasy
Created October 27, 2018 07:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cceasy/65fa3930f81d1055893192d2b2c00966 to your computer and use it in GitHub Desktop.
Save cceasy/65fa3930f81d1055893192d2b2c00966 to your computer and use it in GitHub Desktop.
batch resize images
#!/usr/bin/env python
import os
from PIL import Image, ExifTags
from shutil import copyfile
orientation = 0x0112 # image exif info for Orientation 274
device = 0x010f # image exif info for Make 271
src_dir = 'todo'
thumbnail_config = {
'max_size': 50 * 1024, # 50 kB
'min_wh': 400, # 0 measn not to resize height or width
'min_quality': 20, # jpg quality
'quality_fall': 5, # quality will decrease per step
'bytes_quality': 220 * 1024, # every x byte get 1 quality decrease
'debug': True,
'output_dir': 'todo',
}
opt_config = {
'max_size': 2 * 1024 * 1024, # 2 MB
'min_wh': 1560, # 0 measn not to resize height or width
'min_quality': 75, # jpg quality
'quality_fall': 3, # quality will decrease per step
'bytes_quality': 1024 * 1024, # every x byte get 1 quality decrease
'debug': True,
'output_dir': 'todo',
}
# config = thumbnail_config
config = opt_config
def init_exif_keys():
global orientation
global device
for key in ExifTags.TAGS.keys():
if ExifTags.TAGS[key] == 'Orientation':
orientation = key
elif ExifTags.TAGS[key] == 'Make':
device = key
def resize(src_path, output_path):
max_size = config['max_size']
min_wh = config['min_wh']
min_quality = config['min_quality']
quality_fall = config['quality_fall']
bytes_quality = config['bytes_quality']
debug = config['debug']
if not os.path.exists(output_path):
os.mkdir(output_path)
files = list(os.listdir(src_path))
print('⬇ resize and copy {} files from {} to {}'.format(len(files), src_path, output_path))
for file in files:
if debug:
print('# process file', file)
src_file = os.path.join(src_path, file)
src_size = os.path.getsize(src_file)
if os.path.isdir(src_file):
continue
if src_file.endswith('DS_Store'):
os.remove(src_file)
continue
output_file = os.path.join(output_path, file)
img = Image.open(src_file)
img = process_exif(img)
if (src_size > max_size):
min_px = min(img.size)
if min_px > min_wh:
scale = min_px / float(min_wh)
w, h = int(img.width / scale), int(img.height / scale)
img = img.resize((w, h), Image.ANTIALIAS)
quality = int(100 - src_size / bytes_quality)
if quality < min_quality:
quality = min_quality
if debug:
print('init quality is', quality)
img.save(output_file, optimize=True, quality=quality)
while os.path.getsize(output_file) > max_size and quality > min_quality:
quality -= quality_fall
if debug:
print('+ adjust quality to', quality)
img.save(output_file, optimize=True, quality=quality)
img.close()
else:
copyfile(src_file, output_file)
return len(files)
def process_exif(image):
if hasattr(image, '_getexif') and isinstance(image._getexif(), dict):
exif = image._getexif()
# if device in exif and exif[device] == 'Apple': # Canon XiaoMi Apple
if orientation in exif:
if exif[orientation] == 3:
image = image.rotate(180, expand=True)
elif exif[orientation] == 6:
image = image.rotate(270, expand=True)
elif exif[orientation] == 8:
image = image.rotate(90, expand=True)
return image
if __name__ == '__main__':
init_exif_keys()
cnt = 0
output_dir = config['output_dir']
for file in os.listdir(src_dir):
src_path = os.path.join(src_dir, file)
if os.path.isdir(src_path):
output_path = os.path.join(output_dir, file)
cnt += resize(src_path, output_path)
print('cnt:', cnt)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment