Esempio n. 1
0
    def __init__(self, filename=None):
        """Initializes a new Raw object."""
        if filename is None:
            raise NoFileSpecified()
        self.libraw = LibRaw()
        self.data = self.libraw.libraw_init(0)
        self.libraw.libraw_open_file(self.data, filename.encode('ascii'))

        self.options = Options()

        self.image_unpacked = False
        self.thumb_unpacked = False
Esempio n. 2
0
def camera_list():
    """
    Return a list of cameras which are supported by the currently linked
    version of LibRaw.

    Returns:
        str array: A list of supported cameras.
    """

    libraw = LibRaw()
    libraw.libraw_cameraList.restype = ctypes.POINTER(
        ctypes.c_char_p * libraw.libraw_cameraCount())
    data_pointer = libraw.libraw_cameraList()
    return [x.decode('ascii') for x in data_pointer.contents]
Esempio n. 3
0
def camera_list():
    """
    Return a list of cameras which are supported by the currently linked
    version of LibRaw.

    Returns:
        str array: A list of supported cameras.
    """

    libraw = LibRaw()
    libraw.libraw_cameraList.restype = ctypes.POINTER(
        ctypes.c_char_p * libraw.libraw_cameraCount()
    )
    data_pointer = libraw.libraw_cameraList()
    return [x.decode('ascii') for x in data_pointer.contents]
Esempio n. 4
0
    def __init__(self, filename=None):
        """Initializes a new Raw object."""
        if filename is None:
            raise NoFileSpecified()
        self.libraw = LibRaw()
        self.data = self.libraw.libraw_init(0)
        try:  # pragma: no cover
            _fname = os.fsencode(filename)
        except Exception:  # pragma: no cover
            _fname = filename
        self.libraw.libraw_open_file(self.data, _fname)

        self.options = Options()

        self.image_unpacked = False
        self.thumb_unpacked = False
Esempio n. 5
0
    def __init__(self, filename=None):
        """Initializes a new Raw object."""
        if filename is None:
            raise NoFileSpecified()
        self.libraw = LibRaw()
        self.data = self.libraw.libraw_init(0)
        self.libraw.libraw_open_file(self.data, filename.encode('ascii'))

        self.options = Options()

        self.image_unpacked = False
        self.thumb_unpacked = False
Esempio n. 6
0
def discover(path):
    """
    Recursively search for raw files in a given directory.

    Args:
        path (str): A tree to recursively search.
    """
    file_list = []
    libraw = LibRaw()
    raw = libraw.libraw_init(0)

    for root, _, files in os.walk(path):
        for file_name in files:
            file_path = os.path.join(root, file_name).encode('ascii')
            try:
                libraw.libraw_open_file(raw, file_path)
            except FileUnsupported:
                continue
            finally:
                libraw.libraw_recycle(raw)
            file_list.append(file_path)

    libraw.libraw_close(raw)
    return file_list
Esempio n. 7
0
def discover(path):
    """
    Recursively search for raw files in a given directory.

    Args:
        path (str): A tree to recursively search.
    """
    file_list = []
    libraw = LibRaw()
    raw = libraw.libraw_init(0)

    for root, _, files in os.walk(path):
        for file_name in files:
            file_path = os.path.join(root, file_name).encode('ascii')
            try:
                libraw.libraw_open_file(raw, file_path)
            except FileUnsupported:
                continue
            finally:
                libraw.libraw_recycle(raw)
            file_list.append(file_path)

    libraw.libraw_close(raw)
    return file_list
Esempio n. 8
0
class Raw(object):

    """
    Represents a raw file (of any format) and exposes development options to
    the user.

    For example, the basic workflow (open a file, process the file, save the
    file) looks like this::

        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')

    Args:
        filename (str): The name of a raw file to load.

    Returns:
        Raw: A raw object.

    Raises:
        rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        libraw.errors.FileUnsupported: If the specified file is not a supported
                                       raw type.
        libraw.errors.InsufficientMemory: If we run out of memory while loading
                                          the raw file.
        IOError: If the file does not exist, or cannot be opened (eg. incorrect
                 permissions).
    """

    def __init__(self, filename=None):
        """Initializes a new Raw object."""
        if filename is None:
            raise NoFileSpecified()
        self.libraw = LibRaw()
        self.data = self.libraw.libraw_init(0)
        self.libraw.libraw_open_file(self.data, filename.encode('ascii'))

        self.options = Options()

        self.image_unpacked = False
        self.thumb_unpacked = False

    def __enter__(self):
        """Return a Raw object for use in context managers."""
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Clean up after ourselves when leaving the context manager."""
        self.close()

    def close(self):
        """Free the underlying raw representation."""
        self.libraw.libraw_close(self.data)

    def unpack(self):
        """Unpack the raw data."""
        if not self.image_unpacked:
            self.libraw.libraw_unpack(self.data)
            self.image_unpacked = True

    def unpack_thumb(self):
        """
        Unpack the thumbnail data.
        Raises:
            libraw.errors.NoThumbnail: If the raw file does not contain a
                                       thumbnail.
            libraw.errors.UnsupportedThumbnail: If the thumbnail format is
                                                unsupported.
        """
        if not self.thumb_unpacked:
            self.libraw.libraw_unpack_thumb(self.data)
            self.thumb_unpacked = True

    def process(self):
        """
        Process the raw data based on ``self.options``.

        Raises:
            libraw.errors.DataError: If invalid or corrupt data is encountered
                                     in the data struct.
            libraw.errors.BadCrop: If the image has been cropped poorly (eg.
                                   the edges are outside of the image bounds,
                                   or the crop box coordinates don't make
                                   sense).
        """
        self.options._map_to_libraw_params(self.data.contents.params)
        self.libraw.libraw_dcraw_process(self.data)

    def save(self, filename=None, filetype=None):
        """
        Save the image data as a new PPM or TIFF image.

        Args:
            filename (str): The name of an image file to save.
            filetype (output_file_types): The type of file to output. By
                                          default, guess based on the filename,
                                          falling back to PPM.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
            rawkit.errors.InvalidFileType: If `filetype` is not None or in
                                           :class:`output_file_types`.
        """
        if filename is None:
            raise NoFileSpecified()

        if filetype is None:
            ext = os.path.splitext(filename)[-1].lower()[1:]
            filetype = ext or output_file_types.ppm

        if filetype not in output_file_types:
            raise InvalidFileType(
                "Output filetype must be in raw.output_file_types")

        self.data.contents.params.output_tiff = (
            filetype == output_file_types.tiff
        )

        self.unpack()
        self.process()

        self.libraw.libraw_dcraw_ppm_tiff_writer(
            self.data, filename.encode('ascii'))

    def save_thumb(self, filename=None):
        """
        Save the thumbnail data.

        Args:
            filename (str): The name of an image file to save.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        """
        if filename is None:
            raise NoFileSpecified()

        self.unpack_thumb()

        self.libraw.libraw_dcraw_thumb_writer(
            self.data, filename.encode('ascii'))

    def to_buffer(self):
        """
        Convert the image to an RGB buffer.

        Returns:
            bytearray: RGB data of the image.
        """
        self.unpack()
        self.process()

        status = ctypes.c_int(0)
        processed_image = self.libraw.libraw_dcraw_make_mem_image(
            self.data,
            ctypes.cast(
                ctypes.addressof(status),
                ctypes.POINTER(ctypes.c_int),
            ),
        )
        raise_if_error(status.value)
        data_pointer = ctypes.cast(
            processed_image.contents.data,
            ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size)
        )
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    def thumbnail_to_buffer(self):
        """
        Convert the thumbnail data as an RGB buffer.

        Returns:
            bytearray: RGB data of the thumbnail.
        """
        self.unpack_thumb()

        status = ctypes.c_int(0)
        processed_image = self.libraw.libraw_dcraw_make_mem_thumb(
            self.data,
            ctypes.cast(
                ctypes.addressof(status),
                ctypes.POINTER(ctypes.c_int),
            ),
        )
        raise_if_error(status.value)
        data_pointer = ctypes.cast(
            processed_image.contents.data,
            ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size)
        )
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    @property
    def metadata(self):
        """
        Common metadata for the photo

        Returns:
            rawkit.metadata.Metadata: A metadata object.
        """
        return Metadata(
            aperture=self.data.contents.other.aperture,
            timestamp=self.data.contents.other.timestamp,
            shutter=self.data.contents.other.shutter,
            flash=bool(self.data.contents.color.flash_used),
            focal_length=self.data.contents.other.focal_len,
            height=self.data.contents.sizes.height,
            iso=self.data.contents.other.iso_speed,
            make=self.data.contents.idata.make,
            model=self.data.contents.idata.model,
            orientation=self.data.contents.sizes.flip,
            width=self.data.contents.sizes.width,
        )
Esempio n. 9
0
class Raw(object):

    """
    Represents a raw file (of any format) and exposes development options to
    the user.

    For example, the basic workflow (open a file, process the file, save the
    file) looks like this::

        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')

    Args:
        filename (str): The name of a raw file to load.

    Returns:
        Raw: A raw object.

    Raises:
        rawkit.errors.NoFileSpecified: If `filename` is ``None``.
    """

    def __init__(self, filename=None):
        """Initializes a new Raw object."""
        if filename is None:
            raise NoFileSpecified()
        self.libraw = LibRaw()
        self.data = self.libraw.libraw_init(0)
        self.libraw.libraw_open_file(self.data, filename.encode("ascii"))

        self.options = Options()

        self.image_unpacked = False
        self.thumb_unpacked = False

    def __enter__(self):
        """Return a Raw object for use in context managers."""
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Clean up after ourselves when leaving the context manager."""
        self.close()

    def close(self):
        """Free the underlying raw representation."""
        self.libraw.libraw_close(self.data)

    def unpack(self):
        """Unpack the raw data."""
        if not self.image_unpacked:
            self.libraw.libraw_unpack(self.data)
            self.image_unpacked = True

    def unpack_thumb(self):
        """Unpack the thumbnail data."""
        if not self.thumb_unpacked:
            self.libraw.libraw_unpack_thumb(self.data)
            self.thumb_unpacked = True

    def process(self):
        """Process the raw data based on self.options"""
        self.options._map_to_libraw_params(self.data.contents.params)
        self.libraw.libraw_dcraw_process(self.data)

    def save(self, filename=None, filetype="ppm"):
        """
        Save the image data as a new PPM or TIFF image.

        Args:
            filename (str): The name of an image file to save.
            filetype (str): The type of file to output (``ppm`` or ``tiff``).

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
            rawkit.errors.InvalidFileTypeError: If `filetype` is not ``ppm`` or
                ``tiff``.
        """
        if filename is None:
            raise NoFileSpecified()
        if filetype not in ("ppm", "tiff"):
            raise InvalidFileType('Output filetype must be "ppm" or "tiff"')
        self.data.contents.params.output_tiff = 0 if filetype is "ppm" else 1

        self.unpack()
        self.process()

        self.libraw.libraw_dcraw_ppm_tiff_writer(self.data, filename.encode("ascii"))

    def save_thumb(self, filename=None):
        """
        Save the thumbnail data.

        Args:
            filename (str): The name of an image file to save.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        """
        if filename is None:
            raise NoFileSpecified()

        self.unpack_thumb()

        self.libraw.libraw_dcraw_thumb_writer(self.data, filename.encode("ascii"))

    def to_buffer(self):
        """
        Convert the image to an RGB buffer.

        Returns:
            bytearray: RGB data of the image.
        """
        self.unpack()
        self.process()

        # TODO: check the error code stored in the provided int pointer
        processed_image = self.libraw.libraw_dcraw_make_mem_image(self.data, ctypes.byref(ctypes.c_int()))
        data_pointer = ctypes.cast(
            processed_image.contents.data, ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size)
        )
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    def thumbnail_to_buffer(self):
        """
        Convert the thumbnail data as an RGB buffer.

        Returns:
            bytearray: RGB data of the thumbnail.
        """
        self.unpack_thumb()

        # TODO: check the error code stored in the provided int pointer
        processed_image = self.libraw.libraw_dcraw_make_mem_thumb(self.data, ctypes.byref(ctypes.c_int()))
        data_pointer = ctypes.cast(
            processed_image.contents.data, ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size)
        )
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    @property
    def metadata(self):
        """
        Common metadata for the photo

        Returns:
            rawkit.metadata.Metadata: A metadata object.
        """
        return Metadata(
            aperture=self.data.contents.other.aperture,
            timestamp=self.data.contents.other.timestamp,
            shutter=self.data.contents.other.shutter,
            flash=bool(self.data.contents.color.flash_used),
            focal_length=self.data.contents.other.focal_len,
            height=self.data.contents.sizes.height,
            iso=self.data.contents.other.iso_speed,
            make=self.data.contents.idata.make,
            model=self.data.contents.idata.model,
            orientation=self.data.contents.sizes.flip,
            width=self.data.contents.sizes.width,
        )
Esempio n. 10
0
class Raw(object):
    """
    Represents a raw file (of any format) and exposes development options to
    the user.

    For example, the basic workflow (open a file, process the file, save the
    file) looks like this::

        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')

    Args:
        filename (str): The name of a raw file to load.

    Returns:
        Raw: A raw object.

    Raises:
        rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        libraw.errors.FileUnsupported: If the specified file is not a supported
                                       raw type.
        libraw.errors.InsufficientMemory: If we run out of memory while loading
                                          the raw file.
        IOError: If the file does not exist, or cannot be opened (eg. incorrect
                 permissions).
    """
    def __init__(self, filename=None):
        """Initializes a new Raw object."""
        if filename is None:
            raise NoFileSpecified()
        self.libraw = LibRaw()
        self.data = self.libraw.libraw_init(0)
        self.libraw.libraw_open_file(self.data, filename.encode('ascii'))

        self.options = Options()

        self.image_unpacked = False
        self.thumb_unpacked = False

    def __enter__(self):
        """Return a Raw object for use in context managers."""
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Clean up after ourselves when leaving the context manager."""
        self.close()

    def close(self):
        """Free the underlying raw representation."""
        self.libraw.libraw_close(self.data)

    def unpack(self):
        """Unpack the raw data."""
        if not self.image_unpacked:
            self.libraw.libraw_unpack(self.data)
            self.image_unpacked = True

    def unpack_thumb(self):
        """
        Unpack the thumbnail data.

        Raises:
            libraw.errors.NoThumbnail: If the raw file does not contain a
            thumbnail.
            libraw.errors.UnsupportedThumbnail: If the thumbnail format is
            unsupported.
        """
        if not self.thumb_unpacked:
            self.libraw.libraw_unpack_thumb(self.data)
            self.thumb_unpacked = True

    def process(self):
        """
        Process the raw data based on ``self.options``.

        Raises:
            libraw.errors.DataError: If invalid or corrupt data is encountered
                                     in the data struct.
            libraw.errors.BadCrop: If the image has been cropped poorly (eg.
                                   the edges are outside of the image bounds,
                                   or the crop box coordinates don't make
                                   sense).
        """
        self.options._map_to_libraw_params(self.data.contents.params)
        self.libraw.libraw_dcraw_process(self.data)

    def save(self, filename=None, filetype=None):
        """
        Save the image data as a new PPM or TIFF image.

        Args:
            filename (str): The name of an image file to save.
            filetype (output_file_types): The type of file to output. By
                                          default, guess based on the filename,
                                          falling back to PPM.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
            rawkit.errors.InvalidFileType: If `filetype` is not None or in
                                           :class:`output_file_types`.
        """
        if filename is None:
            raise NoFileSpecified()

        if filetype is None:
            ext = os.path.splitext(filename)[-1].lower()[1:]
            filetype = ext or output_file_types.ppm

        if filetype not in output_file_types:
            raise InvalidFileType(
                "Output filetype must be in raw.output_file_types")

        self.data.contents.params.output_tiff = (
            filetype == output_file_types.tiff)

        self.unpack()
        self.process()

        self.libraw.libraw_dcraw_ppm_tiff_writer(self.data,
                                                 filename.encode('ascii'))

    def save_thumb(self, filename=None):
        """
        Save the thumbnail data.

        Args:
            filename (str): The name of an image file to save.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        """
        if filename is None:
            raise NoFileSpecified()

        self.unpack_thumb()

        self.libraw.libraw_dcraw_thumb_writer(self.data,
                                              filename.encode('ascii'))

    @property
    def color_description(self):
        """
        Get the color_description of an image.

        Returns:
            str: 4 character string representing color format, such as 'RGGB'.
        """
        # TODO: remove the pragma once there is integration testing,
        # but until then testing this is entirely pointless.
        return self.data.contents.idata.cdesc  # pragma: no cover

    def color(self, y, x):
        """
        Get the active color of a pixel of bayer data.

        Args:
            y (int): the y coordinate (or row) of the pixel
            x (int): the x coordinate (or column) of the pixel

        Returns:
            str: Character representing the color, such as 'R' for red.
        """
        color_index = self.libraw.libraw_COLOR(self.data, y, x)
        return chr(self.color_description[color_index])

    @property
    def color_filter_array(self):
        """
        EXPERIMENTAL: This method only supports bayer filters for the time
        being. It will be incorrect when used with other types of sensors.

        Get the color filter array for the camera sensor.

        Returns:
            list: 2D array representing the color format array pattern.
                  For example, the typical 'RGGB' pattern of abayer sensor
                  would be of the format::

                      [
                          ['R', 'G'],
                          ['G', 'B'],
                      ]

        """

        # TODO: don't assume 2x2 bayer sensor
        return [[self.color(0, 0), self.color(0, 1)],
                [self.color(1, 0), self.color(1, 1)]]

    def raw_image(self, include_margin=False):
        """
        Get the bayer data for an image if it exists.

        Args:
            include_margin (bool): Include margin with calibration pixels.

        Returns:
            list: 2D array of bayer pixel data structured as a list of rows,
                  or None if there is no bayer data.
                  For example, if the color format is `RGGB`, the array
                  would be of the format::

                      [
                          [R, G, R, G, ...],
                          [G, B, G, B, ...],
                          [R, G, R, G, ...],
                          ...
                      ]

        """
        self.unpack()
        image = self.data.contents.rawdata.raw_image

        if not bool(image):
            return []

        sizes = self.data.contents.sizes

        # TODO: handle this
        if sizes.pixel_aspect != 1:
            warnings.warn("The pixel aspect is not unity, it is: " +
                          str(sizes.pixel_aspect))

        # TODO: handle this
        if sizes.flip != 0:
            warnings.warn("The image is flipped.")

        pitch = sizes.raw_width

        if include_margin:
            first = 0
            width = sizes.raw_width
            height = sizes.raw_height
        else:
            first = sizes.raw_width * sizes.top_margin + sizes.left_margin
            width = sizes.width
            height = sizes.height

        data_pointer = ctypes.cast(image, ctypes.POINTER(ctypes.c_ushort))

        data = []

        for y in range(height):
            row = []
            for x in range(width):
                row.append(data_pointer[first + y * pitch + x])
            data.append(row)

        return data

    def bayer_data(self, include_margin=False):
        """
        Get the bayer data and color_description for an image.

        Returns:
            tuple: Tuple of bayer data and color filter array. This is a
                   convenience method to return `rawkit.raw.Raw.raw_image`
                   and `rawkit.raw.Raw.color_filter_array` as a single tuple.
        """
        return self.raw_image(include_margin), self.color_filter_array

    def to_buffer(self):
        """
        Convert the image to an RGB buffer.

        Returns:
            bytearray: RGB data of the image.
        """
        self.unpack()
        self.process()

        status = ctypes.c_int(0)
        processed_image = self.libraw.libraw_dcraw_make_mem_image(
            self.data,
            ctypes.cast(
                ctypes.addressof(status),
                ctypes.POINTER(ctypes.c_int),
            ),
        )
        raise_if_error(status.value)
        data_pointer = ctypes.cast(
            processed_image.contents.data,
            ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size))
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    def thumbnail_to_buffer(self):
        """
        Convert the thumbnail data as an RGB buffer.

        Returns:
            bytearray: RGB data of the thumbnail.
        """
        self.unpack_thumb()

        status = ctypes.c_int(0)
        processed_image = self.libraw.libraw_dcraw_make_mem_thumb(
            self.data,
            ctypes.cast(
                ctypes.addressof(status),
                ctypes.POINTER(ctypes.c_int),
            ),
        )
        raise_if_error(status.value)
        data_pointer = ctypes.cast(
            processed_image.contents.data,
            ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size))
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    @property
    def metadata(self):
        """
        Common metadata for the photo

        Returns:
            rawkit.metadata.Metadata: A metadata object.
        """
        return Metadata(
            aperture=self.data.contents.other.aperture,
            timestamp=self.data.contents.other.timestamp,
            shutter=self.data.contents.other.shutter,
            flash=bool(self.data.contents.color.flash_used),
            focal_length=self.data.contents.other.focal_len,
            height=self.data.contents.sizes.height,
            iso=self.data.contents.other.iso_speed,
            make=self.data.contents.idata.make,
            model=self.data.contents.idata.model,
            orientation=self.data.contents.sizes.flip,
            width=self.data.contents.sizes.width,
        )
Esempio n. 11
0
class Raw(object):

    """
    Represents a raw file (of any format) and exposes development options to
    the user.

    For example, the basic workflow (open a file, process the file, save the
    file) looks like this::

        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')

    Args:
        filename (str): The name of a raw file to load.

    Returns:
        Raw: A raw object.

    Raises:
        rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        libraw.errors.FileUnsupported: If the specified file is not a supported
                                       raw type.
        libraw.errors.InsufficientMemory: If we run out of memory while loading
                                          the raw file.
        IOError: If the file does not exist, or cannot be opened (eg. incorrect
                 permissions).
    """

    def __init__(self, filename=None):
        """Initializes a new Raw object."""
        if filename is None:
            raise NoFileSpecified()
        self.libraw = LibRaw()
        self.data = self.libraw.libraw_init(0)
        self.libraw.libraw_open_file(self.data, filename.encode('ascii'))

        self.options = Options()

        self.image_unpacked = False
        self.thumb_unpacked = False

    def __enter__(self):
        """Return a Raw object for use in context managers."""
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Clean up after ourselves when leaving the context manager."""
        self.close()

    def close(self):
        """Free the underlying raw representation."""
        self.libraw.libraw_close(self.data)

    def unpack(self):
        """Unpack the raw data."""
        if not self.image_unpacked:
            self.libraw.libraw_unpack(self.data)
            self.image_unpacked = True

    def unpack_thumb(self):
        """
        Unpack the thumbnail data.

        Raises:
            libraw.errors.NoThumbnail: If the raw file does not contain a
            thumbnail.
            libraw.errors.UnsupportedThumbnail: If the thumbnail format is
            unsupported.
        """
        if not self.thumb_unpacked:
            self.libraw.libraw_unpack_thumb(self.data)
            self.thumb_unpacked = True

    def process(self):
        """
        Process the raw data based on ``self.options``.

        Raises:
            libraw.errors.DataError: If invalid or corrupt data is encountered
                                     in the data struct.
            libraw.errors.BadCrop: If the image has been cropped poorly (eg.
                                   the edges are outside of the image bounds,
                                   or the crop box coordinates don't make
                                   sense).
        """
        self.options._map_to_libraw_params(self.data.contents.params)
        self.libraw.libraw_dcraw_process(self.data)

    def save(self, filename=None, filetype=None):
        """
        Save the image data as a new PPM or TIFF image.

        Args:
            filename (str): The name of an image file to save.
            filetype (output_file_types): The type of file to output. By
                                          default, guess based on the filename,
                                          falling back to PPM.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
            rawkit.errors.InvalidFileType: If `filetype` is not None or in
                                           :class:`output_file_types`.
        """
        if filename is None:
            raise NoFileSpecified()

        if filetype is None:
            ext = os.path.splitext(filename)[-1].lower()[1:]
            filetype = ext or output_file_types.ppm

        if filetype not in output_file_types:
            raise InvalidFileType(
                "Output filetype must be in raw.output_file_types")

        self.data.contents.params.output_tiff = (
            filetype == output_file_types.tiff
        )

        self.unpack()
        self.process()

        self.libraw.libraw_dcraw_ppm_tiff_writer(
            self.data, filename.encode('ascii'))

    def save_thumb(self, filename=None):
        """
        Save the thumbnail data.

        Args:
            filename (str): The name of an image file to save.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        """
        if filename is None:
            raise NoFileSpecified()

        self.unpack_thumb()

        self.libraw.libraw_dcraw_thumb_writer(
            self.data, filename.encode('ascii'))

    @property
    def color_description(self):
        """
        Get the color_description of an image.

        Returns:
            str: 4 character string representing color format, such as 'RGGB'.
        """
        # TODO: remove the pragma once there is integration testing,
        # but until then testing this is entirely pointless.
        return self.data.contents.idata.cdesc  # pragma: no cover

    def color(self, y, x):
        """
        Get the active color of a pixel of bayer data.

        Args:
            y (int): the y coordinate (or row) of the pixel
            x (int): the x coordinate (or column) of the pixel

        Returns:
            str: Character representing the color, such as 'R' for red.
        """
        color_index = self.libraw.libraw_COLOR(self.data, y, x)
        return chr(self.color_description[color_index])

    @property
    def color_filter_array(self):
        """
        EXPERIMENTAL: This method only supports bayer filters for the time
        being. It will be incorrect when used with other types of sensors.

        Get the color filter array for the camera sensor.

        Returns:
            list: 2D array representing the color format array pattern.
                  For example, the typical 'RGGB' pattern of abayer sensor
                  would be of the format::

                      [
                          ['R', 'G'],
                          ['G', 'B'],
                      ]

        """

        # TODO: don't assume 2x2 bayer sensor
        return [[self.color(0, 0), self.color(0, 1)],
                [self.color(1, 0), self.color(1, 1)]]

    def raw_image(self, include_margin=False):
        """
        Get the bayer data for an image if it exists.

        Args:
            include_margin (bool): Include margin with calibration pixels.

        Returns:
            list: 2D array of bayer pixel data structured as a list of rows,
                  or None if there is no bayer data.
                  For example, if the color format is `RGGB`, the array
                  would be of the format::

                      [
                          [R, G, R, G, ...],
                          [G, B, G, B, ...],
                          [R, G, R, G, ...],
                          ...
                      ]

        """
        self.unpack()
        image = self.data.contents.rawdata.raw_image

        if not bool(image):
            return []

        sizes = self.data.contents.sizes

        # TODO: handle this
        if sizes.pixel_aspect != 1:
            warnings.warn(
                "The pixel aspect is not unity, it is: " +
                str(sizes.pixel_aspect)
            )

        # TODO: handle this
        if sizes.flip != 0:
            warnings.warn(
                "The image is flipped."
            )

        pitch = sizes.raw_width

        if include_margin:
            first = 0
            width = sizes.raw_width
            height = sizes.raw_height
        else:
            first = sizes.raw_width * sizes.top_margin + sizes.left_margin
            width = sizes.width
            height = sizes.height

        data_pointer = ctypes.cast(
            image,
            ctypes.POINTER(ctypes.c_ushort)
        )

        data = []

        for y in range(height):
            row = []
            for x in range(width):
                row.append(data_pointer[first + y * pitch + x])
            data.append(row)

        return data

    def bayer_data(self, include_margin=False):
        """
        Get the bayer data and color_description for an image.

        Returns:
            tuple: Tuple of bayer data and color filter array. This is a
                   convenience method to return `rawkit.raw.Raw.raw_image`
                   and `rawkit.raw.Raw.color_filter_array` as a single tuple.
        """
        return self.raw_image(include_margin), self.color_filter_array

    def to_buffer(self):
        """
        Convert the image to an RGB buffer.

        Returns:
            bytearray: RGB data of the image.
        """
        self.unpack()
        self.process()

        status = ctypes.c_int(0)
        processed_image = self.libraw.libraw_dcraw_make_mem_image(
            self.data,
            ctypes.cast(
                ctypes.addressof(status),
                ctypes.POINTER(ctypes.c_int),
            ),
        )
        raise_if_error(status.value)
        data_pointer = ctypes.cast(
            processed_image.contents.data,
            ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size)
        )
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    def thumbnail_to_buffer(self):
        """
        Convert the thumbnail data as an RGB buffer.

        Returns:
            bytearray: RGB data of the thumbnail.
        """
        self.unpack_thumb()

        status = ctypes.c_int(0)
        processed_image = self.libraw.libraw_dcraw_make_mem_thumb(
            self.data,
            ctypes.cast(
                ctypes.addressof(status),
                ctypes.POINTER(ctypes.c_int),
            ),
        )
        raise_if_error(status.value)
        data_pointer = ctypes.cast(
            processed_image.contents.data,
            ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size)
        )
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    @property
    def metadata(self):
        """
        Common metadata for the photo

        Returns:
            rawkit.metadata.Metadata: A metadata object.
        """
        return Metadata(
            aperture=self.data.contents.other.aperture,
            timestamp=self.data.contents.other.timestamp,
            shutter=self.data.contents.other.shutter,
            flash=bool(self.data.contents.color.flash_used),
            focal_length=self.data.contents.other.focal_len,
            height=self.data.contents.sizes.height,
            iso=self.data.contents.other.iso_speed,
            make=self.data.contents.idata.make,
            model=self.data.contents.idata.model,
            orientation=self.data.contents.sizes.flip,
            width=self.data.contents.sizes.width,
        )
Esempio n. 12
0
class Raw(object):
    """
    Represents a raw file (of any format) and exposes development options to
    the user.

    For example, the basic workflow (open a file, process the file, save the
    file) looks like this::

        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')

    Args:
        filename (str): The name of a raw file to load.

    Returns:
        Raw: A raw object.

    Raises:
        rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        libraw.errors.FileUnsupported: If the specified file is not a supported
                                       raw type.
        libraw.errors.InsufficientMemory: If we run out of memory while loading
                                          the raw file.
        IOError: If the file does not exist, or cannot be opened (eg. incorrect
                 permissions).
    """
    def __init__(self, filename=None):
        """Initializes a new Raw object."""
        if filename is None:
            raise NoFileSpecified()
        self.libraw = LibRaw()
        self.data = self.libraw.libraw_init(0)
        self.libraw.libraw_open_file(self.data, filename.encode('ascii'))

        self.options = Options()

        self.image_unpacked = False
        self.thumb_unpacked = False

    def __enter__(self):
        """Return a Raw object for use in context managers."""
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Clean up after ourselves when leaving the context manager."""
        self.close()

    def close(self):
        """Free the underlying raw representation."""
        self.libraw.libraw_close(self.data)

    def unpack(self):
        """Unpack the raw data."""
        if not self.image_unpacked:
            self.libraw.libraw_unpack(self.data)
            self.image_unpacked = True

    def unpack_thumb(self):
        """
        Unpack the thumbnail data.
        Raises:
            libraw.errors.NoThumbnail: If the raw file does not contain a
                                       thumbnail.
            libraw.errors.UnsupportedThumbnail: If the thumbnail format is
                                                unsupported.
        """
        if not self.thumb_unpacked:
            self.libraw.libraw_unpack_thumb(self.data)
            self.thumb_unpacked = True

    def process(self):
        """
        Process the raw data based on ``self.options``.

        Raises:
            libraw.errors.DataError: If invalid or corrupt data is encountered
                                     in the data struct.
            libraw.errors.BadCrop: If the image has been cropped poorly (eg.
                                   the edges are outside of the image bounds,
                                   or the crop box coordinates don't make
                                   sense).
        """
        self.options._map_to_libraw_params(self.data.contents.params)
        self.libraw.libraw_dcraw_process(self.data)

    def save(self, filename=None, filetype=None):
        """
        Save the image data as a new PPM or TIFF image.

        Args:
            filename (str): The name of an image file to save.
            filetype (output_file_types): The type of file to output. By
                                          default, guess based on the filename,
                                          falling back to PPM.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
            rawkit.errors.InvalidFileType: If `filetype` is not None or in
                                           :class:`output_file_types`.
        """
        if filename is None:
            raise NoFileSpecified()

        if filetype is None:
            ext = os.path.splitext(filename)[-1].lower()[1:]
            filetype = ext or output_file_types.ppm

        if filetype not in output_file_types:
            raise InvalidFileType(
                "Output filetype must be in raw.output_file_types")

        self.data.contents.params.output_tiff = (
            filetype == output_file_types.tiff)

        self.unpack()
        self.process()

        self.libraw.libraw_dcraw_ppm_tiff_writer(self.data,
                                                 filename.encode('ascii'))

    def save_thumb(self, filename=None):
        """
        Save the thumbnail data.

        Args:
            filename (str): The name of an image file to save.

        Raises:
            rawkit.errors.NoFileSpecified: If `filename` is ``None``.
        """
        if filename is None:
            raise NoFileSpecified()

        self.unpack_thumb()

        self.libraw.libraw_dcraw_thumb_writer(self.data,
                                              filename.encode('ascii'))

    def to_buffer(self):
        """
        Convert the image to an RGB buffer.

        Returns:
            bytearray: RGB data of the image.
        """
        self.unpack()
        self.process()

        status = ctypes.c_int(0)
        processed_image = self.libraw.libraw_dcraw_make_mem_image(
            self.data,
            ctypes.cast(
                ctypes.addressof(status),
                ctypes.POINTER(ctypes.c_int),
            ),
        )
        raise_if_error(status.value)
        data_pointer = ctypes.cast(
            processed_image.contents.data,
            ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size))
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    def thumbnail_to_buffer(self):
        """
        Convert the thumbnail data as an RGB buffer.

        Returns:
            bytearray: RGB data of the thumbnail.
        """
        self.unpack_thumb()

        status = ctypes.c_int(0)
        processed_image = self.libraw.libraw_dcraw_make_mem_thumb(
            self.data,
            ctypes.cast(
                ctypes.addressof(status),
                ctypes.POINTER(ctypes.c_int),
            ),
        )
        raise_if_error(status.value)
        data_pointer = ctypes.cast(
            processed_image.contents.data,
            ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size))
        data = bytearray(data_pointer.contents)
        self.libraw.libraw_dcraw_clear_mem(processed_image)

        return data

    @property
    def metadata(self):
        """
        Common metadata for the photo

        Returns:
            rawkit.metadata.Metadata: A metadata object.
        """
        return Metadata(
            aperture=self.data.contents.other.aperture,
            timestamp=self.data.contents.other.timestamp,
            shutter=self.data.contents.other.shutter,
            flash=bool(self.data.contents.color.flash_used),
            focal_length=self.data.contents.other.focal_len,
            height=self.data.contents.sizes.height,
            iso=self.data.contents.other.iso_speed,
            make=self.data.contents.idata.make,
            model=self.data.contents.idata.model,
            orientation=self.data.contents.sizes.flip,
            width=self.data.contents.sizes.width,
        )
Esempio n. 13
0
def libraw():
    with mock.patch.object(LibRaw, '__init__', mock.Mock(return_value=None)):
        yield LibRaw()