Speed and Memory Use
From VipsWiki
| Revision as of 12:41, 11 August 2010 John (Talk | contribs) (→2 x Opteron 254 workstation, 2.7 GHz) ← Previous diff |
Revision as of 12:41, 11 August 2010 John (Talk | contribs) (→Notes) Next diff → |
||
| Line 44: | Line 44: | ||
| On a quiet system, and the quickest real time of three runs recorded. I didn't try to clear the disc cache so the disc speed should not be a factor. | On a quiet system, and the quickest real time of three runs recorded. I didn't try to clear the disc cache so the disc speed should not be a factor. | ||
| - | |||
| - | I tested with VIPS SVN trunk but older versions of VIPS are pretty similar. | ||
| nip2 looks slow because of the long start-up time. Once the VIPS pipeline has been built, it process at the same speed as the Python version. It would seem faster on larger images or more complex calculations. | nip2 looks slow because of the long start-up time. Once the VIPS pipeline has been built, it process at the same speed as the Python version. It would seem faster on larger images or more complex calculations. | ||
Revision as of 12:41, 11 August 2010
We've written scripts to load an image, crop, shrink, sharpen and save again in a number of image processing systems. It's a trivial test, but it does give some idea of the speed and memory behaviour of these programs (and it's also quite fun to compare the code).
See also our main Benchmarks page for a more complex benchmark and timings on a variety of machines.
Contents |
Results
2 x Opteron 254 workstation, 2.7 GHz
| Software | Run time (secs real) | Memory (peak RSS MB) |
|---|---|---|
| VIPS C++ 7.18 | 1.8 | 13 |
| VIPS Python 7.18 | 1.8 | 15 |
| VIPS command-line 7.18 | 2.7 | 15 |
| NetPBM 10 | 3.6 | 70 |
| GraphicsMagick 1.3.5 | 3.8 | 240 |
| PIL 1.1.6 | 4.4 | 188 |
| VIPS nip2 7.17 | 5.1 | 85 |
| RMagick 2.13.1 (ImageMagick 6.5.7) | 5.3 | 670 |
| ImageMagick 6.5.7 | 5.9 | 480 |
| FreeImage 3.10 (incomplete) | 7.0 | 180 |
| ImageScience 1.2.1 (incomplete) | 9.5 | 260 |
| Octave 3.0.1 | 64 (est.) | 8500 (est.) |
Notes
All timings are for a 5,000 by 5,000 pixel 8-bit RGB image in uncompressed tiled TIFF format, 128 by 128 pixel tiles. Each test was run with something like:
time ./vips.sh wtc_tiled_small.tif wtc2.tif
On a quiet system, and the quickest real time of three runs recorded. I didn't try to clear the disc cache so the disc speed should not be a factor.
nip2 looks slow because of the long start-up time. Once the VIPS pipeline has been built, it process at the same speed as the Python version. It would seem faster on larger images or more complex calculations.
Octave aims to be a very high-level prototyping language and is not primarily targeting speed. I timed a 2,000 by 2,000 pixel monochrome JPEG and extrapolated from that.
Both ImageMagick and GraphicsMagick were compiled with Q16, ie. 16 bits per pixel.
FreeImage does not have a sharpening or convolution operation so I skipped that part of the benchmark.
NetPBM will not read tiled tiff so we used a striped tiff file for this one.
Implementations
Here it is in a variety of image processing systems.
VIPS Python
#!/usr/bin/python
import sys
from vipsCC import *
im = VImage.VImage (sys.argv[1])
im = im.extract_area (100, 100, im.Xsize () - 200, im.Ysize () - 200)
im = im.affine (0.9, 0, 0, 0.9, 0, 0, 0, 0,
int (im.Xsize() * 0.9), int (im.Ysize() * 0.9))
mask = VMask.VIMask (3, 3, 8, 0,
[-1, -1, -1,
-1, 16, -1,
-1, -1, -1])
im = im.conv (mask)
im.write (sys.argv[2])
VIPS nip2
#!/home/john/vips/bin/nip2 -s
main
= error "usage: infile -o outfile", argc != 2
= (sharpen @ shrink @ crop) (Image_file argv?1)
{
crop x = extract_area 100 100 (x.width - 200) (x.height - 200) x;
shrink = resize Interpolate_bilinear 0.9 0.9;
sharpen = conv (Matrix_con 8 0 [[-1, -1, -1], [-1, 16, -1], [-1, -1, -1]]);
}
VIPS command-line
#!/bin/bash width=`header -f Xsize $1` height=`header -f Ysize $1` width=$((width - 200)) height=$((height - 200)) set -x vips im_extract_area $1 t1.v 100 100 $width $height vips im_affinei_all t1.v t2.v bilinear 0.9 0 0 0.9 0 0 cat > mask.con <<EOF 3 3 8 0 -1 -1 -1 -1 16 -1 -1 -1 -1 EOF vips im_conv t2.v $2 mask.con rm t1.v t2.v mask.con
VIPS C++
#include <vips/vips>
int main (int argc, char **argv)
{
vips::VImage in (argv[1]);
vips::VIMask mask (3, 3, 8, 0,
-1, -1, -1, -1, 16,-1, -1, -1, -1);
in.
extract_area (100, 100, in.Xsize () - 200, in.Ysize () - 200).
affine (0.9, 0, 0, 0.9, 0, 0,
0, 0, in.Xsize () * 0.9, in.Ysize () * 0.9).
conv (mask).
write (argv[2]);
return 0;
}
PIL
#!/usr/bin/python
import Image, sys
import ImageFilter
im = Image.open (sys.argv[1])
im = im.crop ((100, 100, im.size[0] - 100, im.size[1] - 100))
im = im.resize ((int (im.size[0] * 0.9), int (im.size[1] * 0.9)),
Image.BILINEAR)
filter = ImageFilter.Kernel ((3, 3),
(-1, -1, -1,
-1, 16, -1,
-1, -1, -1))
im = im.filter (filter)
im.save (sys.argv[2])
Octave
#!/usr/bin/octave -qf
pkg load image
im = imread(argv(){1});
im = im(101:end-100, 101:end-100); % Crop
im = imresize(im, 0.9, 'linear'); % Shrink
myFilter = [-1 -1 -1
-1 16 -1
-1 -1 -1];
im = conv2(double(im), myFilter); % Sharpen
im = max(0, im ./ (max(max(im)) / 255)); % Renormalize
imwrite(argv(){2}, uint8(im)); % Write back again
ImageMagick
#!/bin/bash
# we crop on load, it's a bit quicker and saves some memory
# we can't crop 100 pixels with the crop-on-load syntax, so we have to
# find the width and height ourselves
width=`header -f Xsize $1`
height=`header -f Ysize $1`
width=$((width - 200))
height=$((height - 200))
set -x
convert "$1[${width}x${height}+100+100]" \
-resize 90x90% \
-convolve "-1, -1, -1, -1, 16, -1, -1, -1, -1" \
$2
GraphicsMagick
GraphicsMagick does not have crop-on-load so we use -shave instead.
#!/bin/bash
set -x
gm convert $1 \
-shave 100x100 \
-resize 90x90% \
-convolve "-1, -1, -1, -1, 16, -1, -1, -1, -1" \
$2
FreeImage
/* Compile with:
gcc freeimage.c -lfreeimage
*/
#include <FreeImage.h>
int
main (int argc, char **argv)
{
FIBITMAP *t1;
FIBITMAP *t2;
int width;
int height;
FreeImage_Initialise (FALSE);
t1 = FreeImage_Load (FIF_TIFF, argv[1], TIFF_DEFAULT);
width = FreeImage_GetWidth (t1);
height = FreeImage_GetHeight (t1);
t2 = FreeImage_Copy (t1, 100, 100, width - 100, height - 100);
FreeImage_Unload (t1);
t1 = FreeImage_Rescale (t2, (width - 200) * 0.9, (height - 200) * 0.9,
FILTER_BILINEAR);
FreeImage_Unload (t2);
/* FreeImage does not have a sharpen operation, so we skip that.
*/
FreeImage_Save (FIF_TIFF, t1, argv[2], TIFF_DEFAULT);
FreeImage_Unload (t1);
FreeImage_DeInitialise ();
return 0;
}
NetPBM
#!/bin/bash cat > mask <<EOF P2 3 3 32 14 14 14 14 48 14 14 14 14 EOF tifftopnm $1 | \ pnmcut -left 100 -right -100 -top 100 -bottom -100 | \ pnmscale 0.9 | \ pnmconvol mask | \ pnmtotiff -truecolor -color > $2
ImageScience
ImageScience is a popular Ruby extension for making thumbnails. It's based on FreeImage.
It does not support sharpening, so I've skipped that part of the test. The resize() method is always bicubic which is a little unfair as the other benchmarks here use bilinear. It also generates sections of the wrapper at run-time, which again hurts it's position in the table at the top.
#!/usr/bin/ruby
require 'rubygems'
require 'image_science'
ImageScience.with_image(ARGV[0]) do |img|
img.with_crop(100, 100, img.width() - 100, img.height() - 100) do |crop|
crop.resize(crop.width() * 0.9, crop.height() * 0.9) do |small|
small.save(ARGV[1])
end
end
end
RMagick
RMagick is a popular Ruby extension, wrapping the ImageMagick image processing library.
#!/usr/bin/ruby
require 'rubygems'
require 'RMagick'
include Magick
im = ImageList.new(ARGV[0])
im = im.shave(100, 100)
im = im.scale(0.9)
kernel = [-1, -1, -1, -1, 16, -1, -1, -1, -1]
im = im.convolve(3, kernel)
im.write(ARGV[1])
