Skip to content

Commit

Permalink
[cscore] CvSink: Allow specifying output PixelFormat (wpilibsuite#5943)
Browse files Browse the repository at this point in the history
  • Loading branch information
MrRedness authored Nov 22, 2023
1 parent 25b7dca commit 437cc91
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static native int createCvSource(

public static native void putSourceFrame(int source, long imageNativeObj);

public static native int createCvSink(String name);
public static native int createCvSink(String name, int pixelFormat);

// public static native int createCvSinkCallback(String name,
// void (*processFrame)(long time));
Expand Down
14 changes: 13 additions & 1 deletion cscore/src/main/java/edu/wpi/first/cscore/CvSink.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package edu.wpi.first.cscore;

import edu.wpi.first.cscore.VideoMode.PixelFormat;
import org.opencv.core.Mat;

/**
Expand All @@ -16,9 +17,20 @@ public class CvSink extends ImageSink {
* get each new image.
*
* @param name Source name (arbitrary unique identifier)
* @param pixelFormat Source pixel format
*/
public CvSink(String name, PixelFormat pixelFormat) {
super(CameraServerCvJNI.createCvSink(name, pixelFormat.getValue()));
}

/**
* Create a sink for accepting OpenCV images. WaitForFrame() must be called on the created sink to
* get each new image. Defaults to kBGR for pixelFormat
*
* @param name Source name (arbitrary unique identifier)
*/
public CvSink(String name) {
super(CameraServerCvJNI.createCvSink(name));
this(name, PixelFormat.kBGR);
}

/// Create a sink for accepting OpenCV images in a separate thread.
Expand Down
35 changes: 22 additions & 13 deletions cscore/src/main/native/cpp/CvSinkImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@
using namespace cs;

CvSinkImpl::CvSinkImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry)
: SinkImpl{name, logger, notifier, telemetry} {
Notifier& notifier, Telemetry& telemetry,
VideoMode::PixelFormat pixelFormat)
: SinkImpl{name, logger, notifier, telemetry}, m_pixelFormat{pixelFormat} {
m_active = true;
// m_thread = std::thread(&CvSinkImpl::ThreadMain, this);
}

CvSinkImpl::CvSinkImpl(std::string_view name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
VideoMode::PixelFormat pixelFormat,
std::function<void(uint64_t time)> processFrame)
: SinkImpl{name, logger, notifier, telemetry} {}
: SinkImpl{name, logger, notifier, telemetry}, m_pixelFormat{pixelFormat} {}

CvSinkImpl::~CvSinkImpl() {
Stop();
Expand Down Expand Up @@ -65,7 +67,7 @@ uint64_t CvSinkImpl::GrabFrame(cv::Mat& image) {
return 0; // signal error
}

if (!frame.GetCv(image)) {
if (!frame.GetCv(image, m_pixelFormat)) {
// Shouldn't happen, but just in case...
std::this_thread::sleep_for(std::chrono::milliseconds(20));
return 0;
Expand All @@ -91,7 +93,7 @@ uint64_t CvSinkImpl::GrabFrame(cv::Mat& image, double timeout) {
return 0; // signal error
}

if (!frame.GetCv(image)) {
if (!frame.GetCv(image, m_pixelFormat)) {
// Shouldn't happen, but just in case...
std::this_thread::sleep_for(std::chrono::milliseconds(20));
return 0;
Expand Down Expand Up @@ -127,20 +129,23 @@ void CvSinkImpl::ThreadMain() {

namespace cs {

CS_Sink CreateCvSink(std::string_view name, CS_Status* status) {
CS_Sink CreateCvSink(std::string_view name, VideoMode::PixelFormat pixelFormat,
CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSink(
CS_SINK_CV, std::make_shared<CvSinkImpl>(name, inst.logger, inst.notifier,
inst.telemetry));
inst.telemetry, pixelFormat));
}

CS_Sink CreateCvSinkCallback(std::string_view name,
VideoMode::PixelFormat pixelFormat,
std::function<void(uint64_t time)> processFrame,
CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSink(
CS_SINK_CV, std::make_shared<CvSinkImpl>(name, inst.logger, inst.notifier,
inst.telemetry, processFrame));
CS_SINK_CV,
std::make_shared<CvSinkImpl>(name, inst.logger, inst.notifier,
inst.telemetry, pixelFormat, processFrame));
}

static constexpr unsigned SinkMask = CS_SINK_CV | CS_SINK_RAW;
Expand Down Expand Up @@ -206,15 +211,19 @@ void SetSinkEnabled(CS_Sink sink, bool enabled, CS_Status* status) {

extern "C" {

CS_Sink CS_CreateCvSink(const char* name, CS_Status* status) {
return cs::CreateCvSink(name, status);
CS_Sink CS_CreateCvSink(const char* name, enum CS_PixelFormat pixelFormat,
CS_Status* status) {
return cs::CreateCvSink(
name, static_cast<VideoMode::PixelFormat>(pixelFormat), status);
}

CS_Sink CS_CreateCvSinkCallback(const char* name, void* data,
CS_Sink CS_CreateCvSinkCallback(const char* name,
enum CS_PixelFormat pixelFormat, void* data,
void (*processFrame)(void* data, uint64_t time),
CS_Status* status) {
return cs::CreateCvSinkCallback(
name, [=](uint64_t time) { processFrame(data, time); }, status);
name, static_cast<VideoMode::PixelFormat>(pixelFormat),
[=](uint64_t time) { processFrame(data, time); }, status);
}

void CS_SetSinkDescription(CS_Sink sink, const char* description,
Expand Down
5 changes: 3 additions & 2 deletions cscore/src/main/native/cpp/CvSinkImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class SourceImpl;
class CvSinkImpl : public SinkImpl {
public:
CvSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry);
Telemetry& telemetry, VideoMode::PixelFormat pixelFormat);
CvSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry,
Telemetry& telemetry, VideoMode::PixelFormat pixelFormat,
std::function<void(uint64_t time)> processFrame);
~CvSinkImpl() override;

Expand All @@ -42,6 +42,7 @@ class CvSinkImpl : public SinkImpl {
std::atomic_bool m_active; // set to false to terminate threads
std::thread m_thread;
std::function<void(uint64_t time)> m_processFrame;
VideoMode::PixelFormat m_pixelFormat;
};

} // namespace cs
Expand Down
5 changes: 3 additions & 2 deletions cscore/src/main/native/cpp/Frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,8 +709,9 @@ Image* Frame::GetImageImpl(int width, int height,
return ConvertImpl(cur, pixelFormat, requiredJpegQuality, defaultJpegQuality);
}

bool Frame::GetCv(cv::Mat& image, int width, int height) {
Image* rawImage = GetImage(width, height, VideoMode::kBGR);
bool Frame::GetCv(cv::Mat& image, int width, int height,
VideoMode::PixelFormat pixelFormat) {
Image* rawImage = GetImage(width, height, pixelFormat);
if (!rawImage) {
return false;
}
Expand Down
7 changes: 4 additions & 3 deletions cscore/src/main/native/cpp/Frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,11 @@ class Frame {
defaultQuality);
}

bool GetCv(cv::Mat& image) {
return GetCv(image, GetOriginalWidth(), GetOriginalHeight());
bool GetCv(cv::Mat& image, VideoMode::PixelFormat pixelFormat) {
return GetCv(image, GetOriginalWidth(), GetOriginalHeight(), pixelFormat);
}
bool GetCv(cv::Mat& image, int width, int height);
bool GetCv(cv::Mat& image, int width, int height,
VideoMode::PixelFormat pixelFormat);

private:
Image* ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
Expand Down
8 changes: 5 additions & 3 deletions cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1392,18 +1392,20 @@ Java_edu_wpi_first_cscore_CameraServerJNI_createMjpegServer
/*
* Class: edu_wpi_first_cscore_CameraServerCvJNI
* Method: createCvSink
* Signature: (Ljava/lang/String;)I
* Signature: (Ljava/lang/String;I)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_cscore_CameraServerCvJNI_createCvSink
(JNIEnv* env, jclass, jstring name)
(JNIEnv* env, jclass, jstring name, jint pixelFormat)
{
if (!name) {
nullPointerEx.Throw(env, "name cannot be null");
return 0;
}
CS_Status status = 0;
auto val = cs::CreateCvSink(JStringRef{env, name}.str(), &status);
auto val = cs::CreateCvSink(
JStringRef{env, name}.str(),
static_cast<cs::VideoMode::PixelFormat>(pixelFormat), &status);
CheckStatus(env, status);
return val;
}
Expand Down
6 changes: 4 additions & 2 deletions cscore/src/main/native/include/cscore_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,10 @@ void CS_SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
*/
CS_Sink CS_CreateMjpegServer(const char* name, const char* listenAddress,
int port, CS_Status* status);
CS_Sink CS_CreateCvSink(const char* name, CS_Status* status);
CS_Sink CS_CreateCvSinkCallback(const char* name, void* data,
CS_Sink CS_CreateCvSink(const char* name, enum CS_PixelFormat pixelFormat,
CS_Status* status);
CS_Sink CS_CreateCvSinkCallback(const char* name,
enum CS_PixelFormat pixelFormat, void* data,
void (*processFrame)(void* data, uint64_t time),
CS_Status* status);
/** @} */
Expand Down
4 changes: 3 additions & 1 deletion cscore/src/main/native/include/cscore_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,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, CS_Status* status);
CS_Sink CreateCvSink(std::string_view name, VideoMode::PixelFormat pixelFormat,
CS_Status* status);
CS_Sink CreateCvSinkCallback(std::string_view name,
VideoMode::PixelFormat pixelFormat,
std::function<void(uint64_t time)> processFrame,
CS_Status* status);

Expand Down
19 changes: 12 additions & 7 deletions cscore/src/main/native/include/cscore_cv.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,10 @@ class CvSink : public ImageSink {
* image.
*
* @param name Source name (arbitrary unique identifier)
* @param pixelFormat Source pixel format
*/
explicit CvSink(std::string_view name);
explicit CvSink(std::string_view name, VideoMode::PixelFormat pixelFormat =
VideoMode::PixelFormat::kBGR);

/**
* Create a sink for accepting OpenCV images in a separate thread.
Expand All @@ -141,9 +143,10 @@ class CvSink : public ImageSink {
* time=0 if an error occurred. processFrame should call GetImage()
* or GetError() as needed, but should not call (except in very
* unusual circumstances) WaitForImage().
* @param pixelFormat Source pixel format
*/
CvSink(std::string_view name,
std::function<void(uint64_t time)> processFrame);
CvSink(std::string_view name, std::function<void(uint64_t time)> processFrame,
VideoMode::PixelFormat pixelFormat = VideoMode::PixelFormat::kBGR);

/**
* Wait for the next frame and get the image.
Expand Down Expand Up @@ -184,13 +187,15 @@ inline void CvSource::PutFrame(cv::Mat& image) {
PutSourceFrame(m_handle, image, &m_status);
}

inline CvSink::CvSink(std::string_view name) {
m_handle = CreateCvSink(name, &m_status);
inline CvSink::CvSink(std::string_view name,
VideoMode::PixelFormat pixelFormat) {
m_handle = CreateCvSink(name, pixelFormat, &m_status);
}

inline CvSink::CvSink(std::string_view name,
std::function<void(uint64_t time)> processFrame) {
m_handle = CreateCvSinkCallback(name, processFrame, &m_status);
std::function<void(uint64_t time)> processFrame,
VideoMode::PixelFormat pixelFormat) {
m_handle = CreateCvSinkCallback(name, pixelFormat, processFrame, &m_status);
}

inline uint64_t CvSink::GrabFrame(cv::Mat& image, double timeout) const {
Expand Down

0 comments on commit 437cc91

Please sign in to comment.