-
-
Save fangpsh/b9b3a52cc525f6b43ea55dbd034c359a to your computer and use it in GitHub Desktop.
Python Dynamic Image Quality Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cStringIO | |
import PIL.Image | |
from ssim import compute_ssim | |
def get_ssim_at_quality(photo, quality): | |
"""Return the ssim for this JPEG image saved at the specified quality""" | |
ssim_photo = cStringIO.StringIO() | |
# optimize is omitted here as it doesn't affect | |
# quality but requires additional memory and cpu | |
photo.save(ssim_photo, format="JPEG", quality=quality, progressive=True) | |
ssim_photo.seek(0) | |
ssim_score = compute_ssim(photo, PIL.Image.open(ssim_photo)) | |
return ssim_score | |
def _ssim_iteration_count(lo, hi): | |
"""Return the depth of the binary search tree for this range""" | |
if lo >= hi: | |
return 0 | |
else: | |
return int(log(hi - lo, 2)) + 1 | |
def jpeg_dynamic_quality(original_photo): | |
"""Return an integer representing the quality that this JPEG image should be | |
saved at to attain the quality threshold specified for this photo class. | |
Args: | |
original_photo - a prepared PIL JPEG image (only JPEG is supported) | |
""" | |
ssim_goal = 0.95 | |
hi = 85 | |
lo = 80 | |
# working on a smaller size image doesn't give worse results but is faster | |
# changing this value requires updating the calculated thresholds | |
photo = original_photo.resize((400, 400)) | |
if not _should_use_dynamic_quality(): | |
default_ssim = get_ssim_at_quality(photo, hi) | |
return hi, default_ssim | |
# 95 is the highest useful value for JPEG. Higher values cause different behavior | |
# Used to establish the image's intrinsic ssim without encoder artifacts | |
normalized_ssim = get_ssim_at_quality(photo, 95) | |
selected_quality = selected_ssim = None | |
# loop bisection. ssim function increases monotonically so this will converge | |
for i in xrange(_ssim_iteration_count(lo, hi)): | |
curr_quality = (lo + hi) // 2 | |
curr_ssim = get_ssim_at_quality(photo, curr_quality) | |
ssim_ratio = curr_ssim / normalized_ssim | |
if ssim_ratio >= ssim_goal: | |
# continue to check whether a lower quality level also exceeds the goal | |
selected_quality = curr_quality | |
selected_ssim = curr_ssim | |
hi = curr_quality | |
else: | |
lo = curr_quality | |
if selected_quality: | |
return selected_quality, selected_ssim | |
else: | |
default_ssim = get_ssim_at_quality(photo, hi) | |
return hi, default_ssim |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment