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

Method for exposing the raw data #104

Closed
wants to merge 9 commits into from
174 changes: 174 additions & 0 deletions rawkit/raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import random
import string
import tempfile
import warnings

from collections import namedtuple
from libraw.bindings import LibRaw
Expand Down Expand Up @@ -175,6 +176,179 @@ def save_thumb(self, filename=None):
self.libraw.libraw_dcraw_thumb_writer(
self.data, filename.encode('ascii'))

def get_4_col_raw(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing I'm thinking (though this doesn't have to be done right now by any means, I just wanted to comment while I was thinking about it so that it was documented somewhere) is that the public facing API method for this should just work for all types of raws (regardless of how many channels there are). @campaul and I will probably sit down sometime this weekend and figure out a more unified API (including return values and types) for all methods that return image data like this. Will advise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi.

It is easy to change this to the other case (with only 3 colours), as the data then is in color3_image and not in color4_image. Though before I modify this I would like to know what the return types should be. In my mind we should return something that is 2D, as this removes the need to pass along height and width.

"""
EXPERIMENTAL

Read the 4 colour raw data, this does NOT include the data outside of
the frame (calibration data, etc)

Returns None if the input is not a 4 colour image

The returned array is (given colour RGGB)
---------
R B R B ...
G B G B ...
R B R B ...
. . . . .
. . . . .
. . . . .
--------


Returns:
list of lists: 4 colour data of the image in unit16.
(height x width)
str : colour channel description (ie. RGGB, RGBG)
"""
# Unpack the data, so that rawdata is populated
self.unpack()
rawdata = self.data.contents.rawdata
sizes = self.data.contents.sizes

# Return None if the image isn't 4 colour, which will happen for some
# cameras

try:
rawdata.color4_image.contents
except ValueError:
return None

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

if sizes.flip != 0:
raise NotImplemented("Does not support rotations in the image")

# Get colour cdesc
cdesc = self.data.contents.idata.cdesc

# Make pointer to data
data_pointer = ctypes.cast(
rawdata.color4_image.contents,
ctypes.POINTER(ctypes.c_ushort)
)

iwidth = sizes.width

# Get the data
data = self.__get_raw_data(sizes, data_pointer, iwidth)
# Return data and colour descriptor
return data, cdesc

def get_3_col_raw(self):
"""
EXPERIMENTAL

Read the 3 colour raw data, this does NOT include the data outside of
the frame (calibration data, etc)

Returns None if the input is not a 3 colour image
NB: most raw data is 4 colour, use get_4_col_raw for them

The returned array is (given colour RGBG)
---------
R G B 0 R ...
R G B 0 R ...
R G B 0 R ...
. . . . .
. . . . .
. . . . .
--------

Returns:
list of lists: 3 colour data of the image in unit16.
( height x 4 width)
str : colour channel description (ie. RGBG)


"""
# Unpack the data, so that rawdata is populated
self.unpack()
rawdata = self.data.contents.rawdata
sizes = self.data.contents.sizes

# Return None if the image isn't 3 colour, which will happen for some
# cameras and for most raw images

try:
rawdata.color3_image.contents
except ValueError:
return None

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

if sizes.flip != 0:
raise NotImplemented("Does not support rotations in the image")

# Make pointer to data
data_pointer = ctypes.cast(
rawdata.color3_image.contents,
ctypes.POINTER(ctypes.c_ushort)
)
# Get colour cdesc
cdesc = self.data.contents.idata.cdesc
num_col = len(cdesc)
# The width needs to be num_col (4) times this due to the way the image
# is saved
iwidth = sizes.width * num_col

# Get the data
data = self.__get_raw_data(sizes, data_pointer, iwidth)

# Return data and colour descriptor
return data, cdesc

def __get_raw_data(self, sizes, data_pointer, iwidth):
"""
Internal method for getting raw data. Should not be called outside

Parameters
----------
sizes : Sizes variable

data_pointer : c-pointer to the data

iwidth : image width


Returns
----------
data : 2D matrix of the raw data
"""

# Get raw image size
raw_width = sizes.raw_width
# raw_height = sizes.raw_height

# Get margins
left_margin = sizes.left_margin
top_margin = sizes.top_margin

# Get row pitch (returned in bytes, therefore we divide it in two
# since we are using 16 bit (2 bytes) per pixel

pitch = int(sizes.raw_pitch / 2)

# Get frame height size
iheight = int(sizes.height)

# Compute first
first = raw_width * top_margin + left_margin

# make 2D list
data = [[0 for i in range(iwidth)] for j in range(iheight)]

for ii in range(iheight):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change ii and jj to y and x respectively so we're working with Cartesian coordinates?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't actually coordinates on the image though since there are multiple indexes per pixel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They will be coordinates, though you have to interpolate the image first, as in that loop they change between different colour channels.

for jj in range(iwidth):
data[ii][jj] = data_pointer[first + ii * pitch + jj]

# Return data and colour descriptor
return data

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