Skip to content

Instantly share code, notes, and snippets.

@biazzotto
Last active July 30, 2018 17:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save biazzotto/b5e571d32e0b002e660d393e15845e00 to your computer and use it in GitHub Desktop.
Save biazzotto/b5e571d32e0b002e660d393e15845e00 to your computer and use it in GitHub Desktop.
GIMP Plugin that allows exporting as JPEG using the Google Guetzli
#!/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