Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for user defined color palettes #8

Merged
14 commits merged into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Highlights
* Common set of APIs for both Mosaic and Micro Core cameras
* Robust error handling and logging interface
* Numerous frame output formats
* Example applications to learn and get started
* Example applications to learn and get started
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,27 @@ $LD_LIBRARY_PATH/

---

The minimum SDK runtime version for each stable release of the Python language bindings is shown below:

<table>
<thead>
<tr>
<th>seekcamera-python</th>
<th>Minimum SDK runtime version</th>
</tr>
</thead>
<tbody>
<tr>
<td>1.0.0</td>
<td>4.0.0</td>
</tr>
<tr>
<td>1.1.0</td>
<td>4.1.0</td>
</tr>
</tbody>
</table>

## License :balance_scale:

The project is licensed under the Apache 2.0 License.
Expand Down
1 change: 1 addition & 0 deletions seekcamera/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
SeekCameraFirmwareVersion,
SeekCameraAppResourcesRegion,
SeekCameraColorPalette,
SeekCameraColorPaletteData,
SeekCameraAGCMode,
SeekCameraShutterMode,
SeekCameraTemperatureUnit,
Expand Down
25 changes: 24 additions & 1 deletion seekcamera/_clib.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def configure_dll():
return

min_runtime_version_major = 4
min_runtime_version_minor = 0
min_runtime_version_minor = 1
min_runtime_version_patch = 0

if os.name == "nt":
Expand Down Expand Up @@ -312,6 +312,14 @@ def assert_runtime_version_met(version, min_version, name):
_cdll.seekcamera_set_color_palette.restype = ctypes.c_int32
_cdll.seekcamera_set_color_palette.argtypes = [ctypes.c_void_p, ctypes.c_int32]

# seekcamera_set_color_palette_data
_cdll.seekcamera_set_color_palette_data.restype = ctypes.c_int32
_cdll.seekcamera_set_color_palette_data.argtypes = [
ctypes.c_void_p,
ctypes.c_int32,
ctypes.POINTER(CSeekCameraColorPaletteDataEntry * 256),
]

