• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / Whisker-Analysis / 12795636512

15 Jan 2025 07:30PM UTC coverage: 78.293% (+0.05%) from 78.242%
12795636512

push

github

paulmthompson
update loader files

59 of 82 new or added lines in 4 files covered. (71.95%)

1266 of 1617 relevant lines covered (78.29%)

65132697.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

34.74
/src/WhiskerTracking/whiskertracker_pybind.cpp
1
#include "whiskertracker.hpp"
2
#include "loaders.hpp"
3
#include "Geometry/lines.hpp"
4
#include "Geometry/points.hpp"
5

6
#include <omp.h>
7
#include <pybind11/pybind11.h>
8
#include <pybind11/numpy.h>
9
#include <pybind11/stl.h>
10

11
#include <chrono>
12
#include <cstddef>
13
#include <iostream>
14

15
namespace py = pybind11;
16

17
whisker::Line2D convert_np_array_to_line2d(const py::array_t<float>& array) {
×
18
    whisker::Line2D line;
×
19
    auto buf = array.request();
×
20
    if (buf.ndim != 2 || buf.shape[1] != 2) {
×
21
        throw std::runtime_error("Input should be a 2D numpy array with shape (n, 2)");
×
22
    }
23

24
    float* ptr = static_cast<float*>(buf.ptr);
×
25
    for (std::size_t i = 0; i < buf.shape[0]; ++i) {
×
26
        line.push_back(whisker::Point2D<float>{ptr[i * 2], ptr[i * 2 + 1]});
×
27
    }
28

29
    return line;
×
30
};
×
31

