mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-01 02:41:48 +00:00
[cscore] Add wpigui-based USB camera viewer
This commit is contained in:
@@ -120,6 +120,11 @@ endif()
|
||||
add_subdirectory(wpiutil)
|
||||
add_subdirectory(ntcore)
|
||||
|
||||
if (WITH_SIMULATION_MODULES AND NOT USE_EXTERNAL_HAL)
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(wpigui)
|
||||
endif()
|
||||
|
||||
if (NOT WITHOUT_CSCORE)
|
||||
set(CSCORE_DEP_REPLACE ${CSCORE_DEP_REPLACE_IMPL})
|
||||
set(CAMERASERVER_DEP_REPLACE ${CAMERASERVER_DEP_REPLACE_IMPL})
|
||||
@@ -137,8 +142,6 @@ if (NOT WITHOUT_CSCORE)
|
||||
endif()
|
||||
|
||||
if (WITH_SIMULATION_MODULES AND NOT USE_EXTERNAL_HAL)
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(wpigui)
|
||||
add_subdirectory(simulation)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -52,10 +52,18 @@ install(EXPORT cscore DESTINATION ${cscore_config_dir})
|
||||
SUBDIR_LIST(cscore_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
|
||||
foreach(example ${cscore_examples})
|
||||
file(GLOB cscore_example_src examples/${example}/*.cpp)
|
||||
unset(add_libs)
|
||||
if(${example} STREQUAL "usbviewer")
|
||||
if(TARGET wpigui)
|
||||
set(add_libs wpigui)
|
||||
else()
|
||||
unset(cscore_example_src)
|
||||
endif()
|
||||
endif()
|
||||
if(cscore_example_src)
|
||||
add_executable(cscore_${example} ${cscore_example_src})
|
||||
wpilib_target_warnings(cscore_${example})
|
||||
target_link_libraries(cscore_${example} cscore)
|
||||
target_link_libraries(cscore_${example} cscore ${add_libs})
|
||||
set_property(TARGET cscore_${example} PROPERTY FOLDER "examples")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -170,17 +170,50 @@ nativeUtils.exportsConfigs {
|
||||
model {
|
||||
components {
|
||||
examplesMap.each { key, value ->
|
||||
"${key}"(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
binaries.all {
|
||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
|
||||
lib library: 'cscore', linkage: 'shared'
|
||||
if (key == "usbviewer") {
|
||||
if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxraspbian') && !project.hasProperty('onlylinuxaarch64bionic')) {
|
||||
"${key}"(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
binaries.all {
|
||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
|
||||
lib project: ':wpigui', library: 'wpigui', linkage: 'static'
|
||||
lib library: 'cscore', linkage: 'shared'
|
||||
nativeUtils.useRequiredLibrary(it, 'imgui_static')
|
||||
if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.raspbian || it.targetPlatform.name == nativeUtils.wpi.platforms.aarch64bionic) {
|
||||
it.buildable = false
|
||||
return
|
||||
}
|
||||
if (it.targetPlatform.operatingSystem.isWindows()) {
|
||||
it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib'
|
||||
} else if (it.targetPlatform.operatingSystem.isMacOsX()) {
|
||||
it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore'
|
||||
} else {
|
||||
it.linker.args << '-lX11'
|
||||
}
|
||||
}
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
srcDirs 'examples/' + "${key}"
|
||||
include '**/*.cpp'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
srcDirs 'examples/' + "${key}"
|
||||
include '**/*.cpp'
|
||||
} else {
|
||||
"${key}"(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
binaries.all {
|
||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
|
||||
lib library: 'cscore', linkage: 'shared'
|
||||
}
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
srcDirs 'examples/' + "${key}"
|
||||
include '**/*.cpp'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
114
cscore/examples/usbviewer/usbviewer.cpp
Normal file
114
cscore/examples/usbviewer/usbviewer.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <wpi/raw_ostream.h>
|
||||
#include <wpi/spinlock.h>
|
||||
#include <wpigui.h>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
|
||||
namespace gui = wpi::gui;
|
||||
|
||||
int main() {
|
||||
std::atomic<cv::Mat*> latestFrame;
|
||||
std::vector<cv::Mat*> sharedFreeList;
|
||||
wpi::spinlock sharedFreeListMutex;
|
||||
std::vector<cv::Mat*> sourceFreeList;
|
||||
std::atomic<bool> stopCamera{false};
|
||||
|
||||
cs::UsbCamera camera{"usbcam", 0};
|
||||
camera.SetVideoMode(cs::VideoMode::kMJPEG, 640, 480, 30);
|
||||
cs::CvSink cvsink{"cvsink"};
|
||||
cvsink.SetSource(camera);
|
||||
|
||||
std::thread thr([&] {
|
||||
cv::Mat frame;
|
||||
while (!stopCamera) {
|
||||
// get frame from camera
|
||||
uint64_t time = cvsink.GrabFrame(frame);
|
||||
if (time == 0) {
|
||||
wpi::outs() << "error: " << cvsink.GetError() << '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
// get or create a mat, prefer sourceFreeList over sharedFreeList
|
||||
cv::Mat* out;
|
||||
if (!sourceFreeList.empty()) {
|
||||
out = sourceFreeList.back();
|
||||
sourceFreeList.pop_back();
|
||||
} else {
|
||||
{
|
||||
std::scoped_lock lock(sharedFreeListMutex);
|
||||
for (auto mat : sharedFreeList) sourceFreeList.emplace_back(mat);
|
||||
sharedFreeList.clear();
|
||||
}
|
||||
if (!sourceFreeList.empty()) {
|
||||
out = sourceFreeList.back();
|
||||
sourceFreeList.pop_back();
|
||||
} else {
|
||||
out = new cv::Mat;
|
||||
}
|
||||
}
|
||||
|
||||
// convert to RGBA
|
||||
cv::cvtColor(frame, *out, cv::COLOR_BGR2RGBA);
|
||||
|
||||
// make available
|
||||
auto prev = latestFrame.exchange(out);
|
||||
|
||||
// put prev on free list
|
||||
if (prev) sourceFreeList.emplace_back(prev);
|
||||
}
|
||||
});
|
||||
|
||||
gui::CreateContext();
|
||||
gui::Initialize("Hello World", 1024, 768);
|
||||
gui::Texture tex;
|
||||
gui::AddEarlyExecute([&] {
|
||||
auto frame = latestFrame.exchange(nullptr);
|
||||
if (frame) {
|
||||
// create or update texture
|
||||
if (!tex || frame->cols != tex.GetWidth() ||
|
||||
frame->rows != tex.GetHeight()) {
|
||||
tex = gui::Texture(gui::kPixelRGBA, frame->cols, frame->rows,
|
||||
frame->data);
|
||||
} else {
|
||||
tex.Update(frame->data);
|
||||
}
|
||||
// put back on shared freelist
|
||||
std::scoped_lock lock(sharedFreeListMutex);
|
||||
sharedFreeList.emplace_back(frame);
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(640, 480), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::Begin("Video")) {
|
||||
// render to window (best fit)
|
||||
if (tex && tex.GetWidth() != 0 && tex.GetHeight() != 0) {
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
ImVec2 windowPos = ImGui::GetWindowPos();
|
||||
ImVec2 imageMin = ImGui::GetWindowContentRegionMin();
|
||||
ImVec2 imageMax = ImGui::GetWindowContentRegionMax();
|
||||
gui::MaxFit(&imageMin, &imageMax, tex.GetWidth(), tex.GetHeight());
|
||||
drawList->AddImage(tex, windowPos + imageMin, windowPos + imageMax);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
});
|
||||
gui::Main();
|
||||
stopCamera = true;
|
||||
thr.join();
|
||||
}
|
||||
Reference in New Issue
Block a user