Getting Started

If you read the beginning of this documentation, you’ve seen one example of using rawkit already. Let’s see an even simpler form of it:

from rawkit.raw import Raw

with Raw(filename='some/raw/image.CR2') as raw:
  raw.save(filename='some/destination/image.ppm')

This constructs a rawkit.raw.Raw object which loads the file image.CR2 as a context manager and then saves the output file image.ppm. One of the design goals of rawkit is “have sane defaults”, which means that this is pretty much all you need to do to get a decent looking photo. Of course, you probably want to customize how your photo is developed. For this you can use rawkit.options.

The Raw object you created has a rawkit.options.Options object already with the aforementioned sane defaults, so instead of constructing a new object let’s just modify the existing one to tweak the white balance and a few other options:

from rawkit.raw import Raw
from rawkit.options import WhiteBalance, colorspaces, gamma_curves

with Raw(filename='some/raw/image.CR2') as raw:
  raw.options.white_balance = WhiteBalance(camera=False, auto=True)
  raw.options.colorspace = colorspaces.adobe_rgb
  raw.options.gamma = gamma_curves.adobe_rgb
  raw.save(filename='some/destination/image.ppm')

By default rawkit uses the white balance written to the raw file by your camera (if available) and falls back to automatically guessing at the white balance if no camera specified white balance is available. However, here we’ve constructed a new rawkit.options.WhiteBalance object which does not attempt to use the camera white balance (note that WhiteBalance objects are immutable, so you’ll always need to construct a new one if you’re changing the white balance). We’ve also changed the colorspace to Adobe RGB instead of the default sRGB, and changed the gamma curve to use the corrective power function for the Adobe RGB colorspace.

Lots of other options can be set. A full list can be found in the API documentation for the rawkit.options module.

Of course, we probably don’t want to process just one raw file. A common photography workflow is to do some basic level of processing to lots of files at once (eg. an entire days worth of shooting) and then go back and tweak individual photos as necessary. To do this, we can construct our own options object and reuse it:

import sys

from rawkit.raw import Raw
from rawkit.options import WhiteBalance, colorspaces, gamma_curves
from rawkit.options import Options

opts = Options({
  'white_balance': WhiteBalance(camera=False, auto=True),
  'colorspace': colorspaces.adobe_rgb,
})

opts.gamma = gamma_curves.adobe_rgb


for rawfile in sys.argv[1:]
  with Raw(filename=rawfile) as raw:
    raw.options = opts
    raw.save(filename='{}.ppm'.format(rawfile))

As you can see, two methods for setting options on an Options object are presented here: via a dict passed to the constructor, or by manually setting the properties. Because the dict method tolerates arbitrary fields, you must be very careful not to make a typo. Eg. setting:

opts = Options({
  'colourspace': colorspaces.adobe_rgb,
  'white_blaance': WhiteBalance(greybox=[1034, 1058, 1096, 1085])
})

will run without error, but there will be no difference to your output photos. However, trying to set options via:

opts = Options()
opts.colourspace = colorspaces.adobe_rgb
opts.white_blaance = WhiteBalance(greybox=[1034, 1058, 1096, 1085])

Will result in an AttributeError. This is the recommended method for manually setting options because it will fail early and loudly!

Now that we’ve seen the basics (loading and saving raw files and setting options), let’s turn our simple example into something useful: A program which will take in the name of one or more raw files and attempt to save them as standard TIFF files. First, we’ll snag the arguments and add a bit of error checking (we’ll also get rid of the options and just use the defaults for now):

import sys

from libraw.errors import FileUnsupported
from rawkit.errors import InvalidFileType
from rawkit.raw import Raw

if __name__ == "__main__":

  for rawfile in sys.argv[1:]:
    try:
      with Raw(filename=rawfile) as raw:
        outfile = '{}.tiff'.format(rawfile)
        raw.save(filename=outfile)
        print(
          'Wrote file: "{}".'.format(
            outfile
          )
        )
    except (InvalidFileType, FileUnsupported):
        print(
          'WARNING: File "{}" could not be processed.'.format(
            rawfile
          ),
          file=sys.stderr
        )

Of course, while this works, it’s still a bit slow. Let’s add a thread pool to the mix and process multiple raw files at once (not that this has anything to do with actually using rawkit, but we might as well do things right):

import concurrent.futures
import os
import sys

from libraw.errors import FileUnsupported
from rawkit.errors import InvalidFileType
from rawkit.raw import Raw

def develop_photo(rawfile):
    with Raw(filename=rawfile) as raw:
        outfile = '{}.tiff'.format(rawfile)
        raw.save(filename=outfile)
        return outfile

if __name__ == "__main__":

    with concurrent.futures.ThreadPoolExecutor(max_workers=(
        (os.cpu_count() or 2) * 2)) as executor:
        develop_futures = {executor.submit(develop_photo, raw): raw for raw
            in sys.argv[1:]}
        for future in concurrent.futures.as_completed(develop_futures):
            raw = develop_futures[future]
            try:
                data = future.result()
            except (InvalidFileType, FileUnsupported):
                print(
                  'WARNING: File "{}" could not be processed'.format(raw),
                  file=sys.stderr
                )
            else:
                print('Wrote file: "{}"'.format(data))

That’s it, you’ve made a useful application which uses rawkit to develop raw photos! For a slightly more interesting example, take a look at the source to photoREPL, an experimental interface for editing photos from the command line.