# Cropping an image succintly

I am constantly frustrated by how complicated it is to write graphical programs that would have been only a few lines of code 20+ years ago. Since the invention of the mouse this has become even worse it seems. I was hoping someone could put me out of my misery and show the shortest code to do the following really basic "modern" operations.

The code will read in an image in any sensible format of your choosing (png, jpg, etc.), display it on the screen, allow you to choose a rectangular region with your mouse and then just save the selected region as an image file.

You are free to use any programming language and any library which hasn't been written especially for this competition. Both should be freely (in both senses) online. The code should be runnable in linux.

To start things off.... here is a very lightly golfed solution in python modified from code to Andrew Cox from https://stackoverflow.com/questions/601874/digital-image-cropping-in-python which takes 1294 1101 characters.

import pygame, sys
from PIL import Image
pygame.init()

def displayImage( s, px, t):
s.blit(px, px.get_rect())
if t:
pygame.draw.rect( s, (128,128,128), pygame.Rect(t[0], t[1], pygame.mouse.get_pos()[0] - t[0], pygame.mouse.get_pos()[1] - t[1]))
pygame.display.flip()

def setup(path):
s = pygame.display.set_mode( px.get_rect()[2:] )
s.blit(px, px.get_rect())
pygame.display.flip()
return s, px

def mainLoop(s, px):
t = None
b = None
runProgram = True
while runProgram:
for event in pygame.event.get():
if event.type == pygame.QUIT:
runProgram = False
elif event.type == pygame.MOUSEBUTTONUP:
if not t:
t = event.pos
else:
b = event.pos
runProgram = False
displayImage(s, px, t)
return ( t + b )

s, px = setup(sys.argv[1])
left, upper, right, lower = mainLoop(s, px)
im = Image.open(sys.argv[1])
im = im.crop(( left, upper, right, lower))
im.save(sys.argv[2])


I believe it is traditional to give a time limit, so I will accept the best answer in exactly one week's time.

Current count

• Octave - 44 + 15 = 59 characters
• R - 305 characters
• Python - 343 characters
• Java - 560 characters
• You should golf your program a little bit more :) Use one letter variables and as little whitespace as possible. Also, you don't need the if __name__ == "__main__": if you're just writing a program. – beary605 Feb 9 '13 at 0:40
• @beary605 Thanks. I don't want to obfuscate it too much as I was hoping that it might inspire someone to make a great alternative solution. I suspect but don't know that the key may be to find a language+library pair that does almost the work for you. – felipa Feb 9 '13 at 9:04
• I don't like "You are free to use any (..) libraries you like as long as they are available freely (..) online." This means I can write a library that does all the work, make it available online, and then publish a one-line program in an answer here that does nothing but call the library! – Mr Lister Feb 10 '13 at 15:06
• @MrLister Thanks you are right. I need to fix that. Is the new version better? – felipa Feb 10 '13 at 16:35
• in bash, I just type gimp – boothby Feb 12 '13 at 7:11

# Octave - 59 chars

pkg load image;imwrite(imcrop(imread(input(''))),input(''))


# MATLAB - 44 chars (not free, out of the contest)

imwrite(imcrop(imread(input(''))),input(''))


MATLAB seemed spot-on for this. Requires the Image Processing Toolbox (or package image 2.0.0 from Octave Forge). Usage:

1. Write at prompt 'input-filename' (single quotes included)
2. MATLAB: Drag from top-left to bottom-right corner and double click the rectangle. Octave: Click top-left and bottom-right corner
3. Write 'output-filename' (between single quotes too)
• This code doesn't work for me in octave. I am not sure which mouse buttons I am meant to select with but I always get "subscript indices must be either positive integers or logicals" in any case. – felipa Feb 10 '13 at 22:38
• It works in MATLAB, but apparently not in Octave. My bad for not testing it. I'll fix it ASAP. – kaoD Feb 10 '13 at 22:59
• @felipa I fixed it and shaved some chars. Works fine with Octave + package image 2.0.0. Am I allowed to assume in/out filenames are in a var instead of the ugly input('')s? – kaoD Feb 11 '13 at 0:24
• @koaD I got it to work in the end. You might have to add "pkg load image" strictly speaking. – felipa Feb 11 '13 at 21:44
• @koaD You can assume the filenames are given on the command line but the code you give should be self contained and runnable as is. – felipa Feb 12 '13 at 8:54

## Java (580 560 559)

import java.awt.*;import javax.imageio.*;public class R extends
Frame{static java.awt.image.BufferedImage i;int
x,y,w,h;{setSize(i.getWidth(),i.getHeight());setUndecorated(0<1);show();}public static void
e){if(e.id==501){x=e.x;y=e.y;}if(e.id==502){w=e.x-x;if(w<0)x-=w=-w;h=e.y-y;if(h<0)y-=h=-h;try{ImageIO.write(i.getSubimage(x,y,w,h),"png",System.out);System.exit(0);}catch(Exception
E){}}return 0<1;}
public void paint(Graphics g){g.drawImage(i,0,0,this);}}


Takes input image from stdin and writes cropped image to stdout. You can drag in whichever direction you choose and it will automatically flip the coordinates as required. Input in GIF, PNG, JPEG, TIFF, probably one or two others; output in PNG.

With whitespace and a couple of comments:

import java.awt.*;
import javax.imageio.*;

// Extending Frame is the simplest and ugliest way of showing a GUI.
public class R extends Frame {

// static so that we can read it in main() and save a try/catch block
static java.awt.image.BufferedImage i;
int x,y,w,h;

// Instance initialiser ~= constructor without the signature
{
setSize(i.getWidth(),i.getHeight());
// Don't show frames. This saves handling inset dimensions everywhere
setUndecorated(0<1);
show();
}

public static void main(String[]a) throws Exception
{
new R();
}

// This is deprecated, but it's the shortest way of handling events.
// I learnt this trick from the Java4k Game Competition
public boolean handleEvent(Event e) {
if (e.id == 501) { // MOUSE_DOWN
x=e.x;
y=e.y;
}
if (e.id == 502) { // MOUSE_UP
w = e.x - x;
// If the second click was to the left of the first one, flip them.
if (w < 0) x -= w = -w;
h = e.y - y;
// Similarly.
if (h < 0) y -= h = -h;

try {
ImageIO.write(i.getSubimage(x,y,w,h), "png", System.out);
System.exit(0);
} catch(Exception E){
}
}
return 0<1;
}

public void paint(Graphics g) {
g.drawImage(i,0,0,this);
}
}

• Would you mind putting a nicely formatted version too so we can appreciate the golfing tricks you have done? – felipa Feb 14 '13 at 20:53

R - 325 305 characters

f=scan(,"");l=locator;r=round;i=as.raster(png::readPNG(f));n=nrow(i);m=ncol(i);x11(h=7,w=7*m/n);p=function(X){par(mar=rep(0,4));m=ncol(X);n=nrow(X);plot(c(1,m),c(1,n),xaxs="i",yaxs="i");rasterImage(X,1,1,m,n)};p(i);a=l(1);b=l(1);j=i[r(n-a$y):r(n-b$y),r(a$x):r(b$x)];png(w=ncol(j),h=nrow(j));p(j);dev.off()


Or fully developed:

f=scan(,"")
l=locator
r=round
n=nrow(i)
m=ncol(i)
x11(h=7,w=7*m/n)
p=function(X){
par(mar=rep(0,4))
m=ncol(X)
n=nrow(X)
plot(c(1,m),c(1,n),xaxs="i",yaxs="i")
rasterImage(X,1,1,m,n)
}
p(i)
a=l(1)
b=l(1)
j=i[r(n-a$y):r(n-b$y),r(a$x):r(b$x)]
png(w=ncol(j),h=nrow(j))
p(j)
dev.off()


Tested on a Mac, untested on Linux but should work.
Package png needs to be installed.
At prompt, should be provided with path to png file to crop. Then when the picture appears, click twice: once for the left upper corner of selection rectangle and a second time for the right lower corner.
Create a cropped png file in working directory with default name ("Rplot001.png").

• How are you counting the chars? It seems to be 306 to me using tr -d '[[:blank:]]' < temp.R |wc. – felipa Feb 12 '13 at 10:19
• So I'm guessing you're counting the line-break then. It's my first week code-golfing I might have overlook that. The issue is that not all line-break are meaningful in R so I might need to edit accordingly. – plannapus Feb 12 '13 at 10:45
• So the actual number is 305 (the line-break after the opening braces and the one before the closing braces being meaningless). – plannapus Feb 12 '13 at 10:57

# Java (Applet), 706698 590

By no means is Java ever considered terse, and it certainly doesn't have the most elegant handling of image display or mouse interaction, but this really isn't that bad. I'm wondering if a solution using Swing or some other library might be shorter.

import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.imageio.*;

public class R extends java.applet.Applet{
Scanner s=new java.util.Scanner(System.in);
java.awt.image.BufferedImage i;
int x,y;
{
try{
}catch(Exception e){}
public void mousePressed(MouseEvent e){
x=e.getX();y=e.getY();
}
public void mouseReleased(MouseEvent e){
try{
ImageIO.write(i.getSubimage(x,y,e.getX()-x,e.getY()-y),"png",new File(s.nextLine()));
}catch(Exception z){}
}
});
}
public void paint(java.awt.Graphics g){
g.drawImage(i,0,0,this);
}
}

• I see you are counting characters using tr -d '[[:blank:]]' < file.java |wc . If you take the view that you don't need to include the imports you are down to 590 chars. – felipa Feb 12 '13 at 10:13
• @felipa If part of the solution is to match the best code with the best library, then perhaps counting the include is a good idea? – Gaffi Feb 12 '13 at 11:48
• You can almost certainly golf this more by not using listeners. Just override an event handling method of Applet (handleEvent or processEvent). I think you'll also find that BufferedImage.getSubimage is shorter than copying the data manually. – Peter Taylor Feb 12 '13 at 12:30
• @Gaffi Sure. It's just not what the Octave solution did . I think you are right though. – felipa Feb 12 '13 at 14:31
• @felipa Yep, pretty much deleting every newline and tab character and then just putting the cursor at the end of the program and looking at the column number. – Joe K Feb 13 '13 at 18:28

# Python/PyGame (343C)

Usage:

python this.py image.png result.png


which read from image.png and write to result.png.

import pygame as P
import sys
D=P.display
I=P.image
D.init()
f,g=sys.argv[1:3]
s=D.set_mode(m.get_size())
r=k=0
c=1
while c:
for e in P.event.get():
t=e.type
if t==5:x,y=e.pos;k=1
if t==4 and k:i,j=e.pos;r=(x,y,i-x,j-y)
if t==6:c=0
s.blit(m,(0,0))
if r and c:P.draw.rect(s,0,r,1)
D.flip()
q=s.subsurface(r)
I.save(q,g)


I know the time limit is up, but here's how to do it in 34 characters using Bash:

display $1&K=$!;import $2;kill$K


Call it like this: 'script.sh input.png output.png'.

This uses the ImageMagick tools/library to first display the image. Then it lets you select an area to take a screenshot of using the mouse. This screenshot is effectively a crop of the image. Finally, the display is closed.