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

paulmthompson / Whisker-Analysis / 12773794279

14 Jan 2025 06:13PM UTC coverage: 78.242%. Remained the same
12773794279

push

github

paulmthompson
benchmark workflow should be able toupload to pages

1237 of 1581 relevant lines covered (78.24%)

50346077.51 hits per line

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

36.67
/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 <cstddef>
12
#include <iostream>
13

14
namespace py = pybind11;
15

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

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

28
    return line;
×
29
};
×
30

31
PYBIND11_MODULE(whiskertracker, m) {
2✔
32
    py::class_<whisker::WhiskerTracker>(m, "WhiskerTracker")
1✔
33
            .def(py::init<>())
1✔
34
            .def("setWhiskerLengthThreshold", &whisker::WhiskerTracker::setWhiskerLengthThreshold)
1✔
35
            .def("getWhiskerPadRadius", &whisker::WhiskerTracker::getWhiskerPadRadius)
1✔
36
            .def("setWhiskerPadRadius", &whisker::WhiskerTracker::setWhiskerPadRadius)
1✔
37
            .def("getWhiskerPad", [](whisker::WhiskerTracker& wt) {
1✔
38
                auto pad = wt.getWhiskerPad();
×
39
                return py::make_tuple(pad.x, pad.y);
×
40
            })
41
            .def("setWhiskerPad", &whisker::WhiskerTracker::setWhiskerPad)
1✔
42
            .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✔
43
                py::buffer_info info = image.request();
1✔
44

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

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

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

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

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

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

116
                std::string output_filepath = filepath.substr(0, filepath.find_last_of('.')) + "_aligned.csv";
×
117
                whisker::save_lines_csv(data_map,output_filepath);
×
118
            })
×
119
            .def("frechet_distance", [](whisker::WhiskerTracker &wt, const py::array_t<float>& whisker1, const py::array_t<float>& whisker2) {
1✔
120
                
121
                whisker::Line2D line1 = convert_np_array_to_line2d(whisker1);
×
122
                whisker::Line2D line2 = convert_np_array_to_line2d(whisker2);
×
123
                return whisker::fast_discrete_frechet_matrix(line1, line2);
×
124
            });
×
125
    m.def("get_max_threads", &omp_get_max_threads);
1✔
126
    m.def("set_num_threads", &omp_set_num_threads);
1✔
127
};
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