Skip to content
This repository has been archived by the owner on Oct 4, 2020. It is now read-only.

Commit

Permalink
Add support for development options
Browse files Browse the repository at this point in the history
Fixes #6

Squashed commit of the following:

commit 6677eb7
Author: Sam Whited <sam@samwhited.com>
Date:   Mon May 25 01:26:44 2015 -0500

    Add tests for options

commit 3d82cfe
Author: Sam Whited <sam@samwhited.com>
Date:   Mon May 25 01:26:20 2015 -0500

    Simplify libraw param mapping

    Tweak error message

commit 935c54b
Author: Sam Whited <sam@samwhited.com>
Date:   Sun May 24 20:25:03 2015 -0500

    Reduce boilerplate in options code

commit d68ac51
Author: Sam Whited <sam@samwhited.com>
Date:   Sun May 24 16:27:18 2015 -0500

    Handle WB defaults like everything else

commit 638fa7d
Author: Sam Whited <sam@samwhited.com>
Date:   Sun May 24 16:22:16 2015 -0500

    Fix WhiteBalance() defaults and don't allocate dict

commit 0cb3e7e
Author: Sam Whited <sam@samwhited.com>
Date:   Sun May 24 16:16:11 2015 -0500

    Fix setting the output colorspace

commit 300c4b5
Author: Sam Whited <sam@samwhited.com>
Date:   Sun May 24 15:57:53 2015 -0500

    Make WhiteBalance a named tuple

commit 6f1ef2c
Author: Sam Whited <sam@samwhited.com>
Date:   Sun May 24 15:52:47 2015 -0500

    Move "constants" into immutable named tuples

commit 3d31abf
Author: Sam Whited <sam@samwhited.com>
Date:   Sun May 24 14:02:41 2015 -0500

    Add colorspace option

commit f86bc99
Author: Sam Whited <sam@samwhited.com>
Date:   Sun May 24 13:51:20 2015 -0500

    Create documentation for all options parameters

commit 9508ab6
Author: Sam Whited <sam@samwhited.com>
Date:   Sat May 23 18:04:26 2015 -0500

    Make options its own class

commit a4304f6
Author: Sam Whited <sam@samwhited.com>
Date:   Sat May 23 16:58:44 2015 -0500

    Add white balance options

commit b095324
Author: Sam Whited <sam@samwhited.com>
Date:   Sat May 23 16:57:54 2015 -0500

    Clean up boolean options

commit 39e0218
Author: Sam Whited <sam@samwhited.com>
Date:   Sat May 23 12:41:42 2015 -0500

    Add support for some libraw options
  • Loading branch information
SamWhited committed May 25, 2015
1 parent 13aeca7 commit a05e2bd
Show file tree
Hide file tree
Showing 4 changed files with 454 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ rawkit
Python inspired by the Wand_ API. ::

from rawkit.raw import Raw
from rawkit.options import WhiteBalance

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

