Last active
July 30, 2018 17:53
-
-
Save biazzotto/b5e571d32e0b002e660d393e15845e00 to your computer and use it in GitHub Desktop.
GIMP Plugin that allows exporting as JPEG using the Google Guetzli
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
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
from gimpfu import * | |
from os import remove, name | |
from os.path import basename | |
from tempfile import NamedTemporaryFile | |
from subprocess import check_output, CalledProcessError, STDOUT | |
# Export with Google Guetzli Perceptual JPEG encoder | |
# (copyLEFT) 2017 Fabricio Biazzotto. No rights reserved. | |
# based on Andrey "Efenstor" Pivovarov MozJPEG export plugin | |
# IMPORTANT!!! | |
# ------------ | |
# You must edit the "guetzli_path" variable below to point it to your guetzli | |
# location. This plugin REQUIRES the "Guetzli" utility from Google. | |
# You can download Gueztli from https://github.com/google/guetzli/releases | |
guetzli_path = 'guetzli' | |
# INSTALLATION | |
# ------------ | |
# Linux: | |
# 1. cp export_guetzli.py ~/.gimp-2.8/plug-ins | |
# 2. chmod +x export_guetzli.py | |
# Windows: | |
# 1. Remove Windows | |
# 2. Install Linux | |
# USAGE | |
# ----- | |
# Method 1 (preferred): | |
# 1. File > Export As... | |
# 2. Enter the output file name but specify .gtz as extension instead of .jpg | |
# 3. The resulting file will still be called <filename>.jpg | |
# NOTE: if the outfile file already exists it is overwritten without warning! | |
# Method 2: | |
# 1. File > Export As... | |
# 2. Open the "Select file type" list below | |
# 3. Select "JPEG image (Google Guetzli)" | |
# 4. On the "Extension mismatch" warning click "Save" | |
# Method 3: | |
# Rename/remove the original JPEG plugin (file-jpeg) | |
# Linux: | |
# 1. cd /usr/lib/gimp/2.0/plug-ins | |
# 2. sudo mv file-jpeg .file-jpeg | |
# Windows: | |
# 1. Remove Windows | |
# 2. Install Linux | |
# NOTES ON PRE-PROCESSING | |
# ----------------------- | |
# Selective smoothing: Reduces file size at the cost of some tolerable image | |
# quality degradation, sometimes also reducing perceptual blockiness. Values | |
# of 0-50 are usually enough for the most cases. | |
# Ringing reduction: Smoothens the image slightly, effectively suppressing | |
# ringing at the cost of some sharpness loss. The value of 1 is quite enough | |
# for the most cases. | |
# HISTORY | |
# ------- | |
# v0.1 - Initial release | |
# If guetzli_path is not set, it assumes it's somewhere on path | |
if name == 'nt': | |
guetzli_path = 'guetzli.exe' | |
else: | |
guetzli_path = 'guetzli' | |
def guetzli(img, drawable, filename, raw_filename, unknown, quality, memlimit, | |
nomemlimit, preproc1, preproc2): | |
# Initialization | |
gimp.progress_init('Saving ' + raw_filename) | |
# Prepare the temporary file | |
s_tmpfile = NamedTemporaryFile(suffix='.png', delete=False) | |
s_tmpfile_name = s_tmpfile.name | |
s_tmpfile.close() | |
# Pre-processing | |
proc_img = pdb.gimp_image_duplicate(img) | |
# proc_drawable = proc_img.active_layer | |
proc_drawable = proc_img.flatten() | |
if (preproc1 > 0): | |
radius = round((preproc1 / 100.0) * 10.0) | |
pdb.plug_in_sel_gauss(proc_img, proc_drawable, radius, preproc1) | |
if (preproc2 > 0): | |
pdb.plug_in_gauss(proc_img, proc_drawable, preproc2, preproc2, 0) | |
# Save | |
pdb.file_png_save(proc_img, proc_drawable, s_tmpfile_name, | |
basename(s_tmpfile_name), 0, 0, 0, 0, 0, 0, 0) | |
# Close the processed image | |
gimp.delete(proc_img) | |
# Change .gtz extension to .jpg (if needed) | |
last_dot = str.rfind(filename, '.') | |
ext = filename[last_dot:] | |
if (ext == '.gtz'): | |
filename = filename[:last_dot] + '.jpg' | |
# Convert | |
args = [] | |
args.append(guetzli_path) | |
if (int(quality) != 95): | |
args.append('--quality') | |
args.append(str(int(quality))) | |
if (nomemlimit == 1): | |
args.append('--nomemlimit') | |
elif (int(memlimit) != 6000): | |
args.append('--memlimit') | |
args.append(str(int(memlimit))) | |
args.append(s_tmpfile_name) | |
args.append(filename) | |
gimp.progress_init('Saving ' + basename(filename)) | |
try: | |
output = check_output(args, stderr=STDOUT) | |
except CalledProcessError as err: | |
gimp.progress_init('File' + basename(filename) + 'could not be saved.') | |
if err.output == 'Memory limit would be exceeded. Failing.\n': | |
gimp.message(err.output + 'Try to increase memory limit.') | |
else: | |
gimp.message(err.output) | |
# Delete the temporary file | |
remove(s_tmpfile_name) | |
# Exit | |
pdb.gimp_progress_end() | |
pdb.gimp_displays_flush() | |
def register_save(): | |
gimp.register_save_handler('file-guetzli-save', 'jpg,jpeg,jpe,gtz', '') | |
register( | |
proc_name='file-guetzli-save', | |
blurb='Save as JPEG using the Google Guetzli', | |
help='Save as JPEG using the Google Guetzli', | |
author='Fabricio Biazzotto', | |
copyright='(copyLEFT) 2017 Fabricio Biazzotto', | |
date='2017', | |
label='<Save>/JPEG image (Google Guetzli)', | |
imagetypes='RGB*, GRAY*', | |
params=[ | |
(PF_STRING, 'unknown', | |
'Undocumented (?) compulsory parameter, set to None', None), | |
# NOTE: Is this some kind of undocumented behaviour? It seems that the | |
# 1st parameter (actually it's the 5th) is COMPULSORY but ignored in | |
# the GUI and must be of the PF_STRING type (not sure about this). | |
(PF_SLIDER, 'quality', '_Quality', 95, (84, 100, 1)), | |
(PF_INT, 'memlimit', 'Memory _limit (MB)', 6000), | |
(PF_TOGGLE, 'nomemlimit', '_No memory limit', 0), | |
(PF_SLIDER, 'preproc1', 'Pre-processing: Selective s_moothing', 0, | |
(0, 100, 1)), | |
(PF_SLIDER, 'preproc2', 'Pre-processing: _Ringing reduction', 0, | |
(0, 2, 1)) | |
], | |
results=[], | |
function=guetzli, | |
on_query=register_save | |
) | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment