Development

A Simple Web Service To Resize Images

What it does

I wanted a little stand alone web service that could resize, crop, and convert to black and white a library of images, on demand.

The requester will know the exact filename of the source image, but wanted the resultant image returned as the content of the http request, along with size information of the resized image.

I put together this, which seemed to work for what I wanted. Its not going to win any prizes for elegance, but it works quite reliably.

How to use

curl http://localhost:5300?file=/data/imagedata/A/M/A/B/3087&x=100&y=100&crop=1&filetype=TIFF'

Parameters

  • file Filename (as seen by the server!) of the image to scale
  • x Width of Required Image
  • y Height of Required Image
  • crop =1 Crop the image after resizing
  • bw=1 Convert image to greyscale
  • dull=n Reduce contrast of image n times
  • filetype=x Make output filetype x (see list at: http://www.imagemagick.org/script/formats.php)

If you don’t specify one of x or y then the image will be resized keeping its proportions.

The crop parameter works by reducing the size of the image in proportion to either the width or the height, then crops the image along the other dimension. This allows for making square thumbnails from either portrait or landscape images. It crops from the top or left.

#!/usr/bin/perl
 
# resize.pl - (C) James Richardson 2001-2007
# Licensed under the GPL v2 or your choice of a later version.
# If you use this / find it helpful please send me an email to let me know.
 
use HTTP::Daemon;
use HTTP::Response;
use HTTP::Headers;
 
use Image::Magick;
 
use FileHandle;
 
use Data::Dumper;
 
use strict;
 
my $FILTER = "Cubic";
my $BLUR = 0.125;
 
$SIG{CHLD} = sub { 
  my $pid = wait;
  print "Child $pid exited\n";
};
 
sub Log {
  print join(":", @_ );
  print "\n";
}
 
sub Info { Log ( "Info", @_ ); }
 
sub ratio {
    my ( $image ) = @_;
 
    my $currentx = $image->Get("width");
    my $currenty = $image->Get("height");
    my $ratio = $currenty / $currentx;
 
    Info ("Current file is $currentx x $currenty, Ratio = $ratio");
 
    return $ratio;
}
 
sub resize_without_crop {
    my ($image, $wantedx, $wantedy) = @_;
 
    my $ratio = ratio($image);
 
    if ( $wantedx && $wantedy ) {
 
    }
    elsif ( $wantedx ) {
    $wantedy = $wantedx * $ratio;
    }
    elsif ( $wantedy ) {
    $wantedx = $wantedy / $ratio;
    }
 
    $image->Resize(filter=>$FILTER, blur=>$BLUR, width=>$wantedx, height=>$wantedy);
}
 
sub resize_with_crop {
    my ( $image, $wantedx, $wantedy ) = @_;
 
    if ( ! ( $wantedx && $wantedy )) {
    die "Need both x and y sizes for cropping";
    }
 
    my $ratio = ratio($image);
 
    if ( $ratio >= 1 ) {
    # Portrait (or square) Image
    # Resize using the given width and then crop to the height
    resize_without_crop($image, $wantedx, undef);
    }
    else {
    # Landscape Image
    resize_without_crop($image, undef, $wantedy);
    }
 
    $image->Crop(width=>$wantedx, height=>$wantedy, x=>0, y=>0);
}
  
sub resize {
  my ( $filename, $filetype, $wantedx, $wantedy, $allowcrop, $tobw, $dull ) = @_;
 
  my $quality = 95;
 
  my $image = Image::Magick->new();
 
  $image->Read(filename=>$filename);
 
 
  if ( $allowcrop ) {
      resize_with_crop($image, $wantedx, $wantedy);
  }
  else {
      resize_without_crop($image, $wantedx, $wantedy);
  }
 
  if ( $tobw ) {
      Info("Converting to B/W");
      $image->Set(colorspace=>"gray");
      $image->Quantize();
  }
 
  if ( $dull ) {
      for ( my $i = 0 ; $i < $dull ; $i++ ) {
      Info("Dulling");
      $image->Contrast(sharpen=>"False");
      }
  }
 
  my $outputx = $image->Get("width");
  my $outputy = $image->Get("height");
 
  $image->Set(quality=>$quality);
 
  Info ( "Returned image is: Width = $outputx, Height = $outputy, Format = $filetype" );
   
  my @bytes = $image->ImageToBlob(magick=>$filetype);
 
  my $buffer = $bytes[0];
 
  return $outputx, $outputy, $buffer;
 
}
 
#################################################################
#
# Main Program
# 
#################################################################
 
my $listenport = 5300;
 
my $daemon = HTTP::Daemon->new ( LocalPort => $listenport, Reuse=>1 );
 
if ( !defined ( $daemon ) ) {
  Info ( "Can't listen on listenport $listenport: $!" );
  exit;
}
 
Info ( "Listening on port $listenport" );
 
while ( 1 ) {
 
  while ( my $client = $daemon->accept() ) {
 
    my $pid = fork();
 
    if ( $pid == 0 ) {
      # This is the client
 
      my $request = $client->get_request();
 
      my $uri = $request->uri();
 
      my %q = $uri->query_form();
 
      my $file = $q{file};
      my $x = $q{x};
      my $y = $q{y};
      my $filetype = $q{filetype};
      my $crop = exists $q{crop} ? 1 : 0;
      my $bw = exists $q{bw} ? 1 : 0;
      my $dull = $q{dull};
       
      Info ( "File is $file, $filetype, X=$x,Y=$y, Crop=$crop, B/W=$bw" );
 
      my ( $width, $height, $image ) = resize ( $file, $filetype, $x, $y, $crop, $bw, $dull );
 
      if ( defined ( $image ) ) {
    my $header = new HTTP::Headers ( "Content-Type" => "image/jpeg",
                       "X-Image-Width" => $width,
                       "X-Image-Height" => $height );
                      
    $client->send_response ( new HTTP::Response ( 200, "OK", $header, $image ) );
 
    exit;
      }
    }
    elsif ( $pid > 0 ) {
      # This is the parent
    }
    else {
      print "fork() failed $!\n";
    }
  }
}
comments powered by Disqus