Extracting thumbnails from camera RAW files (.CR2 and .NEF) with PHP

I'm working on a small project where I need to process a butt-load of camera raw files. Most of the images that I have are from Canon systems, i.e. .CR2 files. A camera RAW file is a file that contains the raw sensor data from the camera. On most high-end cameras you can choose if you want to save the image as just a .jpg, .jpg+raw or just the raw file. These file tend to get pretty large seeing as they're uncompressed and non-processed image data. Most of the files that I have are around 25MB per file.

For the project I'm doing I need to generate thumbnails in various sizes that represents these files. At first I just rendered the raw file to a .jpg with imagemagick and ufraw. This works, but it's really slow, it takes about half a second per image, and it eats a lot of resources, the raw file can expand to hundreds of megabytes when it's being rendered.

Every raw file contains preview examples of the image which are embedded in the file. You can extract these previews with a CLI tool called exiv2. The great thing with extracting the previews versus actually rendering the file is speed and resources. Extracting takes far less time and it doesn't really need any resources. It's also worth to note that extracting and resizing an image is faster than rendering an image.

// This commands shows the available previews that are embedded
exiv2 -pp IMG_2205.CR2
// Returns something like:
Preview 1: image/jpeg, 160x120 pixels, 15888 bytes
Preview 2: image/tiff, 668x432 pixels, 1731456 bytes
Preview 3: image/jpeg, 5184x3456 pixels, 2235171 bytes

To actually extract an image, issue the following command;

// Extract the third preview (ep3) to the location (-l) /tmp/
// Note: You can only specify where you want to save the file
// you cannot change the file name of the new file.
exiv2 -ep3 -l /tmp/ IMG_2205.CR2
// Returns void.
// A file named IMG_2205-preview3.jpg will be created in /tmp/

So now that we know how to extract a preview in the command line, will need to bake this into php with the exec() command. When ever issuing exec commands, be sure to check that the input you're sending isn't user generated, if it is, a malicious user could potentially send in whatever commands he wants by piping in new commands.

exec('exiv2 -ep3 -l /tmp/ IMG_2205.CR2');

I've built a small class with static methods that handles the checking of previews in a raw file and extracts the largest preview or a user specified preview from a source file and saves it to a user specified location (with a new name).

Here's how to use it:

// View all available previews from IMG_2205.CR2 (returns an array)
// Generate a new file called preview.jpg from IMG_2205.CR2
CameraRaw::extractPreview('images/IMG_2205.CR2', 'images/thumbnails/preview.jpg');
// Resize preview.jpg to 200 x 200 (new file IMG_2205-200x200.jpg)
CameraRaw::generateImage('images/thumbnails/preview.jpg', 'images/thumbnails/IMG_2205-200x200.jpg', 200, 200);
// You can also render a raw file with generateImage, but it will be a lot slower
CameraRaw::generateImage('images/IMG_2205.CR2', 'images/thumbnails/IMG_2205-200x200.jpg', 200, 200);

In order to use the class you'll need the following items installed on the server.

  • ImageMagick (apt-get install php5-imagick)
  • ufraw (apt-get install ufraw)
  • exiv2 (apt-get install exiv2)

Please note that I've only tried the class on a Apache+PHP setup on debian 7. Your mileage may vary.

Anyway, here's the PHP class, Class CameraRaw.

Benjamin Horn
Benjamin Horn
Developer at Bazooka
A Finnish-German full-stack developer who's worked with a multitude of different technologies throughout the years.
Vaasa, Finland