# seekcamera_get_agc_mode
_cdll.seekcamera_get_agc_mode.restype = ctypes.c_int32
_cdll.seekcamera_get_agc_mode.argtypes = [
Expand Down Expand Up @@ -459,6 +467,15 @@ def assert_runtime_version_met(version, min_version, name):
_cdll.seekframe_get_header.argtypes = [ctypes.c_void_p]


class CSeekCameraColorPaletteDataEntry(ctypes.Structure):
_fields_ = [
("b", ctypes.c_uint8),
("g", ctypes.c_uint8),
("r", ctypes.c_uint8),
("a", ctypes.c_uint8),
]


class CSeekCameraFrameHeader(ctypes.Structure):
_pack_ = 1
_fields_ = [
Expand Down Expand Up @@ -792,6 +809,12 @@ def cseekcamera_set_color_palette(camera, palette):
return _cdll.seekcamera_set_color_palette(camera.pointer, ctypes.c_int32(palette))


def cseekcamera_set_color_palette_data(camera, palette, palette_data):
return _cdll.seekcamera_set_color_palette_data(
camera.pointer, ctypes.c_int32(palette), ctypes.byref(palette_data)
)


def cseekcamera_get_agc_mode(camera):
mode = ctypes.c_int32()
status = _cdll.seekcamera_get_agc_mode(camera.pointer, ctypes.byref(mode))
Expand Down
163 changes: 163 additions & 0 deletions seekcamera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ class SeekCameraColorPalette(IntEnum):
AMBER = 6
HI = 7
GREEN = 8
USER_0 = 9
USER_1 = 10
USER_2 = 11
USER_3 = 12
USER_4 = 13

def __str__(self):
return self.name
Expand All @@ -174,6 +179,120 @@ def __repr__(self):
return "SeekCameraColorPalette({})".format(self.value)


class SeekCameraColorPaletteData(object):
"""Collection of color values used to colorize a thermal image.

Each entry represents a component of a pixel. The values should be in ascending
order going from coldest to hottest temperature. It has 256 distinct entries.
Each entry is a tuple of color channels ordered as (b, g, r, a).

Examples
--------
Creating a new color palette data object with default data (all zeros).
>>> palette_data = SeekCameraColorPaletteData()

Iterating the values of a color palette data object.
>>> for index, value in enumerate(palette_data): print(value)

Slicing a color palette object.
>>> palette_data[1:4] = [(255, 0, 0, 0), (0, 255, 0, 0), (0, 0, 255, 0)]
>>> print(palette_data[1:4])
"""

def __init__(self, data=None):
"""Creates a color palette data object.

Parameters
----------
data: Optional[Iterable[Tuple[int, int, int, int]]]
Collection of tuples that specify the color values for the color palette.
It should have length 256; the tuples should be specified in (b, g, r, a)
order.
"""
if data is None:
data = [(0, 0, 0, 0)] * 256

self._data = data
self._data_iter = 0

def __repr__(self):
return "SeekCameraColorPaletteData({})".format(self._data)

def __iter__(self):
"""Iterates through the color values in the color palette data.

Returns
-------
SeekCameraColorPaletteData
Reference to the color palette data object to iterate.
"""
self._data_iter = 0
return self

def __next__(self):
"""Gets the next color value in the current iteration.

Returns
-------
Tuple[int, int, int, int]
The next color palette value in (b, g, r, a) order.

Raises
------
StopIteration
If at the end of the collection.
"""
if self._data_iter >= len(self):
raise StopIteration

result = self._data[self._data_iter]
self._data_iter += 1
return result

def __getitem__(self, key):
"""Gets an color value or slice of color values.

Parameters
----------
key: Union[slice, int]
Either a slice or a single index used to get the color values.

Returns
-------
Union[List[Tuple[int, int, int, int]], Tuple[int, int, int, int]]
Either a slice of color values or a single color value.
"""
if isinstance(key, slice):
return self._data[key.start : key.stop : key.step]
else:
return self._data[key]

def __setitem__(self, key, data):
"""Sets an color value or slice of color values.

Parameters
----------
key: Union[slice, int]
Either a slice or a single index used to set the color values.
data: Union[List[Tuple[int, int, int, int]], Tuple[int, int, int, int]]
Either a slice of color values or a single color value.
"""
if isinstance(key, slice):
self._data[key.start : key.stop : key.step] = data
else:
self._data[key] = data

def __len__(self):
"""Gets the number of color values in the color palette data.

Returns
-------
int
Number of color values in the color palette data.
"""
return len(self._data)


class SeekCameraAGCMode(IntEnum):
"""Types of automated gain correction (AGC) modes.

Expand Down Expand Up @@ -558,6 +677,8 @@ class SeekCamera(object):
Registers a user frame available callback function with the camera.
shutter_trigger()
Triggers the camera to shutter as soon as possible.
set_color_palette_data(palette, palette_data)
Sets the color palette data for a particular color palette.
set_filter_state(filter_type, filter_state)
Sets the state of an image processing filter.
get_filter_state(filter_type)
Expand Down Expand Up @@ -1303,6 +1424,48 @@ def thermography_offset(self, offset):
if is_error(status):
raise error_from_status(status)

def set_color_palette_data(self, palette, palette_data):
"""Sets the color palette data for a particular color palette.

Parameters
----------
palette: SeekCameraColorPalette
Enumerated type corresponding to the color palette for which to set
the data.
palette_data: SeekCameraColorPalettteData
Color values used to colorize the thermal image.

Raises
------
SeekCameraInvalidParameterError
1) If the palette is not of type SeekCameraColorPalette.
2) If the plaette data is not of type SeekCameraColorPaletteData.
SeekCameraError
If an error occurs.
"""
if not isinstance(palette, SeekCameraColorPalette):
raise SeekCameraInvalidParameterError

if not isinstance(palette_data, SeekCameraColorPaletteData):
raise SeekCameraInvalidParameterError

data = (_clib.CSeekCameraColorPaletteDataEntry * len(palette_data))()

for index, value in enumerate(palette_data):
(b, g, r, a) = value

data[index] = (
ctypes.c_uint8(b),
ctypes.c_uint8(g),
ctypes.c_uint8(r),
ctypes.c_uint8(a),
)

status = _clib.cseekcamera_set_color_palette_data(self._camera, palette, data)

if is_error(status):
raise error_from_status(status)

def set_filter_state(self, filter_type, filter_state):
"""Sets the state of an image processing filter.

Expand Down