32
PYBIND11_MODULE(whiskertracker, m) {
2✔
33
    py::class_<whisker::WhiskerTracker>(m, "WhiskerTracker")
1✔
34
            .def(py::init<>())
1✔
35
            .def("setWhiskerLengthThreshold", &whisker::WhiskerTracker::setWhiskerLengthThreshold)
1✔
36
            .def("getWhiskerPadRadius", &whisker::WhiskerTracker::getWhiskerPadRadius)
1✔
37
            .def("setWhiskerPadRadius", &whisker::WhiskerTracker::setWhiskerPadRadius)
1✔
38
            .def("getWhiskerPad", [](whisker::WhiskerTracker& wt) {
1✔
39
                auto pad = wt.getWhiskerPad();
×
40
                return py::make_tuple(pad.x, pad.y);
×
41
            })
42
            .def("setWhiskerPad", &whisker::WhiskerTracker::setWhiskerPad)
1✔
43
            .def("trace", [](whisker::WhiskerTracker &wt, py::array_t<uint8_t, py::array::c_style | py::array::forcecast> image, int image_height, int image_width) {
2✔
44
                py::buffer_info info = image.request();
1✔
45

46
                if (info.ndim != 1) {
1✔
47
                    throw std::runtime_error("Number of dimensions must be one");
×
48
                }
49

50
                if (info.format != py::format_descriptor<uint8_t>::format()) {
1✔
51
                    throw std::runtime_error("Image must be of type uint8_t");
×
52
                }
53

54
                if (info.size != image_height * image_width) {
1✔
55
                    throw std::runtime_error("Image size must match image height and width");
×
56
                }
57

58
                auto ptr = static_cast<uint8_t *>(info.ptr);
1✔
59
                std::vector<uint8_t> image_data(ptr, ptr + info.size);
1✔
60
                auto whiskers = wt.trace(image_data, image_height, image_width);
1✔
61

62
                std::vector<py::array_t<float>> whiskers_py;
1✔
63
                for (const auto &line : whiskers) {
6✔
64
                    std::vector<float> line_py;
5✔
65
                    for (const auto &point : line) {
1,832✔
66
                        line_py.push_back(point.x);
1,827✔
67
                        line_py.push_back(point.y);
1,827✔
68
                    }
69
                    whiskers_py.push_back(py::array(line_py.size(), line_py.data()));
5✔
70
                }
5✔
71
                return whiskers_py;
2✔
72
            })
1✔
73
            .def("trace_multiple_images", [](whisker::WhiskerTracker &wt, py::list images_list, int image_height, int image_width) {
1✔
74
                std::vector<std::vector<uint8_t>> images_cpp;
×
75
                for (py::handle image_py : images_list) {
×
76
                    py::array_t<uint8_t, py::array::c_style | py::array::forcecast> image = image_py.cast<py::array_t<uint8_t>>();
×
77
                    py::buffer_info info = image.request();
×
78
                    if (info.ndim != 1 || info.format != py::format_descriptor<uint8_t>::format() || info.size != image_height * image_width) {
×
79
                        throw std::runtime_error("Each image must be a 1D uint8_t array with size matching image_height * image_width");
×
80
                    }
81
                    auto ptr = static_cast<uint8_t *>(info.ptr);
×
82
                    images_cpp.push_back(std::vector<uint8_t>(ptr, ptr + info.size));
×
83
                }
×
84
                auto whiskers = wt.trace_multiple_images(images_cpp, image_height, image_width);
×
85

86
                py::list whiskers_py;
×
87
                for (const auto &image_whiskers : whiskers) {
×
88
                    py::list image_whiskers_py;
×
89
                    for (const auto &line : image_whiskers) {
×
90
                        py::list line_py;
×
91
                        for (const auto &point : line) {
×
92
                            line_py.append(py::make_tuple(point.x, point.y));
×
93
                        }
94
                        image_whiskers_py.append(line_py);
×
95
                    }
×
96
                    whiskers_py.append(image_whiskers_py);
×
97
                }
×
98
                return whiskers_py;
×
99
            })
×
100
            .def("setFaceMask", [](whisker::WhiskerTracker &wt, py::list mask) {
1✔
101
                std::vector<whisker::Point2D<float>> mask_cpp;
×
102
                for (py::handle obj : mask) {
×
103
                    py::tuple t = obj.cast<py::tuple>();
×
104
                    mask_cpp.push_back(whisker::Point2D<float>{t[0].cast<float>(), t[1].cast<float>()});
×
105
                }
×
106
                wt.setFaceMask(mask_cpp);
×
107
            })
×
108
            .def("load_and_align_whiskers", [](whisker::WhiskerTracker &wt, const std::string &filepath) {
1✔
NEW
109
                auto data = whisker::load_line_csv(filepath, true);
×
NEW
110
                std::cout << "Loaded " << data.size() << " frames from " << filepath << std::endl;
×
111

NEW
112
                auto t1 = std::chrono::high_resolution_clock::now();
×
NEW
113
                const auto whisker_pad = wt.getWhiskerPad();
×
114
                
NEW
115
                for (auto & [frame_num, lines] : data) {
×
116
                    for (auto & line : lines) {
×
NEW
117
                        whisker::align_whisker_to_follicle(line, whisker_pad);
×
118
                    }
119
                }
NEW
120
                auto t2 = std::chrono::high_resolution_clock::now();
×
121

NEW
122
                auto duration = std::chrono::duration<double>(t2 - t1).count();
×
NEW
123
                std::cout << "Aligned " << data.size() << " frames in " << duration << "s" << std::endl;
×
124

125
                std::string output_filepath = filepath.substr(0, filepath.find_last_of('.')) + "_aligned.csv";
×
NEW
126
                whisker::save_lines_csv(data,output_filepath);
×
127
            })
×
128
            .def("frechet_distance", [](whisker::WhiskerTracker &wt, const py::array_t<float>& whisker1, const py::array_t<float>& whisker2) {
1✔
129
                
130
                whisker::Line2D line1 = convert_np_array_to_line2d(whisker1);
×
131
                whisker::Line2D line2 = convert_np_array_to_line2d(whisker2);
×
132
                return whisker::fast_discrete_frechet_matrix(line1, line2);
×
133
            });
×
134
    m.def("get_max_threads", &omp_get_max_threads);
1✔
135
    m.def("set_num_threads", &omp_set_num_threads);
1✔
136
};
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc