ImageOptim

Yesterday, I listened to the most recent Mac Power Users episode, “20 Mac Apps Under $20.” Although MPU is probably best known for its deep dive episodes, I always like these more rapid-fire discussions. As usual, this one has a good mix of apps that are new to me and those I already know about.1 One of Stephen’s picks is ImageOptim (discussion starts at 1:00:50), an app I use all the time but haven’t talked about here.

As suggested by its name, ImageOptim optimizes images by reducing their file size. It doesn’t resize an image in the sense of changing its width or height in pixels. What ImageOptim leaves you with is an image that takes up less space on your disk; it doesn’t take up less space on your screen.

I’m sure you already know that a JPEG’s file size can be reduced by lowering its “quality.” ImageOptim can do this kind of optimization, but so can lots of apps. what ImageOptim really excels at is reducing the file size without changing the image quality. And it can do this for PNGs, which is the format I use for the screenshots I post here. Generally, I get 25–40% smaller files after running them through ImageOptim.

Fundamentally, ImageOptim is a front end for a set of open source image optimization programs. You can see and choose the programs in either the Preferences or the Tools menu.

ImageOptim Tools menu

The given image is run through each of the checked programs appropriate for its file type, and the one that provides the smallest file is selected. The image file is overwritten with that optimized version and you’re told how much smaller the file became. You’ll note that I don’t have my images run through Zopfli or PNGOUT, as these programs run slower than the others, and I don’t think the extra time is worth slight extra reduction in file size.

This is all very well and good, but I wouldn’t think as highly of ImageOptim if it weren’t for one very useful feature: it can be run as a command line program. Which means that my Keyboard Maestro macro for taking screenshots and uploading them to the blog’s server can reduce their file size during the process.

(I used to use OptiPNG for this, but at some point a couple of years ago it started running very slowly, and the delay was disrupting even the slow pace of my writing. I just ran a few tests, and OptiPNG seems to be running fine on my Macs now, but I’m sticking with ImageOptim.)

My screenshot-and-upload macro, SnapSCP, looks like this:

SnapSCP KM macro

The Python script in Step 1 is this:

python:
 1:  #!/usr/bin/env python3
 2:  
 3:  import Pashua
 4:  import tempfile
 5:  from PIL import Image
 6:  import sys, os, os.path
 7:  import subprocess
 8:  import urllib.parse
 9:  from datetime import date
10:  import sys
11:  
12:  # Parameters
13:  dstring = date.today().strftime('%Y%m%d')
14:  year = date.today().strftime('%Y')
15:  type = "png"
16:  localdir = os.environ['HOME'] + "/Pictures/Screenshots"
17:  tf, tfname = tempfile.mkstemp(suffix='.'+type, dir=localdir)
18:  bgcolor = (61, 101, 156)
19:  border = 16
20:  optimizer = '/Applications/ImageOptim.app/Contents/MacOS/ImageOptim'
21:  server = f'user@leancrew.com/path/to/images{year}/'
22:  port = '9876'
23:  
24:  # Dialog box configuration
25:  conf = b'''
26:  # Window properties
27:  *.title = Snapshot
28:  
29:  # File name text field properties
30:  fn.type = textfield
31:  fn.default = Snapshot
32:  fn.width = 264
33:  fn.x = 50
34:  fn.y = 40
35:  fnl.type = text
36:  fnl.default = Name:
37:  fnl.x = 0
38:  fnl.y = 42
39:  
40:  # Border checkbox properties
41:  bd.type = checkbox
42:  bd.label = Add background
43:  bd.x = 10
44:  bd.y = 0
45:  
46:  # Default button
47:  db.type = defaultbutton
48:  db.label = Save
49:  
50:  # Cancel button
51:  cb.type = cancelbutton
52:  '''
53:  
54:  # Capture a portion of the screen and save it to a temporary file.
55:  status = subprocess.run(["screencapture", "-io", "-t", type, tfname])
56:  
57:  # Exit if the user canceled the screencapture.
58:  if not status.returncode == 0:
59:    os.remove(tfname)
60:    print("Canceled")
61:    sys.exit()
62:  
63:  # Open the dialog box and get the input.
64:  dialog = Pashua.run(conf)
65:  if dialog['cb'] == '1':
66:    os.remove(tfname)
67:    print("Canceled")
68:    sys.exit()
69:  
70:  # Add a desktop background border if asked for.
71:  snap = Image.open(tfname)
72:  if dialog['bd'] == '1':
73:    # Make a solid-colored background bigger than the screenshot.
74:    snapsize = tuple([ x + 2*border for x in snap.size ])
75:    bg = Image.new('RGBA', snapsize, bgcolor)
76:    bg.alpha_composite(snap, dest= (border, border))
77:    bg.save(tfname)
78:  
79:  # Rename the temporary file using today's date (yyyymmdd) and the
80:  # name provided by the user.
81:  name = dialog['fn'].strip()
82:  fname =  f'{localdir}/{dstring}-{name}.{type}'
83:  os.rename(tfname, fname)
84:  bname = os.path.basename(fname)
85:  
86:  # Optimize the PNG.
87:  subprocess.run([optimizer, fname], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
88:  
89:  # Upload the file via scp.
90:  subprocess.run(['scp', '-P', port, fname, server])
91:  
92:  # Generate the URL of the uploaded image.
93:  bname = urllib.parse.quote(bname)
94:  print(f'https://leancrew.com/all-this/images{year}/{bname}', end='')

How this script uses Pashua, screencapture, and scp are described in an older post. The only thing that’s different now is the use of ImageOptim. The full path to the executable is given in Line 20,

python:
optimizer = '/Applications/ImageOptim.app/Contents/MacOS/ImageOptim'

and it’s run via the subprocess library in Line 87,

python:
subprocess.run([optimizer, fname], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

When the optimized screenshot is done uploading and its URL is on the clipboard, the macro plays the Glass sound to alert me that I can paste the URL wherever I need it. It typically takes 3–5 seconds.

So ImageOptim gives you the best of both worlds: a nice app for optimizing images through the GUI and a command-line tool for doing the same thing in scripts and macros. It’s even better than Stephen said it was.


  1. What’s good about a discussion of apps I’m familiar with? Well, I’m usually not as familiar with them as I think. There’s often a feature or two I never knew about.