Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cscore] CvSink: Allow specifying output PixelFormat #5943

Merged
merged 7 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
MrRedness marked this conversation as resolved.
Show resolved Hide resolved

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