.. _LibRaw: http://www.libraw.org/
Expand Down
384 changes: 384 additions & 0 deletions rawkit/options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,384 @@
""":mod:`rawkit.options` --- High level options for processing raw files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""

import ctypes

from collections import namedtuple


class option(object):

"""
The :class:`option` decorator is an internal decorator which allows you to
define an option in a clean manner (specifying its name and how it maps to
the libraw params).
"""

def __init__(self, param=None, ctype=None):
if callable(param):
func = param
param = None
else:
func = None
self._prepare_func(func)
self.param = param
self.ctype = ctype
self.setter_func = None
self.param_func = None

def _prepare_func(self, func):
self.func = func
if func is not None:
self.__doc__ = getattr(func, '__doc__')
self.__name__ = getattr(func, '__name__')
self.internal_name = '_{name}'.format(name=self.__name__)
self.__module__ = getattr(func, '__module__')

def __call__(self, func=None):
self._prepare_func(func)
if func is None:
raise TypeError("option should not be called except as a property")
self.func = func
return self

def setter(self, func):
self.setter_func = func
return self

def param_writer(self, func):
self.param_func = func
return self

def write_param(self, obj, params):
if self.param_func is None:
setattr(params, self.param, self.ctype(self.__get__(obj, None)))
else:
self.param_func(obj, params)

def __set__(self, obj, value):
if self.setter_func is None:
setattr(obj, self.internal_name, value)
else:
self.setter_func(obj, value)

def __get__(self, obj, cls):
try:
val = getattr(obj, self.internal_name)
if val is None:
return self.func(obj)
else:
return val
except AttributeError:
# We're probably generating the documentation...
return self

highlight_modes = namedtuple(
'HighlightMode', ['clip', 'ignore', 'blend', 'reconstruct']
)(0, 1, 2, 5)
"""
Constants for setting the highlight mode.
- ``clip`` --- Clip all highlights to white (default).
- ``ignore`` --- Leave highlights unclipped.
- ``blend`` --- Blend clipped and unclipped highlights.
- ``reconstruct`` --- A good average value for reconstruction of clipped
highlights which compromises between favoring whites and favoring colors.
"""

colorspaces = namedtuple(
'ColorSpaces', ['raw', 'srgb', 'adobe_rgb', 'wide_gammut_rgb',
'kodak_prophoto_rgb', 'xyz']
)(0, 1, 2, 3, 4, 5)
"""
Constants for setting the colorspace.
- ``raw_color`` --- Raw colorspace (unique to each camera)
- ``srgb`` --- sRGB D65 (default colorspace)
- ``adobe_rgb`` --- Adobe RGB (1998) D65
- ``wide_gammut_rgb`` --- Wide Gamut RGB D65
- ``kodak_prophoto_rgb`` --- Kodak ProPhoto RGB D65
- ``xyz`` --- XYZ colorspace
"""


class WhiteBalance(namedtuple('WhiteBalance',
['auto', 'camera', 'greybox', 'rgbg'])):

"""
Represents the white balance of a photo. If the camera white balance is
used, but not present, we fallback to the other options. Other options
white balance multipliers stack (eg. you can use auto white balance, and
then specify a manual ``rgbg`` multiplier on top of that).
:param auto: determines if we should automatically set the WB
:type auto: :class:`boolean`
:param camera: causes us to use the camera defined WB if present
:type camera: :class:`boolean`
:param greybox: set the WB based on a neutral grey region of the image
:type greybox: :class:`4 int tuple`
:param rgbg: set the WB manually based on an RGBG channel multiplier
:type rgbg: :class:`4 float tuple`
:returns: A white blance object
:rtype: :class:`WhiteBalance`
"""

__slots__ = ()

def __new__(cls, auto=False, camera=False, greybox=None, rgbg=None):
return super(WhiteBalance, cls).__new__(
cls, auto, camera, greybox, rgbg)


class Options(object):

"""
Represents a set of options which can be used when processing raw data.
"""

__slots__ = [
'_bps',
'_brightness',
'_chromatic_aberration',
'_darkness',
'_half_size',
'_noise_threshold',
'_rgbg_interpolation',
'_saturation',
'_shot',
'_use_camera_matrix',
'_white_balance',
'_highlight_mode',
'_colorspace'
]
"""The options which are supported by this class."""

def __init__(self):
"""
Create the options object, initializing values to ``None``.
"""
for i in self.__slots__:
setattr(self, i, None)

@option(param='output_color', ctype=ctypes.c_int)
def colorspace(self):
"""
Sets the colorspace used for the output image. Supported colorspaces
are defined as constants in :class:`rawkit.options.colorspaces`.
:type: :class:`int`
:default: :class:`rawkit.options.colorspaces.srgb`
:dcraw: ``-o``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.output_color`
"""
return colorspaces.srgb

@option(param='highlight', ctype=ctypes.c_int)
def highlight_mode(self):
"""
The mode for dealing with highlights in the image. Some constants have
been defined in :class:`rawkit.options.highlight_modes` to make things
easier, or you can set an integer directly.
Integers that are greater than or equal to 3 will attempt to
reconstruct highlights. Lower numbers favor whites, and higher colors
favor colors. :class:`rawkit.options.RECONSTRUCT` (5) is a good
compromise.
:type: :class:`int`
:default: :class:`rawkit.options.highlight_modes.clip`
:dcraw: ``-H``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.highlight`
"""
return highlight_modes.clip

@option
def white_balance(self):
"""
The white balance of the image.
:type: :class:`rawkit.options.WhiteBalance`
:default: WhiteBalance(auto=True, camera=True)
:dcraw: ``-a``
``-w``
``-A``
``-r``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.use_auto_wb`
:class:`rawkit.libraw.libraw_output_params_t.use_camera_wb`
:class:`rawkit.libraw.libraw_output_params_t.greybox`
:class:`rawkit.libraw.libraw_output_params_t.user_mul`
"""
return WhiteBalance(auto=True, camera=True)

@white_balance.param_writer
def white_balance(self, params):
if self.white_balance.greybox is not None:
params.greybox = (ctypes.c_uint * 4)(*self.white_balance.greybox)
if self.white_balance.rgbg is not None:
params.user_mul = (ctypes.c_float * 4)(*self.white_balance.rgbg)
params.use_camera_wb = ctypes.c_int(self.white_balance.camera)
params.use_auto_wb = ctypes.c_int(self.white_balance.auto)

@option(param='use_camera_matrix', ctype=ctypes.c_int)
def use_camera_matrix(self):
"""
Use the color matrix from the raw's metadata. Only affects Olympus,
Leaf, and Phase One cameras (and DNG files). Default is True if the
photo is in DNG format or the camera white balance is being used, False
otherwise.
:type: :class:`boolean`
:default: 0
:dcraw: ``+M``
``-M``
:libraw: :class:`libraw.libraw_output_params_t.use_camera_matrix`
"""
return None

@option(param='shot_select', ctype=ctypes.c_uint)
def shot(self):
"""
Selects the shot to process for raw images that contain multiple
images.
:type: :class:`int`
:default: 0
:dcraw: ``-s``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.shot_select`
"""
return None

@option(param='user_sat', ctype=ctypes.c_int)
def saturation(self):
"""
Determines the saturation level of the output image.
:type: :class:`int`
:default: None
:dcraw: ``-S``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.user_sat`
"""
return None

@option(param='four_color_rgb', ctype=ctypes.c_int)
def rgbg_interpolation(self):
"""
Determines if we should use four channel RGB interpolation.
:type: :class:`boolean`
:default: False
:dcraw: ``-f``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.four_color_rgb`
"""
return False

@option(param='threshold', ctype=ctypes.c_float)
def noise_threshold(self):
"""
Sets the threshold for noise reduction using wavelet denoising.
:type: :class:`float`
:default: None
:dcraw: ``-n``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.threshold`
"""
return None

@option(param='half_size', ctype=ctypes.c_int)
def half_size(self):
"""
When developing the image, output it at 50% size. This makes developing
preview images much faster.
:type: :class:`boolean`
:default: False
:dcraw: ``-h``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.half_size`
"""
return False

@option(param='user_black', ctype=ctypes.c_int)
def darkness(self):
"""
Raise the black level of a photo.
:type: :class:`int`
:default: None
:dcraw: ``-k``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.user_black`
"""
return None

@option
def chromatic_aberration(self):
"""
A Red-Blue scale factor that's used to correct for chromatic aberration
by scaling the respective channels.
eg. ::
# (red_scale, blue_scale)
raw.options.chromatic_aberration = (0.999, 1.001)
:type: :class:`double tuple`
:default: (1, 1)
:dcraw: ``-C``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.aber`
"""
return (1, 1)

@chromatic_aberration.param_writer
def chromatic_aberration(self, params):
params.aber = (ctypes.c_double * 4)(*(
self.chromatic_aberration[0],
0, # TODO: What is this index used for?
self.chromatic_aberration[1],
0 # TODO: What is this index used for?
))

@option(param='output_bps', ctype=ctypes.c_int)
def bps(self):
"""
Set the bits per sample used for the photo (8 or 16).
Setting this to 16 is effectively the same as running dcraw with the
``-4`` option.
:type: :class:`int`
:default: 8
:dcraw: ``-4``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.output_bps`
"""
return 8

@bps.setter
def bps(self, value):
if value in (8, 16):
self._bps = value
else:
raise ValueError("BPS must be 8 or 16")

@option(param='bright', ctype=ctypes.c_float)
def brightness(self):
"""
The brightness of the image.
:type: :class:`float`
:default: 1.0
:dcraw: ``-b``
:libraw: :class:`rawkit.libraw.libraw_output_params_t.bright`
"""
return 1.0

def _map_to_libraw_params(self, params):
"""
Internal method that writes rawkit options into the libraw options
struct with the proper C data types.
:param params: the output params struct to set
:type params: :class:`rawkit.libraw.libraw_output_params_t`
"""
for slot in self.__slots__:
prop = slot[1:]
opt = getattr(Options, prop)
if type(opt) is option and getattr(self, prop) is not None:
opt.write_param(self, params)

0 comments on commit a05e2bd

Please sign in to comment.