mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
@@ -13,6 +13,8 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "wpi/cameraserver/CameraServerShared.hpp"
|
||||
#include "wpi/cs/VideoEvent.hpp"
|
||||
#include "wpi/cs/VideoListener.hpp"
|
||||
#include "wpi/nt/BooleanTopic.hpp"
|
||||
#include "wpi/nt/IntegerTopic.hpp"
|
||||
#include "wpi/nt/NetworkTable.hpp"
|
||||
@@ -247,17 +249,17 @@ void Instance::UpdateStreamValues() {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string PixelFormatToString(int pixelFormat) {
|
||||
static std::string PixelFormatToString(wpi::util::PixelFormat pixelFormat) {
|
||||
switch (pixelFormat) {
|
||||
case cs::VideoMode::PixelFormat::kMJPEG:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
return "MJPEG";
|
||||
case cs::VideoMode::PixelFormat::kYUYV:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
return "YUYV";
|
||||
case cs::VideoMode::PixelFormat::kRGB565:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
return "RGB565";
|
||||
case cs::VideoMode::PixelFormat::kBGR:
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
return "BGR";
|
||||
case cs::VideoMode::PixelFormat::kGray:
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
return "Gray";
|
||||
default:
|
||||
return "Unknown";
|
||||
@@ -506,7 +508,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
|
||||
cs::MjpegServer CameraServer::AddSwitchedCamera(std::string_view name) {
|
||||
auto& inst = ::GetInstance();
|
||||
// create a dummy CvSource
|
||||
cs::CvSource source{name, cs::VideoMode::PixelFormat::kMJPEG, 160, 120, 30};
|
||||
cs::CvSource source{name, wpi::util::PixelFormat::kMJPEG, 160, 120, 30};
|
||||
cs::MjpegServer server = StartAutomaticCapture(source);
|
||||
inst.m_fixedSources[server.GetHandle()] = source.GetHandle();
|
||||
|
||||
@@ -568,7 +570,7 @@ cs::CvSink CameraServer::GetVideo(const cs::VideoSource& camera) {
|
||||
}
|
||||
|
||||
cs::CvSink CameraServer::GetVideo(const cs::VideoSource& camera,
|
||||
cs::VideoMode::PixelFormat pixelFormat) {
|
||||
wpi::util::PixelFormat pixelFormat) {
|
||||
auto& inst = ::GetInstance();
|
||||
wpi::util::SmallString<64> name{"opencv_"};
|
||||
name += camera.GetName();
|
||||
@@ -611,7 +613,7 @@ cs::CvSink CameraServer::GetVideo(std::string_view name) {
|
||||
}
|
||||
|
||||
cs::CvSink CameraServer::GetVideo(std::string_view name,
|
||||
cs::VideoMode::PixelFormat pixelFormat) {
|
||||
wpi::util::PixelFormat pixelFormat) {
|
||||
auto& inst = ::GetInstance();
|
||||
cs::VideoSource source;
|
||||
{
|
||||
@@ -630,7 +632,7 @@ cs::CvSink CameraServer::GetVideo(std::string_view name,
|
||||
cs::CvSource CameraServer::PutVideo(std::string_view name, int width,
|
||||
int height) {
|
||||
::GetInstance();
|
||||
cs::CvSource source{name, cs::VideoMode::kMJPEG, width, height, 30};
|
||||
cs::CvSource source{name, wpi::util::PixelFormat::kMJPEG, width, height, 30};
|
||||
StartAutomaticCapture(source);
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/cs/cscore_cv.hpp"
|
||||
#include "wpi/cs/CvSink.hpp"
|
||||
#include "wpi/cs/CvSource.hpp"
|
||||
#include "wpi/cs/MjpegServer.hpp"
|
||||
#include "wpi/cs/UsbCamera.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
@@ -107,7 +108,7 @@ class CameraServer {
|
||||
* camera
|
||||
*/
|
||||
static cs::CvSink GetVideo(const cs::VideoSource& camera,
|
||||
cs::VideoMode::PixelFormat pixelFormat);
|
||||
wpi::util::PixelFormat pixelFormat);
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the specified camera. This allows you to get
|
||||
@@ -126,7 +127,7 @@ class CameraServer {
|
||||
* camera
|
||||
*/
|
||||
static cs::CvSink GetVideo(std::string_view name,
|
||||
cs::VideoMode::PixelFormat pixelFormat);
|
||||
wpi::util::PixelFormat pixelFormat);
|
||||
|
||||
/**
|
||||
* Create a MJPEG stream with OpenCV input. This can be called to pass custom
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "wpi/cs/cscore_cv.hpp"
|
||||
#include "wpi/cs/CvSink.hpp"
|
||||
#include "wpi/cs/VideoSource.hpp"
|
||||
#include "wpi/vision/VisionPipeline.hpp"
|
||||
|
||||
namespace wpi::vision {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "wpi/cs/cscore.h"
|
||||
#include "wpi/cs/UsbCamera.hpp"
|
||||
#include "wpi/util/print.hpp"
|
||||
|
||||
int main() {
|
||||
@@ -57,13 +57,13 @@ int main() {
|
||||
for (const auto& mode : camera.EnumerateVideoModes()) {
|
||||
const char* pixelFormat;
|
||||
switch (mode.pixelFormat) {
|
||||
case wpi::cs::VideoMode::kMJPEG:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
pixelFormat = "MJPEG";
|
||||
break;
|
||||
case wpi::cs::VideoMode::kYUYV:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
pixelFormat = "YUYV";
|
||||
break;
|
||||
case wpi::cs::VideoMode::kRGB565:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
pixelFormat = "RGB565";
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -6,17 +6,19 @@
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
#include "wpi/cs/cscore.h"
|
||||
#include "wpi/cs/cscore_cv.hpp"
|
||||
#include "wpi/cs/CvSink.hpp"
|
||||
#include "wpi/cs/CvSource.hpp"
|
||||
#include "wpi/cs/HttpCamera.hpp"
|
||||
#include "wpi/cs/MjpegServer.hpp"
|
||||
#include "wpi/util/print.hpp"
|
||||
|
||||
int main() {
|
||||
wpi::cs::HttpCamera camera{"httpcam", "http://localhost:8081/?action=stream"};
|
||||
camera.SetVideoMode(wpi::cs::VideoMode::kMJPEG, 320, 240, 30);
|
||||
camera.SetVideoMode(wpi::util::PixelFormat::kMJPEG, 320, 240, 30);
|
||||
wpi::cs::CvSink cvsink{"cvsink"};
|
||||
cvsink.SetSource(camera);
|
||||
wpi::cs::CvSource cvsource{"cvsource", wpi::cs::VideoMode::kMJPEG, 320, 240,
|
||||
30};
|
||||
wpi::cs::CvSource cvsource{"cvsource", wpi::util::PixelFormat::kMJPEG, 320,
|
||||
240, 30};
|
||||
wpi::cs::MjpegServer cvMjpegServer{"cvhttpserver", 8083};
|
||||
cvMjpegServer.SetSource(cvsource);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
|
||||
#include "wpi/cs/cscore.h"
|
||||
#include "wpi/cs/UsbCamera.hpp"
|
||||
#include "wpi/util/StringExtras.hpp"
|
||||
#include "wpi/util/print.hpp"
|
||||
|
||||
|
||||
@@ -4,18 +4,21 @@
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
#include "wpi/cs/cscore_cv.hpp"
|
||||
#include "wpi/cs/CvSink.hpp"
|
||||
#include "wpi/cs/CvSource.hpp"
|
||||
#include "wpi/cs/MjpegServer.hpp"
|
||||
#include "wpi/cs/UsbCamera.hpp"
|
||||
#include "wpi/util/print.hpp"
|
||||
|
||||
int main() {
|
||||
wpi::cs::UsbCamera camera{"usbcam", 0};
|
||||
camera.SetVideoMode(wpi::cs::VideoMode::kMJPEG, 320, 240, 30);
|
||||
camera.SetVideoMode(wpi::util::PixelFormat::kMJPEG, 320, 240, 30);
|
||||
wpi::cs::MjpegServer mjpegServer{"httpserver", 8081};
|
||||
mjpegServer.SetSource(camera);
|
||||
wpi::cs::CvSink cvsink{"cvsink"};
|
||||
cvsink.SetSource(camera);
|
||||
wpi::cs::CvSource cvsource{"cvsource", wpi::cs::VideoMode::kMJPEG, 320, 240,
|
||||
30};
|
||||
wpi::cs::CvSource cvsource{"cvsource", wpi::util::PixelFormat::kMJPEG, 320,
|
||||
240, 30};
|
||||
wpi::cs::MjpegServer cvMjpegServer{"cvhttpserver", 8082};
|
||||
cvMjpegServer.SetSource(cvsource);
|
||||
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "wpi/cs/cscore.h"
|
||||
#include "wpi/cs/MjpegServer.hpp"
|
||||
#include "wpi/cs/RawEvent.hpp"
|
||||
#include "wpi/cs/UsbCamera.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/print.hpp"
|
||||
|
||||
int main() {
|
||||
@@ -14,7 +17,7 @@ int main() {
|
||||
wpi::util::print(" {}\n", addr);
|
||||
}
|
||||
wpi::cs::UsbCamera camera{"usbcam", 0};
|
||||
camera.SetVideoMode(wpi::cs::VideoMode::kMJPEG, 320, 240, 30);
|
||||
camera.SetVideoMode(wpi::util::PixelFormat::kMJPEG, 320, 240, 30);
|
||||
wpi::cs::MjpegServer mjpegServer{"httpserver", 8081};
|
||||
mjpegServer.SetSource(camera);
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include <opencv2/core/mat.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include "wpi/cs/cscore.h"
|
||||
#include "wpi/cs/cscore_cv.hpp"
|
||||
#include "wpi/cs/CvSink.hpp"
|
||||
#include "wpi/cs/UsbCamera.hpp"
|
||||
#include "wpi/gui/wpigui.hpp"
|
||||
#include "wpi/util/mutex.hpp"
|
||||
#include "wpi/util/print.hpp"
|
||||
@@ -31,7 +31,7 @@ int main() {
|
||||
std::atomic<bool> stopCamera{false};
|
||||
|
||||
wpi::cs::UsbCamera camera{"usbcam", 0};
|
||||
camera.SetVideoMode(wpi::cs::VideoMode::kMJPEG, 640, 480, 30);
|
||||
camera.SetVideoMode(wpi::util::PixelFormat::kMJPEG, 640, 480, 30);
|
||||
wpi::cs::CvSink cvsink{"cvsink"};
|
||||
cvsink.SetSource(camera);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// 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 "wpi/cs/cscore.h"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/print.hpp"
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -6,11 +6,9 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "SourceImpl.hpp"
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "Instance.hpp"
|
||||
#include "SourceImpl.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
|
||||
using namespace wpi::cs;
|
||||
|
||||
@@ -67,7 +68,7 @@ Image* Frame::GetNearestImage(int width, int height) const {
|
||||
}
|
||||
|
||||
Image* Frame::GetNearestImage(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
wpi::util::PixelFormat pixelFormat,
|
||||
int jpegQuality) const {
|
||||
if (!m_impl) {
|
||||
return nullptr;
|
||||
@@ -93,22 +94,23 @@ Image* Frame::GetNearestImage(int width, int height,
|
||||
|
||||
// 2) Same width, height, different (but non-JPEG) pixelFormat (color conv)
|
||||
// 2a) If we want JPEG output, prefer BGR over other pixel formats
|
||||
if (pixelFormat == VideoMode::kMJPEG) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kMJPEG) {
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->Is(width, height, VideoMode::kBGR)) {
|
||||
if (i->Is(width, height, wpi::util::PixelFormat::kBGR)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->Is(width, height) && i->pixelFormat != VideoMode::kMJPEG) {
|
||||
if (i->Is(width, height) &&
|
||||
i->pixelFormat != wpi::util::PixelFormat::kMJPEG) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Different width, height, same pixelFormat (only if non-JPEG) (resample)
|
||||
if (pixelFormat != VideoMode::kMJPEG) {
|
||||
if (pixelFormat != wpi::util::PixelFormat::kMJPEG) {
|
||||
// 3a) Smallest image at least width/height in size
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->IsLarger(width, height) && i->pixelFormat == pixelFormat &&
|
||||
@@ -135,7 +137,8 @@ Image* Frame::GetNearestImage(int width, int height,
|
||||
// (color conversion + resample)
|
||||
// 4a) Smallest image at least width/height in size
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->IsLarger(width, height) && i->pixelFormat != VideoMode::kMJPEG &&
|
||||
if (i->IsLarger(width, height) &&
|
||||
i->pixelFormat != wpi::util::PixelFormat::kMJPEG &&
|
||||
(!found || (i->IsSmaller(*found)))) {
|
||||
found = i;
|
||||
}
|
||||
@@ -146,7 +149,7 @@ Image* Frame::GetNearestImage(int width, int height,
|
||||
|
||||
// 4b) Largest image (less than width/height)
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->pixelFormat != VideoMode::kMJPEG &&
|
||||
if (i->pixelFormat != wpi::util::PixelFormat::kMJPEG &&
|
||||
(!found || (i->IsLarger(*found)))) {
|
||||
found = i;
|
||||
}
|
||||
@@ -158,7 +161,7 @@ Image* Frame::GetNearestImage(int width, int height,
|
||||
// 5) Same width, height, JPEG pixelFormat (decompression). As there may be
|
||||
// multiple JPEG images, find the highest quality one.
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->Is(width, height, VideoMode::kMJPEG) &&
|
||||
if (i->Is(width, height, wpi::util::PixelFormat::kMJPEG) &&
|
||||
(!found || i->jpegQuality > found->jpegQuality)) {
|
||||
found = i;
|
||||
// consider one without a quality setting to be the highest quality
|
||||
@@ -175,7 +178,8 @@ Image* Frame::GetNearestImage(int width, int height,
|
||||
// 6) Different width, height, JPEG pixelFormat (decompression)
|
||||
// 6a) Smallest image at least width/height in size
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->IsLarger(width, height) && i->pixelFormat == VideoMode::kMJPEG &&
|
||||
if (i->IsLarger(width, height) &&
|
||||
i->pixelFormat == wpi::util::PixelFormat::kMJPEG &&
|
||||
(!found || (i->IsSmaller(*found)))) {
|
||||
found = i;
|
||||
}
|
||||
@@ -186,7 +190,7 @@ Image* Frame::GetNearestImage(int width, int height,
|
||||
|
||||
// 6b) Largest image (less than width/height)
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->pixelFormat != VideoMode::kMJPEG &&
|
||||
if (i->pixelFormat != wpi::util::PixelFormat::kMJPEG &&
|
||||
(!found || (i->IsLarger(*found)))) {
|
||||
found = i;
|
||||
}
|
||||
@@ -199,7 +203,7 @@ Image* Frame::GetNearestImage(int width, int height,
|
||||
return m_impl->images.empty() ? nullptr : m_impl->images[0];
|
||||
}
|
||||
|
||||
Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
|
||||
Image* Frame::ConvertImpl(Image* image, wpi::util::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality) {
|
||||
if (!image || image->Is(image->width, image->height, pixelFormat,
|
||||
requiredJpegQuality)) {
|
||||
@@ -211,169 +215,171 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
|
||||
// anything else with it. Note that if the destination format is JPEG, we
|
||||
// still need to do this (unless it was already a JPEG, in which case we
|
||||
// would have returned above).
|
||||
if (cur->pixelFormat == VideoMode::kMJPEG) {
|
||||
if (cur->pixelFormat == wpi::util::PixelFormat::kMJPEG) {
|
||||
cur = ConvertMJPEGToBGR(cur);
|
||||
if (pixelFormat == VideoMode::kBGR) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kBGR) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
// Color convert
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::kRGB565:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
// If source is YUYV, UYVY, Gray, or Y16, need to convert to BGR first
|
||||
if (cur->pixelFormat == VideoMode::kYUYV) {
|
||||
if (cur->pixelFormat == wpi::util::PixelFormat::kYUYV) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertYUYVToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kUYVY) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kUYVY) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertUYVYToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kGray) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kGray) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertGrayToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kY16) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kY16) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
VideoMode::kGray)) {
|
||||
} else if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kGray)) {
|
||||
cur = ConvertGrayToBGR(newImage);
|
||||
} else {
|
||||
cur = ConvertGrayToBGR(ConvertY16ToGray(cur));
|
||||
}
|
||||
}
|
||||
return ConvertBGRToRGB565(cur);
|
||||
case VideoMode::kGray:
|
||||
case VideoMode::kY16:
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
// If source is also grayscale, convert directly
|
||||
if (pixelFormat == VideoMode::kGray &&
|
||||
cur->pixelFormat == VideoMode::kY16) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kGray &&
|
||||
cur->pixelFormat == wpi::util::PixelFormat::kY16) {
|
||||
return ConvertY16ToGray(cur);
|
||||
} else if (pixelFormat == VideoMode::kY16 &&
|
||||
cur->pixelFormat == VideoMode::kGray) {
|
||||
} else if (pixelFormat == wpi::util::PixelFormat::kY16 &&
|
||||
cur->pixelFormat == wpi::util::PixelFormat::kGray) {
|
||||
return ConvertGrayToY16(cur);
|
||||
}
|
||||
// If source is YUYV, UYVY, convert directly to Gray
|
||||
// If RGB565, need to convert to BGR first
|
||||
if (cur->pixelFormat == VideoMode::kYUYV) {
|
||||
if (cur->pixelFormat == wpi::util::PixelFormat::kYUYV) {
|
||||
cur = ConvertYUYVToGray(cur);
|
||||
} else if (cur->pixelFormat == VideoMode::kUYVY) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kUYVY) {
|
||||
cur = ConvertUYVYToGray(cur);
|
||||
} else if (cur->pixelFormat == VideoMode::kRGB565) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kRGB565) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertRGB565ToBGR(cur);
|
||||
}
|
||||
cur = ConvertBGRToGray(cur);
|
||||
}
|
||||
if (pixelFormat == VideoMode::kY16) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kY16) {
|
||||
cur = ConvertGrayToY16(cur);
|
||||
}
|
||||
return cur;
|
||||
case VideoMode::kBGR:
|
||||
case VideoMode::kMJPEG:
|
||||
if (cur->pixelFormat == VideoMode::kYUYV) {
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
if (cur->pixelFormat == wpi::util::PixelFormat::kYUYV) {
|
||||
cur = ConvertYUYVToBGR(cur);
|
||||
} else if (cur->pixelFormat == VideoMode::kUYVY) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kUYVY) {
|
||||
cur = ConvertUYVYToBGR(cur);
|
||||
} else if (cur->pixelFormat == VideoMode::kRGB565) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kRGB565) {
|
||||
cur = ConvertRGB565ToBGR(cur);
|
||||
} else if (cur->pixelFormat == VideoMode::kGray) {
|
||||
if (pixelFormat == VideoMode::kBGR) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kGray) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kBGR) {
|
||||
return ConvertGrayToBGR(cur);
|
||||
} else {
|
||||
return ConvertGrayToMJPEG(cur, defaultJpegQuality);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kY16) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kY16) {
|
||||
// Check to see if Gray version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kGray)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kGray)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertY16ToGray(cur);
|
||||
}
|
||||
if (pixelFormat == VideoMode::kBGR) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kBGR) {
|
||||
return ConvertGrayToBGR(cur);
|
||||
} else {
|
||||
return ConvertGrayToMJPEG(cur, defaultJpegQuality);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
case wpi::util::PixelFormat::kBGRA:
|
||||
// If source is RGB565, YUYV, UYVY, Gray or Y16, need to convert to BGR
|
||||
// first
|
||||
if (cur->pixelFormat == VideoMode::kRGB565) {
|
||||
if (cur->pixelFormat == wpi::util::PixelFormat::kRGB565) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertRGB565ToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kYUYV) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kYUYV) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertYUYVToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kUYVY) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kUYVY) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertUYVYToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kGray) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kGray) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertGrayToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kY16) {
|
||||
} else if (cur->pixelFormat == wpi::util::PixelFormat::kY16) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kBGR)) {
|
||||
cur = newImage;
|
||||
} else if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
VideoMode::kGray)) {
|
||||
} else if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height,
|
||||
wpi::util::PixelFormat::kGray)) {
|
||||
cur = ConvertGrayToBGR(newImage);
|
||||
} else {
|
||||
cur = ConvertGrayToBGR(ConvertY16ToGray(cur));
|
||||
}
|
||||
}
|
||||
return ConvertBGRToBGRA(cur);
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kUYVY:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
default:
|
||||
return nullptr; // Unsupported
|
||||
}
|
||||
|
||||
// Compress if destination is JPEG
|
||||
if (pixelFormat == VideoMode::kMJPEG) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kMJPEG) {
|
||||
cur = ConvertBGRToMJPEG(cur, defaultJpegQuality);
|
||||
}
|
||||
|
||||
@@ -381,14 +387,14 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
|
||||
}
|
||||
|
||||
Image* Frame::ConvertMJPEGToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kMJPEG) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kMJPEG) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate an BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Decode
|
||||
cv::Mat newMat = newImage->AsMat();
|
||||
@@ -404,14 +410,14 @@ Image* Frame::ConvertMJPEGToBGR(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertMJPEGToGray(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kMJPEG) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kMJPEG) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate an grayscale image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height,
|
||||
image->width * image->height);
|
||||
m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width,
|
||||
image->height, image->width * image->height);
|
||||
|
||||
// Decode
|
||||
cv::Mat newMat = newImage->AsMat();
|
||||
@@ -427,14 +433,14 @@ Image* Frame::ConvertMJPEGToGray(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertYUYVToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kYUYV) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kYUYV) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2BGR_YUYV);
|
||||
@@ -449,14 +455,14 @@ Image* Frame::ConvertYUYVToBGR(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertYUYVToGray(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kYUYV) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kYUYV) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a grayscale image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height,
|
||||
image->width * image->height);
|
||||
m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width,
|
||||
image->height, image->width * image->height);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2GRAY_YUYV);
|
||||
@@ -471,14 +477,14 @@ Image* Frame::ConvertYUYVToGray(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertUYVYToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kUYVY) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kUYVY) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2BGR_UYVY);
|
||||
@@ -493,14 +499,14 @@ Image* Frame::ConvertUYVYToBGR(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertUYVYToGray(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kUYVY) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kUYVY) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a grayscale image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height,
|
||||
image->width * image->height);
|
||||
m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width,
|
||||
image->height, image->width * image->height);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2GRAY_UYVY);
|
||||
@@ -515,14 +521,14 @@ Image* Frame::ConvertUYVYToGray(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToRGB565(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kBGR) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kBGR) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a RGB565 image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kRGB565, image->width, image->height,
|
||||
image->width * image->height * 2);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kRGB565,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 2);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_RGB2BGR565);
|
||||
@@ -537,14 +543,14 @@ Image* Frame::ConvertBGRToRGB565(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertRGB565ToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kRGB565) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kRGB565) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR5652RGB);
|
||||
@@ -559,14 +565,14 @@ Image* Frame::ConvertRGB565ToBGR(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToGray(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kBGR) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kBGR) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a Grayscale image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height,
|
||||
image->width * image->height);
|
||||
m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width,
|
||||
image->height, image->width * image->height);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR2GRAY);
|
||||
@@ -581,14 +587,14 @@ Image* Frame::ConvertBGRToGray(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertGrayToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kGray) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kGray) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_GRAY2BGR);
|
||||
@@ -603,7 +609,7 @@ Image* Frame::ConvertGrayToBGR(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToMJPEG(Image* image, int quality) {
|
||||
if (!image || image->pixelFormat != VideoMode::kBGR) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kBGR) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!m_impl) {
|
||||
@@ -617,9 +623,9 @@ Image* Frame::ConvertBGRToMJPEG(Image* image, int quality) {
|
||||
// Per Wikipedia, Q=100 on a sample image results in 8.25 bits per pixel,
|
||||
// this is a little bit more conservative in assuming 50% space savings over
|
||||
// the equivalent BGR image.
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kMJPEG, image->width, image->height,
|
||||
image->width * image->height * 1.5);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kMJPEG,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 1.5);
|
||||
|
||||
// Compress
|
||||
if (m_impl->compressionParams.empty()) {
|
||||
@@ -638,7 +644,7 @@ Image* Frame::ConvertBGRToMJPEG(Image* image, int quality) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) {
|
||||
if (!image || image->pixelFormat != VideoMode::kGray) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kGray) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!m_impl) {
|
||||
@@ -652,9 +658,9 @@ Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) {
|
||||
// Per Wikipedia, Q=100 on a sample image results in 8.25 bits per pixel,
|
||||
// this is a little bit more conservative in assuming 25% space savings over
|
||||
// the equivalent grayscale image.
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kMJPEG, image->width, image->height,
|
||||
image->width * image->height * 0.75);
|
||||
auto newImage = m_impl->source.AllocImage(
|
||||
wpi::util::PixelFormat::kMJPEG, image->width, image->height,
|
||||
image->width * image->height * 0.75);
|
||||
|
||||
// Compress
|
||||
if (m_impl->compressionParams.empty()) {
|
||||
@@ -673,14 +679,14 @@ Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertGrayToY16(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kGray) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kGray) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a Y16 image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kY16, image->width, image->height,
|
||||
image->width * image->height * 2);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kY16,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 2);
|
||||
|
||||
// Convert with linear scaling
|
||||
image->AsMat().convertTo(newImage->AsMat(), CV_16U, 256);
|
||||
@@ -695,14 +701,14 @@ Image* Frame::ConvertGrayToY16(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertY16ToGray(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kY16) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kY16) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a Grayscale image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height,
|
||||
image->width * image->height);
|
||||
m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width,
|
||||
image->height, image->width * image->height);
|
||||
|
||||
// Scale min to 0 and max to 255
|
||||
cv::normalize(image->AsMat(), newImage->AsMat(), 255, 0, cv::NORM_MINMAX);
|
||||
@@ -717,14 +723,14 @@ Image* Frame::ConvertY16ToGray(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToBGRA(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kBGR) {
|
||||
if (!image || image->pixelFormat != wpi::util::PixelFormat::kBGR) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a RGB565 image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGRA, image->width, image->height,
|
||||
image->width * image->height * 4);
|
||||
auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGRA,
|
||||
image->width, image->height,
|
||||
image->width * image->height * 4);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR2BGRA);
|
||||
@@ -739,7 +745,7 @@ Image* Frame::ConvertBGRToBGRA(Image* image) {
|
||||
}
|
||||
|
||||
Image* Frame::GetImageImpl(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
wpi::util::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality) {
|
||||
if (!m_impl) {
|
||||
return nullptr;
|
||||
@@ -759,7 +765,7 @@ Image* Frame::GetImageImpl(int width, int height,
|
||||
// anything else with it. Note that if the destination format is JPEG, we
|
||||
// still need to do this (unless the width/height/compression were the same,
|
||||
// in which case we already returned the existing JPEG above).
|
||||
if (cur->pixelFormat == VideoMode::kMJPEG) {
|
||||
if (cur->pixelFormat == wpi::util::PixelFormat::kMJPEG) {
|
||||
cur = ConvertMJPEGToBGR(cur);
|
||||
}
|
||||
|
||||
@@ -784,7 +790,7 @@ Image* Frame::GetImageImpl(int width, int height,
|
||||
}
|
||||
|
||||
bool Frame::GetCv(cv::Mat& image, int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat) {
|
||||
wpi::util::PixelFormat pixelFormat) {
|
||||
Image* rawImage = GetImage(width, height, pixelFormat);
|
||||
if (!rawImage) {
|
||||
return false;
|
||||
@@ -809,7 +815,7 @@ std::unique_ptr<Image> CreateImageFromBGRA(wpi::cs::SourceImpl* source,
|
||||
cv::Mat finalImage{static_cast<int>(height), static_cast<int>(width), CV_8UC4,
|
||||
const_cast<uint8_t*>(data), stride};
|
||||
std::unique_ptr<Image> dest = source->AllocImage(
|
||||
VideoMode::PixelFormat::kBGR, width, height, width * height * 3);
|
||||
wpi::util::PixelFormat::kBGR, width, height, width * height * 3);
|
||||
cv::cvtColor(finalImage, dest->AsMat(), cv::COLOR_BGRA2BGR);
|
||||
return dest;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Image.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/RawFrame.h"
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/mutex.hpp"
|
||||
|
||||
@@ -109,13 +110,13 @@ class Frame {
|
||||
return m_impl->images[0]->height;
|
||||
}
|
||||
|
||||
int GetOriginalPixelFormat() const {
|
||||
wpi::util::PixelFormat GetOriginalPixelFormat() const {
|
||||
if (!m_impl) {
|
||||
return 0;
|
||||
return wpi::util::PixelFormat::kUnknown;
|
||||
}
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
if (m_impl->images.empty()) {
|
||||
return 0;
|
||||
return wpi::util::PixelFormat::kUnknown;
|
||||
}
|
||||
return m_impl->images[0]->pixelFormat;
|
||||
}
|
||||
@@ -156,7 +157,7 @@ class Frame {
|
||||
}
|
||||
|
||||
Image* GetExistingImage(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat) const {
|
||||
wpi::util::PixelFormat pixelFormat) const {
|
||||
if (!m_impl) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -170,7 +171,7 @@ class Frame {
|
||||
}
|
||||
|
||||
Image* GetExistingImage(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
wpi::util::PixelFormat pixelFormat,
|
||||
int jpegQuality) const {
|
||||
if (!m_impl) {
|
||||
return nullptr;
|
||||
@@ -186,18 +187,18 @@ class Frame {
|
||||
|
||||
Image* GetNearestImage(int width, int height) const;
|
||||
Image* GetNearestImage(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
wpi::util::PixelFormat pixelFormat,
|
||||
int jpegQuality = -1) const;
|
||||
|
||||
Image* Convert(Image* image, VideoMode::PixelFormat pixelFormat) {
|
||||
if (pixelFormat == VideoMode::kMJPEG) {
|
||||
Image* Convert(Image* image, wpi::util::PixelFormat pixelFormat) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kMJPEG) {
|
||||
return nullptr;
|
||||
}
|
||||
return ConvertImpl(image, pixelFormat, -1, 80);
|
||||
}
|
||||
Image* ConvertToMJPEG(Image* image, int requiredQuality,
|
||||
int defaultQuality = 80) {
|
||||
return ConvertImpl(image, VideoMode::kMJPEG, requiredQuality,
|
||||
return ConvertImpl(image, wpi::util::PixelFormat::kMJPEG, requiredQuality,
|
||||
defaultQuality);
|
||||
}
|
||||
Image* ConvertMJPEGToBGR(Image* image);
|
||||
@@ -216,28 +217,28 @@ class Frame {
|
||||
Image* ConvertY16ToGray(Image* image);
|
||||
Image* ConvertBGRToBGRA(Image* image);
|
||||
|
||||
Image* GetImage(int width, int height, VideoMode::PixelFormat pixelFormat) {
|
||||
if (pixelFormat == VideoMode::kMJPEG) {
|
||||
Image* GetImage(int width, int height, wpi::util::PixelFormat pixelFormat) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kMJPEG) {
|
||||
return nullptr;
|
||||
}
|
||||
return GetImageImpl(width, height, pixelFormat, -1, 80);
|
||||
}
|
||||
Image* GetImageMJPEG(int width, int height, int requiredQuality,
|
||||
int defaultQuality = 80) {
|
||||
return GetImageImpl(width, height, VideoMode::kMJPEG, requiredQuality,
|
||||
defaultQuality);
|
||||
return GetImageImpl(width, height, wpi::util::PixelFormat::kMJPEG,
|
||||
requiredQuality, defaultQuality);
|
||||
}
|
||||
|
||||
bool GetCv(cv::Mat& image, VideoMode::PixelFormat pixelFormat) {
|
||||
bool GetCv(cv::Mat& image, wpi::util::PixelFormat pixelFormat) {
|
||||
return GetCv(image, GetOriginalWidth(), GetOriginalHeight(), pixelFormat);
|
||||
}
|
||||
bool GetCv(cv::Mat& image, int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat);
|
||||
wpi::util::PixelFormat pixelFormat);
|
||||
|
||||
private:
|
||||
Image* ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
|
||||
Image* ConvertImpl(Image* image, wpi::util::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality);
|
||||
Image* GetImageImpl(int width, int height, VideoMode::PixelFormat pixelFormat,
|
||||
Image* GetImageImpl(int width, int height, wpi::util::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality);
|
||||
void DecRef() {
|
||||
if (m_impl && --(m_impl->refcount) == 0) {
|
||||
|
||||
@@ -305,7 +305,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::util::raw_istream& is,
|
||||
// the data directly into it.
|
||||
unsigned int contentLength = v.value();
|
||||
auto image =
|
||||
AllocImage(VideoMode::PixelFormat::kMJPEG, 0, 0, contentLength);
|
||||
AllocImage(wpi::util::PixelFormat::kMJPEG, 0, 0, contentLength);
|
||||
is.read(image->data(), contentLength);
|
||||
if (!m_active || is.has_error()) {
|
||||
return false;
|
||||
@@ -325,7 +325,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::util::raw_istream& is,
|
||||
PutError("did not receive a JPEG image", wpi::util::Now());
|
||||
return false;
|
||||
}
|
||||
PutFrame(VideoMode::PixelFormat::kMJPEG, width, height, imageBuf,
|
||||
PutFrame(wpi::util::PixelFormat::kMJPEG, width, height, imageBuf,
|
||||
wpi::util::Now());
|
||||
}
|
||||
|
||||
@@ -333,9 +333,9 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::util::raw_istream& is,
|
||||
|
||||
// update video mode if not set
|
||||
std::scoped_lock lock(m_mutex);
|
||||
if (m_mode.pixelFormat != VideoMode::PixelFormat::kMJPEG ||
|
||||
if (m_mode.pixelFormat != wpi::util::PixelFormat::kMJPEG ||
|
||||
m_mode.width == 0 || m_mode.height == 0) {
|
||||
m_mode.pixelFormat = VideoMode::PixelFormat::kMJPEG;
|
||||
m_mode.pixelFormat = wpi::util::PixelFormat::kMJPEG;
|
||||
m_mode.width = width;
|
||||
m_mode.height = height;
|
||||
}
|
||||
@@ -474,9 +474,9 @@ bool HttpCameraImpl::CacheProperties(CS_Status* status) const {
|
||||
|
||||
// Pretty typical set of video modes
|
||||
m_videoModes.clear();
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 640, 480, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 320, 240, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 160, 120, 30);
|
||||
m_videoModes.emplace_back(wpi::util::PixelFormat::kMJPEG, 640, 480, 30);
|
||||
m_videoModes.emplace_back(wpi::util::PixelFormat::kMJPEG, 320, 240, 30);
|
||||
m_videoModes.emplace_back(wpi::util::PixelFormat::kMJPEG, 160, 120, 30);
|
||||
|
||||
m_properties_cached = true;
|
||||
return true;
|
||||
@@ -525,7 +525,7 @@ void HttpCameraImpl::SetExposureManual(int value, CS_Status* status) {
|
||||
}
|
||||
|
||||
bool HttpCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
if (mode.pixelFormat != VideoMode::kMJPEG) {
|
||||
if (mode.pixelFormat != wpi::util::PixelFormat::kMJPEG) {
|
||||
return false;
|
||||
}
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
@@ -15,7 +14,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "SourceImpl.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/cs/VideoMode.hpp"
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
#include "wpi/net/HttpUtil.hpp"
|
||||
#include "wpi/util/StringMap.hpp"
|
||||
#include "wpi/util/condition_variable.hpp"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
#include "default_init_allocator.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
@@ -53,20 +53,20 @@ class Image {
|
||||
cv::Mat AsMat() {
|
||||
int type;
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kRGB565:
|
||||
case VideoMode::kY16:
|
||||
case VideoMode::kUYVY:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
type = CV_8UC2;
|
||||
break;
|
||||
case VideoMode::kBGR:
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
type = CV_8UC3;
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
case wpi::util::PixelFormat::kBGRA:
|
||||
type = CV_8UC4;
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
case VideoMode::kMJPEG:
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
default:
|
||||
type = CV_8UC1;
|
||||
break;
|
||||
@@ -76,18 +76,18 @@ class Image {
|
||||
|
||||
int GetStride() const {
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kRGB565:
|
||||
case VideoMode::kY16:
|
||||
case VideoMode::kUYVY:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
return 2 * width;
|
||||
case VideoMode::kBGR:
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
return 3 * width;
|
||||
case VideoMode::kBGRA:
|
||||
case wpi::util::PixelFormat::kBGRA:
|
||||
return 4 * width;
|
||||
case VideoMode::kGray:
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
return width;
|
||||
case VideoMode::kMJPEG:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -98,15 +98,16 @@ class Image {
|
||||
bool Is(int width_, int height_) {
|
||||
return width == width_ && height == height_;
|
||||
}
|
||||
bool Is(int width_, int height_, VideoMode::PixelFormat pixelFormat_) {
|
||||
bool Is(int width_, int height_, wpi::util::PixelFormat pixelFormat_) {
|
||||
return width == width_ && height == height_ && pixelFormat == pixelFormat_;
|
||||
}
|
||||
bool Is(int width_, int height_, VideoMode::PixelFormat pixelFormat_,
|
||||
bool Is(int width_, int height_, wpi::util::PixelFormat pixelFormat_,
|
||||
int jpegQuality_) {
|
||||
// Consider +/-5 on JPEG quality to be "close enough"
|
||||
return width == width_ && height == height_ &&
|
||||
pixelFormat == pixelFormat_ &&
|
||||
(pixelFormat != VideoMode::kMJPEG || jpegQuality_ == -1 ||
|
||||
(pixelFormat != wpi::util::PixelFormat::kMJPEG ||
|
||||
jpegQuality_ == -1 ||
|
||||
(jpegQuality != -1 && std::abs(jpegQuality - jpegQuality_) <= 5));
|
||||
}
|
||||
bool IsLarger(int width_, int height_) {
|
||||
@@ -122,7 +123,7 @@ class Image {
|
||||
std::vector<uchar> m_data;
|
||||
|
||||
public:
|
||||
VideoMode::PixelFormat pixelFormat{VideoMode::kUnknown};
|
||||
wpi::util::PixelFormat pixelFormat{wpi::util::PixelFormat::kUnknown};
|
||||
int width{0};
|
||||
int height{0};
|
||||
int jpegQuality{-1};
|
||||
|
||||
@@ -445,28 +445,28 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::util::raw_ostream& os,
|
||||
for (auto mode : source.EnumerateVideoModes(&status)) {
|
||||
os << "<tr><td>";
|
||||
switch (mode.pixelFormat) {
|
||||
case VideoMode::kMJPEG:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
os << "MJPEG";
|
||||
break;
|
||||
case VideoMode::kYUYV:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
os << "YUYV";
|
||||
break;
|
||||
case VideoMode::kRGB565:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
os << "RGB565";
|
||||
break;
|
||||
case VideoMode::kBGR:
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
os << "BGR";
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
case wpi::util::PixelFormat::kBGRA:
|
||||
os << "BGRA";
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
os << "gray";
|
||||
break;
|
||||
case VideoMode::kY16:
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
os << "Y16";
|
||||
break;
|
||||
case VideoMode::kUYVY:
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
os << "UYVY";
|
||||
break;
|
||||
default:
|
||||
@@ -565,25 +565,25 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::util::raw_ostream& os,
|
||||
os << '{';
|
||||
os << "\n\"pixelFormat\": \"";
|
||||
switch (mode.pixelFormat) {
|
||||
case VideoMode::kMJPEG:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
os << "MJPEG";
|
||||
break;
|
||||
case VideoMode::kYUYV:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
os << "YUYV";
|
||||
break;
|
||||
case VideoMode::kRGB565:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
os << "RGB565";
|
||||
break;
|
||||
case VideoMode::kBGR:
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
os << "BGR";
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
os << "gray";
|
||||
break;
|
||||
case VideoMode::kY16:
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
os << "Y16";
|
||||
break;
|
||||
case VideoMode::kUYVY:
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
os << "UYVY";
|
||||
break;
|
||||
default:
|
||||
@@ -752,15 +752,15 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::net::raw_socket_ostream& os) {
|
||||
bool addDHT = false;
|
||||
size_t locSOF = size;
|
||||
switch (image->pixelFormat) {
|
||||
case VideoMode::kMJPEG:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
// Determine if we need to add DHT to it, and allocate enough space
|
||||
// for adding it if required.
|
||||
addDHT = JpegNeedsDHT(data, &size, &locSOF);
|
||||
break;
|
||||
case VideoMode::kUYVY:
|
||||
case VideoMode::kRGB565:
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kY16:
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
default:
|
||||
// Bad frame; sleep for 10 ms so we don't consume all processor time.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
@@ -13,12 +13,7 @@
|
||||
|
||||
#include "SinkImpl.hpp"
|
||||
#include "wpi/net/NetworkAcceptor.hpp"
|
||||
#include "wpi/net/NetworkStream.hpp"
|
||||
#include "wpi/net/raw_socket_ostream.hpp"
|
||||
#include "wpi/util/SafeThread.hpp"
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/raw_istream.hpp"
|
||||
#include "wpi/util/raw_ostream.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#include <utility>
|
||||
|
||||
#include "Handle.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/cs/RawEvent.hpp"
|
||||
#include "wpi/cs/VideoMode.hpp"
|
||||
#include "wpi/util/CallbackManager.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "PropertyImpl.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
#include "wpi/util/StringMap.hpp"
|
||||
#include "wpi/util/json_fwd.hpp"
|
||||
#include "wpi/util/mutex.hpp"
|
||||
|
||||
@@ -100,7 +100,7 @@ uint64_t RawSinkImpl::GrabFrameImpl(WPI_RawFrame& rawFrame,
|
||||
auto width = rawFrame.width;
|
||||
auto height = rawFrame.height;
|
||||
auto pixelFormat =
|
||||
static_cast<VideoMode::PixelFormat>(rawFrame.pixelFormat);
|
||||
static_cast<wpi::util::PixelFormat>(rawFrame.pixelFormat);
|
||||
if (width <= 0 || height <= 0) {
|
||||
width = incomingFrame.GetOriginalWidth();
|
||||
height = incomingFrame.GetOriginalHeight();
|
||||
@@ -118,7 +118,7 @@ uint64_t RawSinkImpl::GrabFrameImpl(WPI_RawFrame& rawFrame,
|
||||
rawFrame.height = newImage->height;
|
||||
rawFrame.width = newImage->width;
|
||||
rawFrame.stride = newImage->GetStride();
|
||||
rawFrame.pixelFormat = newImage->pixelFormat;
|
||||
rawFrame.pixelFormat = static_cast<int>(newImage->pixelFormat);
|
||||
rawFrame.size = newImage->size();
|
||||
std::copy(newImage->data(), newImage->data() + rawFrame.size, rawFrame.data);
|
||||
rawFrame.timestamp = incomingFrame.GetTime();
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
|
||||
#include "Frame.hpp"
|
||||
#include "SinkImpl.hpp"
|
||||
#include "wpi/cs/cscore_raw.h"
|
||||
#include "wpi/util/condition_variable.hpp"
|
||||
#include "wpi/util/RawFrame.h"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ RawSourceImpl::~RawSourceImpl() = default;
|
||||
void RawSourceImpl::PutFrame(const WPI_RawFrame& image) {
|
||||
auto currentTime = wpi::util::Now();
|
||||
std::string_view data_view{reinterpret_cast<char*>(image.data), image.size};
|
||||
SourceImpl::PutFrame(static_cast<VideoMode::PixelFormat>(image.pixelFormat),
|
||||
SourceImpl::PutFrame(static_cast<wpi::util::PixelFormat>(image.pixelFormat),
|
||||
image.width, image.height, data_view, currentTime);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "ConfigurableSourceImpl.hpp"
|
||||
#include "SourceImpl.hpp"
|
||||
#include "wpi/cs/cscore_raw.h"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "SourceImpl.hpp"
|
||||
#include "wpi/util/Logger.hpp"
|
||||
#include "wpi/util/json_fwd.hpp"
|
||||
#include "wpi/util/mutex.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "Log.hpp"
|
||||
#include "Notifier.hpp"
|
||||
#include "Telemetry.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/StringExtras.hpp"
|
||||
#include "wpi/util/json.hpp"
|
||||
#include "wpi/util/timestamp.hpp"
|
||||
@@ -149,7 +150,7 @@ VideoMode SourceImpl::GetVideoMode(CS_Status* status) const {
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
bool SourceImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
bool SourceImpl::SetPixelFormat(wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status) {
|
||||
auto mode = GetVideoMode(status);
|
||||
if (!mode) {
|
||||
@@ -199,21 +200,21 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config,
|
||||
try {
|
||||
auto str = config.at("pixel format").get<std::string>();
|
||||
if (wpi::util::equals_lower(str, "mjpeg")) {
|
||||
mode.pixelFormat = wpi::cs::VideoMode::kMJPEG;
|
||||
mode.pixelFormat = wpi::util::PixelFormat::kMJPEG;
|
||||
} else if (wpi::util::equals_lower(str, "yuyv")) {
|
||||
mode.pixelFormat = wpi::cs::VideoMode::kYUYV;
|
||||
mode.pixelFormat = wpi::util::PixelFormat::kYUYV;
|
||||
} else if (wpi::util::equals_lower(str, "rgb565")) {
|
||||
mode.pixelFormat = wpi::cs::VideoMode::kRGB565;
|
||||
mode.pixelFormat = wpi::util::PixelFormat::kRGB565;
|
||||
} else if (wpi::util::equals_lower(str, "bgr")) {
|
||||
mode.pixelFormat = wpi::cs::VideoMode::kBGR;
|
||||
mode.pixelFormat = wpi::util::PixelFormat::kBGR;
|
||||
} else if (wpi::util::equals_lower(str, "bgra")) {
|
||||
mode.pixelFormat = wpi::cs::VideoMode::kBGRA;
|
||||
mode.pixelFormat = wpi::util::PixelFormat::kBGRA;
|
||||
} else if (wpi::util::equals_lower(str, "gray")) {
|
||||
mode.pixelFormat = wpi::cs::VideoMode::kGray;
|
||||
mode.pixelFormat = wpi::util::PixelFormat::kGray;
|
||||
} else if (wpi::util::equals_lower(str, "y16")) {
|
||||
mode.pixelFormat = wpi::cs::VideoMode::kY16;
|
||||
mode.pixelFormat = wpi::util::PixelFormat::kY16;
|
||||
} else if (wpi::util::equals_lower(str, "uyvy")) {
|
||||
mode.pixelFormat = wpi::cs::VideoMode::kUYVY;
|
||||
mode.pixelFormat = wpi::util::PixelFormat::kUYVY;
|
||||
} else {
|
||||
SWARNING("SetConfigJson: could not understand pixel format value '{}'",
|
||||
str);
|
||||
@@ -251,19 +252,18 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config,
|
||||
}
|
||||
|
||||
// if all of video mode is set, use SetVideoMode, otherwise piecemeal it
|
||||
if (mode.pixelFormat != VideoMode::kUnknown && mode.width != 0 &&
|
||||
if (mode.pixelFormat != wpi::util::PixelFormat::kUnknown && mode.width != 0 &&
|
||||
mode.height != 0 && mode.fps != 0) {
|
||||
SINFO(
|
||||
"SetConfigJson: setting video mode to pixelFormat {}, width {}, height "
|
||||
"{}, fps {}",
|
||||
mode.pixelFormat, mode.width, mode.height, mode.fps);
|
||||
static_cast<int>(mode.pixelFormat), mode.width, mode.height, mode.fps);
|
||||
SetVideoMode(mode, status);
|
||||
} else {
|
||||
if (mode.pixelFormat != wpi::cs::VideoMode::kUnknown) {
|
||||
SINFO("SetConfigJson: setting pixelFormat {}", mode.pixelFormat);
|
||||
SetPixelFormat(
|
||||
static_cast<wpi::cs::VideoMode::PixelFormat>(mode.pixelFormat),
|
||||
status);
|
||||
if (mode.pixelFormat != wpi::util::PixelFormat::kUnknown) {
|
||||
SINFO("SetConfigJson: setting pixelFormat {}",
|
||||
static_cast<int>(mode.pixelFormat));
|
||||
SetPixelFormat(mode.pixelFormat, status);
|
||||
}
|
||||
if (mode.width != 0 && mode.height != 0) {
|
||||
SINFO("SetConfigJson: setting width {}, height {}", mode.width,
|
||||
@@ -362,28 +362,28 @@ wpi::util::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
|
||||
// pixel format
|
||||
std::string_view pixelFormat;
|
||||
switch (m_mode.pixelFormat) {
|
||||
case VideoMode::kMJPEG:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
pixelFormat = "mjpeg";
|
||||
break;
|
||||
case VideoMode::kYUYV:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
pixelFormat = "yuyv";
|
||||
break;
|
||||
case VideoMode::kRGB565:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
pixelFormat = "rgb565";
|
||||
break;
|
||||
case VideoMode::kBGR:
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
pixelFormat = "bgr";
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
case wpi::util::PixelFormat::kBGRA:
|
||||
pixelFormat = "bgra";
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
pixelFormat = "gray";
|
||||
break;
|
||||
case VideoMode::kY16:
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
pixelFormat = "y16";
|
||||
break;
|
||||
case VideoMode::kUYVY:
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
pixelFormat = "uyvy";
|
||||
break;
|
||||
default:
|
||||
@@ -429,7 +429,7 @@ std::vector<VideoMode> SourceImpl::EnumerateVideoModes(
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> SourceImpl::AllocImage(
|
||||
VideoMode::PixelFormat pixelFormat, int width, int height, size_t size) {
|
||||
wpi::util::PixelFormat pixelFormat, int width, int height, size_t size) {
|
||||
std::unique_ptr<Image> image;
|
||||
{
|
||||
std::scoped_lock lock{m_poolMutex};
|
||||
@@ -464,10 +464,10 @@ std::unique_ptr<Image> SourceImpl::AllocImage(
|
||||
return image;
|
||||
}
|
||||
|
||||
void SourceImpl::PutFrame(VideoMode::PixelFormat pixelFormat, int width,
|
||||
void SourceImpl::PutFrame(wpi::util::PixelFormat pixelFormat, int width,
|
||||
int height, std::string_view data, Frame::Time time,
|
||||
WPI_TimestampSource timeSrc) {
|
||||
if (pixelFormat == VideoMode::PixelFormat::kBGRA) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kBGRA) {
|
||||
// Write BGRA as BGR to save a copy
|
||||
auto image =
|
||||
CreateImageFromBGRA(this, width, height, width * 4,
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Frame.hpp"
|
||||
#include "Handle.hpp"
|
||||
#include "Image.hpp"
|
||||
#include "PropertyContainer.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/cs/VideoMode.hpp"
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
#include "wpi/util/Logger.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/RawFrame.h"
|
||||
#include "wpi/util/condition_variable.hpp"
|
||||
#include "wpi/util/json_fwd.hpp"
|
||||
@@ -119,7 +120,7 @@ class SourceImpl : public PropertyContainer {
|
||||
|
||||
// These have default implementations but can be overridden for custom
|
||||
// or optimized behavior.
|
||||
virtual bool SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
virtual bool SetPixelFormat(wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status);
|
||||
virtual bool SetResolution(int width, int height, CS_Status* status);
|
||||
virtual bool SetFPS(int fps, CS_Status* status);
|
||||
@@ -131,7 +132,7 @@ class SourceImpl : public PropertyContainer {
|
||||
|
||||
std::vector<VideoMode> EnumerateVideoModes(CS_Status* status) const;
|
||||
|
||||
std::unique_ptr<Image> AllocImage(VideoMode::PixelFormat pixelFormat,
|
||||
std::unique_ptr<Image> AllocImage(wpi::util::PixelFormat pixelFormat,
|
||||
int width, int height, size_t size);
|
||||
|
||||
protected:
|
||||
@@ -139,7 +140,7 @@ class SourceImpl : public PropertyContainer {
|
||||
void UpdatePropertyValue(int property, bool setString, int value,
|
||||
std::string_view valueStr) override;
|
||||
|
||||
void PutFrame(VideoMode::PixelFormat pixelFormat, int width, int height,
|
||||
void PutFrame(wpi::util::PixelFormat pixelFormat, int width, int height,
|
||||
std::string_view data, Frame::Time time,
|
||||
WPI_TimestampSource timeSrc = WPI_TIMESRC_FRAME_DEQUEUE);
|
||||
void PutFrame(std::unique_ptr<Image> image, Frame::Time time,
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "Notifier.hpp"
|
||||
#include "SourceImpl.hpp"
|
||||
#include "wpi/util/DenseMap.hpp"
|
||||
#include "wpi/util/timestamp.h"
|
||||
|
||||
using namespace wpi::cs;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
#include "wpi/util/SafeThread.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
44
cscore/src/main/native/cpp/VideoSink.cpp
Normal file
44
cscore/src/main/native/cpp/VideoSink.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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 "wpi/cs/VideoSink.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "wpi/util/json.hpp"
|
||||
|
||||
using namespace wpi::cs;
|
||||
|
||||
wpi::util::json VideoSink::GetConfigJsonObject() const {
|
||||
m_status = 0;
|
||||
return GetSinkConfigJsonObject(m_handle, &m_status);
|
||||
}
|
||||
|
||||
std::vector<VideoProperty> VideoSink::EnumerateProperties() const {
|
||||
wpi::util::SmallVector<CS_Property, 32> handles_buf;
|
||||
CS_Status status = 0;
|
||||
auto handles = EnumerateSinkProperties(m_handle, handles_buf, &status);
|
||||
|
||||
std::vector<VideoProperty> properties;
|
||||
properties.reserve(handles.size());
|
||||
for (CS_Property handle : handles) {
|
||||
properties.emplace_back(VideoProperty{handle});
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::vector<VideoSink> VideoSink::EnumerateSinks() {
|
||||
wpi::util::SmallVector<CS_Sink, 16> handles_buf;
|
||||
CS_Status status = 0;
|
||||
auto handles = ::wpi::cs::EnumerateSinkHandles(handles_buf, &status);
|
||||
|
||||
std::vector<VideoSink> sinks;
|
||||
sinks.reserve(handles.size());
|
||||
for (int handle : handles) {
|
||||
sinks.emplace_back(VideoSink{handle});
|
||||
}
|
||||
return sinks;
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
// 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 "wpi/cs/cscore_oo.hpp"
|
||||
#include "wpi/cs/VideoSource.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "wpi/cs/VideoSink.hpp"
|
||||
#include "wpi/util/json.hpp"
|
||||
|
||||
using namespace wpi::cs;
|
||||
@@ -18,11 +18,6 @@ wpi::util::json VideoSource::GetConfigJsonObject() const {
|
||||
return GetSourceConfigJsonObject(m_handle, &m_status);
|
||||
}
|
||||
|
||||
wpi::util::json VideoSink::GetConfigJsonObject() const {
|
||||
m_status = 0;
|
||||
return GetSinkConfigJsonObject(m_handle, &m_status);
|
||||
}
|
||||
|
||||
std::vector<VideoProperty> VideoSource::EnumerateProperties() const {
|
||||
wpi::util::SmallVector<CS_Property, 32> handles_buf;
|
||||
CS_Status status = 0;
|
||||
@@ -61,29 +56,3 @@ std::vector<VideoSource> VideoSource::EnumerateSources() {
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
std::vector<VideoProperty> VideoSink::EnumerateProperties() const {
|
||||
wpi::util::SmallVector<CS_Property, 32> handles_buf;
|
||||
CS_Status status = 0;
|
||||
auto handles = EnumerateSinkProperties(m_handle, handles_buf, &status);
|
||||
|
||||
std::vector<VideoProperty> properties;
|
||||
properties.reserve(handles.size());
|
||||
for (CS_Property handle : handles) {
|
||||
properties.emplace_back(VideoProperty{handle});
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::vector<VideoSink> VideoSink::EnumerateSinks() {
|
||||
wpi::util::SmallVector<CS_Sink, 16> handles_buf;
|
||||
CS_Status status = 0;
|
||||
auto handles = ::wpi::cs::EnumerateSinkHandles(handles_buf, &status);
|
||||
|
||||
std::vector<VideoSink> sinks;
|
||||
sinks.reserve(handles.size());
|
||||
for (int handle : handles) {
|
||||
sinks.emplace_back(VideoSink{handle});
|
||||
}
|
||||
return sinks;
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "c_util.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/MemAlloc.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/SmallString.hpp"
|
||||
#include "wpi/util/string.hpp"
|
||||
|
||||
@@ -178,9 +179,9 @@ CS_Bool CS_SetSourceVideoModeDiscrete(CS_Source source,
|
||||
CS_Status* status) {
|
||||
return wpi::cs::SetSourceVideoMode(
|
||||
source,
|
||||
wpi::cs::VideoMode{static_cast<wpi::cs::VideoMode::PixelFormat>(
|
||||
static_cast<int>(pixelFormat)),
|
||||
width, height, fps},
|
||||
wpi::cs::VideoMode{
|
||||
static_cast<wpi::util::PixelFormat>(static_cast<int>(pixelFormat)),
|
||||
width, height, fps},
|
||||
status);
|
||||
}
|
||||
|
||||
@@ -189,8 +190,7 @@ CS_Bool CS_SetSourcePixelFormat(CS_Source source,
|
||||
CS_Status* status) {
|
||||
return wpi::cs::SetSourcePixelFormat(
|
||||
source,
|
||||
static_cast<wpi::cs::VideoMode::PixelFormat>(
|
||||
static_cast<int>(pixelFormat)),
|
||||
static_cast<wpi::util::PixelFormat>(static_cast<int>(pixelFormat)),
|
||||
status);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "SourceImpl.hpp"
|
||||
#include "Telemetry.hpp"
|
||||
#include "wpi/net/hostname.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/SmallString.hpp"
|
||||
#include "wpi/util/json.hpp"
|
||||
|
||||
@@ -323,7 +324,7 @@ bool SetSourceVideoMode(CS_Source source, const VideoMode& mode,
|
||||
return data->source->SetVideoMode(mode, status);
|
||||
}
|
||||
|
||||
bool SetSourcePixelFormat(CS_Source source, VideoMode::PixelFormat pixelFormat,
|
||||
bool SetSourcePixelFormat(CS_Source source, wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data) {
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
|
||||
#define WPI_RAWFRAME_JNI
|
||||
#include "org_wpilib_vision_camera_CameraServerJNI.h"
|
||||
#include "wpi/cs/cscore_raw.h"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/cs/cscore_raw.hpp"
|
||||
#include "wpi/cs/cscore_runloop.hpp"
|
||||
#include "wpi/util/RawFrame.h"
|
||||
#include "wpi/util/RawFrame.hpp"
|
||||
#include "wpi/util/SmallString.hpp"
|
||||
#include "wpi/util/jni_util.hpp"
|
||||
|
||||
@@ -583,10 +584,9 @@ Java_org_wpilib_vision_camera_CameraServerJNI_createRawSource
|
||||
CS_Status status = 0;
|
||||
auto val = wpi::cs::CreateRawSource(
|
||||
JStringRef{env, name}.str(), isCv,
|
||||
wpi::cs::VideoMode{
|
||||
static_cast<wpi::cs::VideoMode::PixelFormat>(pixelFormat),
|
||||
static_cast<int>(width), static_cast<int>(height),
|
||||
static_cast<int>(fps)},
|
||||
wpi::cs::VideoMode{static_cast<wpi::util::PixelFormat>(pixelFormat),
|
||||
static_cast<int>(width), static_cast<int>(height),
|
||||
static_cast<int>(fps)},
|
||||
&status);
|
||||
CheckStatus(env, status);
|
||||
return val;
|
||||
@@ -771,9 +771,8 @@ Java_org_wpilib_vision_camera_CameraServerJNI_setSourceVideoMode
|
||||
CS_Status status = 0;
|
||||
auto val = wpi::cs::SetSourceVideoMode(
|
||||
source,
|
||||
wpi::cs::VideoMode(
|
||||
static_cast<wpi::cs::VideoMode::PixelFormat>(pixelFormat), width,
|
||||
height, fps),
|
||||
wpi::cs::VideoMode(static_cast<wpi::util::PixelFormat>(pixelFormat),
|
||||
width, height, fps),
|
||||
&status);
|
||||
CheckStatus(env, status);
|
||||
return val;
|
||||
@@ -790,8 +789,7 @@ Java_org_wpilib_vision_camera_CameraServerJNI_setSourcePixelFormat
|
||||
{
|
||||
CS_Status status = 0;
|
||||
auto val = wpi::cs::SetSourcePixelFormat(
|
||||
source, static_cast<wpi::cs::VideoMode::PixelFormat>(pixelFormat),
|
||||
&status);
|
||||
source, static_cast<wpi::util::PixelFormat>(pixelFormat), &status);
|
||||
CheckStatus(env, status);
|
||||
return val;
|
||||
}
|
||||
|
||||
229
cscore/src/main/native/include/wpi/cs/CvSink.hpp
Normal file
229
cscore/src/main/native/include/wpi/cs/CvSink.hpp
Normal file
@@ -0,0 +1,229 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <opencv2/core/mat.hpp>
|
||||
|
||||
#include "wpi/cs/ImageSink.hpp"
|
||||
#include "wpi/cs/cscore_raw.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/RawFrame.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A sink for user code to accept video frames as OpenCV images.
|
||||
*
|
||||
* This is not dependent on any opencv binary ABI, and can be used
|
||||
* with versions of most versions of OpenCV.
|
||||
*/
|
||||
class CvSink : public ImageSink {
|
||||
public:
|
||||
CvSink() = default;
|
||||
CvSink(const CvSink& sink) : ImageSink{sink}, pixelFormat{sink.pixelFormat} {}
|
||||
|
||||
/**
|
||||
* Create a sink for accepting OpenCV images.
|
||||
*
|
||||
* <p>WaitForFrame() must be called on the created sink to get each new
|
||||
* image.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat The pixel format to read
|
||||
*/
|
||||
explicit CvSink(std::string_view name, wpi::util::PixelFormat pixelFormat =
|
||||
wpi::util::PixelFormat::kBGR) {
|
||||
m_handle = CreateRawSink(name, true, &m_status);
|
||||
this->pixelFormat = pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225) {
|
||||
cv::Mat tmpnam;
|
||||
auto retVal = GrabFrameDirect(tmpnam);
|
||||
if (retVal <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
tmpnam.copyTo(image);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeout(cv::Mat& image) {
|
||||
cv::Mat tmpnam;
|
||||
auto retVal = GrabFrameNoTimeoutDirect(tmpnam);
|
||||
if (retVal <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
tmpnam.copyTo(image);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with. The data is backed by data in the CvSink. It will be invalidated by
|
||||
* any grabFrame*() call on the sink.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = static_cast<int>(pixelFormat);
|
||||
auto timestamp =
|
||||
GrabSinkFrameTimeout(m_handle, rawFrame, timeout, &m_status);
|
||||
if (m_status != CS_OK) {
|
||||
return 0;
|
||||
}
|
||||
image =
|
||||
cv::Mat{rawFrame.height, rawFrame.width,
|
||||
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with. The data is backed by data in the CvSink. It will be invalidated by
|
||||
* any grabFrame*() call on the sink.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = static_cast<int>(pixelFormat);
|
||||
auto timestamp = GrabSinkFrame(m_handle, rawFrame, &m_status);
|
||||
if (m_status != CS_OK) {
|
||||
return 0;
|
||||
}
|
||||
image =
|
||||
cv::Mat{rawFrame.height, rawFrame.width,
|
||||
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with. The data is backed by data in the CvSink. It will be invalidated by
|
||||
* any grabFrame*() call on the sink.
|
||||
*
|
||||
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with
|
||||
* the first frame from the source that is not equal to lastFrameTime. If
|
||||
* lastFrameTime is zero, the time of the current frame owned by the CvSource
|
||||
* is used, and this function will block until the connected CvSource provides
|
||||
* a new frame.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameDirectLastTime(cv::Mat& image, uint64_t lastFrameTime,
|
||||
double timeout = 0.225) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = static_cast<int>(pixelFormat);
|
||||
auto timestamp = GrabSinkFrameTimeoutLastTime(m_handle, rawFrame, timeout,
|
||||
lastFrameTime, &m_status);
|
||||
if (m_status != CS_OK) {
|
||||
return 0;
|
||||
}
|
||||
image =
|
||||
cv::Mat{rawFrame.height, rawFrame.width,
|
||||
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last time a frame was grabbed. This uses the same time base as
|
||||
* wpi::util::Now().
|
||||
*
|
||||
* @return Time in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t LastFrameTime() {
|
||||
return rawFrame.timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time source for the timestamp the last frame was grabbed at.
|
||||
*
|
||||
* @return Time source
|
||||
*/
|
||||
[[nodiscard]]
|
||||
WPI_TimestampSource LastFrameTimeSource() {
|
||||
return static_cast<WPI_TimestampSource>(rawFrame.timestampSrc);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr int GetCvFormat(WPI_PixelFormat pixelFormat) {
|
||||
int type = 0;
|
||||
switch (pixelFormat) {
|
||||
case WPI_PIXFMT_YUYV:
|
||||
case WPI_PIXFMT_RGB565:
|
||||
case WPI_PIXFMT_Y16:
|
||||
case WPI_PIXFMT_UYVY:
|
||||
type = CV_8UC2;
|
||||
break;
|
||||
case WPI_PIXFMT_BGR:
|
||||
type = CV_8UC3;
|
||||
break;
|
||||
case WPI_PIXFMT_BGRA:
|
||||
type = CV_8UC4;
|
||||
break;
|
||||
case WPI_PIXFMT_GRAY:
|
||||
case WPI_PIXFMT_MJPEG:
|
||||
default:
|
||||
type = CV_8UC1;
|
||||
break;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
wpi::util::RawFrame rawFrame;
|
||||
wpi::util::PixelFormat pixelFormat;
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
188
cscore/src/main/native/include/wpi/cs/CvSource.hpp
Normal file
188
cscore/src/main/native/include/wpi/cs/CvSource.hpp
Normal file
@@ -0,0 +1,188 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <opencv2/core/mat.hpp>
|
||||
|
||||
#include "wpi/cs/ImageSource.hpp"
|
||||
#include "wpi/cs/VideoMode.hpp"
|
||||
#include "wpi/cs/cscore_raw.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A source for user code to provide OpenCV images as video frames.
|
||||
*
|
||||
* This is not dependent on any opencv binary ABI, and can be used
|
||||
* with versions of most versions of OpenCV.
|
||||
*/
|
||||
class CvSource : public ImageSource {
|
||||
public:
|
||||
CvSource() = default;
|
||||
|
||||
/**
|
||||
* Create an OpenCV source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param mode Video mode being generated
|
||||
*/
|
||||
CvSource(std::string_view name, const VideoMode& mode) {
|
||||
m_handle = CreateRawSource(name, true, mode, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an OpenCV source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat Pixel format
|
||||
* @param width width
|
||||
* @param height height
|
||||
* @param fps fps
|
||||
*/
|
||||
CvSource(std::string_view name, wpi::util::PixelFormat pixelFormat, int width,
|
||||
int height, int fps) {
|
||||
m_handle = CreateRawSource(
|
||||
name, true, VideoMode{pixelFormat, width, height, fps}, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks
|
||||
*
|
||||
* <p>
|
||||
* The image format is guessed from the number of channels. The channel
|
||||
* mapping is as follows. 1: kGray 2: kYUYV 3: BGR 4: BGRA Any other channel
|
||||
* numbers will throw an error. If your image is an in alternate format, use
|
||||
* the overload that takes a PixelFormat.
|
||||
*
|
||||
* @param image OpenCV Image
|
||||
*/
|
||||
void PutFrame(cv::Mat& image) {
|
||||
// We only support 8-bit images; convert if necessary.
|
||||
cv::Mat finalImage;
|
||||
if (image.depth() == CV_8U) {
|
||||
finalImage = image;
|
||||
} else {
|
||||
image.convertTo(finalImage, CV_8U);
|
||||
}
|
||||
|
||||
int channels = finalImage.channels();
|
||||
wpi::util::PixelFormat format;
|
||||
if (channels == 1) {
|
||||
// 1 channel is assumed Grayscale
|
||||
format = wpi::util::PixelFormat::kGray;
|
||||
} else if (channels == 2) {
|
||||
// 2 channels is assumed YUYV
|
||||
format = wpi::util::PixelFormat::kYUYV;
|
||||
} else if (channels == 3) {
|
||||
// 3 channels is assumed BGR
|
||||
format = wpi::util::PixelFormat::kBGR;
|
||||
} else if (channels == 4) {
|
||||
// 4 channels is assumed BGRA
|
||||
format = wpi::util::PixelFormat::kBGRA;
|
||||
} else {
|
||||
// TODO Error
|
||||
return;
|
||||
}
|
||||
|
||||
PutFrame(finalImage, format, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks.
|
||||
*
|
||||
* <p>
|
||||
* The format of the Mat must match the PixelFormat. You will corrupt memory
|
||||
* if they dont. With skipVerification false, we will verify the number of
|
||||
* channels matches the pixel format. If skipVerification is true, this step
|
||||
* is skipped and is passed straight through.
|
||||
*
|
||||
* @param image OpenCV image
|
||||
* @param pixelFormat The pixel format of the image
|
||||
* @param skipVerification skip verifying pixel format
|
||||
*/
|
||||
void PutFrame(cv::Mat& image, wpi::util::PixelFormat pixelFormat,
|
||||
bool skipVerification) {
|
||||
// We only support 8-bit images; convert if necessary.
|
||||
cv::Mat finalImage;
|
||||
if (image.depth() == CV_8U) {
|
||||
finalImage = image;
|
||||
} else {
|
||||
image.convertTo(finalImage, CV_8U);
|
||||
}
|
||||
|
||||
if (!skipVerification) {
|
||||
if (!VerifyFormat(finalImage, pixelFormat)) {
|
||||
// TODO Error
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
|
||||
frame.data = finalImage.data;
|
||||
frame.freeFunc = nullptr;
|
||||
frame.freeCbData = nullptr;
|
||||
frame.size = finalImage.total() * finalImage.channels();
|
||||
frame.width = finalImage.cols;
|
||||
frame.height = finalImage.rows;
|
||||
frame.stride = finalImage.step;
|
||||
frame.pixelFormat = static_cast<int>(pixelFormat);
|
||||
m_status = 0;
|
||||
PutSourceFrame(m_handle, frame, &m_status);
|
||||
}
|
||||
|
||||
private:
|
||||
static bool VerifyFormat(cv::Mat& image, wpi::util::PixelFormat pixelFormat) {
|
||||
int channels = image.channels();
|
||||
switch (pixelFormat) {
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
if (channels == 3) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case wpi::util::PixelFormat::kBGRA:
|
||||
if (channels == 4) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
if (channels == 1) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
if (channels == 1) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
151
cscore/src/main/native/include/wpi/cs/HttpCamera.hpp
Normal file
151
cscore/src/main/native/include/wpi/cs/HttpCamera.hpp
Normal file
@@ -0,0 +1,151 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/cs/VideoCamera.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A source that represents a MJPEG-over-HTTP (IP) camera.
|
||||
*/
|
||||
class HttpCamera : public VideoCamera {
|
||||
public:
|
||||
/**
|
||||
* HTTP camera kind.
|
||||
*/
|
||||
enum HttpCameraKind {
|
||||
/// Unknown camera kind.
|
||||
kUnknown = CS_HTTP_UNKNOWN,
|
||||
/// MJPG Streamer camera.
|
||||
kMJPGStreamer = CS_HTTP_MJPGSTREAMER,
|
||||
/// CS Core camera.
|
||||
kCSCore = CS_HTTP_CSCORE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg")
|
||||
* @param kind Camera kind (e.g. kAxis)
|
||||
*/
|
||||
HttpCamera(std::string_view name, std::string_view url,
|
||||
HttpCameraKind kind = kUnknown) {
|
||||
m_handle = CreateHttpCamera(
|
||||
name, url, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg")
|
||||
* @param kind Camera kind (e.g. kAxis)
|
||||
*/
|
||||
HttpCamera(std::string_view name, const char* url,
|
||||
HttpCameraKind kind = kUnknown) {
|
||||
m_handle = CreateHttpCamera(
|
||||
name, url, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg")
|
||||
* @param kind Camera kind (e.g. kAxis)
|
||||
*/
|
||||
HttpCamera(std::string_view name, const std::string& url,
|
||||
HttpCameraKind kind = kUnknown)
|
||||
: HttpCamera(name, std::string_view{url}, kind) {}
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param urls Array of Camera URLs
|
||||
* @param kind Camera kind (e.g. kAxis)
|
||||
*/
|
||||
HttpCamera(std::string_view name, std::span<const std::string> urls,
|
||||
HttpCameraKind kind = kUnknown) {
|
||||
m_handle = CreateHttpCamera(
|
||||
name, urls, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param urls Array of Camera URLs
|
||||
* @param kind Camera kind (e.g. kAxis)
|
||||
*/
|
||||
template <typename T>
|
||||
HttpCamera(std::string_view name, std::initializer_list<T> urls,
|
||||
HttpCameraKind kind = kUnknown) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(urls.size());
|
||||
for (const auto& url : urls) {
|
||||
vec.emplace_back(url);
|
||||
}
|
||||
m_handle = CreateHttpCamera(
|
||||
name, vec, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the kind of HTTP camera.
|
||||
*
|
||||
* <p>Autodetection can result in returning a different value than the camera
|
||||
* was created with.
|
||||
*/
|
||||
HttpCameraKind GetHttpCameraKind() const {
|
||||
m_status = 0;
|
||||
return static_cast<HttpCameraKind>(
|
||||
static_cast<int>(::wpi::cs::GetHttpCameraKind(m_handle, &m_status)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the URLs used to connect to the camera.
|
||||
*/
|
||||
void SetUrls(std::span<const std::string> urls) {
|
||||
m_status = 0;
|
||||
::wpi::cs::SetHttpCameraUrls(m_handle, urls, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the URLs used to connect to the camera.
|
||||
*/
|
||||
template <typename T>
|
||||
void SetUrls(std::initializer_list<T> urls) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(urls.size());
|
||||
for (const auto& url : urls) {
|
||||
vec.emplace_back(url);
|
||||
}
|
||||
m_status = 0;
|
||||
::wpi::cs::SetHttpCameraUrls(m_handle, vec, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URLs used to connect to the camera.
|
||||
*/
|
||||
std::vector<std::string> GetUrls() const {
|
||||
m_status = 0;
|
||||
return ::wpi::cs::GetHttpCameraUrls(m_handle, &m_status);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
55
cscore/src/main/native/include/wpi/cs/ImageSink.hpp
Normal file
55
cscore/src/main/native/include/wpi/cs/ImageSink.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "wpi/cs/VideoSink.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A base class for single image reading sinks.
|
||||
*/
|
||||
class ImageSink : public VideoSink {
|
||||
protected:
|
||||
ImageSink() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Set sink description.
|
||||
*
|
||||
* @param description Description
|
||||
*/
|
||||
void SetDescription(std::string_view description) {
|
||||
m_status = 0;
|
||||
SetSinkDescription(m_handle, description, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error string. Call this if WaitForFrame() returns 0 to determine
|
||||
* what the error is.
|
||||
*/
|
||||
std::string GetError() const {
|
||||
m_status = 0;
|
||||
return GetSinkError(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable getting new frames.
|
||||
*
|
||||
* <p>Disabling will cause processFrame (for callback-based CvSinks) to not
|
||||
* be called and WaitForFrame() to not return. This can be used to save
|
||||
* processor resources when frames are not needed.
|
||||
*/
|
||||
void SetEnabled(bool enabled) {
|
||||
m_status = 0;
|
||||
SetSinkEnabled(m_handle, enabled, &m_status);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
170
cscore/src/main/native/include/wpi/cs/ImageSource.hpp
Normal file
170
cscore/src/main/native/include/wpi/cs/ImageSource.hpp
Normal file
@@ -0,0 +1,170 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/cs/VideoProperty.hpp"
|
||||
#include "wpi/cs/VideoSource.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A base class for single image providing sources.
|
||||
*/
|
||||
class ImageSource : public VideoSource {
|
||||
protected:
|
||||
ImageSource() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Signal sinks that an error has occurred. This should be called instead
|
||||
* of NotifyFrame when an error occurs.
|
||||
*
|
||||
* @param msg Notification message.
|
||||
*/
|
||||
void NotifyError(std::string_view msg) {
|
||||
m_status = 0;
|
||||
NotifySourceError(m_handle, msg, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set source connection status. Defaults to true.
|
||||
*
|
||||
* @param connected True for connected, false for disconnected
|
||||
*/
|
||||
void SetConnected(bool connected) {
|
||||
m_status = 0;
|
||||
SetSourceConnected(m_handle, connected, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set source description.
|
||||
*
|
||||
* @param description Description
|
||||
*/
|
||||
void SetDescription(std::string_view description) {
|
||||
m_status = 0;
|
||||
SetSourceDescription(m_handle, description, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a property.
|
||||
*
|
||||
* @param name Property name
|
||||
* @param kind Property kind
|
||||
* @param minimum Minimum value
|
||||
* @param maximum Maximum value
|
||||
* @param step Step value
|
||||
* @param defaultValue Default value
|
||||
* @param value Current value
|
||||
* @return Property
|
||||
*/
|
||||
VideoProperty CreateProperty(std::string_view name, VideoProperty::Kind kind,
|
||||
int minimum, int maximum, int step,
|
||||
int defaultValue, int value) {
|
||||
m_status = 0;
|
||||
return VideoProperty{CreateSourceProperty(
|
||||
m_handle, name, static_cast<CS_PropertyKind>(static_cast<int>(kind)),
|
||||
minimum, maximum, step, defaultValue, value, &m_status)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an integer property.
|
||||
*
|
||||
* @param name Property name
|
||||
* @param minimum Minimum value
|
||||
* @param maximum Maximum value
|
||||
* @param step Step value
|
||||
* @param defaultValue Default value
|
||||
* @param value Current value
|
||||
* @return Property
|
||||
*/
|
||||
VideoProperty CreateIntegerProperty(std::string_view name, int minimum,
|
||||
int maximum, int step, int defaultValue,
|
||||
int value) {
|
||||
m_status = 0;
|
||||
return VideoProperty{CreateSourceProperty(
|
||||
m_handle, name,
|
||||
static_cast<CS_PropertyKind>(
|
||||
static_cast<int>(VideoProperty::Kind::kInteger)),
|
||||
minimum, maximum, step, defaultValue, value, &m_status)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a boolean property.
|
||||
*
|
||||
* @param name Property name
|
||||
* @param defaultValue Default value
|
||||
* @param value Current value
|
||||
* @return Property
|
||||
*/
|
||||
VideoProperty CreateBooleanProperty(std::string_view name, bool defaultValue,
|
||||
bool value) {
|
||||
m_status = 0;
|
||||
return VideoProperty{CreateSourceProperty(
|
||||
m_handle, name,
|
||||
static_cast<CS_PropertyKind>(
|
||||
static_cast<int>(VideoProperty::Kind::kBoolean)),
|
||||
0, 1, 1, defaultValue ? 1 : 0, value ? 1 : 0, &m_status)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string property.
|
||||
*
|
||||
* @param name Property name
|
||||
* @param value Current value
|
||||
* @return Property
|
||||
*/
|
||||
VideoProperty CreateStringProperty(std::string_view name,
|
||||
std::string_view value) {
|
||||
m_status = 0;
|
||||
auto prop = VideoProperty{CreateSourceProperty(
|
||||
m_handle, name,
|
||||
static_cast<CS_PropertyKind>(
|
||||
static_cast<int>(VideoProperty::Kind::kString)),
|
||||
0, 0, 0, 0, 0, &m_status)};
|
||||
prop.SetString(value);
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure enum property choices.
|
||||
*
|
||||
* @param property Property
|
||||
* @param choices Choices
|
||||
*/
|
||||
void SetEnumPropertyChoices(const VideoProperty& property,
|
||||
std::span<const std::string> choices) {
|
||||
m_status = 0;
|
||||
SetSourceEnumPropertyChoices(m_handle, property.m_handle, choices,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure enum property choices.
|
||||
*
|
||||
* @param property Property
|
||||
* @param choices Choices
|
||||
*/
|
||||
template <typename T>
|
||||
void SetEnumPropertyChoices(const VideoProperty& property,
|
||||
std::initializer_list<T> choices) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(choices.size());
|
||||
for (const auto& choice : choices) {
|
||||
vec.emplace_back(choice);
|
||||
}
|
||||
m_status = 0;
|
||||
SetSourceEnumPropertyChoices(m_handle, property.m_handle, vec, &m_status);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
120
cscore/src/main/native/include/wpi/cs/MjpegServer.hpp
Normal file
120
cscore/src/main/native/include/wpi/cs/MjpegServer.hpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "wpi/cs/VideoSink.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A sink that acts as a MJPEG-over-HTTP network server.
|
||||
*/
|
||||
class MjpegServer : public VideoSink {
|
||||
public:
|
||||
MjpegServer() = default;
|
||||
|
||||
/**
|
||||
* Create a MJPEG-over-HTTP server sink.
|
||||
*
|
||||
* @param name Sink name (arbitrary unique identifier)
|
||||
* @param listenAddress TCP listen address (empty string for all addresses)
|
||||
* @param port TCP port number
|
||||
*/
|
||||
MjpegServer(std::string_view name, std::string_view listenAddress, int port) {
|
||||
m_handle = CreateMjpegServer(name, listenAddress, port, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MJPEG-over-HTTP server sink.
|
||||
*
|
||||
* @param name Sink name (arbitrary unique identifier)
|
||||
* @param port TCP port number
|
||||
*/
|
||||
MjpegServer(std::string_view name, int port) : MjpegServer(name, "", port) {}
|
||||
|
||||
/**
|
||||
* Get the listen address of the server.
|
||||
*/
|
||||
std::string GetListenAddress() const {
|
||||
m_status = 0;
|
||||
return wpi::cs::GetMjpegServerListenAddress(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port number of the server.
|
||||
*/
|
||||
int GetPort() const {
|
||||
m_status = 0;
|
||||
return wpi::cs::GetMjpegServerPort(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stream resolution for clients that don't specify it.
|
||||
*
|
||||
* <p>It is not necessary to set this if it is the same as the source
|
||||
* resolution.
|
||||
*
|
||||
* <p>Setting this different than the source resolution will result in
|
||||
* increased CPU usage, particularly for MJPEG source cameras, as it will
|
||||
* decompress, resize, and recompress the image, instead of using the
|
||||
* camera's MJPEG image directly.
|
||||
*
|
||||
* @param width width, 0 for unspecified
|
||||
* @param height height, 0 for unspecified
|
||||
*/
|
||||
void SetResolution(int width, int height) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSinkProperty(m_handle, "width", &m_status), width,
|
||||
&m_status);
|
||||
SetProperty(GetSinkProperty(m_handle, "height", &m_status), height,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stream frames per second (FPS) for clients that don't specify it.
|
||||
*
|
||||
* <p>It is not necessary to set this if it is the same as the source FPS.
|
||||
*
|
||||
* @param fps FPS, 0 for unspecified
|
||||
*/
|
||||
void SetFPS(int fps) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSinkProperty(m_handle, "fps", &m_status), fps, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the compression for clients that don't specify it.
|
||||
*
|
||||
* <p>Setting this will result in increased CPU usage for MJPEG source cameras
|
||||
* as it will decompress and recompress the image instead of using the
|
||||
* camera's MJPEG image directly.
|
||||
*
|
||||
* @param quality JPEG compression quality (0-100), -1 for unspecified
|
||||
*/
|
||||
void SetCompression(int quality) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSinkProperty(m_handle, "compression", &m_status), quality,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default compression used for non-MJPEG sources. If not set,
|
||||
* 80 is used. This function has no effect on MJPEG source cameras; use
|
||||
* SetCompression() instead to force recompression of MJPEG source images.
|
||||
*
|
||||
* @param quality JPEG compression quality (0-100)
|
||||
*/
|
||||
void SetDefaultCompression(int quality) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSinkProperty(m_handle, "default_compression", &m_status),
|
||||
quality, &m_status);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
91
cscore/src/main/native/include/wpi/cs/RawEvent.hpp
Normal file
91
cscore/src/main/native/include/wpi/cs/RawEvent.hpp
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "wpi/cs/VideoMode.hpp"
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* Listener event
|
||||
*/
|
||||
struct RawEvent {
|
||||
enum Kind {
|
||||
kSourceCreated = CS_SOURCE_CREATED,
|
||||
kSourceDestroyed = CS_SOURCE_DESTROYED,
|
||||
kSourceConnected = CS_SOURCE_CONNECTED,
|
||||
kSourceDisconnected = CS_SOURCE_DISCONNECTED,
|
||||
kSourceVideoModesUpdated = CS_SOURCE_VIDEOMODES_UPDATED,
|
||||
kSourceVideoModeChanged = CS_SOURCE_VIDEOMODE_CHANGED,
|
||||
kSourcePropertyCreated = CS_SOURCE_PROPERTY_CREATED,
|
||||
kSourcePropertyValueUpdated = CS_SOURCE_PROPERTY_VALUE_UPDATED,
|
||||
kSourcePropertyChoicesUpdated = CS_SOURCE_PROPERTY_CHOICES_UPDATED,
|
||||
kSinkSourceChanged = CS_SINK_SOURCE_CHANGED,
|
||||
kSinkCreated = CS_SINK_CREATED,
|
||||
kSinkDestroyed = CS_SINK_DESTROYED,
|
||||
kSinkEnabled = CS_SINK_ENABLED,
|
||||
kSinkDisabled = CS_SINK_DISABLED,
|
||||
kNetworkInterfacesChanged = CS_NETWORK_INTERFACES_CHANGED,
|
||||
kTelemetryUpdated = CS_TELEMETRY_UPDATED,
|
||||
kSinkPropertyCreated = CS_SINK_PROPERTY_CREATED,
|
||||
kSinkPropertyValueUpdated = CS_SINK_PROPERTY_VALUE_UPDATED,
|
||||
kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED,
|
||||
kUsbCamerasChanged = CS_USB_CAMERAS_CHANGED
|
||||
};
|
||||
|
||||
RawEvent() = default;
|
||||
explicit RawEvent(RawEvent::Kind kind_) : kind{kind_} {}
|
||||
RawEvent(std::string_view name_, CS_Handle handle_, RawEvent::Kind kind_)
|
||||
: kind{kind_}, name{name_} {
|
||||
if (kind_ == kSinkCreated || kind_ == kSinkDestroyed ||
|
||||
kind_ == kSinkEnabled || kind_ == kSinkDisabled) {
|
||||
sinkHandle = handle_;
|
||||
} else {
|
||||
sourceHandle = handle_;
|
||||
}
|
||||
}
|
||||
RawEvent(std::string_view name_, CS_Source source_, const VideoMode& mode_)
|
||||
: kind{kSourceVideoModeChanged},
|
||||
sourceHandle{source_},
|
||||
name{name_},
|
||||
mode{mode_} {}
|
||||
RawEvent(std::string_view name_, CS_Source source_, RawEvent::Kind kind_,
|
||||
CS_Property property_, CS_PropertyKind propertyKind_, int value_,
|
||||
std::string_view valueStr_)
|
||||
: kind{kind_},
|
||||
sourceHandle{source_},
|
||||
name{name_},
|
||||
propertyHandle{property_},
|
||||
propertyKind{propertyKind_},
|
||||
value{value_},
|
||||
valueStr{valueStr_} {}
|
||||
|
||||
Kind kind;
|
||||
|
||||
// Valid for kSource* and kSink* respectively
|
||||
CS_Source sourceHandle = CS_INVALID_HANDLE;
|
||||
CS_Sink sinkHandle = CS_INVALID_HANDLE;
|
||||
|
||||
// Source/sink/property name
|
||||
std::string name;
|
||||
|
||||
// Fields for kSourceVideoModeChanged event
|
||||
VideoMode mode;
|
||||
|
||||
// Fields for kSourceProperty* events
|
||||
CS_Property propertyHandle;
|
||||
CS_PropertyKind propertyKind;
|
||||
int value;
|
||||
std::string valueStr;
|
||||
|
||||
// Listener that was triggered
|
||||
CS_Listener listener{0};
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
107
cscore/src/main/native/include/wpi/cs/RawSink.hpp
Normal file
107
cscore/src/main/native/include/wpi/cs/RawSink.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
#include "wpi/cs/ImageSink.hpp"
|
||||
#include "wpi/cs/cscore_raw.hpp"
|
||||
#include "wpi/util/RawFrame.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A sink for user code to accept video frames as raw bytes.
|
||||
*
|
||||
* This is a complex API, most cases should use CvSource.
|
||||
*/
|
||||
class RawSink : public ImageSink {
|
||||
public:
|
||||
RawSink() = default;
|
||||
|
||||
/**
|
||||
* Create a sink for accepting raw images.
|
||||
*
|
||||
* <p>GrabFrame() must be called on the created sink to get each new
|
||||
* image.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
*/
|
||||
explicit RawSink(std::string_view name) {
|
||||
m_handle = CreateRawSink(name, false, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sink for accepting raws images in a separate thread.
|
||||
*
|
||||
* <p>A thread will be created that calls WaitForFrame() and calls the
|
||||
* processFrame() callback each time a new frame arrives.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param processFrame Frame processing function; will be called with a
|
||||
* time=0 if an error occurred. processFrame should call GetImage()
|
||||
* or GetError() as needed, but should not call (except in very
|
||||
* unusual circumstances) WaitForImage().
|
||||
*/
|
||||
RawSink(std::string_view name,
|
||||
std::function<void(uint64_t time)> processFrame) {
|
||||
m_handle = CreateRawSinkCallback(name, false, processFrame, &m_status);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have three 8-bit channels stored in BGR order.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrame(wpi::util::RawFrame& image, double timeout = 0.225) const {
|
||||
m_status = 0;
|
||||
return GrabSinkFrameTimeout(m_handle, image, timeout, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have three 8-bit channels stored in BGR order.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeout(wpi::util::RawFrame& image) const {
|
||||
m_status = 0;
|
||||
return GrabSinkFrame(m_handle, image, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have three 8-bit channels stored in BGR order.
|
||||
*
|
||||
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with
|
||||
* the first frame from the source that is not equal to lastFrameTime. If
|
||||
* lastFrameTime is zero, the time of the current frame owned by the CvSource
|
||||
* is used, and this function will block until the connected CvSource provides
|
||||
* a new frame.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameLastTime(wpi::util::RawFrame& image, uint64_t lastFrameTime,
|
||||
double timeout = 0.225) const {
|
||||
m_status = 0;
|
||||
return GrabSinkFrameTimeoutLastTime(m_handle, image, timeout, lastFrameTime,
|
||||
&m_status);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
62
cscore/src/main/native/include/wpi/cs/RawSource.hpp
Normal file
62
cscore/src/main/native/include/wpi/cs/RawSource.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "wpi/cs/ImageSource.hpp"
|
||||
#include "wpi/cs/VideoMode.hpp"
|
||||
#include "wpi/cs/cscore_raw.hpp"
|
||||
#include "wpi/util/RawFrame.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A source for user code to provide video frames as raw bytes.
|
||||
*
|
||||
* This is a complex API, most cases should use CvSource.
|
||||
*/
|
||||
class RawSource : public ImageSource {
|
||||
public:
|
||||
RawSource() = default;
|
||||
|
||||
/**
|
||||
* Create a raw frame source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param mode Video mode being generated
|
||||
*/
|
||||
RawSource(std::string_view name, const VideoMode& mode) {
|
||||
m_handle = CreateRawSource(name, false, mode, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a raw frame source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat Pixel format
|
||||
* @param width width
|
||||
* @param height height
|
||||
* @param fps fps
|
||||
*/
|
||||
RawSource(std::string_view name, VideoMode::PixelFormat pixelFormat,
|
||||
int width, int height, int fps) {
|
||||
m_handle = CreateRawSource(
|
||||
name, false, VideoMode{pixelFormat, width, height, fps}, &m_status);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Put a raw image and notify sinks.
|
||||
*
|
||||
* @param image raw frame image
|
||||
*/
|
||||
void PutFrame(wpi::util::RawFrame& image) {
|
||||
m_status = 0;
|
||||
PutSourceFrame(m_handle, image, &m_status);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
89
cscore/src/main/native/include/wpi/cs/UsbCamera.hpp
Normal file
89
cscore/src/main/native/include/wpi/cs/UsbCamera.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/cs/VideoCamera.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A source that represents a USB camera.
|
||||
*/
|
||||
class UsbCamera : public VideoCamera {
|
||||
public:
|
||||
UsbCamera() = default;
|
||||
|
||||
/**
|
||||
* Create a source for a USB camera based on device number.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param dev Device number (e.g. 0 for /dev/video0)
|
||||
*/
|
||||
UsbCamera(std::string_view name, int dev) {
|
||||
m_handle = CreateUsbCameraDev(name, dev, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a USB camera based on device path.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param path Path to device (e.g. "/dev/video0" on Linux)
|
||||
*/
|
||||
UsbCamera(std::string_view name, std::string_view path) {
|
||||
m_handle = CreateUsbCameraPath(name, path, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate USB cameras on the local system.
|
||||
*
|
||||
* @return Vector of USB camera information (one for each camera)
|
||||
*/
|
||||
static std::vector<UsbCameraInfo> EnumerateUsbCameras() {
|
||||
CS_Status status = 0;
|
||||
return ::wpi::cs::EnumerateUsbCameras(&status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the path to the device.
|
||||
*/
|
||||
void SetPath(std::string_view path) {
|
||||
m_status = 0;
|
||||
return ::wpi::cs::SetUsbCameraPath(m_handle, path, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the device.
|
||||
*/
|
||||
std::string GetPath() const {
|
||||
m_status = 0;
|
||||
return ::wpi::cs::GetUsbCameraPath(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full camera information for the device.
|
||||
*/
|
||||
UsbCameraInfo GetInfo() const {
|
||||
m_status = 0;
|
||||
return ::wpi::cs::GetUsbCameraInfo(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set how verbose the camera connection messages are.
|
||||
*
|
||||
* @param level 0=don't display Connecting message, 1=do display message
|
||||
*/
|
||||
void SetConnectVerbose(int level) {
|
||||
m_status = 0;
|
||||
SetProperty(GetSourceProperty(m_handle, "connect_verbose", &m_status),
|
||||
level, &m_status);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
30
cscore/src/main/native/include/wpi/cs/UsbCameraInfo.hpp
Normal file
30
cscore/src/main/native/include/wpi/cs/UsbCameraInfo.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* USB camera information
|
||||
*/
|
||||
struct UsbCameraInfo {
|
||||
/** Device number (e.g. N in '/dev/videoN' on Linux) */
|
||||
int dev = -1;
|
||||
/** Path to device if available (e.g. '/dev/video0' on Linux) */
|
||||
std::string path;
|
||||
/** Vendor/model name of the camera as provided by the USB driver */
|
||||
std::string name;
|
||||
/** Other path aliases to device (e.g. '/dev/v4l/by-id/...' etc on Linux) */
|
||||
std::vector<std::string> otherPaths;
|
||||
/** USB Vendor Id */
|
||||
int vendorId = -1;
|
||||
/** USB Product Id */
|
||||
int productId = -1;
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
103
cscore/src/main/native/include/wpi/cs/VideoCamera.hpp
Normal file
103
cscore/src/main/native/include/wpi/cs/VideoCamera.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/cs/VideoSource.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* A source that represents a video camera.
|
||||
*/
|
||||
class VideoCamera : public VideoSource {
|
||||
public:
|
||||
/**
|
||||
* White balance.
|
||||
*/
|
||||
enum WhiteBalance {
|
||||
/// Fixed indoor white balance.
|
||||
kFixedIndoor = 3000,
|
||||
/// Fixed outdoor white balance 1.
|
||||
kFixedOutdoor1 = 4000,
|
||||
/// Fixed outdoor white balance 2.
|
||||
kFixedOutdoor2 = 5000,
|
||||
/// Fixed fluorescent white balance 1.
|
||||
kFixedFluorescent1 = 5100,
|
||||
/// Fixed fluorescent white balance 2.
|
||||
kFixedFlourescent2 = 5200
|
||||
};
|
||||
|
||||
VideoCamera() = default;
|
||||
|
||||
/**
|
||||
* Set the brightness, as a percentage (0-100).
|
||||
*/
|
||||
void SetBrightness(int brightness) {
|
||||
m_status = 0;
|
||||
SetCameraBrightness(m_handle, brightness, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the brightness, as a percentage (0-100).
|
||||
*/
|
||||
int GetBrightness() {
|
||||
m_status = 0;
|
||||
return GetCameraBrightness(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the white balance to auto.
|
||||
*/
|
||||
void SetWhiteBalanceAuto() {
|
||||
m_status = 0;
|
||||
SetCameraWhiteBalanceAuto(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the white balance to hold current.
|
||||
*/
|
||||
void SetWhiteBalanceHoldCurrent() {
|
||||
m_status = 0;
|
||||
SetCameraWhiteBalanceHoldCurrent(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the white balance to manual, with specified color temperature.
|
||||
*/
|
||||
void SetWhiteBalanceManual(int value) {
|
||||
m_status = 0;
|
||||
SetCameraWhiteBalanceManual(m_handle, value, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exposure to auto aperture.
|
||||
*/
|
||||
void SetExposureAuto() {
|
||||
m_status = 0;
|
||||
SetCameraExposureAuto(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exposure to hold current.
|
||||
*/
|
||||
void SetExposureHoldCurrent() {
|
||||
m_status = 0;
|
||||
SetCameraExposureHoldCurrent(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exposure to manual, as a percentage (0-100).
|
||||
*/
|
||||
void SetExposureManual(int value) {
|
||||
m_status = 0;
|
||||
SetCameraExposureManual(m_handle, value, &m_status);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit VideoCamera(CS_Source handle) : VideoSource(handle) {}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
51
cscore/src/main/native/include/wpi/cs/VideoEvent.hpp
Normal file
51
cscore/src/main/native/include/wpi/cs/VideoEvent.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/cs/VideoProperty.hpp"
|
||||
#include "wpi/cs/VideoSink.hpp"
|
||||
#include "wpi/cs/VideoSource.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* An event generated by the library and provided to event listeners.
|
||||
*/
|
||||
class VideoEvent : public RawEvent {
|
||||
public:
|
||||
/**
|
||||
* Returns the source associated with the event (if any).
|
||||
*
|
||||
* @return The source associated with the event (if any).
|
||||
*/
|
||||
VideoSource GetSource() const {
|
||||
CS_Status status = 0;
|
||||
return VideoSource{sourceHandle == 0 ? 0
|
||||
: CopySource(sourceHandle, &status)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sink associated with the event (if any).
|
||||
*
|
||||
* @return The sink associated with the event (if any).
|
||||
*/
|
||||
VideoSink GetSink() const {
|
||||
CS_Status status = 0;
|
||||
return VideoSink{sinkHandle == 0 ? 0 : CopySink(sinkHandle, &status)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property associated with the event (if any).
|
||||
*
|
||||
* @return The property associated with the event (if any).
|
||||
*/
|
||||
VideoProperty GetProperty() const {
|
||||
return VideoProperty{propertyHandle,
|
||||
static_cast<VideoProperty::Kind>(propertyKind)};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
69
cscore/src/main/native/include/wpi/cs/VideoListener.hpp
Normal file
69
cscore/src/main/native/include/wpi/cs/VideoListener.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "wpi/cs/VideoEvent.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* An event listener. This calls back to a designated callback function when
|
||||
* an event matching the specified mask is generated by the library.
|
||||
*/
|
||||
class VideoListener {
|
||||
public:
|
||||
VideoListener() = default;
|
||||
|
||||
/**
|
||||
* Create an event listener.
|
||||
*
|
||||
* @param callback Callback function
|
||||
* @param eventMask Bitmask of VideoEvent::Kind values
|
||||
* @param immediateNotify Whether callback should be immediately called with
|
||||
* a representative set of events for the current library state.
|
||||
*/
|
||||
VideoListener(std::function<void(const VideoEvent& event)> callback,
|
||||
int eventMask, bool immediateNotify) {
|
||||
CS_Status status = 0;
|
||||
m_handle = AddListener(
|
||||
[=](const RawEvent& event) {
|
||||
callback(static_cast<const VideoEvent&>(event));
|
||||
},
|
||||
eventMask, immediateNotify, &status);
|
||||
}
|
||||
|
||||
VideoListener(const VideoListener&) = delete;
|
||||
VideoListener& operator=(const VideoListener&) = delete;
|
||||
|
||||
VideoListener(VideoListener&& other) noexcept : VideoListener() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
VideoListener& operator=(VideoListener&& other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~VideoListener() {
|
||||
CS_Status status = 0;
|
||||
if (m_handle != 0) {
|
||||
RemoveListener(m_handle, &status);
|
||||
}
|
||||
}
|
||||
|
||||
friend void swap(VideoListener& first, VideoListener& second) noexcept {
|
||||
using std::swap;
|
||||
swap(first.m_handle, second.m_handle);
|
||||
}
|
||||
|
||||
private:
|
||||
CS_Listener m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
55
cscore/src/main/native/include/wpi/cs/VideoMode.hpp
Normal file
55
cscore/src/main/native/include/wpi/cs/VideoMode.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* Video mode
|
||||
*/
|
||||
struct VideoMode {
|
||||
VideoMode() = default;
|
||||
VideoMode(wpi::util::PixelFormat pixelFormat_, int width_, int height_,
|
||||
int fps_)
|
||||
: pixelFormat{pixelFormat_}, width{width_}, height{height_}, fps{fps_} {}
|
||||
VideoMode(const CS_VideoMode& mode) // NOLINT
|
||||
: pixelFormat{static_cast<wpi::util::PixelFormat>(mode.pixelFormat)},
|
||||
width{mode.width},
|
||||
height{mode.height},
|
||||
fps{mode.fps} {}
|
||||
|
||||
operator CS_VideoMode() const { // NOLINT
|
||||
CS_VideoMode mode;
|
||||
mode.pixelFormat = static_cast<int>(pixelFormat);
|
||||
mode.width = width;
|
||||
mode.height = height;
|
||||
mode.fps = fps;
|
||||
return mode;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return pixelFormat == wpi::util::PixelFormat::kUnknown;
|
||||
}
|
||||
|
||||
bool operator==(const VideoMode& other) const {
|
||||
return pixelFormat == other.pixelFormat && width == other.width &&
|
||||
height == other.height && fps == other.fps;
|
||||
}
|
||||
|
||||
bool CompareWithoutFps(const VideoMode& other) const {
|
||||
return pixelFormat == other.pixelFormat && width == other.width &&
|
||||
height == other.height;
|
||||
}
|
||||
|
||||
wpi::util::PixelFormat pixelFormat = wpi::util::PixelFormat::kUnknown;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int fps = 0;
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
243
cscore/src/main/native/include/wpi/cs/VideoProperty.hpp
Normal file
243
cscore/src/main/native/include/wpi/cs/VideoProperty.hpp
Normal file
@@ -0,0 +1,243 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* @defgroup cscore_oo cscore C++ object-oriented API
|
||||
*
|
||||
* Recommended interface for C++, identical to Java API.
|
||||
*
|
||||
* <p>The classes are RAII and handle reference counting internally.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
// Forward declarations so friend declarations work correctly
|
||||
class ImageSource;
|
||||
class VideoEvent;
|
||||
class VideoSink;
|
||||
class VideoSource;
|
||||
|
||||
/**
|
||||
* A source or sink property.
|
||||
*/
|
||||
class VideoProperty {
|
||||
friend class ImageSource;
|
||||
friend class VideoEvent;
|
||||
friend class VideoSink;
|
||||
friend class VideoSource;
|
||||
|
||||
public:
|
||||
enum Kind {
|
||||
/// No specific property.
|
||||
kNone = CS_PROP_NONE,
|
||||
/// Boolean property.
|
||||
kBoolean = CS_PROP_BOOLEAN,
|
||||
/// Integer property.
|
||||
kInteger = CS_PROP_INTEGER,
|
||||
/// String property.
|
||||
kString = CS_PROP_STRING,
|
||||
/// Enum property.
|
||||
kEnum = CS_PROP_ENUM
|
||||
};
|
||||
|
||||
VideoProperty() = default;
|
||||
|
||||
/**
|
||||
* Returns property name.
|
||||
*
|
||||
* @return Property name.
|
||||
*/
|
||||
std::string GetName() const {
|
||||
m_status = 0;
|
||||
return GetPropertyName(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns property kind.
|
||||
*
|
||||
* @return Property kind.
|
||||
*/
|
||||
Kind GetKind() const { return m_kind; }
|
||||
|
||||
/**
|
||||
* Returns true if property is valid.
|
||||
*
|
||||
* @return True if property is valid.
|
||||
*/
|
||||
explicit operator bool() const { return m_kind != kNone; }
|
||||
|
||||
/**
|
||||
* Returns true if property is a boolean.
|
||||
*
|
||||
* @return True if property is a boolean.
|
||||
*/
|
||||
bool IsBoolean() const { return m_kind == kBoolean; }
|
||||
|
||||
/**
|
||||
* Returns true if property is an integer.
|
||||
*
|
||||
* @return True if property is an integer.
|
||||
*/
|
||||
bool IsInteger() const { return m_kind == kInteger; }
|
||||
|
||||
/**
|
||||
* Returns true if property is a string.
|
||||
*
|
||||
* @return True if property is a string.
|
||||
*/
|
||||
bool IsString() const { return m_kind == kString; }
|
||||
|
||||
/**
|
||||
* Returns true if property is an enum.
|
||||
*
|
||||
* @return True if property is an enum.
|
||||
*/
|
||||
bool IsEnum() const { return m_kind == kEnum; }
|
||||
|
||||
/**
|
||||
* Returns property value.
|
||||
*
|
||||
* @return Property value.
|
||||
*/
|
||||
int Get() const {
|
||||
m_status = 0;
|
||||
return GetProperty(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets property value.
|
||||
*
|
||||
* @param value Property value.
|
||||
*/
|
||||
void Set(int value) {
|
||||
m_status = 0;
|
||||
SetProperty(m_handle, value, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns property minimum value.
|
||||
*
|
||||
* @return Property minimum value.
|
||||
*/
|
||||
int GetMin() const {
|
||||
m_status = 0;
|
||||
return GetPropertyMin(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns property maximum value.
|
||||
*
|
||||
* @return Property maximum value.
|
||||
*/
|
||||
int GetMax() const {
|
||||
m_status = 0;
|
||||
return GetPropertyMax(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns property step size.
|
||||
*
|
||||
* @return Property step size.
|
||||
*/
|
||||
int GetStep() const {
|
||||
m_status = 0;
|
||||
return GetPropertyStep(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns property default value.
|
||||
*
|
||||
* @return Property default value.
|
||||
*/
|
||||
int GetDefault() const {
|
||||
m_status = 0;
|
||||
return GetPropertyDefault(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string property value.
|
||||
*
|
||||
* <p>This function is string-specific.
|
||||
*
|
||||
* @return The string property value.
|
||||
*/
|
||||
std::string GetString() const {
|
||||
m_status = 0;
|
||||
return GetStringProperty(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string property value as a reference to the given buffer.
|
||||
*
|
||||
* This function is string-specific.
|
||||
*
|
||||
* @param buf The backing storage to which to write the property value.
|
||||
* @return The string property value as a reference to the given buffer.
|
||||
*/
|
||||
std::string_view GetString(wpi::util::SmallVectorImpl<char>& buf) const {
|
||||
m_status = 0;
|
||||
return GetStringProperty(m_handle, buf, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the string property value.
|
||||
*
|
||||
* This function is string-specific.
|
||||
*
|
||||
* @param value String property value.
|
||||
*/
|
||||
void SetString(std::string_view value) {
|
||||
m_status = 0;
|
||||
SetStringProperty(m_handle, value, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the possible values for the enum property value.
|
||||
*
|
||||
* This function is enum-specific.
|
||||
*
|
||||
* @return The possible values for the enum property value.
|
||||
*/
|
||||
std::vector<std::string> GetChoices() const {
|
||||
m_status = 0;
|
||||
return GetEnumPropertyChoices(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last status.
|
||||
*
|
||||
* @return The last status.
|
||||
*/
|
||||
CS_Status GetLastStatus() const { return m_status; }
|
||||
|
||||
private:
|
||||
explicit VideoProperty(CS_Property handle) : m_handle(handle) {
|
||||
m_status = 0;
|
||||
if (handle == 0) {
|
||||
m_kind = kNone;
|
||||
} else {
|
||||
m_kind = static_cast<Kind>(
|
||||
static_cast<int>(GetPropertyKind(handle, &m_status)));
|
||||
}
|
||||
}
|
||||
|
||||
VideoProperty(CS_Property handle, Kind kind)
|
||||
: m_handle(handle), m_kind(kind) {}
|
||||
|
||||
mutable CS_Status m_status{0};
|
||||
CS_Property m_handle{0};
|
||||
Kind m_kind{kNone};
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
232
cscore/src/main/native/include/wpi/cs/VideoSink.hpp
Normal file
232
cscore/src/main/native/include/wpi/cs/VideoSink.hpp
Normal file
@@ -0,0 +1,232 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/cs/VideoProperty.hpp"
|
||||
#include "wpi/cs/VideoSource.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/json_fwd.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
class VideoEvent;
|
||||
|
||||
/**
|
||||
* A sink for video that accepts a sequence of frames.
|
||||
*/
|
||||
class VideoSink {
|
||||
friend class VideoEvent;
|
||||
friend class VideoSource;
|
||||
|
||||
public:
|
||||
enum Kind {
|
||||
/// Unknown sink type.
|
||||
kUnknown = CS_SINK_UNKNOWN,
|
||||
/// MJPEG video sink.
|
||||
kMjpeg = CS_SINK_MJPEG,
|
||||
/// CV video sink.
|
||||
kCv = CS_SINK_CV,
|
||||
/// Raw video sink.
|
||||
kRaw = CS_SINK_RAW,
|
||||
};
|
||||
|
||||
VideoSink() noexcept = default;
|
||||
|
||||
VideoSink(const VideoSink& sink)
|
||||
: m_handle(sink.m_handle == 0 ? 0 : CopySink(sink.m_handle, &m_status)) {}
|
||||
|
||||
VideoSink(VideoSink&& other) noexcept : VideoSink() { swap(*this, other); }
|
||||
|
||||
VideoSink& operator=(VideoSink other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~VideoSink() {
|
||||
m_status = 0;
|
||||
if (m_handle != 0) {
|
||||
ReleaseSink(m_handle, &m_status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the VideoSink is valid.
|
||||
*
|
||||
* @return True if the VideoSink is valid.
|
||||
*/
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
/**
|
||||
* Returns the VideoSink handle.
|
||||
*
|
||||
* @return The VideoSink handle.
|
||||
*/
|
||||
int GetHandle() const { return m_handle; }
|
||||
|
||||
bool operator==(const VideoSink& other) const {
|
||||
return m_handle == other.m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the kind of the sink.
|
||||
*/
|
||||
Kind GetKind() const {
|
||||
m_status = 0;
|
||||
return static_cast<VideoSink::Kind>(GetSinkKind(m_handle, &m_status));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the sink. The name is an arbitrary identifier
|
||||
* provided when the sink is created, and should be unique.
|
||||
*/
|
||||
std::string GetName() const {
|
||||
m_status = 0;
|
||||
return GetSinkName(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sink description. This is sink-kind specific.
|
||||
*/
|
||||
std::string GetDescription() const {
|
||||
m_status = 0;
|
||||
return GetSinkDescription(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property of the sink.
|
||||
*
|
||||
* @param name Property name
|
||||
* @return Property (kind Property::kNone if no property with
|
||||
* the given name exists)
|
||||
*/
|
||||
VideoProperty GetProperty(std::string_view name) {
|
||||
m_status = 0;
|
||||
return VideoProperty{GetSinkProperty(m_handle, name, &m_status)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all properties of this sink.
|
||||
*/
|
||||
std::vector<VideoProperty> EnumerateProperties() const;
|
||||
|
||||
/**
|
||||
* Set properties from a JSON configuration string.
|
||||
*
|
||||
* The format of the JSON input is:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "properties": [
|
||||
* {
|
||||
* "name": property name
|
||||
* "value": property value
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param config configuration
|
||||
* @return True if set successfully
|
||||
*/
|
||||
bool SetConfigJson(std::string_view config) {
|
||||
m_status = 0;
|
||||
return SetSinkConfigJson(m_handle, config, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties from a JSON configuration object.
|
||||
*
|
||||
* @param config configuration
|
||||
* @return True if set successfully
|
||||
*/
|
||||
bool SetConfigJson(const wpi::util::json& config) {
|
||||
m_status = 0;
|
||||
return SetSinkConfigJson(m_handle, config, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON configuration string.
|
||||
*
|
||||
* @return JSON configuration string
|
||||
*/
|
||||
std::string GetConfigJson() const {
|
||||
m_status = 0;
|
||||
return GetSinkConfigJson(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON configuration object.
|
||||
*
|
||||
* @return JSON configuration object
|
||||
*/
|
||||
wpi::util::json GetConfigJsonObject() const;
|
||||
|
||||
/**
|
||||
* Configure which source should provide frames to this sink. Each sink
|
||||
* can accept frames from only a single source, but a single source can
|
||||
* provide frames to multiple clients.
|
||||
*
|
||||
* @param source Source
|
||||
*/
|
||||
void SetSource(VideoSource source) {
|
||||
m_status = 0;
|
||||
if (!source) {
|
||||
SetSinkSource(m_handle, 0, &m_status);
|
||||
} else {
|
||||
SetSinkSource(m_handle, source.m_handle, &m_status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connected source.
|
||||
*
|
||||
* @return Connected source (empty if none connected).
|
||||
*/
|
||||
VideoSource GetSource() const {
|
||||
m_status = 0;
|
||||
auto handle = GetSinkSource(m_handle, &m_status);
|
||||
return VideoSource{handle == 0 ? 0 : CopySource(handle, &m_status)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property of the associated source.
|
||||
*
|
||||
* @param name Property name
|
||||
* @return Property (kind Property::kNone if no property with
|
||||
* the given name exists or no source connected)
|
||||
*/
|
||||
VideoProperty GetSourceProperty(std::string_view name) {
|
||||
m_status = 0;
|
||||
return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)};
|
||||
}
|
||||
|
||||
CS_Status GetLastStatus() const { return m_status; }
|
||||
|
||||
/**
|
||||
* Enumerate all existing sinks.
|
||||
*
|
||||
* @return Vector of sinks.
|
||||
*/
|
||||
static std::vector<VideoSink> EnumerateSinks();
|
||||
|
||||
friend void swap(VideoSink& first, VideoSink& second) noexcept {
|
||||
using std::swap;
|
||||
swap(first.m_status, second.m_status);
|
||||
swap(first.m_handle, second.m_handle);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit VideoSink(CS_Sink handle) : m_handle(handle) {}
|
||||
|
||||
mutable CS_Status m_status = 0;
|
||||
CS_Sink m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
378
cscore/src/main/native/include/wpi/cs/VideoSource.hpp
Normal file
378
cscore/src/main/native/include/wpi/cs/VideoSource.hpp
Normal file
@@ -0,0 +1,378 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/cs/VideoProperty.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
class VideoEvent;
|
||||
class VideoSink;
|
||||
|
||||
/**
|
||||
* A source for video that provides a sequence of frames.
|
||||
*/
|
||||
class VideoSource {
|
||||
friend class VideoEvent;
|
||||
friend class VideoSink;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Video source kind.
|
||||
*/
|
||||
enum Kind {
|
||||
/// Unknown video source.
|
||||
kUnknown = CS_SOURCE_UNKNOWN,
|
||||
/// USB video source.
|
||||
kUsb = CS_SOURCE_USB,
|
||||
/// HTTP video source.
|
||||
kHttp = CS_SOURCE_HTTP,
|
||||
/// CV video source.
|
||||
kCv = CS_SOURCE_CV,
|
||||
/// Raw video source.
|
||||
kRaw = CS_SOURCE_RAW,
|
||||
};
|
||||
|
||||
/** Connection strategy. Used for SetConnectionStrategy(). */
|
||||
enum ConnectionStrategy {
|
||||
/**
|
||||
* Automatically connect or disconnect based on whether any sinks are
|
||||
* connected to this source. This is the default behavior.
|
||||
*/
|
||||
kConnectionAutoManage = CS_CONNECTION_AUTO_MANAGE,
|
||||
|
||||
/**
|
||||
* Try to keep the connection open regardless of whether any sinks are
|
||||
* connected.
|
||||
*/
|
||||
kConnectionKeepOpen = CS_CONNECTION_KEEP_OPEN,
|
||||
|
||||
/**
|
||||
* Never open the connection. If this is set when the connection is open,
|
||||
* close the connection.
|
||||
*/
|
||||
kConnectionForceClose = CS_CONNECTION_FORCE_CLOSE
|
||||
};
|
||||
|
||||
VideoSource() noexcept = default;
|
||||
|
||||
VideoSource(const VideoSource& source)
|
||||
: m_handle(
|
||||
source.m_handle == 0 ? 0 : CopySource(source.m_handle, &m_status)) {
|
||||
}
|
||||
|
||||
VideoSource(VideoSource&& other) noexcept : VideoSource() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
VideoSource& operator=(VideoSource other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~VideoSource() {
|
||||
m_status = 0;
|
||||
if (m_handle != 0) {
|
||||
ReleaseSource(m_handle, &m_status);
|
||||
}
|
||||
}
|
||||
|
||||
explicit operator bool() const { return m_handle != 0; }
|
||||
|
||||
int GetHandle() const { return m_handle; }
|
||||
|
||||
bool operator==(const VideoSource& other) const {
|
||||
return m_handle == other.m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the kind of the source.
|
||||
*/
|
||||
Kind GetKind() const {
|
||||
m_status = 0;
|
||||
return static_cast<VideoSource::Kind>(GetSourceKind(m_handle, &m_status));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the source. The name is an arbitrary identifier
|
||||
* provided when the source is created, and should be unique.
|
||||
*/
|
||||
std::string GetName() const {
|
||||
m_status = 0;
|
||||
return GetSourceName(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source description. This is source-kind specific.
|
||||
*/
|
||||
std::string GetDescription() const {
|
||||
m_status = 0;
|
||||
return GetSourceDescription(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last time a frame was captured.
|
||||
* This uses the same time base as wpi::util::Now().
|
||||
*
|
||||
* @return Time in 1 us increments.
|
||||
*/
|
||||
uint64_t GetLastFrameTime() const {
|
||||
m_status = 0;
|
||||
return GetSourceLastFrameTime(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection strategy. By default, the source will automatically
|
||||
* connect or disconnect based on whether any sinks are connected.
|
||||
*
|
||||
* <p>This function is non-blocking; look for either a connection open or
|
||||
* close event or call IsConnected() to determine the connection state.
|
||||
*
|
||||
* @param strategy connection strategy (auto, keep open, or force close)
|
||||
*/
|
||||
void SetConnectionStrategy(ConnectionStrategy strategy) {
|
||||
m_status = 0;
|
||||
SetSourceConnectionStrategy(
|
||||
m_handle,
|
||||
static_cast<CS_ConnectionStrategy>(static_cast<int>(strategy)),
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the source currently connected to whatever is providing the images?
|
||||
*/
|
||||
bool IsConnected() const {
|
||||
m_status = 0;
|
||||
return IsSourceConnected(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets source enable status. This is determined with a combination of
|
||||
* connection strategy and the number of sinks connected.
|
||||
*
|
||||
* @return True if enabled, false otherwise.
|
||||
*/
|
||||
bool IsEnabled() const {
|
||||
m_status = 0;
|
||||
return IsSourceEnabled(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/** Get a property.
|
||||
*
|
||||
* @param name Property name
|
||||
* @return Property contents (of kind Property::kNone if no property with
|
||||
* the given name exists)
|
||||
*/
|
||||
VideoProperty GetProperty(std::string_view name) {
|
||||
m_status = 0;
|
||||
return VideoProperty{GetSourceProperty(m_handle, name, &m_status)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all properties of this source.
|
||||
*/
|
||||
std::vector<VideoProperty> EnumerateProperties() const;
|
||||
|
||||
/**
|
||||
* Get the current video mode.
|
||||
*/
|
||||
VideoMode GetVideoMode() const {
|
||||
m_status = 0;
|
||||
return GetSourceVideoMode(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the video mode.
|
||||
*
|
||||
* @param mode Video mode
|
||||
*/
|
||||
bool SetVideoMode(const VideoMode& mode) {
|
||||
m_status = 0;
|
||||
return SetSourceVideoMode(m_handle, mode, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the video mode.
|
||||
*
|
||||
* @param pixelFormat desired pixel format
|
||||
* @param width desired width
|
||||
* @param height desired height
|
||||
* @param fps desired FPS
|
||||
* @return True if set successfully
|
||||
*/
|
||||
bool SetVideoMode(wpi::util::PixelFormat pixelFormat, int width, int height,
|
||||
int fps) {
|
||||
m_status = 0;
|
||||
return SetSourceVideoMode(
|
||||
m_handle, VideoMode{pixelFormat, width, height, fps}, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pixel format.
|
||||
*
|
||||
* @param pixelFormat desired pixel format
|
||||
* @return True if set successfully
|
||||
*/
|
||||
bool SetPixelFormat(wpi::util::PixelFormat pixelFormat) {
|
||||
m_status = 0;
|
||||
return SetSourcePixelFormat(m_handle, pixelFormat, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resolution.
|
||||
*
|
||||
* @param width desired width
|
||||
* @param height desired height
|
||||
* @return True if set successfully
|
||||
*/
|
||||
bool SetResolution(int width, int height) {
|
||||
m_status = 0;
|
||||
return SetSourceResolution(m_handle, width, height, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frames per second (FPS).
|
||||
*
|
||||
* @param fps desired FPS
|
||||
* @return True if set successfully
|
||||
*/
|
||||
bool SetFPS(int fps) {
|
||||
m_status = 0;
|
||||
return SetSourceFPS(m_handle, fps, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set video mode and properties from a JSON configuration string.
|
||||
*
|
||||
* The format of the JSON input is:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "pixel format": "MJPEG", "YUYV", etc
|
||||
* "width": video mode width
|
||||
* "height": video mode height
|
||||
* "fps": video mode fps
|
||||
* "brightness": percentage brightness
|
||||
* "white balance": "auto", "hold", or value
|
||||
* "exposure": "auto", "hold", or value
|
||||
* "properties": [
|
||||
* {
|
||||
* "name": property name
|
||||
* "value": property value
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param config configuration
|
||||
* @return True if set successfully
|
||||
*/
|
||||
bool SetConfigJson(std::string_view config) {
|
||||
m_status = 0;
|
||||
return SetSourceConfigJson(m_handle, config, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set video mode and properties from a JSON configuration object.
|
||||
*
|
||||
* @param config configuration
|
||||
* @return True if set successfully
|
||||
*/
|
||||
bool SetConfigJson(const wpi::util::json& config) {
|
||||
m_status = 0;
|
||||
return SetSourceConfigJson(m_handle, config, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON configuration string.
|
||||
*
|
||||
* @return JSON configuration string
|
||||
*/
|
||||
std::string GetConfigJson() const {
|
||||
m_status = 0;
|
||||
return GetSourceConfigJson(m_handle, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON configuration object.
|
||||
*
|
||||
* @return JSON configuration object
|
||||
*/
|
||||
wpi::util::json GetConfigJsonObject() const;
|
||||
|
||||
/**
|
||||
* Get the actual FPS.
|
||||
*
|
||||
* <p>SetTelemetryPeriod() must be called for this to be valid.
|
||||
*
|
||||
* @return Actual FPS averaged over the telemetry period.
|
||||
*/
|
||||
double GetActualFPS() const {
|
||||
m_status = 0;
|
||||
return wpi::cs::GetTelemetryAverageValue(
|
||||
m_handle, CS_SOURCE_FRAMES_RECEIVED, &m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data rate (in bytes per second).
|
||||
*
|
||||
* <p>SetTelemetryPeriod() must be called for this to be valid.
|
||||
*
|
||||
* @return Data rate averaged over the telemetry period.
|
||||
*/
|
||||
double GetActualDataRate() const {
|
||||
m_status = 0;
|
||||
return wpi::cs::GetTelemetryAverageValue(m_handle, CS_SOURCE_BYTES_RECEIVED,
|
||||
&m_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all known video modes for this source.
|
||||
*/
|
||||
std::vector<VideoMode> EnumerateVideoModes() const {
|
||||
CS_Status status = 0;
|
||||
return EnumerateSourceVideoModes(m_handle, &status);
|
||||
}
|
||||
|
||||
CS_Status GetLastStatus() const { return m_status; }
|
||||
|
||||
/**
|
||||
* Enumerate all sinks connected to this source.
|
||||
*
|
||||
* @return Vector of sinks.
|
||||
*/
|
||||
std::vector<VideoSink> EnumerateSinks();
|
||||
|
||||
/**
|
||||
* Enumerate all existing sources.
|
||||
*
|
||||
* @return Vector of sources.
|
||||
*/
|
||||
static std::vector<VideoSource> EnumerateSources();
|
||||
|
||||
friend void swap(VideoSource& first, VideoSource& second) noexcept {
|
||||
using std::swap;
|
||||
swap(first.m_status, second.m_status);
|
||||
swap(first.m_handle, second.m_handle);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit VideoSource(CS_Source handle) : m_handle(handle) {}
|
||||
|
||||
mutable CS_Status m_status = 0;
|
||||
|
||||
/// Video source handle.
|
||||
CS_Source m_handle{0};
|
||||
};
|
||||
|
||||
} // namespace wpi::cs
|
||||
@@ -1,14 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
/* C API */
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* C++ API */
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/cs/cscore_oo.hpp"
|
||||
#endif /* __cplusplus */
|
||||
@@ -4,17 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h> // NOLINT
|
||||
#include <stdint.h>
|
||||
|
||||
#include "wpi/util/RawFrame.h"
|
||||
#include "wpi/util/PixelFormat.h"
|
||||
#include "wpi/util/string.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "wpi/cs/RawEvent.hpp"
|
||||
#include "wpi/cs/UsbCameraInfo.hpp"
|
||||
#include "wpi/cs/VideoMode.hpp"
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
#include "wpi/util/RawFrame.h"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/json_fwd.hpp"
|
||||
|
||||
@@ -36,140 +39,6 @@ namespace wpi::cs {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* USB camera information
|
||||
*/
|
||||
struct UsbCameraInfo {
|
||||
/** Device number (e.g. N in '/dev/videoN' on Linux) */
|
||||
int dev = -1;
|
||||
/** Path to device if available (e.g. '/dev/video0' on Linux) */
|
||||
std::string path;
|
||||
/** Vendor/model name of the camera as provided by the USB driver */
|
||||
std::string name;
|
||||
/** Other path aliases to device (e.g. '/dev/v4l/by-id/...' etc on Linux) */
|
||||
std::vector<std::string> otherPaths;
|
||||
/** USB Vendor Id */
|
||||
int vendorId = -1;
|
||||
/** USB Product Id */
|
||||
int productId = -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Video mode
|
||||
*/
|
||||
struct VideoMode : public CS_VideoMode {
|
||||
enum PixelFormat {
|
||||
kUnknown = WPI_PIXFMT_UNKNOWN,
|
||||
kMJPEG = WPI_PIXFMT_MJPEG,
|
||||
kYUYV = WPI_PIXFMT_YUYV,
|
||||
kRGB565 = WPI_PIXFMT_RGB565,
|
||||
kBGR = WPI_PIXFMT_BGR,
|
||||
kGray = WPI_PIXFMT_GRAY,
|
||||
kY16 = WPI_PIXFMT_Y16,
|
||||
kUYVY = WPI_PIXFMT_UYVY,
|
||||
kBGRA = WPI_PIXFMT_BGRA,
|
||||
};
|
||||
VideoMode() {
|
||||
pixelFormat = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
fps = 0;
|
||||
}
|
||||
VideoMode(PixelFormat pixelFormat_, int width_, int height_, int fps_) {
|
||||
pixelFormat = pixelFormat_;
|
||||
width = width_;
|
||||
height = height_;
|
||||
fps = fps_;
|
||||
}
|
||||
explicit operator bool() const { return pixelFormat == kUnknown; }
|
||||
|
||||
bool operator==(const VideoMode& other) const {
|
||||
return pixelFormat == other.pixelFormat && width == other.width &&
|
||||
height == other.height && fps == other.fps;
|
||||
}
|
||||
|
||||
bool CompareWithoutFps(const VideoMode& other) const {
|
||||
return pixelFormat == other.pixelFormat && width == other.width &&
|
||||
height == other.height;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener event
|
||||
*/
|
||||
struct RawEvent {
|
||||
enum Kind {
|
||||
kSourceCreated = CS_SOURCE_CREATED,
|
||||
kSourceDestroyed = CS_SOURCE_DESTROYED,
|
||||
kSourceConnected = CS_SOURCE_CONNECTED,
|
||||
kSourceDisconnected = CS_SOURCE_DISCONNECTED,
|
||||
kSourceVideoModesUpdated = CS_SOURCE_VIDEOMODES_UPDATED,
|
||||
kSourceVideoModeChanged = CS_SOURCE_VIDEOMODE_CHANGED,
|
||||
kSourcePropertyCreated = CS_SOURCE_PROPERTY_CREATED,
|
||||
kSourcePropertyValueUpdated = CS_SOURCE_PROPERTY_VALUE_UPDATED,
|
||||
kSourcePropertyChoicesUpdated = CS_SOURCE_PROPERTY_CHOICES_UPDATED,
|
||||
kSinkSourceChanged = CS_SINK_SOURCE_CHANGED,
|
||||
kSinkCreated = CS_SINK_CREATED,
|
||||
kSinkDestroyed = CS_SINK_DESTROYED,
|
||||
kSinkEnabled = CS_SINK_ENABLED,
|
||||
kSinkDisabled = CS_SINK_DISABLED,
|
||||
kNetworkInterfacesChanged = CS_NETWORK_INTERFACES_CHANGED,
|
||||
kTelemetryUpdated = CS_TELEMETRY_UPDATED,
|
||||
kSinkPropertyCreated = CS_SINK_PROPERTY_CREATED,
|
||||
kSinkPropertyValueUpdated = CS_SINK_PROPERTY_VALUE_UPDATED,
|
||||
kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED,
|
||||
kUsbCamerasChanged = CS_USB_CAMERAS_CHANGED
|
||||
};
|
||||
|
||||
RawEvent() = default;
|
||||
explicit RawEvent(RawEvent::Kind kind_) : kind{kind_} {}
|
||||
RawEvent(std::string_view name_, CS_Handle handle_, RawEvent::Kind kind_)
|
||||
: kind{kind_}, name{name_} {
|
||||
if (kind_ == kSinkCreated || kind_ == kSinkDestroyed ||
|
||||
kind_ == kSinkEnabled || kind_ == kSinkDisabled) {
|
||||
sinkHandle = handle_;
|
||||
} else {
|
||||
sourceHandle = handle_;
|
||||
}
|
||||
}
|
||||
RawEvent(std::string_view name_, CS_Source source_, const VideoMode& mode_)
|
||||
: kind{kSourceVideoModeChanged},
|
||||
sourceHandle{source_},
|
||||
name{name_},
|
||||
mode{mode_} {}
|
||||
RawEvent(std::string_view name_, CS_Source source_, RawEvent::Kind kind_,
|
||||
CS_Property property_, CS_PropertyKind propertyKind_, int value_,
|
||||
std::string_view valueStr_)
|
||||
: kind{kind_},
|
||||
sourceHandle{source_},
|
||||
name{name_},
|
||||
propertyHandle{property_},
|
||||
propertyKind{propertyKind_},
|
||||
value{value_},
|
||||
valueStr{valueStr_} {}
|
||||
|
||||
Kind kind;
|
||||
|
||||
// Valid for kSource* and kSink* respectively
|
||||
CS_Source sourceHandle = CS_INVALID_HANDLE;
|
||||
CS_Sink sinkHandle = CS_INVALID_HANDLE;
|
||||
|
||||
// Source/sink/property name
|
||||
std::string name;
|
||||
|
||||
// Fields for kSourceVideoModeChanged event
|
||||
VideoMode mode;
|
||||
|
||||
// Fields for kSourceProperty* events
|
||||
CS_Property propertyHandle;
|
||||
CS_PropertyKind propertyKind;
|
||||
int value;
|
||||
std::string valueStr;
|
||||
|
||||
// Listener that was triggered
|
||||
CS_Listener listener{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @defgroup cscore_property_func Property Functions
|
||||
* @{
|
||||
@@ -238,7 +107,7 @@ std::span<CS_Property> EnumerateSourceProperties(
|
||||
VideoMode GetSourceVideoMode(CS_Source source, CS_Status* status);
|
||||
bool SetSourceVideoMode(CS_Source source, const VideoMode& mode,
|
||||
CS_Status* status);
|
||||
bool SetSourcePixelFormat(CS_Source source, VideoMode::PixelFormat pixelFormat,
|
||||
bool SetSourcePixelFormat(CS_Source source, wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status);
|
||||
bool SetSourceResolution(CS_Source source, int width, int height,
|
||||
CS_Status* status);
|
||||
@@ -316,10 +185,10 @@ void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
|
||||
*/
|
||||
CS_Sink CreateMjpegServer(std::string_view name, std::string_view listenAddress,
|
||||
int port, CS_Status* status);
|
||||
CS_Sink CreateCvSink(std::string_view name, VideoMode::PixelFormat pixelFormat,
|
||||
CS_Sink CreateCvSink(std::string_view name, wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status);
|
||||
CS_Sink CreateCvSinkCallback(std::string_view name,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
wpi::util::PixelFormat pixelFormat,
|
||||
std::function<void(uint64_t time)> processFrame,
|
||||
CS_Status* status);
|
||||
|
||||
|
||||
@@ -1,431 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <opencv2/core/mat.hpp>
|
||||
|
||||
#include "wpi/cs/cscore_oo.hpp"
|
||||
#include "wpi/cs/cscore_raw.h"
|
||||
#include "wpi/util/RawFrame.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
/**
|
||||
* A source for user code to provide OpenCV images as video frames.
|
||||
*
|
||||
* This is not dependent on any opencv binary ABI, and can be used
|
||||
* with versions of most versions of OpenCV.
|
||||
*/
|
||||
class CvSource : public ImageSource {
|
||||
public:
|
||||
CvSource() = default;
|
||||
|
||||
/**
|
||||
* Create an OpenCV source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param mode Video mode being generated
|
||||
*/
|
||||
CvSource(std::string_view name, const VideoMode& mode);
|
||||
|
||||
/**
|
||||
* Create an OpenCV source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat Pixel format
|
||||
* @param width width
|
||||
* @param height height
|
||||
* @param fps fps
|
||||
*/
|
||||
CvSource(std::string_view name, VideoMode::PixelFormat pixelFormat, int width,
|
||||
int height, int fps);
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks
|
||||
*
|
||||
* <p>
|
||||
* The image format is guessed from the number of channels. The channel
|
||||
* mapping is as follows. 1: kGray 2: kYUYV 3: BGR 4: BGRA Any other channel
|
||||
* numbers will throw an error. If your image is an in alternate format, use
|
||||
* the overload that takes a PixelFormat.
|
||||
*
|
||||
* @param image OpenCV Image
|
||||
*/
|
||||
void PutFrame(cv::Mat& image);
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks.
|
||||
*
|
||||
* <p>
|
||||
* The format of the Mat must match the PixelFormat. You will corrupt memory
|
||||
* if they dont. With skipVerification false, we will verify the number of
|
||||
* channels matches the pixel format. If skipVerification is true, this step
|
||||
* is skipped and is passed straight through.
|
||||
*
|
||||
* @param image OpenCV image
|
||||
* @param pixelFormat The pixel format of the image
|
||||
* @param skipVerification skip verifying pixel format
|
||||
*/
|
||||
void PutFrame(cv::Mat& image, VideoMode::PixelFormat pixelFormat,
|
||||
bool skipVerification);
|
||||
|
||||
private:
|
||||
static bool VerifyFormat(cv::Mat& image, VideoMode::PixelFormat pixelFormat);
|
||||
};
|
||||
|
||||
/**
|
||||
* A source for user code to accept video frames as OpenCV images.
|
||||
*
|
||||
* This is not dependent on any opencv binary ABI, and can be used
|
||||
* with versions of most versions of OpenCV.
|
||||
*/
|
||||
class CvSink : public ImageSink {
|
||||
public:
|
||||
CvSink() = default;
|
||||
CvSink(const CvSink& sink);
|
||||
|
||||
/**
|
||||
* Create a sink for accepting OpenCV images.
|
||||
*
|
||||
* <p>WaitForFrame() must be called on the created sink to get each new
|
||||
* image.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat The pixel format to read
|
||||
*/
|
||||
explicit CvSink(std::string_view name, VideoMode::PixelFormat pixelFormat =
|
||||
VideoMode::PixelFormat::kBGR);
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeout(cv::Mat& image);
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with. The data is backed by data in the CvSink. It will be invalidated by
|
||||
* any grabFrame*() call on the sink.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225);
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with. The data is backed by data in the CvSink. It will be invalidated by
|
||||
* any grabFrame*() call on the sink.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with. The data is backed by data in the CvSink. It will be invalidated by
|
||||
* any grabFrame*() call on the sink.
|
||||
*
|
||||
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with
|
||||
* the first frame from the source that is not equal to lastFrameTime. If
|
||||
* lastFrameTime is zero, the time of the current frame owned by the CvSource
|
||||
* is used, and this function will block until the connected CvSource provides
|
||||
* a new frame.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameDirectLastTime(cv::Mat& image, uint64_t lastFrameTime,
|
||||
double timeout = 0.225);
|
||||
|
||||
/**
|
||||
* Get the last time a frame was grabbed. This uses the same time base as
|
||||
* wpi::util::Now().
|
||||
*
|
||||
* @return Time in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t LastFrameTime();
|
||||
|
||||
/**
|
||||
* Get the time source for the timestamp the last frame was grabbed at.
|
||||
*
|
||||
* @return Time source
|
||||
*/
|
||||
[[nodiscard]]
|
||||
WPI_TimestampSource LastFrameTimeSource();
|
||||
|
||||
private:
|
||||
constexpr int GetCvFormat(WPI_PixelFormat pixelFormat);
|
||||
|
||||
wpi::util::RawFrame rawFrame;
|
||||
VideoMode::PixelFormat pixelFormat;
|
||||
};
|
||||
|
||||
inline CvSource::CvSource(std::string_view name, const VideoMode& mode) {
|
||||
m_handle = CreateRawSource(name, true, mode, &m_status);
|
||||
}
|
||||
|
||||
inline CvSource::CvSource(std::string_view name, VideoMode::PixelFormat format,
|
||||
int width, int height, int fps) {
|
||||
m_handle = CreateRawSource(name, true, VideoMode{format, width, height, fps},
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline bool CvSource::VerifyFormat(cv::Mat& image,
|
||||
VideoMode::PixelFormat pixelFormat) {
|
||||
int channels = image.channels();
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::PixelFormat::kBGR:
|
||||
if (channels == 3) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kBGRA:
|
||||
if (channels == 4) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kGray:
|
||||
if (channels == 1) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kRGB565:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kUYVY:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kY16:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kYUYV:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kMJPEG:
|
||||
if (channels == 1) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void CvSource::PutFrame(cv::Mat& image) {
|
||||
// We only support 8-bit images; convert if necessary.
|
||||
cv::Mat finalImage;
|
||||
if (image.depth() == CV_8U) {
|
||||
finalImage = image;
|
||||
} else {
|
||||
image.convertTo(finalImage, CV_8U);
|
||||
}
|
||||
|
||||
int channels = finalImage.channels();
|
||||
VideoMode::PixelFormat format;
|
||||
if (channels == 1) {
|
||||
// 1 channel is assumed Grayscale
|
||||
format = VideoMode::PixelFormat::kGray;
|
||||
} else if (channels == 2) {
|
||||
// 2 channels is assumed YUYV
|
||||
format = VideoMode::PixelFormat::kYUYV;
|
||||
} else if (channels == 3) {
|
||||
// 3 channels is assumed BGR
|
||||
format = VideoMode::PixelFormat::kBGR;
|
||||
} else if (channels == 4) {
|
||||
// 4 channels is assumed BGRA
|
||||
format = VideoMode::PixelFormat::kBGRA;
|
||||
} else {
|
||||
// TODO Error
|
||||
return;
|
||||
}
|
||||
|
||||
PutFrame(finalImage, format, true);
|
||||
}
|
||||
|
||||
inline void CvSource::PutFrame(cv::Mat& image,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
bool skipVerification) {
|
||||
// We only support 8-bit images; convert if necessary.
|
||||
cv::Mat finalImage;
|
||||
if (image.depth() == CV_8U) {
|
||||
finalImage = image;
|
||||
} else {
|
||||
image.convertTo(finalImage, CV_8U);
|
||||
}
|
||||
|
||||
if (!skipVerification) {
|
||||
if (!VerifyFormat(finalImage, pixelFormat)) {
|
||||
// TODO Error
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
|
||||
frame.data = finalImage.data;
|
||||
frame.freeFunc = nullptr;
|
||||
frame.freeCbData = nullptr;
|
||||
frame.size = finalImage.total() * finalImage.channels();
|
||||
frame.width = finalImage.cols;
|
||||
frame.height = finalImage.rows;
|
||||
frame.stride = finalImage.step;
|
||||
frame.pixelFormat = pixelFormat;
|
||||
m_status = 0;
|
||||
PutSourceFrame(m_handle, frame, &m_status);
|
||||
}
|
||||
|
||||
inline CvSink::CvSink(std::string_view name,
|
||||
VideoMode::PixelFormat pixelFormat) {
|
||||
m_handle = CreateRawSink(name, true, &m_status);
|
||||
this->pixelFormat = pixelFormat;
|
||||
}
|
||||
|
||||
inline CvSink::CvSink(const CvSink& sink)
|
||||
: ImageSink{sink}, pixelFormat{sink.pixelFormat} {}
|
||||
|
||||
inline uint64_t CvSink::GrabFrame(cv::Mat& image, double timeout) {
|
||||
cv::Mat tmpnam;
|
||||
auto retVal = GrabFrameDirect(tmpnam);
|
||||
if (retVal <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
tmpnam.copyTo(image);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::GrabFrameNoTimeout(cv::Mat& image) {
|
||||
cv::Mat tmpnam;
|
||||
auto retVal = GrabFrameNoTimeoutDirect(tmpnam);
|
||||
if (retVal <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
tmpnam.copyTo(image);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline constexpr int CvSink::GetCvFormat(WPI_PixelFormat pixelFormat) {
|
||||
int type = 0;
|
||||
switch (pixelFormat) {
|
||||
case WPI_PIXFMT_YUYV:
|
||||
case WPI_PIXFMT_RGB565:
|
||||
case WPI_PIXFMT_Y16:
|
||||
case WPI_PIXFMT_UYVY:
|
||||
type = CV_8UC2;
|
||||
break;
|
||||
case WPI_PIXFMT_BGR:
|
||||
type = CV_8UC3;
|
||||
break;
|
||||
case WPI_PIXFMT_BGRA:
|
||||
type = CV_8UC4;
|
||||
break;
|
||||
case WPI_PIXFMT_GRAY:
|
||||
case WPI_PIXFMT_MJPEG:
|
||||
default:
|
||||
type = CV_8UC1;
|
||||
break;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::GrabFrameDirect(cv::Mat& image, double timeout) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = pixelFormat;
|
||||
auto timestamp = GrabSinkFrameTimeout(m_handle, rawFrame, timeout, &m_status);
|
||||
if (m_status != CS_OK) {
|
||||
return 0;
|
||||
}
|
||||
image =
|
||||
cv::Mat{rawFrame.height, rawFrame.width,
|
||||
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::GrabFrameNoTimeoutDirect(cv::Mat& image) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = pixelFormat;
|
||||
auto timestamp = GrabSinkFrame(m_handle, rawFrame, &m_status);
|
||||
if (m_status != CS_OK) {
|
||||
return 0;
|
||||
}
|
||||
image =
|
||||
cv::Mat{rawFrame.height, rawFrame.width,
|
||||
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::GrabFrameDirectLastTime(cv::Mat& image,
|
||||
uint64_t lastFrameTime,
|
||||
double timeout) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = pixelFormat;
|
||||
auto timestamp = GrabSinkFrameTimeoutLastTime(m_handle, rawFrame, timeout,
|
||||
lastFrameTime, &m_status);
|
||||
if (m_status != CS_OK) {
|
||||
return 0;
|
||||
}
|
||||
image =
|
||||
cv::Mat{rawFrame.height, rawFrame.width,
|
||||
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::LastFrameTime() {
|
||||
return rawFrame.timestamp;
|
||||
}
|
||||
|
||||
inline WPI_TimestampSource CvSink::LastFrameTimeSource() {
|
||||
return static_cast<WPI_TimestampSource>(rawFrame.timestampSrc);
|
||||
}
|
||||
|
||||
} // namespace wpi::cs
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,20 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
|
||||
// NOLINTBEGIN
|
||||
#ifdef __cplusplus
|
||||
#include "wpi/cs/cscore_oo.hpp"
|
||||
#include "wpi/util/RawFrame.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// NOLINTEND
|
||||
|
||||
/**
|
||||
* @defgroup cscore_raw_cfunc Raw Image Functions
|
||||
@@ -50,192 +41,3 @@ CS_Source CS_CreateRawSource(const struct WPI_String* name, CS_Bool isCv,
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* @defgroup cscore_raw_func Raw Image Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
CS_Source CreateRawSource(std::string_view name, bool isCv,
|
||||
const VideoMode& mode, CS_Status* status);
|
||||
|
||||
CS_Sink CreateRawSink(std::string_view name, bool isCv, CS_Status* status);
|
||||
CS_Sink CreateRawSinkCallback(std::string_view name, bool isCv,
|
||||
std::function<void(uint64_t time)> processFrame,
|
||||
CS_Status* status);
|
||||
|
||||
void PutSourceFrame(CS_Source source, const WPI_RawFrame& image,
|
||||
CS_Status* status);
|
||||
uint64_t GrabSinkFrame(CS_Sink sink, WPI_RawFrame& image, CS_Status* status);
|
||||
uint64_t GrabSinkFrameTimeout(CS_Sink sink, WPI_RawFrame& image, double timeout,
|
||||
CS_Status* status);
|
||||
uint64_t GrabSinkFrameTimeoutLastTime(CS_Sink sink, WPI_RawFrame& image,
|
||||
double timeout, uint64_t lastFrameTime,
|
||||
CS_Status* status);
|
||||
|
||||
/**
|
||||
* A source for user code to provide video frames as raw bytes.
|
||||
*
|
||||
* This is a complex API, most cases should use CvSource.
|
||||
*/
|
||||
class RawSource : public ImageSource {
|
||||
public:
|
||||
RawSource() = default;
|
||||
|
||||
/**
|
||||
* Create a raw frame source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param mode Video mode being generated
|
||||
*/
|
||||
RawSource(std::string_view name, const VideoMode& mode);
|
||||
|
||||
/**
|
||||
* Create a raw frame source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat Pixel format
|
||||
* @param width width
|
||||
* @param height height
|
||||
* @param fps fps
|
||||
*/
|
||||
RawSource(std::string_view name, VideoMode::PixelFormat pixelFormat,
|
||||
int width, int height, int fps);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Put a raw image and notify sinks.
|
||||
*
|
||||
* @param image raw frame image
|
||||
*/
|
||||
void PutFrame(wpi::util::RawFrame& image);
|
||||
};
|
||||
|
||||
/**
|
||||
* A sink for user code to accept video frames as raw bytes.
|
||||
*
|
||||
* This is a complex API, most cases should use CvSource.
|
||||
*/
|
||||
class RawSink : public ImageSink {
|
||||
public:
|
||||
RawSink() = default;
|
||||
|
||||
/**
|
||||
* Create a sink for accepting raw images.
|
||||
*
|
||||
* <p>GrabFrame() must be called on the created sink to get each new
|
||||
* image.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
*/
|
||||
explicit RawSink(std::string_view name);
|
||||
|
||||
/**
|
||||
* Create a sink for accepting raws images in a separate thread.
|
||||
*
|
||||
* <p>A thread will be created that calls WaitForFrame() and calls the
|
||||
* processFrame() callback each time a new frame arrives.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param processFrame Frame processing function; will be called with a
|
||||
* time=0 if an error occurred. processFrame should call GetImage()
|
||||
* or GetError() as needed, but should not call (except in very
|
||||
* unusual circumstances) WaitForImage().
|
||||
*/
|
||||
RawSink(std::string_view name,
|
||||
std::function<void(uint64_t time)> processFrame);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have three 8-bit channels stored in BGR order.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrame(wpi::util::RawFrame& image, double timeout = 0.225) const;
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have three 8-bit channels stored in BGR order.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeout(wpi::util::RawFrame& image) const;
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have three 8-bit channels stored in BGR order.
|
||||
*
|
||||
* <p>If lastFrameTime is provided and non-zero, the sink will fill image with
|
||||
* the first frame from the source that is not equal to lastFrameTime. If
|
||||
* lastFrameTime is zero, the time of the current frame owned by the CvSource
|
||||
* is used, and this function will block until the connected CvSource provides
|
||||
* a new frame.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error
|
||||
* message); the frame time is in the same time base as
|
||||
* wpi::util::Now(), and is in 1 us increments.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameLastTime(wpi::util::RawFrame& image, uint64_t lastFrameTime,
|
||||
double timeout = 0.225) const;
|
||||
};
|
||||
|
||||
inline RawSource::RawSource(std::string_view name, const VideoMode& mode) {
|
||||
m_handle = CreateRawSource(name, false, mode, &m_status);
|
||||
}
|
||||
|
||||
inline RawSource::RawSource(std::string_view name,
|
||||
VideoMode::PixelFormat format, int width,
|
||||
int height, int fps) {
|
||||
m_handle = CreateRawSource(name, false, VideoMode{format, width, height, fps},
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline void RawSource::PutFrame(wpi::util::RawFrame& image) {
|
||||
m_status = 0;
|
||||
PutSourceFrame(m_handle, image, &m_status);
|
||||
}
|
||||
|
||||
inline RawSink::RawSink(std::string_view name) {
|
||||
m_handle = CreateRawSink(name, false, &m_status);
|
||||
}
|
||||
|
||||
inline RawSink::RawSink(std::string_view name,
|
||||
std::function<void(uint64_t time)> processFrame) {
|
||||
m_handle = CreateRawSinkCallback(name, false, processFrame, &m_status);
|
||||
}
|
||||
|
||||
inline uint64_t RawSink::GrabFrame(wpi::util::RawFrame& image,
|
||||
double timeout) const {
|
||||
m_status = 0;
|
||||
return GrabSinkFrameTimeout(m_handle, image, timeout, &m_status);
|
||||
}
|
||||
|
||||
inline uint64_t RawSink::GrabFrameNoTimeout(wpi::util::RawFrame& image) const {
|
||||
m_status = 0;
|
||||
return GrabSinkFrame(m_handle, image, &m_status);
|
||||
}
|
||||
|
||||
inline uint64_t RawSink::GrabFrameLastTime(wpi::util::RawFrame& image,
|
||||
uint64_t lastFrameTime,
|
||||
double timeout) const {
|
||||
m_status = 0;
|
||||
return GrabSinkFrameTimeoutLastTime(m_handle, image, timeout, lastFrameTime,
|
||||
&m_status);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
} // namespace wpi::cs
|
||||
|
||||
#endif
|
||||
|
||||
42
cscore/src/main/native/include/wpi/cs/cscore_raw.hpp
Normal file
42
cscore/src/main/native/include/wpi/cs/cscore_raw.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
#include "wpi/cs/VideoMode.hpp"
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
#include "wpi/util/RawFrame.h"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
/**
|
||||
* @defgroup cscore_raw_func Raw Image Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
CS_Source CreateRawSource(std::string_view name, bool isCv,
|
||||
const VideoMode& mode, CS_Status* status);
|
||||
|
||||
CS_Sink CreateRawSink(std::string_view name, bool isCv, CS_Status* status);
|
||||
CS_Sink CreateRawSinkCallback(std::string_view name, bool isCv,
|
||||
std::function<void(uint64_t time)> processFrame,
|
||||
CS_Status* status);
|
||||
|
||||
void PutSourceFrame(CS_Source source, const WPI_RawFrame& image,
|
||||
CS_Status* status);
|
||||
uint64_t GrabSinkFrame(CS_Sink sink, WPI_RawFrame& image, CS_Status* status);
|
||||
uint64_t GrabSinkFrameTimeout(CS_Sink sink, WPI_RawFrame& image, double timeout,
|
||||
CS_Status* status);
|
||||
uint64_t GrabSinkFrameTimeoutLastTime(CS_Sink sink, WPI_RawFrame& image,
|
||||
double timeout, uint64_t lastFrameTime,
|
||||
CS_Status* status);
|
||||
|
||||
/** @} */
|
||||
|
||||
} // namespace wpi::cs
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "wpi/util/SmallString.hpp"
|
||||
#include "wpi/util/StringExtras.hpp"
|
||||
#include "wpi/util/fs.hpp"
|
||||
#include "wpi/util/raw_istream.hpp"
|
||||
#include "wpi/util/timestamp.hpp"
|
||||
|
||||
using namespace wpi::cs;
|
||||
@@ -70,47 +71,47 @@ static inline struct v4l2_fract FPSToFract(int fps) {
|
||||
}
|
||||
|
||||
// Conversion from v4l2_format pixelformat to VideoMode::PixelFormat
|
||||
static VideoMode::PixelFormat ToPixelFormat(__u32 pixelFormat) {
|
||||
static wpi::util::PixelFormat ToPixelFormat(__u32 pixelFormat) {
|
||||
switch (pixelFormat) {
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
return VideoMode::kMJPEG;
|
||||
return wpi::util::PixelFormat::kMJPEG;
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
return VideoMode::kYUYV;
|
||||
return wpi::util::PixelFormat::kYUYV;
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
return VideoMode::kRGB565;
|
||||
return wpi::util::PixelFormat::kRGB565;
|
||||
case V4L2_PIX_FMT_BGR24:
|
||||
return VideoMode::kBGR;
|
||||
return wpi::util::PixelFormat::kBGR;
|
||||
case V4L2_PIX_FMT_ABGR32:
|
||||
return VideoMode::kBGRA;
|
||||
return wpi::util::PixelFormat::kBGRA;
|
||||
case V4L2_PIX_FMT_GREY:
|
||||
return VideoMode::kGray;
|
||||
return wpi::util::PixelFormat::kGray;
|
||||
case V4L2_PIX_FMT_Y16:
|
||||
return VideoMode::kY16;
|
||||
return wpi::util::PixelFormat::kY16;
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
return VideoMode::kUYVY;
|
||||
return wpi::util::PixelFormat::kUYVY;
|
||||
default:
|
||||
return VideoMode::kUnknown;
|
||||
return wpi::util::PixelFormat::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion from VideoMode::PixelFormat to v4l2_format pixelformat
|
||||
static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) {
|
||||
static __u32 FromPixelFormat(wpi::util::PixelFormat pixelFormat) {
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::kMJPEG:
|
||||
case wpi::util::PixelFormat::kMJPEG:
|
||||
return V4L2_PIX_FMT_MJPEG;
|
||||
case VideoMode::kYUYV:
|
||||
case wpi::util::PixelFormat::kYUYV:
|
||||
return V4L2_PIX_FMT_YUYV;
|
||||
case VideoMode::kRGB565:
|
||||
case wpi::util::PixelFormat::kRGB565:
|
||||
return V4L2_PIX_FMT_RGB565;
|
||||
case VideoMode::kBGR:
|
||||
case wpi::util::PixelFormat::kBGR:
|
||||
return V4L2_PIX_FMT_BGR24;
|
||||
case VideoMode::kBGRA:
|
||||
case wpi::util::PixelFormat::kBGRA:
|
||||
return V4L2_PIX_FMT_ABGR32;
|
||||
case VideoMode::kGray:
|
||||
case wpi::util::PixelFormat::kGray:
|
||||
return V4L2_PIX_FMT_GREY;
|
||||
case VideoMode::kY16:
|
||||
case wpi::util::PixelFormat::kY16:
|
||||
return V4L2_PIX_FMT_Y16;
|
||||
case VideoMode::kUYVY:
|
||||
case wpi::util::PixelFormat::kUYVY:
|
||||
return V4L2_PIX_FMT_UYVY;
|
||||
default:
|
||||
return 0;
|
||||
@@ -553,7 +554,7 @@ void UsbCameraImpl::CameraThreadMain() {
|
||||
int width = m_mode.width;
|
||||
int height = m_mode.height;
|
||||
bool good = true;
|
||||
if (m_mode.pixelFormat == VideoMode::kMJPEG &&
|
||||
if (m_mode.pixelFormat == wpi::util::PixelFormat::kMJPEG &&
|
||||
!GetJpegSize(image, &width, &height)) {
|
||||
SWARNING("invalid JPEG image received from camera");
|
||||
good = false;
|
||||
@@ -603,8 +604,8 @@ void UsbCameraImpl::CameraThreadMain() {
|
||||
"Got valid copy time for frame - default to wpi::util::Now");
|
||||
}
|
||||
|
||||
PutFrame(static_cast<VideoMode::PixelFormat>(m_mode.pixelFormat),
|
||||
width, height, image, frameTime, timeSource);
|
||||
PutFrame(m_mode.pixelFormat, width, height, image, frameTime,
|
||||
timeSource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,7 +822,7 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetMode(
|
||||
std::unique_lock<wpi::util::mutex>& lock, const Message& msg) {
|
||||
VideoMode newMode;
|
||||
if (msg.kind == Message::kCmdSetMode) {
|
||||
newMode.pixelFormat = msg.data[0];
|
||||
newMode.pixelFormat = static_cast<wpi::util::PixelFormat>(msg.data[0]);
|
||||
newMode.width = msg.data[1];
|
||||
newMode.height = msg.data[2];
|
||||
newMode.fps = msg.data[3];
|
||||
@@ -830,7 +831,7 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetMode(
|
||||
m_modeSetFPS = true;
|
||||
} else if (msg.kind == Message::kCmdSetPixelFormat) {
|
||||
newMode = m_mode;
|
||||
newMode.pixelFormat = msg.data[0];
|
||||
newMode.pixelFormat = static_cast<wpi::util::PixelFormat>(msg.data[0]);
|
||||
m_modeSetPixelFormat = true;
|
||||
} else if (msg.kind == Message::kCmdSetResolution) {
|
||||
newMode = m_mode;
|
||||
@@ -1017,22 +1018,21 @@ void UsbCameraImpl::DeviceSetMode() {
|
||||
: 0;
|
||||
#endif
|
||||
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
vfmt.fmt.pix.pixelformat =
|
||||
FromPixelFormat(static_cast<VideoMode::PixelFormat>(m_mode.pixelFormat));
|
||||
vfmt.fmt.pix.pixelformat = FromPixelFormat(m_mode.pixelFormat);
|
||||
if (vfmt.fmt.pix.pixelformat == 0) {
|
||||
SWARNING("could not set format {}, defaulting to MJPEG",
|
||||
m_mode.pixelFormat);
|
||||
static_cast<int>(m_mode.pixelFormat));
|
||||
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
|
||||
}
|
||||
vfmt.fmt.pix.width = m_mode.width;
|
||||
vfmt.fmt.pix.height = m_mode.height;
|
||||
vfmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
if (DoIoctl(fd, VIDIOC_S_FMT, &vfmt) != 0) {
|
||||
SWARNING("could not set format {} res {}x{}", m_mode.pixelFormat,
|
||||
m_mode.width, m_mode.height);
|
||||
SWARNING("could not set format {} res {}x{}",
|
||||
static_cast<int>(m_mode.pixelFormat), m_mode.width, m_mode.height);
|
||||
} else {
|
||||
SINFO("set format {} res {}x{}", m_mode.pixelFormat, m_mode.width,
|
||||
m_mode.height);
|
||||
SINFO("set format {} res {}x{}", static_cast<int>(m_mode.pixelFormat),
|
||||
m_mode.width, m_mode.height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1079,10 +1079,10 @@ void UsbCameraImpl::DeviceCacheMode() {
|
||||
if (DoIoctl(fd, VIDIOC_G_FMT, &vfmt) != 0) {
|
||||
SERROR("could not read current video mode");
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_mode = VideoMode{VideoMode::kMJPEG, 320, 240, 30};
|
||||
m_mode = VideoMode{wpi::util::PixelFormat::kMJPEG, 320, 240, 30};
|
||||
return;
|
||||
}
|
||||
VideoMode::PixelFormat pixelFormat = ToPixelFormat(vfmt.fmt.pix.pixelformat);
|
||||
wpi::util::PixelFormat pixelFormat = ToPixelFormat(vfmt.fmt.pix.pixelformat);
|
||||
int width = vfmt.fmt.pix.width;
|
||||
int height = vfmt.fmt.pix.height;
|
||||
|
||||
@@ -1104,13 +1104,13 @@ void UsbCameraImpl::DeviceCacheMode() {
|
||||
// User set pixel format
|
||||
if (pixelFormat != m_mode.pixelFormat) {
|
||||
formatChanged = true;
|
||||
pixelFormat = static_cast<VideoMode::PixelFormat>(m_mode.pixelFormat);
|
||||
pixelFormat = static_cast<wpi::util::PixelFormat>(m_mode.pixelFormat);
|
||||
}
|
||||
} else {
|
||||
// Default to MJPEG
|
||||
if (pixelFormat != VideoMode::kMJPEG) {
|
||||
if (pixelFormat != wpi::util::PixelFormat::kMJPEG) {
|
||||
formatChanged = true;
|
||||
pixelFormat = VideoMode::kMJPEG;
|
||||
pixelFormat = wpi::util::PixelFormat::kMJPEG;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1319,8 +1319,8 @@ void UsbCameraImpl::DeviceCacheVideoModes() {
|
||||
std::memset(&fmt, 0, sizeof(fmt));
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
for (fmt.index = 0; TryIoctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0; ++fmt.index) {
|
||||
VideoMode::PixelFormat pixelFormat = ToPixelFormat(fmt.pixelformat);
|
||||
if (pixelFormat == VideoMode::kUnknown) {
|
||||
wpi::util::PixelFormat pixelFormat = ToPixelFormat(fmt.pixelformat);
|
||||
if (pixelFormat == wpi::util::PixelFormat::kUnknown) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1359,8 +1359,9 @@ void UsbCameraImpl::DeviceCacheVideoModes() {
|
||||
// provide a set of discrete modes; list based on
|
||||
// https://picamera.readthedocs.io/en/release-1.10/fov.html
|
||||
if (modes.empty() && m_picamera) {
|
||||
for (VideoMode::PixelFormat pixelFormat :
|
||||
{VideoMode::kYUYV, VideoMode::kMJPEG, VideoMode::kBGR}) {
|
||||
for (wpi::util::PixelFormat pixelFormat :
|
||||
{wpi::util::PixelFormat::kYUYV, wpi::util::PixelFormat::kMJPEG,
|
||||
wpi::util::PixelFormat::kBGR}) {
|
||||
modes.emplace_back(pixelFormat, 1920, 1080, 30);
|
||||
modes.emplace_back(pixelFormat, 2592, 1944, 15);
|
||||
modes.emplace_back(pixelFormat, 1296, 972, 42);
|
||||
@@ -1566,7 +1567,7 @@ void UsbCameraImpl::SetExposureManual(int value, CS_Status* status) {
|
||||
|
||||
bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
Message msg{Message::kCmdSetMode};
|
||||
msg.data[0] = mode.pixelFormat;
|
||||
msg.data[0] = static_cast<int>(mode.pixelFormat);
|
||||
msg.data[1] = mode.width;
|
||||
msg.data[2] = mode.height;
|
||||
msg.data[3] = mode.fps;
|
||||
@@ -1574,10 +1575,10 @@ bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
return *status == CS_OK;
|
||||
}
|
||||
|
||||
bool UsbCameraImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
bool UsbCameraImpl::SetPixelFormat(wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status) {
|
||||
Message msg{Message::kCmdSetPixelFormat};
|
||||
msg.data[0] = pixelFormat;
|
||||
msg.data[0] = static_cast<int>(pixelFormat);
|
||||
*status = SendAndWait(std::move(msg));
|
||||
return *status == CS_OK;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,9 @@
|
||||
#include "SourceImpl.hpp"
|
||||
#include "UsbCameraBuffer.hpp"
|
||||
#include "UsbCameraProperty.hpp"
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/condition_variable.hpp"
|
||||
#include "wpi/util/mutex.hpp"
|
||||
#include "wpi/util/raw_istream.hpp"
|
||||
#include "wpi/util/raw_ostream.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
@@ -53,7 +51,7 @@ class UsbCameraImpl : public SourceImpl {
|
||||
void SetExposureManual(int value, CS_Status* status) override;
|
||||
|
||||
bool SetVideoMode(const VideoMode& mode, CS_Status* status) override;
|
||||
bool SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
bool SetPixelFormat(wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status) override;
|
||||
bool SetResolution(int width, int height, CS_Status* status) override;
|
||||
bool SetFPS(int fps, CS_Status* status) override;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/StringMap.hpp"
|
||||
|
||||
#include "SourceImpl.hpp"
|
||||
@@ -56,7 +57,7 @@ class UsbCameraImpl : public SourceImpl {
|
||||
void SetExposureManual(int value, CS_Status* status) override;
|
||||
|
||||
bool SetVideoMode(const VideoMode& mode, CS_Status* status) override;
|
||||
bool SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
bool SetPixelFormat(wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status) override;
|
||||
bool SetResolution(int width, int height, CS_Status* status) override;
|
||||
bool SetFPS(int fps, CS_Status* status) override;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "Instance.hpp"
|
||||
#include "c_util.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "UsbCameraImpl.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
@@ -88,7 +89,7 @@ void UsbCameraImpl::SetExposureManual(int value, CS_Status* status) {
|
||||
bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
return [m_objc setVideoMode:mode status:status];
|
||||
}
|
||||
bool UsbCameraImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
bool UsbCameraImpl::SetPixelFormat(wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status) {
|
||||
return [m_objc setPixelFormat:pixelFormat status:status];
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#import "UvcControlImpl.hpp"
|
||||
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
|
||||
// Quirk: exposure auto is 3 for on, 1 for off
|
||||
#define kPropertyAutoExposureOn 3
|
||||
@@ -83,7 +84,7 @@ class UsbCameraImpl;
|
||||
- (void)setExposureManual:(int)value status:(CS_Status*)status;
|
||||
|
||||
- (bool)setVideoMode:(const wpi::cs::VideoMode&)mode status:(CS_Status*)status;
|
||||
- (bool)setPixelFormat:(wpi::cs::VideoMode::PixelFormat)pixelFormat
|
||||
- (bool)setPixelFormat:(wpi::util::PixelFormat)pixelFormat
|
||||
status:(CS_Status*)status;
|
||||
- (bool)setResolutionWidth:(int)width
|
||||
withHeight:(int)height
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Notifier.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "UsbCameraImpl.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
|
||||
template <typename S, typename... Args>
|
||||
inline void NamedLog(UsbCameraImplObjc* objc, unsigned int level,
|
||||
@@ -414,7 +415,7 @@ using namespace wpi::cs;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
- (bool)setPixelFormat:(wpi::cs::VideoMode::PixelFormat)pixelFormat
|
||||
- (bool)setPixelFormat:(wpi::util::PixelFormat)pixelFormat
|
||||
status:(CS_Status*)status {
|
||||
dispatch_async_and_wait(self.sessionQueue, ^{
|
||||
auto sharedThis = self.cppImpl.lock();
|
||||
@@ -464,8 +465,8 @@ using namespace wpi::cs;
|
||||
}
|
||||
|
||||
if (newMode != sharedThis->objcGetVideoMode()) {
|
||||
OBJCDEBUG3("Trying Mode {} {} {} {}", newMode.pixelFormat, newMode.width,
|
||||
newMode.height, newMode.fps);
|
||||
OBJCDEBUG3("Trying Mode {} {} {} {}", static_cast<int>(newMode.pixelFormat),
|
||||
newMode.width, newMode.height, newMode.fps);
|
||||
int localFPS = 0;
|
||||
AVCaptureDeviceFormat* newModeType = [self deviceCheckModeValid:&newMode
|
||||
withFps:&localFPS];
|
||||
@@ -693,13 +694,13 @@ using namespace wpi::cs;
|
||||
propertyAutoCache[nameStr] = propID;
|
||||
}
|
||||
|
||||
static wpi::cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) {
|
||||
static wpi::util::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) {
|
||||
switch (fourcc) {
|
||||
case kCVPixelFormatType_422YpCbCr8_yuvs:
|
||||
case kCVPixelFormatType_422YpCbCr8FullRange:
|
||||
return wpi::cs::VideoMode::PixelFormat::kYUYV;
|
||||
return wpi::util::PixelFormat::kYUYV;
|
||||
default:
|
||||
return wpi::cs::VideoMode::PixelFormat::kBGR;
|
||||
return wpi::util::PixelFormat::kBGR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,7 +769,8 @@ static wpi::cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc)
|
||||
return nil;
|
||||
}
|
||||
|
||||
OBJCDEBUG3("Checking mode {} {} {} {}", toCheck->pixelFormat, toCheck->width,
|
||||
OBJCDEBUG3("Checking mode {} {} {} {}",
|
||||
static_cast<int>(toCheck->pixelFormat), toCheck->width,
|
||||
toCheck->height, toCheck->fps);
|
||||
std::vector<CameraModeStore>& platformModes =
|
||||
sharedThis->objcGetPlatformVideoModes();
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
#include "wpi/util/ConvertUTF.hpp"
|
||||
#include "wpi/util/MemAlloc.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/SmallString.hpp"
|
||||
#include "wpi/util/StringExtras.hpp"
|
||||
#include "wpi/util/timestamp.hpp"
|
||||
@@ -135,13 +136,13 @@ void UsbCameraImpl::SetExposureManual(int value, CS_Status* status) {
|
||||
}
|
||||
|
||||
bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
if (mode.pixelFormat == VideoMode::kUnknown) {
|
||||
if (mode.pixelFormat == wpi::util::PixelFormat::kUnknown) {
|
||||
*status = CS_UNSUPPORTED_MODE;
|
||||
return false;
|
||||
}
|
||||
|
||||
Message msg{Message::kCmdSetMode};
|
||||
msg.data[0] = mode.pixelFormat;
|
||||
msg.data[0] = static_cast<int>(mode.pixelFormat);
|
||||
msg.data[1] = mode.width;
|
||||
msg.data[2] = mode.height;
|
||||
msg.data[3] = mode.fps;
|
||||
@@ -152,14 +153,14 @@ bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
bool UsbCameraImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
bool UsbCameraImpl::SetPixelFormat(wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status) {
|
||||
if (pixelFormat == VideoMode::kUnknown) {
|
||||
if (pixelFormat == wpi::util::PixelFormat::kUnknown) {
|
||||
*status = CS_UNSUPPORTED_MODE;
|
||||
return false;
|
||||
}
|
||||
Message msg{Message::kCmdSetPixelFormat};
|
||||
msg.data[0] = pixelFormat;
|
||||
msg.data[0] = static_cast<int>(pixelFormat);
|
||||
auto result =
|
||||
m_messagePump->SendWindowMessage<CS_Status, Message::Kind, Message*>(
|
||||
SetCameraMessage, msg.kind, &msg);
|
||||
@@ -340,7 +341,7 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample,
|
||||
}
|
||||
|
||||
std::string_view data_view{reinterpret_cast<char*>(ptr), length};
|
||||
SourceImpl::PutFrame(static_cast<VideoMode::PixelFormat>(mode.pixelFormat),
|
||||
SourceImpl::PutFrame(static_cast<wpi::util::PixelFormat>(mode.pixelFormat),
|
||||
mode.width, mode.height, data_view, currentTime);
|
||||
|
||||
if (buffer2d) {
|
||||
@@ -425,22 +426,22 @@ LRESULT UsbCameraImpl::PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam,
|
||||
return 0l;
|
||||
}
|
||||
|
||||
static wpi::cs::VideoMode::PixelFormat GetFromGUID(const GUID& guid) {
|
||||
static wpi::util::PixelFormat GetFromGUID(const GUID& guid) {
|
||||
// Compare GUID to one of the supported ones
|
||||
if (IsEqualGUID(guid, MFVideoFormat_L8)) {
|
||||
return wpi::cs::VideoMode::PixelFormat::kGray;
|
||||
return wpi::util::PixelFormat::kGray;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_L16)) {
|
||||
return wpi::cs::VideoMode::PixelFormat::kY16;
|
||||
return wpi::util::PixelFormat::kY16;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_YUY2)) {
|
||||
return wpi::cs::VideoMode::PixelFormat::kYUYV;
|
||||
return wpi::util::PixelFormat::kYUYV;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_MJPG)) {
|
||||
return wpi::cs::VideoMode::PixelFormat::kMJPEG;
|
||||
return wpi::util::PixelFormat::kMJPEG;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_RGB565)) {
|
||||
return wpi::cs::VideoMode::PixelFormat::kRGB565;
|
||||
return wpi::util::PixelFormat::kRGB565;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_UYVY)) {
|
||||
return wpi::cs::VideoMode::PixelFormat::kUYVY;
|
||||
return wpi::util::PixelFormat::kUYVY;
|
||||
} else {
|
||||
return wpi::cs::VideoMode::PixelFormat::kUnknown;
|
||||
return wpi::util::PixelFormat::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,13 +832,13 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetMode(
|
||||
std::unique_lock<wpi::util::mutex>& lock, const Message& msg) {
|
||||
VideoMode newMode;
|
||||
if (msg.kind == Message::kCmdSetMode) {
|
||||
newMode.pixelFormat = msg.data[0];
|
||||
newMode.pixelFormat = static_cast<wpi::util::PixelFormat>(msg.data[0]);
|
||||
newMode.width = msg.data[1];
|
||||
newMode.height = msg.data[2];
|
||||
newMode.fps = msg.data[3];
|
||||
} else if (msg.kind == Message::kCmdSetPixelFormat) {
|
||||
newMode = m_mode;
|
||||
newMode.pixelFormat = msg.data[0];
|
||||
newMode.pixelFormat = static_cast<wpi::util::PixelFormat>(msg.data[0]);
|
||||
} else if (msg.kind == Message::kCmdSetResolution) {
|
||||
newMode = m_mode;
|
||||
newMode.width = msg.data[0];
|
||||
@@ -991,7 +992,7 @@ void UsbCameraImpl::DeviceCacheVideoModes() {
|
||||
nativeType->GetGUID(MF_MT_SUBTYPE, &guid);
|
||||
|
||||
auto format = GetFromGUID(guid);
|
||||
if (format == VideoMode::kUnknown) {
|
||||
if (format == wpi::util::PixelFormat::kUnknown) {
|
||||
count++;
|
||||
// Don't put in unknowns
|
||||
continue;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "SourceImpl.hpp"
|
||||
#include "UsbCameraProperty.hpp"
|
||||
#include "WindowsMessagePump.hpp"
|
||||
#include "wpi/util/PixelFormat.hpp"
|
||||
#include "wpi/util/SmallVector.hpp"
|
||||
#include "wpi/util/condition_variable.hpp"
|
||||
#include "wpi/util/mutex.hpp"
|
||||
@@ -61,7 +62,7 @@ class UsbCameraImpl : public SourceImpl,
|
||||
void SetExposureManual(int value, CS_Status* status) override;
|
||||
|
||||
bool SetVideoMode(const VideoMode& mode, CS_Status* status) override;
|
||||
bool SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
bool SetPixelFormat(wpi::util::PixelFormat pixelFormat,
|
||||
CS_Status* status) override;
|
||||
bool SetResolution(int width, int height, CS_Status* status) override;
|
||||
bool SetFPS(int fps, CS_Status* status) override;
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "wpi/cs/cscore.h"
|
||||
#include "wpi/cs/HttpCamera.hpp"
|
||||
#include "wpi/cs/cscore_cpp.hpp"
|
||||
|
||||
namespace wpi::cs {
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
// 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 "wpi/cs/cscore.h"
|
||||
#include "wpi/cs/cscore_c.h"
|
||||
#include "wpi/hal/HAL.h"
|
||||
#include "wpi/nt/ntcore.h"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
|
||||
#include "wpi/cameraserver/CameraServer.hpp"
|
||||
#include "wpi/cs/HttpCamera.hpp"
|
||||
#include "wpi/framework/TimedRobot.hpp"
|
||||
|
||||
/**
|
||||
|
||||
8
wpiutil/robotpy_pybind_build_info.bzl
generated
8
wpiutil/robotpy_pybind_build_info.bzl
generated
@@ -44,6 +44,14 @@ def wpiutil_extension(srcs = [], header_to_dat_deps = [], extra_hdrs = [], inclu
|
||||
tmpl_class_names = [],
|
||||
trampolines = [],
|
||||
),
|
||||
struct(
|
||||
class_name = "PixelFormat",
|
||||
yml_file = "semiwrap/PixelFormat.yml",
|
||||
header_root = "$(execpath :robotpy-native-wpiutil.copy_headers)",
|
||||
header_file = "$(execpath :robotpy-native-wpiutil.copy_headers)/wpi/util/PixelFormat.hpp",
|
||||
tmpl_class_names = [],
|
||||
trampolines = [],
|
||||
),
|
||||
struct(
|
||||
class_name = "RawFrame_c",
|
||||
yml_file = "semiwrap/RawFrame_c.yml",
|
||||
|
||||
20
wpiutil/src/main/native/include/wpi/util/PixelFormat.h
Normal file
20
wpiutil/src/main/native/include/wpi/util/PixelFormat.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Pixel formats
|
||||
*/
|
||||
enum WPI_PixelFormat {
|
||||
WPI_PIXFMT_UNKNOWN = 0, // unknown
|
||||
WPI_PIXFMT_MJPEG, // Motion-JPEG (compressed image data)
|
||||
WPI_PIXFMT_YUYV, // YUV 4:2:2, 16 bpp
|
||||
WPI_PIXFMT_RGB565, // RGB 5-6-5, 16 bpp
|
||||
WPI_PIXFMT_BGR, // BGR 8-8-8, 24 bpp
|
||||
WPI_PIXFMT_GRAY, // Grayscale, 8 bpp
|
||||
WPI_PIXFMT_Y16, // Grayscale, 16 bpp
|
||||
WPI_PIXFMT_UYVY, // YUV 4:2:2, 16 bpp
|
||||
WPI_PIXFMT_BGRA, // BGRA 8-8-8-8-, 32 bpp
|
||||
};
|
||||
26
wpiutil/src/main/native/include/wpi/util/PixelFormat.hpp
Normal file
26
wpiutil/src/main/native/include/wpi/util/PixelFormat.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/util/PixelFormat.h"
|
||||
|
||||
namespace wpi::util {
|
||||
|
||||
/**
|
||||
* Pixel formats
|
||||
*/
|
||||
enum class PixelFormat {
|
||||
kUnknown = WPI_PIXFMT_UNKNOWN,
|
||||
kMJPEG = WPI_PIXFMT_MJPEG,
|
||||
kYUYV = WPI_PIXFMT_YUYV,
|
||||
kRGB565 = WPI_PIXFMT_RGB565,
|
||||
kBGR = WPI_PIXFMT_BGR,
|
||||
kGray = WPI_PIXFMT_GRAY,
|
||||
kY16 = WPI_PIXFMT_Y16,
|
||||
kUYVY = WPI_PIXFMT_UYVY,
|
||||
kBGRA = WPI_PIXFMT_BGRA,
|
||||
};
|
||||
|
||||
} // namespace wpi::util
|
||||
@@ -30,21 +30,6 @@ typedef struct WPI_RawFrame { // NOLINT
|
||||
int timestampSrc; // WPI_TimestampSource
|
||||
} WPI_RawFrame;
|
||||
|
||||
/**
|
||||
* Pixel formats
|
||||
*/
|
||||
enum WPI_PixelFormat {
|
||||
WPI_PIXFMT_UNKNOWN = 0, // unknown
|
||||
WPI_PIXFMT_MJPEG, // Motion-JPEG (compressed image data)
|
||||
WPI_PIXFMT_YUYV, // YUV 4:2:2, 16 bpp
|
||||
WPI_PIXFMT_RGB565, // RGB 5-6-5, 16 bpp
|
||||
WPI_PIXFMT_BGR, // BGR 8-8-8, 24 bpp
|
||||
WPI_PIXFMT_GRAY, // Grayscale, 8 bpp
|
||||
WPI_PIXFMT_Y16, // Grayscale, 16 bpp
|
||||
WPI_PIXFMT_UYVY, // YUV 4:2:2, 16 bpp
|
||||
WPI_PIXFMT_BGRA, // BGRA 8-8-8-8-, 32 bpp
|
||||
};
|
||||
|
||||
/**
|
||||
* Timestamp metadata. Timebase is the same as wpi::util::Now
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wpi/util/PixelFormat.h"
|
||||
#include "wpi/util/RawFrame.h"
|
||||
|
||||
#ifdef WPI_RAWFRAME_JNI
|
||||
|
||||
@@ -71,6 +71,7 @@ Color = "wpi/util/Color.hpp"
|
||||
Color8Bit = "wpi/util/Color8Bit.hpp"
|
||||
StackTrace = "wpi/util/StackTrace.hpp"
|
||||
Synchronization = "wpi/util/Synchronization.hpp"
|
||||
PixelFormat = "wpi/util/PixelFormat.hpp"
|
||||
RawFrame_c = "wpi/util/RawFrame.h"
|
||||
RawFrame = "wpi/util/RawFrame.hpp"
|
||||
|
||||
|
||||
2
wpiutil/src/main/python/semiwrap/PixelFormat.yml
Normal file
2
wpiutil/src/main/python/semiwrap/PixelFormat.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
enums:
|
||||
PixelFormat:
|
||||
Reference in New Issue
Block a user