Skip to content
Open
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
15 changes: 15 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 2.6)
project(LIBSEEK)
#find_package(libusb REQUIRED)
find_path(LIBUSB_INCLUDE_PATH libusb.h /usr/include/libusb-1.0/)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -fpic --std=c++11")

add_library(seek STATIC seek.cpp)
add_executable(seek-test seek-test.cpp)
add_executable(seek-test-calib seek-test-calib.cpp)

target_include_directories(seek PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${LIBUSB_INCLUDE_PATH})

target_link_libraries(seek LINK_PUBLIC usb-1.0)
target_link_libraries(seek-test LINK_PUBLIC seek)
target_link_libraries(seek-test-calib LINK_PUBLIC seek)
20 changes: 20 additions & 0 deletions libseek_python.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import pylibseek as lseek


class ThermalCamera(object):
def __init__(self, k=None, c=None):
if k: k = float(k)
if c: c = float(c)
lseek.init(k, c)
(width, height) = lseek.get_dimensions()
self.width = width
self.height = height

def get_frame(self, absolute=True):
return lseek.get_frame(absolute)

def get_flat_frame(self, absolute=True):
return lseek.get_flat_frame(absolute)

def __del__(self):
lseek.deinit()
191 changes: 191 additions & 0 deletions pylibseek.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
//
// Created by Dmitry Sorokin on 2/15/16.
//

#include <Python.h>
#include "seek.hpp"

static LibSeek::Imager *imgr;
static LibSeek::Frame *frm;
static bool initialized = false;
static unsigned int length = 0;
static float *absolute_data;
static float _K = 0.02; // Experimental values, looks realistic
static float _C = -618;

// TODO: get_frame absolute should accept convertation coeff?

void
parse_coef_args(PyObject *args) {
PyObject *p1 = nullptr;
PyObject *p2 = nullptr;
// TODO: can be a memleak here?
PyArg_UnpackTuple(args, "ref", 0, 2, &p1, &p2);
if (p1 != nullptr && PyFloat_Check(p1)) {
_K = PyFloat_AsDouble(p1);
}
if (p2 != nullptr && PyFloat_Check(p2)) {
_C = PyFloat_AsDouble(p2);
}
}

bool
is_absolute(PyObject *args) {
PyObject *p1 = nullptr;
// TODO: can be a memleak here?
PyArg_UnpackTuple(args, "ref", 0, 1, &p1);
if (p1 != nullptr && PyBool_Check(p1)) {
return p1 == Py_True;
}
return false;
}

static PyObject *
init(PyObject *self, PyObject *args) {
if (initialized) {
return nullptr;
}
parse_coef_args(args);
// TODO: throw exceptions
imgr = new LibSeek::Imager();
frm = new LibSeek::Frame();
imgr->init();
imgr->frame_init(*frm);
length = frm->width()*frm->height();
absolute_data = new float[length];
initialized = true;
return Py_None;
}

static bool check_init() {
if (initialized)
return true;
init(nullptr, nullptr);
return initialized;
}

static PyObject *
deinit(PyObject *self, PyObject *args) {
check_init(); // TODO: throw exception if unitialized
delete frm;
delete imgr;
delete absolute_data;
return Py_None;
}

const uint16_t *
_get_frame_data() {
imgr->frame_acquire(*frm);
return frm->data();
}

const float *
_convert_to_absolute(const uint16_t *data) {
for (unsigned int i = 0; i < length; i++) {
absolute_data[i] = data[i]*_K + _C;
}
return absolute_data;
}

static PyObject *
_value_builder(const float value) {
return PyFloat_FromDouble(value);
}

static PyObject *
_value_builder(const uint16_t value) {
return PyLong_FromLong(value);
}

template <typename T>
static PyObject *
_construct_flat_frame (T *data) {
PyObject *res = PyList_New(length);

for (unsigned int i = 0; i < length; i++) {
PyList_SetItem(res, i, _value_builder(data[i]));
}
return res;
}

template <typename T>
static PyObject *
_construct_frame (T *data) {
PyObject *res = PyList_New(frm->height());
unsigned int offset = 0;

for (unsigned int i = 0; i < frm->height(); i++) {
PyObject *row = PyList_New(frm->width());
PyList_SetItem(res, i, row);
for (unsigned int j = 0; j < frm->width(); j++) {
PyList_SetItem(row, j, _value_builder(data[offset]));
offset++;
}
}
return res;
}

static PyObject *
get_frame_flat(PyObject *self, PyObject *args) {
check_init();

auto data = _get_frame_data();
return is_absolute(args) ?
_construct_flat_frame(_convert_to_absolute(data)) :
_construct_flat_frame(data);
}

static PyObject *
get_frame(PyObject *self, PyObject *args) {
check_init();

auto data = _get_frame_data();
return is_absolute(args) ?
_construct_frame(_convert_to_absolute(data)) :
_construct_frame(data);
}

static PyObject *
get_dimensions(PyObject *self, PyObject *args) {
check_init();
PyObject *res = PyTuple_New(2);
PyTuple_SetItem(res, 0, PyLong_FromLong(frm->width()));
PyTuple_SetItem(res, 1, PyLong_FromLong(frm->height()));
return res;
}


static PyMethodDef libseekMethods[] = {
{"init", init, METH_VARARGS,
"Initialize device."},
{"deinit", deinit, METH_NOARGS,
"Deinitialize device."},
{"get_frame_flat", get_frame, METH_VARARGS,
"Get flattened frame from device."},
{"get_frame", get_frame, METH_VARARGS,
"Get frame from device."},
{"get_dimensions", get_dimensions, METH_NOARGS,
"Get frame width and height."},
{NULL, NULL, 0, NULL} /* Sentinel */
};

#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef pylibseekmodule = {
PyModuleDef_HEAD_INIT,
"libseek_python", /* name of module */
NULL, /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module,
or -1 if the module keeps state in global variables. */
libseekMethods
};

PyMODINIT_FUNC
PyInit_pylibseek(void) {
return PyModule_Create(&pylibseekmodule);
}
#else
PyMODINIT_FUNC
initpylibseek(void) {
PyObject *module = Py_InitModule("pylibseek", libseekMethods);
}
#endif
15 changes: 15 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from distutils.core import setup, Extension

pylibseek = Extension('pylibseek',
sources = ["pylibseek.cpp"],
extra_compile_args = ["--std=c++11"],
include_dirs = ["./"],
library_dirs = ["./build", "./cmake_build"],
libraries = ["seek", "usb-1.0"],
#define_macros = [("DEBUG", 1)]
)

setup (name = 'LibSeek Python interface',
version = '1.0',
description = 'Thermal Imager interface module',
ext_modules = [pylibseek])
7 changes: 7 additions & 0 deletions test_seekmod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python3
import libseek_python as l

l.lseek.init(0.3, -500)
f = l.lseek.get_frame(True)
l.lseek.deinit()
print(f)
36 changes: 36 additions & 0 deletions view-absolute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.widgets import Slider

import libseek_python as l

mintemp, maxtemp = 0, 100

c = l.ThermalCamera()
f = c.get_frame()

fig = plt.figure()
image = plt.imshow(f, cmap="gnuplot2", animated=True)
plt.clim(mintemp, maxtemp)

ax_maxtemp = plt.axes([0.2, 0.01, 0.65, 0.03])
ax_mintemp = plt.axes([0.2, 0.05, 0.65, 0.03])
s_maxtemp = Slider(ax_maxtemp, 'Maximum', 30, 150, valinit=maxtemp)
s_mintemp = Slider(ax_mintemp, 'Minumum', 0, 40, valinit=mintemp)


def controls_update(_):
plt.clim(s_mintemp.val, s_maxtemp.val)


def anim(_):
f = c.get_frame()
image.set_array(f)
return image,

s_maxtemp.on_changed(controls_update)
s_mintemp.on_changed(controls_update)
ani = animation.FuncAnimation(fig, anim, interval=100)
plt.show()

2 changes: 1 addition & 1 deletion wscript
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def configure(conf):
conf.load('compiler_cxx')

if conf.env.CXX_NAME in ('gcc', 'clang'):
conf.env.CXXFLAGS += [ '-std=c++11' ]
conf.env.CXXFLAGS += [ '-std=c++11', '-fpic' ]

conf.check_cfg(package='libusb-1.0', args='--cflags --libs',
uselib_store="LIBUSB")
Expand Down