mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
119 lines
3.2 KiB
C++
119 lines
3.2 KiB
C++
// Copyright (c) FIRST and other WPILib contributors.
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <thread>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <imgui.h>
|
|
#include <imgui_internal.h>
|
|
#include <opencv2/core/core.hpp>
|
|
#include <opencv2/core/mat.hpp>
|
|
#include <opencv2/imgproc.hpp>
|
|
#include "wpi/util/mutex.hpp"
|
|
#include "wpi/util/print.hpp"
|
|
#include "wpi/util/spinlock.hpp"
|
|
#include "wpi/gui/wpigui.hpp"
|
|
|
|
#include "wpi/cs/cscore.h"
|
|
#include "wpi/cs/cscore_cv.hpp"
|
|
|
|
namespace gui = wpi::gui;
|
|
|
|
int main() {
|
|
wpi::spinlock latestFrameMutex;
|
|
std::unique_ptr<cv::Mat> latestFrame;
|
|
wpi::mutex freeListMutex;
|
|
std::vector<std::unique_ptr<cv::Mat>> freeList;
|
|
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::print("error: {}\n", cvsink.GetError());
|
|
continue;
|
|
}
|
|
|
|
// get or create a mat
|
|
std::unique_ptr<cv::Mat> out;
|
|
{
|
|
std::scoped_lock lock{freeListMutex};
|
|
if (!freeList.empty()) {
|
|
out = std::move(freeList.back());
|
|
freeList.pop_back();
|
|
} else {
|
|
out = std::make_unique<cv::Mat>();
|
|
}
|
|
}
|
|
|
|
// convert to RGBA
|
|
cv::cvtColor(frame, *out, cv::COLOR_BGR2RGBA);
|
|
|
|
{
|
|
// make available
|
|
std::scoped_lock lock{latestFrameMutex};
|
|
latestFrame.swap(out);
|
|
}
|
|
|
|
// put the previous frame on free list
|
|
if (out) {
|
|
std::scoped_lock lock{freeListMutex};
|
|
freeList.emplace_back(std::move(out));
|
|
}
|
|
}
|
|
});
|
|
|
|
gui::CreateContext();
|
|
gui::Initialize("Hello World", 1024, 768);
|
|
gui::Texture tex;
|
|
gui::AddEarlyExecute([&] {
|
|
std::unique_ptr<cv::Mat> frame;
|
|
{
|
|
std::scoped_lock lock{latestFrameMutex};
|
|
latestFrame.swap(frame);
|
|
}
|
|
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);
|
|
}
|
|
{
|
|
std::scoped_lock lock{freeListMutex};
|
|
freeList.emplace_back(std::move(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();
|
|
}
|