Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

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 http://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):
    px = pygame.image.load(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
share|improve this question
    
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
1  
in bash, I just type gimp –  boothby Feb 12 '13 at 7:11

6 Answers 6

up vote 3 down vote accepted

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)
share|improve this answer
    
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
main(String[]a)throws Exception{i=ImageIO.read(System.in);new R();}public boolean handleEvent(Event
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
    {
        i=ImageIO.read(System.in);
        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);
    }
}
share|improve this answer
    
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
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()

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").

share|improve this answer
    
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), 706 698 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{
            i=ImageIO.read(new File(s.nextLine()));
        }catch(Exception e){}
        addMouseListener(new MouseAdapter(){
            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);
    }
}
share|improve this answer
    
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
1  
@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]
m=I.load(f)
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)
share|improve this answer
    
Thanks very much! –  felipa Feb 15 '13 at 19:11

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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.