diff --git a/.gitignore b/.gitignore index e48269c..36aada0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ build .cache/ compile_commands.json +photon_sysroot* +build-pi/* diff --git a/.styleguide b/.styleguide new file mode 100644 index 0000000..ff0e63b --- /dev/null +++ b/.styleguide @@ -0,0 +1,33 @@ +cppHeaderFileInclude { + \.h$ + \.hpp$ + \.inc$ + \.inl$ +} + +cppSrcFileInclude { + \.cpp$ +} + +modifiableFileExclude { + \.jpg$ + \.jpeg$ + \.png$ + \.gif$ + \.so$ + \.dll$ + \.webp$ + \.ico$ + gradlew +} + +includeProject { + ^photonLib/ +} + +includeOtherLibs { + ^frc/ + ^networktables/ + ^units/ + ^wpi/ +} diff --git a/.styleguide-license b/.styleguide-license new file mode 100644 index 0000000..bcc3fc0 --- /dev/null +++ b/.styleguide-license @@ -0,0 +1,16 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e08420a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "LibCameraJNI", + "request": "launch", + "mainClass": "org.photonvision.raspi.LibCameraJNI", + "projectName": "photon-libcamera-gl-driver_ef1a9c31" + }, + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/libcamera_meme", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "Build libcamera_meme" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..40551ff --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,22 @@ +{ + "tasks": [ + { + "label": "Build libcamera_meme", + "type": "shell", + "command": "cmake", + "args": [ + "--build", + "build", + "--target", + "libcamera_meme" + ], + "options": { + "cwd": "${workspaceFolder}/" + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 73a38f3..01d58f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.25.1) +cmake_minimum_required(VERSION 3.18) project(libcamera_meme) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -25,23 +25,26 @@ endif () SET(FAST_FLAGS "-Ofast -ftree-vectorize -fPIC") add_definitions(${FAST_FLAGS}) -add_library(photonlibcamera SHARED concurrent_blocking_queue.h - camera_grabber.cpp dma_buf_alloc.cpp - gl_hsv_thresholder.cpp - libcamera_opengl_utility.cpp - libcamera_jni.cpp - camera_manager.cpp - camera_runner.cpp - camera_model.cpp - headless_opengl.cpp + +add_library(photonlibcamera SHARED + src/camera_grabber.cpp + src/dma_buf_alloc.cpp + src/gl_hsv_thresholder.cpp + src/libcamera_opengl_utility.cpp + src/camera_manager.cpp + src/camera_runner.cpp + src/camera_model.cpp + src/headless_opengl.cpp + src/libcamera_jni.cpp ) target_compile_definitions(photonlibcamera PUBLIC EGL_NO_X11=1) -target_include_directories(photonlibcamera PUBLIC ${OPENGL_INCLUDE_DIRS} ${LIBDRM_INCLUDE_DIRS} ${LIBCAMERA_INCLUDE_DIRS} +target_include_directories(photonlibcamera PUBLIC include ${OPENGL_INCLUDE_DIRS} ${LIBDRM_INCLUDE_DIRS} ${LIBCAMERA_INCLUDE_DIRS} ${LIBGBM_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${JNI_INCLUDE_DIRS}) target_link_libraries(photonlibcamera PUBLIC OpenGL::GL OpenGL::EGL Threads::Threads ${LIBCAMERA_LINK_LIBRARIES} ${LIBGBM_LINK_LIBRARIES} ${OpenCV_LIBS} ${LIBDRM_LINK_LIBRARIES}) +target_compile_options(photonlibcamera PRIVATE -Wall -Wextra -Wpedantic -Werror) add_executable(libcamera_meme main.cpp) target_include_directories(libcamera_meme PUBLIC ${OPENGL_INCLUDE_DIRS} ${LIBDRM_INCLUDE_DIRS} ${LIBCAMERA_INCLUDE_DIRS} ${LIBGBM_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${JNI_INCLUDE_DIRS}) -target_link_libraries(libcamera_meme photonlibcamera) \ No newline at end of file +target_link_libraries(libcamera_meme photonlibcamera) diff --git a/blocking_future.h b/blocking_future.h deleted file mode 100644 index 7660caf..0000000 --- a/blocking_future.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -template class BlockingFuture { - public: - BlockingFuture() = default; - - void set(T &&item) { - std::unique_lock lock(m_mutex); - m_data = std::make_optional<>(std::forward(item)); - lock.unlock(); - m_cond.notify_one(); - } - - T take() { - std::unique_lock lock(m_mutex); - m_cond.wait(lock, [&] { return m_data.has_value(); }); - - auto item = std::move(m_data.value()); - m_data.reset(); - return item; - } - - private: - std::optional m_data; - std::mutex m_mutex; - std::condition_variable m_cond; -}; \ No newline at end of file diff --git a/camera_manager.cpp b/camera_manager.cpp deleted file mode 100644 index 20ee5e4..0000000 --- a/camera_manager.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "camera_manager.h" - -std::vector> GetAllCameraIDs() { - static libcamera::CameraManager *camera_manager; - if (!camera_manager) { - camera_manager = new libcamera::CameraManager(); - camera_manager->start(); - } - std::vector> cams = - camera_manager->cameras(); - return cams; -} diff --git a/camera_manager.h b/camera_manager.h deleted file mode 100644 index 6fe4b30..0000000 --- a/camera_manager.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -std::vector> GetAllCameraIDs(); diff --git a/camera_model.cpp b/camera_model.cpp deleted file mode 100644 index 938b6f0..0000000 --- a/camera_model.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "camera_model.h" -#include - -CameraModel stringToModel(const std::string& model) { - printf("Checking model: %s\n", model.c_str()); - const char* famname = model.c_str(); - if (!strcmp(famname, "ov5647")) return OV5647; - else if (!strcmp(famname, "imx219")) return IMX219; - else if (!strcmp(famname, "imx708")) return IMX708; - else if (!strcmp(famname, "imx477")) return IMX477; - else if (!strcmp(famname, "ov9281")) return OV9281; - else if (!strcmp(famname, "ov7251")) return OV7251; - else if (!strcmp(famname, "Disconnected")) return Disconnected; - else return Unknown; -} diff --git a/camera_model.h b/camera_model.h deleted file mode 100644 index 1822b22..0000000 --- a/camera_model.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -enum CameraModel { - Disconnected = 0, - OV5647, // Picam v1 - IMX219, // Picam v2 - IMX708, // Picam v3 - IMX477, // Picam HQ - OV9281, - OV7251, - Unknown -}; - -CameraModel stringToModel(const std::string& model); diff --git a/dma_buf_alloc.h b/dma_buf_alloc.h deleted file mode 100644 index bdd0e11..0000000 --- a/dma_buf_alloc.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -class DmaBufAlloc { - public: - explicit DmaBufAlloc(const std::string &heap_name); - ~DmaBufAlloc(); - - // Allocates a DMA-BUF of size len. The returned - // fd can be closed using `close`. - int alloc_buf_fd(std::size_t len); - - private: - int m_heap_fd; -}; diff --git a/glerror.h b/glerror.h deleted file mode 100644 index f8ace34..0000000 --- a/glerror.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#define GLERROR() glerror(__LINE__) -#define EGLERROR() eglerror(__LINE__) - -inline void glerror(int line) { - GLenum error = glGetError(); - if (error != GL_NO_ERROR) { - std::string output; - output.resize(128); - snprintf(output.data(), 128, "GL error detected on line %d: 0x%04x\n", - line, error); - std::cout << output << std::endl; - throw std::runtime_error(output); - } -} - -inline void eglerror(int line) { - EGLint error = eglGetError(); - if (error != EGL_SUCCESS) { - std::string output; - output.resize(128); - snprintf(output.data(), 128, "EGL error detected on line %d: 0x%04x\n", - line, error); - std::cout << output << std::endl; - throw std::runtime_error(output); - } -} \ No newline at end of file diff --git a/headless_opengl.h b/headless_opengl.h deleted file mode 100644 index 78bfeeb..0000000 --- a/headless_opengl.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct HeadlessData { - int gbmFd; - struct gbm_device *gbmDevice; - - EGLDisplay display; - EGLContext context; -}; - -struct HeadlessData createHeadless(); -void destroyHeadless(struct HeadlessData status); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/include/blocking_future.h b/include/blocking_future.h new file mode 100644 index 0000000..fee0b18 --- /dev/null +++ b/include/blocking_future.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +template class BlockingFuture { + public: + BlockingFuture() = default; + + void set(T &&item) { + std::unique_lock lock(m_mutex); + m_data = std::make_optional<>(std::forward(item)); + lock.unlock(); + m_cond.notify_one(); + } + + T take() { + std::unique_lock lock(m_mutex); + m_cond.wait(lock, [&] { return m_data.has_value(); }); + + auto item = std::move(m_data.value()); + m_data.reset(); + return item; + } + + private: + std::optional m_data; + std::mutex m_mutex; + std::condition_variable m_cond; +}; diff --git a/camera_grabber.h b/include/camera_grabber.h similarity index 67% rename from camera_grabber.h rename to include/camera_grabber.h index a37d1c5..49a0717 100644 --- a/camera_grabber.h +++ b/include/camera_grabber.h @@ -1,13 +1,31 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once #include #include -#include "camera_model.h" - #include #include #include +#include + +#include "camera_model.h" struct CameraSettings { int32_t exposureTimeUs = 10000; @@ -31,7 +49,7 @@ class CameraGrabber { void setOnData(std::function onData); void resetOnData(); - inline const CameraModel model() { return m_model; } + inline CameraModel model() const { return m_model; } inline CameraSettings &cameraSettings() { return m_settings; } @@ -50,7 +68,8 @@ class CameraGrabber { std::vector> m_requests; std::shared_ptr m_camera; CameraModel m_model; - std::optional> m_cameraExposureProfiles; + std::optional> + m_cameraExposureProfiles; std::unique_ptr m_config; std::optional> m_onData; @@ -58,6 +77,5 @@ class CameraGrabber { CameraSettings m_settings{}; bool running = false; - void setControls(libcamera::Request *request); }; diff --git a/include/camera_manager.h b/include/camera_manager.h new file mode 100644 index 0000000..5978197 --- /dev/null +++ b/include/camera_manager.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include +#include +#include + +std::vector> GetAllCameraIDs(); diff --git a/include/camera_model.h b/include/camera_model.h new file mode 100644 index 0000000..e1082ce --- /dev/null +++ b/include/camera_model.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +enum CameraModel { + Disconnected = 0, + OV5647, // Picam v1 + IMX219, // Picam v2 + IMX708, // Picam v3 + IMX477, // Picam HQ + OV9281, + OV7251, + Unknown +}; + +CameraModel stringToModel(const std::string &model); diff --git a/camera_runner.h b/include/camera_runner.h similarity index 69% rename from camera_runner.h rename to include/camera_runner.h index ada44cb..947540c 100644 --- a/camera_runner.h +++ b/include/camera_runner.h @@ -1,30 +1,49 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once -#include "blocking_future.h" -#include "camera_grabber.h" -#include "concurrent_blocking_queue.h" -#include "dma_buf_alloc.h" -#include "gl_hsv_thresholder.h" -#include "libcamera_opengl_utility.h" +#include #include -#include #include #include #include +#include #include +#include "blocking_future.h" +#include "camera_grabber.h" +#include "concurrent_blocking_queue.h" +#include "dma_buf_alloc.h" +#include "gl_hsv_thresholder.h" +#include "libcamera_opengl_utility.h" + struct MatPair { cv::Mat color; cv::Mat processed; - long captureTimestamp; // In libcamera time units, hopefully uS? TODO actually implement + int64_t captureTimestamp; // In libcamera time units, hopefully uS? TODO + // actually implement int32_t frameProcessingType; // enum value of shader run on the image MatPair() = default; explicit MatPair(int width, int height) - : color(height, width, CV_8UC3), - processed(height, width, CV_8UC1) {} + : color(height, width, CV_8UC3), processed(height, width, CV_8UC1) {} }; // Note: destructing this class without calling `stop` if `start` was called @@ -37,7 +56,7 @@ class CameraRunner { inline CameraGrabber &cameraGrabber() { return grabber; } inline GlHsvThresholder &thresholder() { return m_thresholder; } - inline const CameraModel model() { return grabber.model(); } + inline CameraModel model() const { return grabber.model(); } void setCopyOptions(bool copyInput, bool copyOutput); // Note: all following functions must be protected by mutual exclusion. @@ -55,9 +74,7 @@ class CameraRunner { void requestShaderIdx(int idx); - private: - struct GpuQueueData { int fd; ProcessType type; @@ -81,7 +98,6 @@ class CameraRunner { std::thread threshold; std::thread display; - std::atomic m_shaderIdx = 0; std::atomic m_copyInput; diff --git a/concurrent_blocking_queue.h b/include/concurrent_blocking_queue.h similarity index 65% rename from concurrent_blocking_queue.h rename to include/concurrent_blocking_queue.h index ab712e8..d462911 100644 --- a/concurrent_blocking_queue.h +++ b/include/concurrent_blocking_queue.h @@ -1,9 +1,27 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once #include #include #include #include +#include template class ConcurrentBlockingQueue { public: diff --git a/include/dma_buf_alloc.h b/include/dma_buf_alloc.h new file mode 100644 index 0000000..eedfff8 --- /dev/null +++ b/include/dma_buf_alloc.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +class DmaBufAlloc { + public: + explicit DmaBufAlloc(const std::string &heap_name); + ~DmaBufAlloc(); + + // Allocates a DMA-BUF of size len. The returned + // fd can be closed using `close`. + int alloc_buf_fd(size_t len); + + private: + int m_heap_fd; +}; diff --git a/gl_hsv_thresholder.h b/include/gl_hsv_thresholder.h similarity index 69% rename from gl_hsv_thresholder.h rename to include/gl_hsv_thresholder.h index 2bf02e2..50af3d2 100644 --- a/gl_hsv_thresholder.h +++ b/include/gl_hsv_thresholder.h @@ -1,7 +1,25 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once +#include + #include -#include #include #include #include @@ -16,11 +34,13 @@ #include "headless_opengl.h" -enum class ProcessType: int32_t { +enum class ProcessType : int32_t { None = 0, Hsv, Gray, Adaptive, + Gray_passthrough, + NUM_PROCESS_TYPES }; class GlHsvThresholder { @@ -56,7 +76,6 @@ class GlHsvThresholder { void setHsvThresholds(double hl, double sl, double vl, double hu, double su, double vu, bool hueInverted); - private: int m_width; int m_height; @@ -72,7 +91,7 @@ class GlHsvThresholder { GLuint m_min_max_framebuffer = 0; std::vector m_programs = {}; - HeadlessData m_status{}; + HeadlessData m_status; EGLDisplay m_display; EGLContext m_context; diff --git a/gl_shader_source.h b/include/gl_shader_source.h similarity index 78% rename from gl_shader_source.h rename to include/gl_shader_source.h index 306e437..d0784db 100644 --- a/gl_shader_source.h +++ b/include/gl_shader_source.h @@ -1,3 +1,24 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +// clang-format off + static constexpr const char *VERTEX_SOURCE = "#version 100\n" "" @@ -9,6 +30,7 @@ static constexpr const char *VERTEX_SOURCE = " gl_Position = vec4(vertex, 0.0, 1.0);" "}"; + static constexpr const char *NONE_FRAGMENT_SOURCE = "#version 100\n" "#extension GL_OES_EGL_image_external : require\n" @@ -25,6 +47,7 @@ static constexpr const char *NONE_FRAGMENT_SOURCE = " gl_FragColor = vec4(color.bgr, 0);" "}"; + static constexpr const char *HSV_FRAGMENT_SOURCE = "#version 100\n" "#extension GL_OES_EGL_image_external : require\n" @@ -67,6 +90,25 @@ static constexpr const char *HSV_FRAGMENT_SOURCE = " gl_FragColor = vec4(col.bgr, int(inRange(rgb2hsv(col))));" "}"; + +static constexpr const char *GRAY_PASSTHROUGH_FRAGMENT_SOURCE = + "#version 100\n" + "#extension GL_OES_EGL_image_external : require\n" + "" + "precision lowp float;" + "precision lowp int;" + "" + "varying vec2 texcoord;" + "" + "uniform samplerExternalOES tex;" + "" + "void main(void) {" + // We get in (gray, gray, gray), I think. So just copy the R channel + " vec3 gray_gray_gray = texture2D(tex, texcoord).rgb;" + " gl_FragColor = vec4(gray_gray_gray.bgr, gray_gray_gray[0]);" + "}"; + + static constexpr const char *GRAY_FRAGMENT_SOURCE = "#version 100\n" "#extension GL_OES_EGL_image_external : require\n" @@ -86,6 +128,7 @@ static constexpr const char *GRAY_FRAGMENT_SOURCE = " gl_FragColor = vec4(color.bgr, gammaGray);" "}"; + static constexpr const char *TILING_FRAGMENT_SOURCE = "#version 100\n" "" @@ -110,6 +153,7 @@ static constexpr const char *TILING_FRAGMENT_SOURCE = " gl_FragColor = vec4(max_so_far, min_so_far, 0.0, 0.0);" "}"; + static constexpr const char *THRESHOLDING_FRAGMENT_SOURCE = "#version 100\n" "" @@ -142,3 +186,5 @@ static constexpr const char *THRESHOLDING_FRAGMENT_SOURCE = " }" " gl_FragColor = vec4(color.bgr, output_);" "}"; + +// clang-format on diff --git a/include/glerror.h b/include/glerror.h new file mode 100644 index 0000000..e834b6b --- /dev/null +++ b/include/glerror.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include +#include + +#define GLERROR() glerror(__LINE__) +#define EGLERROR() eglerror(__LINE__) + +inline void glerror(int line) { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + std::string output; + output.resize(128); + std::snprintf(output.data(), output.size(), + "GL error detected on line %d: 0x%04x\n", line, error); + std::cout << output << std::endl; + throw std::runtime_error(output); + } +} + +inline void eglerror(int line) { + EGLint error = eglGetError(); + if (error != EGL_SUCCESS) { + std::string output; + output.resize(128); + std::snprintf(output.data(), output.size(), + "EGL error detected on line %d: 0x%04x\n", line, error); + std::cout << output << std::endl; + throw std::runtime_error(output); + } +} diff --git a/include/headless_opengl.h b/include/headless_opengl.h new file mode 100644 index 0000000..4406789 --- /dev/null +++ b/include/headless_opengl.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct HeadlessData { + int gbmFd; + struct gbm_device *gbmDevice; + + EGLDisplay display; + EGLContext context; +}; + +struct HeadlessData createHeadless(void); +void destroyHeadless(struct HeadlessData status); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/include/latch.hpp b/include/latch.hpp new file mode 100644 index 0000000..3e55bdd --- /dev/null +++ b/include/latch.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +// A poor substitute for C++20's latch +// This one is blocking on more operations, but +// exposes a similar API + +class Latch { + public: + explicit Latch(std::ptrdiff_t expected) : expected(expected) {} + + ~Latch() = default; + Latch(const Latch &) = delete; + Latch &operator=(const Latch &) = delete; + + void count_down() { + std::unique_lock lock(mut); + expected -= 1; + if (expected == 0) { + lock.unlock(); + cv.notify_all(); + } + } + + void wait() { + std::unique_lock lock(mut); + cv.wait(lock, [this] { return expected == 0; }); + } + + void arrive_and_wait() { + std::unique_lock lock(mut); + expected -= 1; + if (expected == 0) { + lock.unlock(); + cv.notify_all(); + } else { + cv.wait(lock, [this] { return expected == 0; }); + } + } + + private: + std::ptrdiff_t expected; + std::condition_variable cv; + std::mutex mut; +}; diff --git a/include/libcamera_jni.hpp b/include/libcamera_jni.hpp new file mode 100644 index 0000000..671190e --- /dev/null +++ b/include/libcamera_jni.hpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* DO NOT EDIT THIS std::FILE - it is machine generated */ +#include + +/* Header for class org_photonvision_raspi_LibCameraJNI */ + +#ifndef PHOTON_LIBCAMERA_GL_DRIVER_INCLUDE_LIBCAMERA_JNI_HPP_ +#define PHOTON_LIBCAMERA_GL_DRIVER_INCLUDE_LIBCAMERA_JNI_HPP_ +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: getSensorModelRaw + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jint JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getSensorModelRaw(JNIEnv *, jclass, + jstring); + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: isVCSMSupported + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_isLibraryWorking(JNIEnv *, jclass); + +/* + * Class: test + * Method: getCameraNames + * Signature: ()[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getCameraNames(JNIEnv *, jclass); + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: createCamera + * Signature: (III)Z + */ +JNIEXPORT jlong JNICALL Java_org_photonvision_raspi_LibCameraJNI_createCamera( + JNIEnv *, jclass, jstring, jint, jint, jint); + +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_startCamera(JNIEnv *, jclass, jlong); + +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_stopCamera(JNIEnv *, jclass, jlong); + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: destroyCamera + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(JNIEnv *, jclass, jlong); + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setThresholds + * Signature: (DDDDDD)V + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setThresholds(JNIEnv *, jclass, jlong, + jdouble, jdouble, + jdouble, jdouble, + jdouble, jdouble, + jboolean); + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setExposure + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setExposure( + JNIEnv *, jclass, jlong, jint); + +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure( + JNIEnv *env, jclass, jlong, jboolean doAutoExposure); + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setBrightness + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setBrightness(JNIEnv *, jclass, jlong, + jdouble); + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setGain + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain(JNIEnv *, jclass, jlong, + jdouble); + +JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAwbGain( + JNIEnv *, jclass, jlong, jdouble red, jdouble blue); + +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(JNIEnv *, + jclass); + +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(JNIEnv *, jclass, + jlong); + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: grabFrame + * Signature: (Z)J + */ +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame(JNIEnv *, jclass, jlong); + +JNIEXPORT jlong JNICALL Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame( + JNIEnv *, jclass, jlong); + +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame(JNIEnv *, jclass, + jlong); + +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy(JNIEnv *, jclass, + jlong, jboolean copyIn, + jboolean copyOut); + +JNIEXPORT jint JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getGpuProcessType(JNIEnv *, jclass, + jlong); + +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(JNIEnv *, jclass, + jlong, jint); + +JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_releasePair( + JNIEnv *env, jclass, jlong pair_); + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // PHOTON_LIBCAMERA_GL_DRIVER_INCLUDE_LIBCAMERA_JNI_HPP_ diff --git a/include/libcamera_opengl_utility.h b/include/libcamera_opengl_utility.h new file mode 100644 index 0000000..13cb41b --- /dev/null +++ b/include/libcamera_opengl_utility.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +// These headers are weird for some reason + +// clang-format off +#include + +#include + +// clang-format on + +EGLint rangeFromColorspace(const libcamera::ColorSpace &colorSpace); +EGLint encodingFromColorspace(const libcamera::ColorSpace &colorSpace); diff --git a/latch.hpp b/latch.hpp deleted file mode 100644 index 11f52e6..0000000 --- a/latch.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma onec - -#include -#include -#include - -// A poor substitute for C++20's latch -// This one is blocking on more operations, but -// exposes a similar API - -class Latch { -public: - explicit Latch(ptrdiff_t expected): expected(expected) {}; - - ~Latch() = default; - Latch(const Latch&) = delete; - Latch& operator=(const Latch&) = delete; - - void count_down() { - std::unique_lock lock(mut); - expected -= 1; - if (expected == 0) { - lock.unlock(); - cv.notify_all(); - } - } - - void wait() { - std::unique_lock lock(mut); - cv.wait(lock, [this] { return expected == 0; }); - } - - void arrive_and_wait() { - std::unique_lock lock(mut); - expected -= 1; - if (expected == 0) { - lock.unlock(); - cv.notify_all(); - } else { - cv.wait(lock, [this] { return expected == 0; }); - } - } -private: - ptrdiff_t expected; - std::condition_variable cv; - std::mutex mut; -}; \ No newline at end of file diff --git a/libcamera_jni.cpp b/libcamera_jni.cpp deleted file mode 100644 index 81a9e34..0000000 --- a/libcamera_jni.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2022 Photon Vision. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "libcamera_jni.hpp" // Generated -#include - -#include "camera_manager.h" -#include "camera_runner.h" - -static CameraRunner *runner = nullptr; - -extern "C" { - -#include - -// We use jlongs like pointers, so they better be large enough -static_assert(sizeof(void *) <= sizeof(jlong)); - -JNIEXPORT jboolean -Java_org_photonvision_raspi_LibCameraJNI_isLibraryWorking(JNIEnv *env, jclass) { - // TODO - return true; -} - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_createCamera(JNIEnv *env, jclass, - jint width, jint height, - jint rotation) { - - std::vector> cameras = GetAllCameraIDs(); - - // Yeet all USB cameras (I hope) - auto rem = std::remove_if(cameras.begin(), cameras.end(), [](auto &cam) { - return cam->id().find("/usb") != std::string::npos; - }); - cameras.erase(rem, cameras.end()); - - if (cameras.empty()) { - runner = 0; - return false; - } - - // Otherwise, just create the first camera left - runner = new CameraRunner(width, height, rotation, cameras[0]); - return true; -} - -JNIEXPORT jint Java_org_photonvision_raspi_LibCameraJNI_getSensorModelRaw( - JNIEnv *env, jclass clazz) { - - bool runner_exists = runner != 0; - if (!runner_exists) { - Java_org_photonvision_raspi_LibCameraJNI_createCamera(env, clazz, - 320, 240, 30); - } - - if (!runner) { - return 0; - } - - jint model = runner->model(); - - if (!runner_exists) { - Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(env, clazz); - } - - return model; -} - - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_startCamera(JNIEnv *, jclass) { - if (!runner) { - return false; - } - - runner->start(); - return true; -} - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_stopCamera(JNIEnv *, jclass) { - if (!runner) { - return false; - } - - runner->stop(); - return true; -} - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(JNIEnv *env, jclass) { - if (!runner) { - return false; - } - - delete runner; - runner = nullptr; - return true; -} - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setThresholds(JNIEnv *env, jclass, - jdouble hl, jdouble sl, - jdouble vl, jdouble hu, - jdouble su, jdouble vu, - jboolean hueInverted) { - if (!runner) { - return false; - } - - // printf("Setting HSV to %f-%f %f-%f %f-%f\n", hl, hu, sl, su, vl, vu); - - // TODO hue inversion - runner->thresholder().setHsvThresholds(hl, sl, vl, hu, su, vu, hueInverted); - return true; -} - -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setExposure( - JNIEnv *env, jclass, jint exposure) { - if (!runner) { - return false; - } - - runner->cameraGrabber().cameraSettings().exposureTimeUs = exposure; - return true; -} - -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure( - JNIEnv *env, jclass, jboolean doAutoExposure) { - if (!runner) { - return false; - } - - runner->cameraGrabber().cameraSettings().doAutoExposure = doAutoExposure; - return true; -} - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setBrightness(JNIEnv *env, jclass, - jdouble brightness) { - if (!runner) { - return false; - } - - runner->cameraGrabber().cameraSettings().brightness = brightness; - return true; -} - -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAwbGain( - JNIEnv *env, jclass, jdouble red, jdouble blue) { - if (!runner) { - return false; - } - - printf("Setting red %f blue %f\n", (float)red, (float)blue); - - runner->cameraGrabber().cameraSettings().awbRedGain = red; - runner->cameraGrabber().cameraSettings().awbBlueGain = blue; - return true; -} - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain(JNIEnv *env, jclass, - jdouble analog) { - if (!runner) { - return false; - } - - runner->cameraGrabber().cameraSettings().analogGain = analog; - return true; -} - -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setRotation( - JNIEnv *env, jclass, jint rotationOrdinal) { - if (!runner) { - return false; - } - - // int rotation = (rotationOrdinal + 3) * 90; // Degrees - // TODO - return true; -} - - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy(JNIEnv *, jclass, jboolean copyIn, jboolean copyOut) { - if (!runner) { - return false; - } - - runner->setCopyOptions(copyIn, copyOut); - return true; -} - -static MatPair pair = {}; - -JNIEXPORT jlong JNICALL -Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(JNIEnv *env, jclass) { - return pair.captureTimestamp; -} - -JNIEXPORT jlong JNICALL -Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(JNIEnv *env, jclass) { - timespec ts; - clock_gettime(CLOCK_BOOTTIME, &ts); - uint64_t now_nsec = (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec; - return (jlong)now_nsec; -} - - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame(JNIEnv *env, jclass) { - if (!runner) { - // NULL - return false; - } - - pair = runner->outgoing.take(); - return true; -} - -JNIEXPORT jlong JNICALL -Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame(JNIEnv *env, jclass) { - if (!runner) { - // NULL - return 0; - } - - return reinterpret_cast(new cv::Mat(std::move(pair.color))); -} - -JNIEXPORT jlong JNICALL -Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame(JNIEnv *env, - jclass) { - if (!runner) { - // NULL - return 0; - } - - return reinterpret_cast(new cv::Mat(std::move(pair.processed))); -} - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(JNIEnv *env, jclass, - jint idx) { - if (!runner) { - return false; - } - - runner->requestShaderIdx(idx); - - return true; -} - -JNIEXPORT jint JNICALL -Java_org_photonvision_raspi_LibCameraJNI_getGpuProcessType(JNIEnv *, jclass) { - return pair.frameProcessingType; -} - -} // extern "C" diff --git a/libcamera_jni.hpp b/libcamera_jni.hpp deleted file mode 100644 index 939e1e5..0000000 --- a/libcamera_jni.hpp +++ /dev/null @@ -1,127 +0,0 @@ - -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_photonvision_raspi_LibCameraJNI */ - -#ifndef _Included_org_photonvision_raspi_LibCameraJNI -#define _Included_org_photonvision_raspi_LibCameraJNI -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: getSensorModelRaw - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jint JNICALL -Java_org_photonvision_raspi_LibCameraJNI_getSensorModelRaw(JNIEnv *, jclass); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: isVCSMSupported - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_isLibraryWorking(JNIEnv *, jclass); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: createCamera - * Signature: (III)Z - */ -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_createCamera( - JNIEnv *, jclass, jint, jint, jint); - -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_startCamera( - JNIEnv *, jclass); - -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_stopCamera( - JNIEnv *, jclass); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: destroyCamera - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(JNIEnv *, jclass); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: setThresholds - * Signature: (DDDDDD)V - */ -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setThresholds( - JNIEnv *, jclass, jdouble, jdouble, jdouble, jdouble, jdouble, jdouble, jboolean); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: setExposure - * Signature: (I)Z - */ -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setExposure(JNIEnv *, jclass, jint); - -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure( - JNIEnv *env, jclass, jboolean doAutoExposure); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: setBrightness - * Signature: (I)Z - */ -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setBrightness(JNIEnv *, jclass, jdouble); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: setGain - * Signature: (I)Z - */ -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain(JNIEnv *, jclass, jdouble); - -JNIEXPORT jboolean JNICALL Java_org_photonvision_raspi_LibCameraJNI_setAwbGain( - JNIEnv *, jclass, jdouble red, jdouble blue); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: setRotation - * Signature: (I)Z - */ -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setRotation(JNIEnv *, jclass, jint); - -JNIEXPORT jlong JNICALL -Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(JNIEnv *, jclass); - -JNIEXPORT jlong JNICALL -Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(JNIEnv *, jclass); - -/* - * Class: org_photonvision_raspi_LibCameraJNI - * Method: grabFrame - * Signature: (Z)J - */ -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame(JNIEnv *, jclass); - -JNIEXPORT jlong JNICALL -Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame(JNIEnv *, jclass); - -JNIEXPORT jlong JNICALL -Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame(JNIEnv *, jclass); - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy(JNIEnv *, jclass, jboolean copyIn, jboolean copyOut); - -JNIEXPORT jint JNICALL -Java_org_photonvision_raspi_LibCameraJNI_getGpuProcessType(JNIEnv *, jclass); - -JNIEXPORT jboolean JNICALL -Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(JNIEnv *, jclass, jint); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/libcamera_opengl_utility.h b/libcamera_opengl_utility.h deleted file mode 100644 index ca3974d..0000000 --- a/libcamera_opengl_utility.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// These headers are weird for some reason - -// clang-format off -#include -#include -// clang-format on - -EGLint rangeFromColorspace(const libcamera::ColorSpace &colorSpace); -EGLint encodingFromColorspace(const libcamera::ColorSpace &colorSpace); diff --git a/main.cpp b/main.cpp index 40ac6b8..e4702e6 100644 --- a/main.cpp +++ b/main.cpp @@ -6,70 +6,99 @@ #include #include +#include + +#include "camera_manager.h" + +#include +#include +#include + +#include "headless_opengl.h" +#include "glerror.h" + +#define GLERROR() glerror(__LINE__) +#define EGLERROR() eglerror(__LINE__) -enum class ProcessType_: int32_t { - None = 0, - Hsv, - Gray, - Adaptive, -}; void test_res(int width, int height) { - int rotation = 180; - Java_org_photonvision_raspi_LibCameraJNI_createCamera(nullptr, nullptr, - width, height, rotation); - // Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(nullptr, nullptr, 1); - Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType(nullptr, nullptr, (jint)ProcessType_::Hsv); - Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy(nullptr, nullptr, true, true); - Java_org_photonvision_raspi_LibCameraJNI_startCamera(nullptr, nullptr); - - Java_org_photonvision_raspi_LibCameraJNI_setExposure(nullptr, nullptr, 80 * 800); - Java_org_photonvision_raspi_LibCameraJNI_setBrightness(nullptr, nullptr, 0.0); - Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain(nullptr, nullptr, 20); - Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure(nullptr, nullptr, true); - - auto start = std::chrono::steady_clock::now(); - - while (std::chrono::steady_clock::now() - start < std::chrono::seconds(3)) { - bool ready = Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame(nullptr, nullptr); - if (ready) { - static int i = 0; - - cv::Mat color_mat = *(cv::Mat*)Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame(nullptr, nullptr); - cv::Mat threshold_mat = *(cv::Mat*)Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame(nullptr, nullptr); - - uint64_t captureTime = Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(nullptr, nullptr); - uint64_t now = Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(nullptr, nullptr); - printf("now %lu capture %lu latency %f\n", now, captureTime, (double)(now - captureTime) / 1000000.0); - - i++; - static char arr[50]; - snprintf(arr,sizeof(arr),"color_%i.png", i); - cv::imwrite(arr, color_mat); - snprintf(arr,sizeof(arr),"thresh_%i.png", i); - cv::imwrite(arr, threshold_mat); - } + + std::vector> cameras = GetAllCameraIDs(); + + // Yeet all USB cameras (I hope) + auto rem = std::remove_if(cameras.begin(), cameras.end(), [](auto &cam) { + return cam->id().find("/usb") != std::string::npos; + }); + cameras.erase(rem, cameras.end()); + + for (const auto& cam : cameras) { + printf("Camera at: %s\n", cam->id().c_str()); } - Java_org_photonvision_raspi_LibCameraJNI_stopCamera(nullptr, nullptr); - Java_org_photonvision_raspi_LibCameraJNI_destroyCamera(nullptr, nullptr); + std::vector runners; + + int rotation = 0; + + for (auto& c : cameras) { + auto r = new CameraRunner(width, height, rotation, c); + runners.push_back(r); + r->start(); + r->setCopyOptions(true, true); + r->requestShaderIdx((int)ProcessType::Gray_passthrough); + + r->cameraGrabber().cameraSettings().exposureTimeUs = 100000; + r->cameraGrabber().cameraSettings().analogGain = 4; + r->cameraGrabber().cameraSettings().brightness = 0.0; - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + printf("Started %s!\n", c->id().c_str()); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + for (int i = 0; i < 50; i++) { + int j = 0; + for (auto r : runners) { + auto pair = r->outgoing.take(); + auto color_mat = cv::Mat(std::move(pair.color)); + auto processed_mat = cv::Mat(std::move(pair.processed)); + + auto now = Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp(nullptr, NULL); + auto then = Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime(nullptr, NULL, (long int)&pair); + + printf("now %li then %li dt %i\n", now, then, now-then); + + + if (i % 30 == 0) { + printf("saving cam %i idx %i\n", j, i); + cv::Mat bgr; + cv::cvtColor(processed_mat, bgr, cv::COLOR_GRAY2BGR); + static char arr[50]; + snprintf(arr,sizeof(arr),"color_cam%i_%i_%ix%i.png", j, i, width, height); + cv::imwrite(arr, color_mat); + snprintf(arr,sizeof(arr),"thresh_cam%i_%i_%ix%i.png", j, i, width, height); + cv::imwrite(arr, bgr); + } + + color_mat.release(); + processed_mat.release(); + + j++; + } + } + + printf("Destroying all!\n"); + for (auto& r : runners) { + r->stop(); + delete r; + } } int main() { - // test_res(1920, 1080); - test_res(320, 240); - // test_res(640, 480); - // test_res(960, 720); - // // test_res(2592, 1944); - // test_res(2592/2, 1944/2); - // test_res(1920, 1080); - // test_res(320, 240); - // test_res(640, 480); - // test_res(960, 720); - // // test_res(2592, 1944); - // test_res(2592/2, 1944/2); + + for (int i = 0; i < 1; i++) { + test_res(1280, 800); + // test_res(1280/2, 800/2); + // test_res(640, 480); + } std::cout << "Done" << std::endl; diff --git a/camera_grabber.cpp b/src/camera_grabber.cpp similarity index 66% rename from camera_grabber.cpp rename to src/camera_grabber.cpp index bdd5695..df239cb 100644 --- a/camera_grabber.cpp +++ b/src/camera_grabber.cpp @@ -1,19 +1,37 @@ -#include "camera_grabber.h" +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -#include -#include +#include "camera_grabber.h" #include #include +#include +#include + CameraGrabber::CameraGrabber(std::shared_ptr camera, int width, int height, int rotation) - : m_buf_allocator(camera), m_camera(std::move(camera)), m_cameraExposureProfiles(std::nullopt) { + : m_buf_allocator(camera), m_camera(std::move(camera)), + m_cameraExposureProfiles(std::nullopt) { if (m_camera->acquire()) { throw std::runtime_error("failed to acquire camera"); } - + // Determine model auto &cprp = m_camera->properties(); auto model = cprp.get(libcamera::properties::Model); @@ -23,27 +41,30 @@ CameraGrabber::CameraGrabber(std::shared_ptr camera, m_model = Unknown; } - std::cout << "Model " << m_model << std::endl; + std::cout << "Model " << m_model << std::endl; auto config = m_camera->generateConfiguration( {libcamera::StreamRole::VideoRecording}); - + // print active arrays - if (m_camera->properties().contains(libcamera::properties::PIXEL_ARRAY_ACTIVE_AREAS)) { - printf("Active areas:\n"); - auto rects = m_camera->properties().get(libcamera::properties::PixelArrayActiveAreas); + if (m_camera->properties().contains( + libcamera::properties::PIXEL_ARRAY_ACTIVE_AREAS)) { + std::printf("Active areas:\n"); + auto rects = m_camera->properties().get( + libcamera::properties::PixelArrayActiveAreas); if (rects.has_value()) { - for(const auto rect : rects.value()) { + for (const auto rect : rects.value()) { std::cout << rect.toString() << std::endl; } } - } else - printf("No active areas\n"); + } else { + std::printf("No active areas??\n"); + } config->at(0).size.width = width; config->at(0).size.height = height; - printf("Rotation = %i\n", rotation); + std::printf("Rotation = %i\n", rotation); if (rotation == 180) { config->orientation = libcamera::Orientation::Rotate180; } else { @@ -58,7 +79,7 @@ CameraGrabber::CameraGrabber(std::shared_ptr camera, throw std::runtime_error("failed to configure stream"); } - std::cout << config->at(0).toString() << std::endl; + std::cout << "Selected configuration: " << config->at(0).toString() << std::endl; auto stream = config->at(0).stream(); if (m_buf_allocator.allocate(stream) < 0) { @@ -69,7 +90,7 @@ CameraGrabber::CameraGrabber(std::shared_ptr camera, for (const auto &buffer : m_buf_allocator.buffers(stream)) { auto request = m_camera->createRequest(); - auto &controls = request->controls(); + // auto &controls = request->controls(); // controls.set(libcamera::controls::FrameDurationLimits, // {static_cast(8333), static_cast(8333)}); // controls.set(libcamera::controls::ExposureTime, 10000); @@ -123,32 +144,34 @@ void CameraGrabber::setControls(libcamera::Request *request) { controls_.set(controls::AwbEnable, false); // AWB disabled } controls_.set(controls::AnalogueGain, - m_settings.analogGain); // Analog gain, min 1 max big number? + m_settings.analogGain); // Analog gain, min 1 max big number? if (m_model != OV9281) { controls_.set(controls::ColourGains, - libcamera::Span{ - {m_settings.awbRedGain, - m_settings.awbBlueGain}}); // AWB gains, red and blue, - // unknown range + libcamera::Span{ + {m_settings.awbRedGain, + m_settings.awbBlueGain}}); // AWB gains, red and + // blue, unknown range } - // Note about brightness: -1 makes everything look deep fried, 0 is probably best for most things + // Note about brightness: -1 makes everything look deep fried, 0 is probably + // best for most things controls_.set(libcamera::controls::Brightness, - m_settings.brightness); // -1 to 1, 0 means unchanged + m_settings.brightness); // -1 to 1, 0 means unchanged controls_.set(controls::Contrast, - m_settings.contrast); // Nominal 1 + m_settings.contrast); // Nominal 1 if (m_model != OV9281) { controls_.set(controls::Saturation, - m_settings.saturation); // Nominal 1, 0 would be greyscale + m_settings.saturation); // Nominal 1, 0 would be greyscale } if (m_settings.doAutoExposure) { controls_.set(controls::AeEnable, - true); // Auto exposure disabled + true); // Auto exposure disabled - controls_.set(controls::AeMeteringMode, controls::MeteringCentreWeighted); + controls_.set(controls::AeMeteringMode, + controls::MeteringCentreWeighted); if (m_model == OV9281) { controls_.set(controls::AeExposureMode, controls::ExposureNormal); } else { @@ -159,25 +182,24 @@ void CameraGrabber::setControls(libcamera::Request *request) { // seconds * 1e6 = uS constexpr const int MIN_FRAME_TIME = 1e6 / 250; constexpr const int MAX_FRAME_TIME = 1e6 / 15; - controls_.set( - libcamera::controls::FrameDurationLimits, - libcamera::Span{ - {MIN_FRAME_TIME, MAX_FRAME_TIME}}); + controls_.set(libcamera::controls::FrameDurationLimits, + libcamera::Span{ + {MIN_FRAME_TIME, MAX_FRAME_TIME}}); } else { controls_.set(controls::AeEnable, - false); // Auto exposure disabled + false); // Auto exposure disabled controls_.set(controls::ExposureTime, - m_settings.exposureTimeUs); // in microseconds + m_settings.exposureTimeUs); // in microseconds controls_.set( libcamera::controls::FrameDurationLimits, libcamera::Span{ {m_settings.exposureTimeUs, - m_settings.exposureTimeUs}}); // Set default to zero, we have - // specified the exposure time + m_settings.exposureTimeUs}}); // Set default to zero, we have + // specified the exposure time } controls_.set(controls::ExposureValue, 0); - + if (m_model != OV7251 && m_model != OV9281) { controls_.set(controls::Sharpness, 1); } @@ -203,7 +225,6 @@ void CameraGrabber::stop() { m_camera->stop(); } - void CameraGrabber::setOnData( std::function onData) { m_onData = std::move(onData); diff --git a/src/camera_manager.cpp b/src/camera_manager.cpp new file mode 100644 index 0000000..b2c9185 --- /dev/null +++ b/src/camera_manager.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "camera_manager.h" + +std::vector> GetAllCameraIDs() { + static libcamera::CameraManager *camera_manager; + if (!camera_manager) { + camera_manager = new libcamera::CameraManager(); + camera_manager->start(); + } + std::vector> cams = + camera_manager->cameras(); + return cams; +} diff --git a/src/camera_model.cpp b/src/camera_model.cpp new file mode 100644 index 0000000..19b4df1 --- /dev/null +++ b/src/camera_model.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "camera_model.h" + +#include + +CameraModel stringToModel(const std::string &model) { + std::printf("Checking model: %s\n", model.c_str()); + const char *famname = model.c_str(); + if (!strcmp(famname, "ov5647")) + return OV5647; + else if (!strcmp(famname, "imx219")) + return IMX219; + else if (!strcmp(famname, "imx708")) + return IMX708; + else if (!strcmp(famname, "imx477")) + return IMX477; + else if (!strcmp(famname, "ov9281")) + return OV9281; + else if (!strcmp(famname, "ov7251")) + return OV7251; + else if (!strcmp(famname, "Disconnected")) + return Disconnected; + else + return Unknown; +} diff --git a/camera_runner.cpp b/src/camera_runner.cpp similarity index 74% rename from camera_runner.cpp rename to src/camera_runner.cpp index 43bfbe1..986366f 100644 --- a/camera_runner.cpp +++ b/src/camera_runner.cpp @@ -1,3 +1,20 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "camera_runner.h" #include @@ -13,17 +30,15 @@ using latch = std::latch; using latch = Latch; #endif -#include -#include - -#include #include - -#include +#include #include +#include +#include +#include -using namespace std::chrono; -using namespace std::chrono_literals; +using steady_clock = std::chrono::steady_clock; +using namespace std::literals::chrono_literals; static double approxRollingAverage(double avg, double new_sample) { avg -= avg / 50; @@ -35,9 +50,8 @@ static double approxRollingAverage(double avg, double new_sample) { CameraRunner::CameraRunner(int width, int height, int rotation, std::shared_ptr cam) : m_camera(std::move(cam)), m_width(width), m_height(height), - grabber(m_camera, m_width, m_height, rotation), m_thresholder(m_width, m_height), - allocer("/dev/dma_heap/linux,cma") { - + grabber(m_camera, m_width, m_height, rotation), + m_thresholder(m_width, m_height), allocer("/dev/dma_heap/linux,cma") { grabber.setOnData( [&](libcamera::Request *request) { camera_queue.push(request); }); @@ -53,9 +67,7 @@ CameraRunner::~CameraRunner() { } } -void CameraRunner::requestShaderIdx(int idx) { - m_shaderIdx = idx; -} +void CameraRunner::requestShaderIdx(int idx) { m_shaderIdx = idx; } void CameraRunner::setCopyOptions(bool copyIn, bool copyOut) { m_copyInput = copyIn; @@ -75,10 +87,9 @@ void CameraRunner::start() { start_frame_grabber.count_down(); while (true) { - // printf("Threshold thread!\n"); + // std::printf("Threshold thread!\n"); auto request = camera_queue.pop(); - if (!request) { break; } @@ -96,26 +107,27 @@ void CameraRunner::start() { static_cast(stride / 2)}, }}; + auto begintime = steady_clock::now(); auto type = static_cast(m_shaderIdx.load()); - int out = m_thresholder.testFrame(yuv_data, - encodingFromColorspace(colorspace), - rangeFromColorspace(colorspace), - type); + int out = m_thresholder.testFrame( + yuv_data, encodingFromColorspace(colorspace), + rangeFromColorspace(colorspace), type); if (out != 0) { /* From libcamera docs: - The timestamp, expressed in nanoseconds, represents a monotonically - increasing counter since the system boot time, as defined by the - Linux-specific CLOCK_BOOTTIME clock id. + The timestamp, expressed in nanoseconds, represents a + monotonically increasing counter since the system boot time, as + defined by the Linux-specific CLOCK_BOOTTIME clock id. */ - uint64_t sensorTimestamp = static_cast(request->metadata() - .get(libcamera::controls::SensorTimestamp) - .value_or(0)); + uint64_t sensorTimestamp = static_cast( + request->metadata() + .get(libcamera::controls::SensorTimestamp) + .value_or(0)); gpu_queue.push({out, type, sensorTimestamp}); } @@ -123,9 +135,10 @@ void CameraRunner::start() { std::chrono::duration elapsedMillis = steady_clock::now() - begintime; if (elapsedMillis > 0.9ms) { - // gpuTimeAvgMs = - // approxRollingAverage(gpuTimeAvgMs, elapsedMillis.count()); - // std::cout << "GLProcess: " << elapsedMillis.count() << std::endl; + gpuTimeAvgMs = + approxRollingAverage(gpuTimeAvgMs, elapsedMillis.count()); + // std::cout << "GLProcess: " << elapsedMillis.count() << + // std::endl; } { @@ -148,13 +161,13 @@ void CameraRunner::start() { mmaped.emplace(fd, static_cast(mmap_ptr)); } - double copyTimeAvgMs = 0; + // double copyTimeAvgMs = 0; double fpsTimeAvgMs = 0; start_frame_grabber.count_down(); auto lastTime = steady_clock::now(); while (true) { - // printf("Display thread!\n"); + // std::printf("Display thread!\n"); auto data = gpu_queue.pop(); if (data.fd == -1) { break; @@ -174,7 +187,6 @@ void CameraRunner::start() { auto input_ptr = mmaped.at(data.fd); int bound = m_width * m_height; - { struct dma_buf_sync dma_sync {}; dma_sync.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW; @@ -194,7 +206,7 @@ void CameraRunner::start() { processed_out_buf[i] = input_ptr[i * 4 + 3]; } } - + { struct dma_buf_sync dma_sync {}; dma_sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW; @@ -212,13 +224,13 @@ void CameraRunner::start() { // approxRollingAverage(copyTimeAvgMs, elapsedMillis.count()); // std::cout << "Copy: " << copyTimeAvgMs << std::endl; - // auto now = steady_clock::now(); - // std::chrono::duration elapsed = - // (now - lastTime); - // fpsTimeAvgMs = approxRollingAverage(fpsTimeAvgMs, elapsed.count()); - // printf("Delta %.2f FPS: %.2f\n", fpsTimeAvgMs, - // 1000.0 / fpsTimeAvgMs); - // lastTime = now; + auto now = steady_clock::now(); + std::chrono::duration elapsed = + (now - lastTime); + fpsTimeAvgMs = approxRollingAverage(fpsTimeAvgMs, elapsed.count()); + // std::printf("Delta %.2f FPS: %.2f\n", fpsTimeAvgMs, 1000.0 / + // fpsTimeAvgMs); + lastTime = now; } for (const auto &[fd, pointer] : mmaped) { @@ -235,7 +247,7 @@ void CameraRunner::start() { } void CameraRunner::stop() { - printf("stopping all\n"); + std::printf("stopping all\n"); // stop the camera { std::lock_guard lock{camera_stop_mutex}; @@ -247,8 +259,8 @@ void CameraRunner::stop() { threshold.join(); // push sentinel value to stop display thread - gpu_queue.push({-1, ProcessType::None}); + gpu_queue.push({-1, ProcessType::None, 0}); display.join(); - printf("stopped all\n"); + std::printf("stopped all\n"); } diff --git a/dma_buf_alloc.cpp b/src/dma_buf_alloc.cpp similarity index 50% rename from dma_buf_alloc.cpp rename to src/dma_buf_alloc.cpp index 11f2188..42ed671 100644 --- a/dma_buf_alloc.cpp +++ b/src/dma_buf_alloc.cpp @@ -1,13 +1,29 @@ -#include "dma_buf_alloc.h" +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -#include +#include "dma_buf_alloc.h" #include -#include - #include #include #include +#include + +#include DmaBufAlloc::DmaBufAlloc(const std::string &heap_name) { int heap_fd = open(heap_name.c_str(), O_RDWR, 0); @@ -19,7 +35,7 @@ DmaBufAlloc::DmaBufAlloc(const std::string &heap_name) { DmaBufAlloc::~DmaBufAlloc() { close(m_heap_fd); } -int DmaBufAlloc::alloc_buf_fd(std::size_t len) { +int DmaBufAlloc::alloc_buf_fd(size_t len) { struct dma_heap_allocation_data alloc = {}; alloc.len = len; alloc.fd_flags = O_CLOEXEC | O_RDWR; diff --git a/eglinfo.c b/src/eglinfo.c similarity index 57% rename from eglinfo.c rename to src/eglinfo.c index 2681c33..e3a4cd7 100644 --- a/eglinfo.c +++ b/src/eglinfo.c @@ -1,5 +1,22 @@ -#include +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include +#include #include // adjustments to paths might be needed @@ -7,17 +24,11 @@ #include #include -static EGLint const attribute_list[] = { - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_NONE -}; +static EGLint const attribute_list[] = {EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, EGL_NONE}; -static EGLint const context_attribute_list[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE -}; +static EGLint const context_attribute_list[] = {EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE}; struct gl_limit { GLint name; @@ -25,28 +36,25 @@ struct gl_limit { int num_args; }; #define EXPAND(x) x, #x -struct gl_limit limits[] = { - {EXPAND(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS), 1}, - {EXPAND(GL_MAX_CUBE_MAP_TEXTURE_SIZE), 1}, - {EXPAND(GL_MAX_FRAGMENT_UNIFORM_VECTORS), 1}, - {EXPAND(GL_MAX_RENDERBUFFER_SIZE), 1}, - {EXPAND(GL_MAX_TEXTURE_IMAGE_UNITS), 1}, - {EXPAND(GL_MAX_TEXTURE_SIZE), 1}, - {EXPAND(GL_MAX_VARYING_VECTORS), 1}, - {EXPAND(GL_MAX_VERTEX_ATTRIBS), 1}, - {EXPAND(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS), 1}, - {EXPAND(GL_MAX_VERTEX_UNIFORM_VECTORS), 1}, - {EXPAND(GL_MAX_VIEWPORT_DIMS), 2}, - {0, NULL} -}; +struct gl_limit limits[] = {{EXPAND(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS), 1}, + {EXPAND(GL_MAX_CUBE_MAP_TEXTURE_SIZE), 1}, + {EXPAND(GL_MAX_FRAGMENT_UNIFORM_VECTORS), 1}, + {EXPAND(GL_MAX_RENDERBUFFER_SIZE), 1}, + {EXPAND(GL_MAX_TEXTURE_IMAGE_UNITS), 1}, + {EXPAND(GL_MAX_TEXTURE_SIZE), 1}, + {EXPAND(GL_MAX_VARYING_VECTORS), 1}, + {EXPAND(GL_MAX_VERTEX_ATTRIBS), 1}, + {EXPAND(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS), 1}, + {EXPAND(GL_MAX_VERTEX_UNIFORM_VECTORS), 1}, + {EXPAND(GL_MAX_VIEWPORT_DIMS), 2}, + {0, NULL}}; #include "headless_opengl.h" -int main(int argc, char ** argv) -{ +int main(int argc, char **argv) { struct HeadlessData status = createHeadless(); - + EGLDisplay display = status.display; // EGLConfig config; EGLContext context = status.context; @@ -97,8 +105,7 @@ int main(int argc, char ** argv) printf(" %s = %d\n", limits[i].string, param[0]); else printf(" %s = %d, %d\n", limits[i].string, param[0], param[1]); - } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/gl_hsv_thresholder.cpp b/src/gl_hsv_thresholder.cpp similarity index 87% rename from gl_hsv_thresholder.cpp rename to src/gl_hsv_thresholder.cpp index 7023095..2575f78 100644 --- a/gl_hsv_thresholder.cpp +++ b/src/gl_hsv_thresholder.cpp @@ -1,22 +1,42 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "gl_hsv_thresholder.h" -#include "glerror.h" -#include "gl_shader_source.h" -#include -#include #include #include #include +#include +#include + +#include "gl_shader_source.h" +#include "glerror.h" + #define GLERROR() glerror(__LINE__) #define EGLERROR() eglerror(__LINE__) GLuint make_shader(GLenum type, const char *source) { auto shader = glCreateShader(type); - void *ctx = eglGetCurrentContext(); - // printf("Shader idx: %i context ptr: %lu\n", (int) shader, (size_t)ctx); + // void *ctx = eglGetCurrentContext(); + // std::printf("Shader idx: %i context ptr: %lu\n", (int) shader, + // (size_t)ctx); if (!shader) { throw std::runtime_error("failed to create shader"); @@ -37,6 +57,7 @@ GLuint make_shader(GLenum type, const char *source) { glGetShaderInfoLog(shader, log_size, nullptr, out.data()); glDeleteShader(shader); + printf("Shader:\n%s\n", source); throw std::runtime_error("failed to compile shader with error: " + out); } @@ -78,26 +99,30 @@ GlHsvThresholder::GlHsvThresholder(int width, int height) m_status = createHeadless(); m_context = m_status.context; m_display = m_status.display; - } GlHsvThresholder::~GlHsvThresholder() { - for (auto& program : m_programs) + for (auto &program : m_programs) glDeleteProgram(program); glDeleteBuffers(1, &m_quad_vbo); - for (const auto [key, value]: m_framebuffers) { + for (const auto &[key, value] : m_framebuffers) { glDeleteFramebuffers(1, &value); } destroyHeadless(m_status); } -static void on_gl_error(EGLenum error,const char *command,EGLint messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char* message) -{ - - printf("Error111: %s\n", message); - -} +// static void on_gl_error(EGLenum error,const char *command,EGLint +// messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char* +// message) +// { +// (void) error; +// (void) command; +// (void) messageType; +// (void) threadLabel; +// (void) objectLabel; +// std::printf("Error111: %s\n", message); +// } void GlHsvThresholder::start(const std::vector &output_buf_fds) { static auto glEGLImageTargetTexture2DOES = @@ -107,14 +132,14 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) { (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR"); static auto eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); - static auto glDebugMessageCallbackKHR = - (PFNEGLDEBUGMESSAGECONTROLKHRPROC)eglGetProcAddress("glDebugMessageCallbackKHR"); if (!eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_context)) { throw std::runtime_error("failed to bind egl context"); } EGLERROR(); + // static auto glDebugMessageCallbackKHR = + // (PFNEGLDEBUGMESSAGECONTROLKHRPROC)eglGetProcAddress("glDebugMessageCallbackKHR"); // glEnable(GL_DEBUG_OUTPUT_KHR); // GLERROR(); // glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); @@ -122,12 +147,13 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) { // glDebugMessageCallbackKHR(on_gl_error, nullptr); // GLERROR(); - m_programs.reserve(5); + m_programs.reserve((int)ProcessType::NUM_PROCESS_TYPES); m_programs[0] = make_program(VERTEX_SOURCE, NONE_FRAGMENT_SOURCE); m_programs[1] = make_program(VERTEX_SOURCE, HSV_FRAGMENT_SOURCE); m_programs[2] = make_program(VERTEX_SOURCE, GRAY_FRAGMENT_SOURCE); m_programs[3] = make_program(VERTEX_SOURCE, TILING_FRAGMENT_SOURCE); m_programs[4] = make_program(VERTEX_SOURCE, THRESHOLDING_FRAGMENT_SOURCE); + m_programs[5] = make_program(VERTEX_SOURCE, GRAY_PASSTHROUGH_FRAGMENT_SOURCE); for (auto fd : output_buf_fds) { GLuint out_tex; @@ -165,7 +191,7 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) { glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); GLERROR(); - eglDestroyImageKHR(m_display, image); + eglDestroyImageKHR(m_display, image); GLERROR(); GLuint framebuffer; @@ -216,7 +242,8 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) { GLERROR(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GLERROR(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, nullptr); GLERROR(); m_grayscale_texture = grayscale_texture; @@ -228,10 +255,12 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) { GLERROR(); glBindFramebuffer(GL_FRAMEBUFFER, grayscale_buffer); GLERROR(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_grayscale_texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, m_grayscale_texture, 0); GLERROR(); - if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != + GL_FRAMEBUFFER_COMPLETE) { throw std::runtime_error("failed to complete grayscale_buffer"); } @@ -252,7 +281,8 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) { GLERROR(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); GLERROR(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width / 4, m_height / 4, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width / 4, m_height / 4, 0, + GL_RGB, GL_UNSIGNED_BYTE, nullptr); GLERROR(); m_min_max_texture = min_max_texture; @@ -264,10 +294,12 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) { GLERROR(); glBindFramebuffer(GL_FRAMEBUFFER, min_max_framebuffer); GLERROR(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_min_max_texture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, m_min_max_texture, 0); GLERROR(); - if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != + GL_FRAMEBUFFER_COMPLETE) { throw std::runtime_error("failed to complete grayscale_buffer"); } @@ -276,7 +308,8 @@ void GlHsvThresholder::start(const std::vector &output_buf_fds) { } void GlHsvThresholder::release() { - if (!eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { + if (!eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { throw std::runtime_error("failed to bind egl context"); } } @@ -305,7 +338,7 @@ int GlHsvThresholder::testFrame( // std::cout << "yes framebuffer" << std::endl; m_renderable.pop(); } else { - std::cout << "no framebuffer, skipping" << std::endl; + // std::cout << "no framebuffer, skipping" << std::endl; return 0; } } @@ -350,7 +383,6 @@ int GlHsvThresholder::testFrame( std::to_string(yuv_plane_data[0].fd)); } - GLuint texture; glGenTextures(1, &texture); GLERROR(); @@ -374,6 +406,8 @@ int GlHsvThresholder::testFrame( initial_program = m_programs[1]; } else if (type == ProcessType::Gray || type == ProcessType::Adaptive) { initial_program = m_programs[2]; + } else if (type == ProcessType::Gray_passthrough) { + initial_program = m_programs[5]; } glUseProgram(initial_program); diff --git a/headless_opengl.cpp b/src/headless_opengl.cpp similarity index 82% rename from headless_opengl.cpp rename to src/headless_opengl.cpp index 64ac6f6..e5965e8 100644 --- a/headless_opengl.cpp +++ b/src/headless_opengl.cpp @@ -1,5 +1,21 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "headless_opengl.h" -#include "glerror.h" #include #include @@ -9,6 +25,8 @@ #include +#include "glerror.h" + // The following code related to DRM/GBM was adapted from the following sources: // https://github.com/eyelash/tutorials/blob/master/drm-gbm.c // and @@ -40,10 +58,8 @@ static const EGLint configAttribs[] = {EGL_RED_SIZE, EGL_NONE}; static const EGLint contextAttribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, - EGL_NONE -}; + EGL_CONTEXT_CLIENT_VERSION, 2, EGL_CONTEXT_FLAGS_KHR, + EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL_NONE}; HeadlessData createHeadless() { std::vector paths = {"/dev/dri/card1", "/dev/dri/card0"}; @@ -83,7 +99,7 @@ HeadlessData createHeadless() { eglBindAPI(EGL_OPENGL_ES_API); EGLERROR(); - printf("Initialized EGL version: %d.%d\n", major, minor); + std::printf("Initialized EGL version: %d.%d\n", major, minor); EGLint count; EGLint numConfigs; @@ -135,4 +151,4 @@ void destroyHeadless(HeadlessData status) { eglTerminate(status.display); gbm_device_destroy(status.gbmDevice); close(status.gbmFd); -} \ No newline at end of file +} diff --git a/src/libcamera_jni.cpp b/src/libcamera_jni.cpp new file mode 100644 index 0000000..ef21f57 --- /dev/null +++ b/src/libcamera_jni.cpp @@ -0,0 +1,477 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * Copyright (C) 2022 Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "libcamera_jni.hpp" // Generated + +#include + +#include + +#include "camera_manager.h" +#include "camera_model.h" +#include "camera_runner.h" +#include "headless_opengl.h" + +extern "C" { + +#include + +// We use jlongs like pointers, so they better be large enough +static_assert(sizeof(void *) <= sizeof(jlong)); + +JNIEXPORT jboolean +Java_org_photonvision_raspi_LibCameraJNI_isLibraryWorking(JNIEnv *, jclass) { + // todo + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: getCameraNames + * Signature: ()[Ljava/lang/Object; + */ +JNIEXPORT jobjectArray JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getCameraNames + (JNIEnv *env, jclass) +{ + std::vector> cameras = GetAllCameraIDs(); + + // Yeet all USB cameras (I hope) + auto rem = std::remove_if(cameras.begin(), cameras.end(), [](auto &cam) { + return cam->id().find("/usb") != std::string::npos; + }); + cameras.erase(rem, cameras.end()); + + jobjectArray ret; + + // https://stackoverflow.com/a/21768693 + ret = (jobjectArray)env->NewObjectArray( + cameras.size(), env->FindClass("java/lang/String"), NULL); + for (unsigned int i = 0; i < cameras.size(); i++) + env->SetObjectArrayElement(ret, i, + env->NewStringUTF(cameras[i]->id().c_str())); + + return (ret); +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: createCamera + * Signature: (Ljava/lang/String;III)J + */ +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_createCamera + (JNIEnv *env, jclass, jstring name, jint width, jint height, jint rotation) +{ + std::vector> cameras = GetAllCameraIDs(); + + const char *c_name = env->GetStringUTFChars(name, 0); + + jlong ret = 0; + + // Find our camera by name + for (auto &c : cameras) { + if (std::strcmp(c->id().c_str(), c_name) == 0) { + ret = reinterpret_cast( + new CameraRunner(width, height, rotation, c)); + break; + } + } + + env->ReleaseStringUTFChars(name, c_name); + + return ret; +} + +JNIEXPORT jint Java_org_photonvision_raspi_LibCameraJNI_getSensorModelRaw( + JNIEnv *env, jclass, jstring name) { + + std::vector> cameras = GetAllCameraIDs(); + + const char *c_name = env->GetStringUTFChars(name, 0); + + // printf("Got in name: %s\n", c_name); + + jint model_enum = Unknown; + for (auto &c : cameras) { + // Find the camera we want to be talking about + if (std::strcmp(c->id().c_str(), c_name) == 0) { + + // Determine model + auto &cprp = c->properties(); + auto model = cprp.get(libcamera::properties::Model); + // printf("Checking camera at path %s w/model %s\n", c->id().c_str(), model.value().c_str()); + if (model) { + model_enum = reinterpret_cast(stringToModel(model.value())); + } + + break; + } + } + + // printf("Got model %i\n", model_enum); + + + env->ReleaseStringUTFChars(name, c_name); + + return model_enum; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: startCamera + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_startCamera + (JNIEnv *, jclass, jlong runner_) +{ + CameraRunner *runner = reinterpret_cast(runner_); + + runner->start(); + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: stopCamera + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_stopCamera + (JNIEnv *, jclass, jlong runner_) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + runner->stop(); + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: destroyCamera + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_destroyCamera + (JNIEnv *, jclass, jlong runner_) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + delete runner; + runner = nullptr; + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setThresholds + * Signature: (JDDDDDDZ)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setThresholds + (JNIEnv *, jclass, jlong runner_, jdouble hl, jdouble sl, jdouble vl, + jdouble hu, jdouble su, jdouble vu, jboolean hueInverted) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + // std::printf("Setting HSV to %f-%f %f-%f %f-%f\n", hl, hu, sl, su, vl, + // vu); + + // TODO hue inversion + runner->thresholder().setHsvThresholds(hl, sl, vl, hu, su, vu, hueInverted); + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setExposure + * Signature: (JI)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setExposure + (JNIEnv *, jclass, jlong runner_, jint exposure) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + runner->cameraGrabber().cameraSettings().exposureTimeUs = exposure; + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setAutoExposure + * Signature: (JZ)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setAutoExposure + (JNIEnv *, jclass, jlong runner_, jboolean doAutoExposure) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + runner->cameraGrabber().cameraSettings().doAutoExposure = doAutoExposure; + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setBrightness + * Signature: (JD)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setBrightness + (JNIEnv *, jclass, jlong runner_, jdouble brightness) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + runner->cameraGrabber().cameraSettings().brightness = brightness; + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setAwbGain + * Signature: (JDD)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setAwbGain + (JNIEnv *, jclass, jlong runner_, jdouble red, jdouble blue) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + runner->cameraGrabber().cameraSettings().awbRedGain = red; + runner->cameraGrabber().cameraSettings().awbBlueGain = blue; + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setAnalogGain + * Signature: (JD)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setAnalogGain + (JNIEnv *, jclass, jlong runner_, jdouble analog) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + runner->cameraGrabber().cameraSettings().analogGain = analog; + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setFramesToCopy + * Signature: (JZZ)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setFramesToCopy + (JNIEnv *, jclass, jlong runner_, jboolean copyIn, jboolean copyOut) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + runner->setCopyOptions(copyIn, copyOut); + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: getLibcameraTimestamp + * Signature: ()J + */ +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getLibcameraTimestamp + (JNIEnv *, jclass) +{ + timespec ts; + clock_gettime(CLOCK_BOOTTIME, &ts); + uint64_t now_nsec = + static_cast(ts.tv_sec) * 1000000000ULL + ts.tv_nsec; + return (jlong)now_nsec; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: awaitNewFrame + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_awaitNewFrame + (JNIEnv *, jclass, jlong runner_) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return 0; + } + + MatPair *pair = new MatPair(); + *pair = runner->outgoing.take(); + return reinterpret_cast(pair); +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: takeColorFrame + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_takeColorFrame + (JNIEnv *, jclass, jlong pair_) +{ + MatPair *pair = reinterpret_cast(pair_); + if (!pair) { + return 0; + } + + return reinterpret_cast(new cv::Mat(std::move(pair->color))); +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: takeProcessedFrame + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_takeProcessedFrame + (JNIEnv *, jclass, jlong pair_) +{ + MatPair *pair = reinterpret_cast(pair_); + if (!pair) { + return 0; + } + + return reinterpret_cast(new cv::Mat(std::move(pair->processed))); +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: getFrameCaptureTime + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getFrameCaptureTime + (JNIEnv *, jclass, jlong pair_) +{ + MatPair *pair = reinterpret_cast(pair_); + if (!pair) { + // NULL + return 0; + } + + return pair->captureTimestamp; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: releasePair + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_releasePair + (JNIEnv *, jclass, jlong pair_) +{ + MatPair *pair = reinterpret_cast(pair_); + if (!pair) { + // NULL + return false; + } + + delete pair; + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: setGpuProcessType + * Signature: (JI)Z + */ +JNIEXPORT jboolean JNICALL +Java_org_photonvision_raspi_LibCameraJNI_setGpuProcessType + (JNIEnv *, jclass, jlong runner_, jint idx) +{ + CameraRunner *runner = reinterpret_cast(runner_); + if (!runner) { + return false; + } + + runner->requestShaderIdx(idx); + + return true; +} + +/* + * Class: org_photonvision_raspi_LibCameraJNI + * Method: getGpuProcessType + * Signature: (J)I + */ +JNIEXPORT jint JNICALL +Java_org_photonvision_raspi_LibCameraJNI_getGpuProcessType + (JNIEnv *, jclass, jlong pair_) +{ + MatPair *pair = reinterpret_cast(pair_); + if (!pair) { + // NULL + return 0; + } + return pair->frameProcessingType; +} + +} // extern "C" diff --git a/libcamera_opengl_utility.cpp b/src/libcamera_opengl_utility.cpp similarity index 56% rename from libcamera_opengl_utility.cpp rename to src/libcamera_opengl_utility.cpp index d562d12..4dc3443 100644 --- a/libcamera_opengl_utility.cpp +++ b/src/libcamera_opengl_utility.cpp @@ -1,9 +1,26 @@ -#include "libcamera_opengl_utility.h" +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ -#include +#include "libcamera_opengl_utility.h" #include +#include + EGLint rangeFromColorspace(const libcamera::ColorSpace &colorSpace) { if (colorSpace.range == libcamera::ColorSpace::Range::Full) { return EGL_YUV_FULL_RANGE_EXT;