mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[cscore] Add BGRA support (#6365)
This commit is contained in:
@@ -36,11 +36,16 @@ public class CvSink extends ImageSink {
|
||||
switch (pixelFormat) {
|
||||
case kYUYV:
|
||||
case kRGB565:
|
||||
case kY16:
|
||||
case kUYVY:
|
||||
type = CvType.CV_8UC2;
|
||||
break;
|
||||
case kBGR:
|
||||
type = CvType.CV_8UC3;
|
||||
break;
|
||||
case kBGRA:
|
||||
type = CvType.CV_8UC4;
|
||||
break;
|
||||
case kGray:
|
||||
case kMJPEG:
|
||||
default:
|
||||
|
||||
@@ -40,16 +40,16 @@ public class CvSource extends ImageSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks.
|
||||
* Put an OpenCV image and notify sinks
|
||||
*
|
||||
* <p>Only 8-bit single-channel or 3-channel (with BGR channel order) images are supported. If the
|
||||
* format, depth or channel order is different, use Mat.convertTo() and/or cvtColor() to convert
|
||||
* it first.
|
||||
* <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
|
||||
* @param image OpenCV Image
|
||||
*/
|
||||
public void putFrame(Mat image) {
|
||||
// We only support 8-bit images, convert if necessary
|
||||
// We only support 8 bit channels
|
||||
boolean cleanupRequired = false;
|
||||
Mat finalImage;
|
||||
if (image.depth() == 0) {
|
||||
@@ -64,22 +64,64 @@ public class CvSource extends ImageSource {
|
||||
int channels = finalImage.channels();
|
||||
PixelFormat format;
|
||||
if (channels == 1) {
|
||||
// 1 channel is assumed Graysacle
|
||||
format = PixelFormat.kGray;
|
||||
} else if (channels == 2) {
|
||||
// 2 channels is assumed YUYV
|
||||
format = PixelFormat.kYUYV;
|
||||
} else if (channels == 3) {
|
||||
// 3 channels is assumed BGR
|
||||
format = PixelFormat.kBGR;
|
||||
} else if (channels == 4) {
|
||||
// 4 channels is assumed BGRA
|
||||
format = PixelFormat.kBGRA;
|
||||
} else {
|
||||
throw new VideoException("Unsupported image type");
|
||||
throw new VideoException("Unable to get pixel format for " + channels + " channels");
|
||||
}
|
||||
// TODO old code supported BGRA, but the only way I can support that is slow.
|
||||
// Update cscore to support BGRA for raw frames
|
||||
|
||||
putFrame(finalImage, format, true);
|
||||
} finally {
|
||||
if (cleanupRequired) {
|
||||
finalImage.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 format The pixel format of the image
|
||||
* @param skipVerification skip verifying pixel format
|
||||
*/
|
||||
public void putFrame(Mat image, PixelFormat format, boolean skipVerification) {
|
||||
// We only support 8-bit images, convert if necessary
|
||||
boolean cleanupRequired = false;
|
||||
Mat finalImage;
|
||||
if (image.depth() == 0) {
|
||||
finalImage = image;
|
||||
} else {
|
||||
finalImage = new Mat();
|
||||
image.convertTo(finalImage, 0);
|
||||
cleanupRequired = true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!skipVerification) {
|
||||
verifyFormat(finalImage, format);
|
||||
}
|
||||
long step = image.step1() * image.elemSize1();
|
||||
CameraServerJNI.putRawSourceFrameData(
|
||||
m_handle,
|
||||
finalImage.dataAddr(),
|
||||
(int) finalImage.total() * channels,
|
||||
(int) finalImage.total() * finalImage.channels(),
|
||||
finalImage.width(),
|
||||
finalImage.height(),
|
||||
image.width(),
|
||||
(int) step,
|
||||
format.getValue());
|
||||
|
||||
} finally {
|
||||
@@ -88,4 +130,52 @@ public class CvSource extends ImageSource {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyFormat(Mat image, PixelFormat pixelFormat) {
|
||||
int channels = image.channels();
|
||||
switch (pixelFormat) {
|
||||
case kBGR:
|
||||
if (channels == 3) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kBGRA:
|
||||
if (channels == 4) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kGray:
|
||||
if (channels == 1) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kRGB565:
|
||||
if (channels == 2) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kUYVY:
|
||||
if (channels == 2) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kY16:
|
||||
if (channels == 2) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kYUYV:
|
||||
if (channels == 2) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kMJPEG:
|
||||
if (channels == 1) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,6 +314,54 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
// If source is RGB565, YUYV, UYVY, Gray or Y16, need to convert to BGR
|
||||
// first
|
||||
if (cur->pixelFormat == VideoMode::kRGB565) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertRGB565ToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kYUYV) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertYUYVToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kUYVY) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertUYVYToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kGray) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertGrayToBGR(cur);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kY16) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
cur = newImage;
|
||||
} else if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
VideoMode::kGray)) {
|
||||
cur = ConvertGrayToBGR(newImage);
|
||||
} else {
|
||||
cur = ConvertGrayToBGR(ConvertY16ToGray(cur));
|
||||
}
|
||||
}
|
||||
return ConvertBGRToBGRA(cur);
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kUYVY:
|
||||
default:
|
||||
@@ -664,6 +712,28 @@ Image* Frame::ConvertY16ToGray(Image* image) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToBGRA(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kBGR) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a RGB565 image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGRA, image->width, image->height,
|
||||
image->width * image->height * 4);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR2BGRA);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
if (m_impl) {
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
m_impl->images.push_back(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::GetImageImpl(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality) {
|
||||
@@ -727,3 +797,16 @@ void Frame::ReleaseFrame() {
|
||||
m_impl->source.ReleaseFrameImpl(std::unique_ptr<Impl>(m_impl));
|
||||
m_impl = nullptr;
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
std::unique_ptr<Image> CreateImageFromBGRA(cs::SourceImpl* source, size_t width,
|
||||
size_t height, size_t stride,
|
||||
const uint8_t* data) {
|
||||
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);
|
||||
cv::cvtColor(finalImage, dest->AsMat(), cv::COLOR_BGRA2BGR);
|
||||
return dest;
|
||||
}
|
||||
} // namespace cs
|
||||
|
||||
@@ -22,6 +22,10 @@ namespace cs {
|
||||
|
||||
class SourceImpl;
|
||||
|
||||
std::unique_ptr<Image> CreateImageFromBGRA(cs::SourceImpl* source, size_t width,
|
||||
size_t height, size_t stride,
|
||||
const uint8_t* data);
|
||||
|
||||
class Frame {
|
||||
friend class SourceImpl;
|
||||
|
||||
@@ -206,6 +210,7 @@ class Frame {
|
||||
Image* ConvertGrayToMJPEG(Image* image, int quality);
|
||||
Image* ConvertGrayToY16(Image* image);
|
||||
Image* ConvertY16ToGray(Image* image);
|
||||
Image* ConvertBGRToBGRA(Image* image);
|
||||
|
||||
Image* GetImage(int width, int height, VideoMode::PixelFormat pixelFormat) {
|
||||
if (pixelFormat == VideoMode::kMJPEG) {
|
||||
|
||||
@@ -63,6 +63,9 @@ class Image {
|
||||
case VideoMode::kBGR:
|
||||
type = CV_8UC3;
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
type = CV_8UC4;
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
case VideoMode::kMJPEG:
|
||||
default:
|
||||
@@ -81,6 +84,8 @@ class Image {
|
||||
return 2 * width;
|
||||
case VideoMode::kBGR:
|
||||
return 3 * width;
|
||||
case VideoMode::kBGRA:
|
||||
return 4 * width;
|
||||
case VideoMode::kGray:
|
||||
return width;
|
||||
case VideoMode::kMJPEG:
|
||||
|
||||
@@ -457,6 +457,9 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
|
||||
case VideoMode::kBGR:
|
||||
os << "BGR";
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
os << "BGRA";
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
os << "gray";
|
||||
break;
|
||||
|
||||
@@ -22,31 +22,10 @@ RawSourceImpl::RawSourceImpl(std::string_view name, wpi::Logger& logger,
|
||||
RawSourceImpl::~RawSourceImpl() = default;
|
||||
|
||||
void RawSourceImpl::PutFrame(const WPI_RawFrame& image) {
|
||||
int type;
|
||||
switch (image.pixelFormat) {
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kRGB565:
|
||||
case VideoMode::kY16:
|
||||
case VideoMode::kUYVY:
|
||||
type = CV_8UC2;
|
||||
break;
|
||||
case VideoMode::kBGR:
|
||||
type = CV_8UC3;
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
case VideoMode::kMJPEG:
|
||||
default:
|
||||
type = CV_8UC1;
|
||||
break;
|
||||
}
|
||||
cv::Mat finalImage{image.height, image.width, type, image.data,
|
||||
static_cast<size_t>(image.stride)};
|
||||
std::unique_ptr<Image> dest =
|
||||
AllocImage(static_cast<VideoMode::PixelFormat>(image.pixelFormat),
|
||||
image.width, image.height, image.size);
|
||||
finalImage.copyTo(dest->AsMat());
|
||||
|
||||
SourceImpl::PutFrame(std::move(dest), wpi::Now());
|
||||
auto currentTime = wpi::Now();
|
||||
std::string_view data_view{reinterpret_cast<char*>(image.data), image.size};
|
||||
SourceImpl::PutFrame(static_cast<VideoMode::PixelFormat>(image.pixelFormat),
|
||||
image.width, image.height, data_view, currentTime);
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
|
||||
@@ -196,6 +196,8 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
|
||||
mode.pixelFormat = cs::VideoMode::kRGB565;
|
||||
} else if (wpi::equals_lower(str, "bgr")) {
|
||||
mode.pixelFormat = cs::VideoMode::kBGR;
|
||||
} else if (wpi::equals_lower(str, "bgra")) {
|
||||
mode.pixelFormat = cs::VideoMode::kBGRA;
|
||||
} else if (wpi::equals_lower(str, "gray")) {
|
||||
mode.pixelFormat = cs::VideoMode::kGray;
|
||||
} else if (wpi::equals_lower(str, "y16")) {
|
||||
@@ -361,6 +363,9 @@ wpi::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
|
||||
case VideoMode::kBGR:
|
||||
pixelFormat = "bgr";
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
pixelFormat = "bgra";
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
pixelFormat = "gray";
|
||||
break;
|
||||
@@ -450,6 +455,15 @@ std::unique_ptr<Image> SourceImpl::AllocImage(
|
||||
|
||||
void SourceImpl::PutFrame(VideoMode::PixelFormat pixelFormat, int width,
|
||||
int height, std::string_view data, Frame::Time time) {
|
||||
if (pixelFormat == VideoMode::PixelFormat::kBGRA) {
|
||||
// Write BGRA as BGR to save a copy
|
||||
auto image =
|
||||
CreateImageFromBGRA(this, width, height, width * 4,
|
||||
reinterpret_cast<const uint8_t*>(data.data()));
|
||||
PutFrame(std::move(image), time);
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = AllocImage(pixelFormat, width, height, data.size());
|
||||
|
||||
// Copy in image data
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/jni_util.h>
|
||||
|
||||
#include "cscore_cv.h"
|
||||
#include "cscore_raw.h"
|
||||
#include "cscore_runloop.h"
|
||||
#include "edu_wpi_first_cscore_CameraServerJNI.h"
|
||||
|
||||
@@ -68,7 +68,8 @@ struct VideoMode : public CS_VideoMode {
|
||||
kBGR = WPI_PIXFMT_BGR,
|
||||
kGray = WPI_PIXFMT_GRAY,
|
||||
kY16 = WPI_PIXFMT_Y16,
|
||||
kUYVY = WPI_PIXFMT_UYVY
|
||||
kUYVY = WPI_PIXFMT_UYVY,
|
||||
kBGRA = WPI_PIXFMT_BGRA,
|
||||
};
|
||||
VideoMode() {
|
||||
pixelFormat = 0;
|
||||
|
||||
@@ -44,15 +44,36 @@ class CvSource : public ImageSource {
|
||||
int height, int fps);
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks.
|
||||
* Put an OpenCV image and notify sinks
|
||||
*
|
||||
* <p>Only 8-bit single-channel or 3-channel (with BGR channel order) images
|
||||
* are supported. If the format, depth or channel order is different, use
|
||||
* cv::Mat::convertTo() and/or cv::cvtColor() to convert it first.
|
||||
* <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
|
||||
* @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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -131,6 +152,8 @@ class CvSink : public ImageSink {
|
||||
uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
|
||||
|
||||
private:
|
||||
constexpr int GetCvFormat(WPI_PixelFormat pixelFormat);
|
||||
|
||||
wpi::RawFrame rawFrame;
|
||||
VideoMode::PixelFormat pixelFormat;
|
||||
};
|
||||
@@ -145,6 +168,56 @@ inline CvSource::CvSource(std::string_view name, VideoMode::PixelFormat format,
|
||||
&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;
|
||||
@@ -155,27 +228,54 @@ inline void CvSource::PutFrame(cv::Mat& image) {
|
||||
}
|
||||
|
||||
int channels = finalImage.channels();
|
||||
WPI_PixelFormat format;
|
||||
VideoMode::PixelFormat format;
|
||||
if (channels == 1) {
|
||||
format = WPI_PIXFMT_GRAY;
|
||||
// 1 channel is assumed Graysacle
|
||||
format = VideoMode::PixelFormat::kGray;
|
||||
} else if (channels == 2) {
|
||||
// 2 channels is assumed YUYV
|
||||
format = VideoMode::PixelFormat::kYUYV;
|
||||
} else if (channels == 3) {
|
||||
format = WPI_PIXFMT_BGR;
|
||||
// 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;
|
||||
}
|
||||
// TODO old code supported BGRA, but the only way I can support that is slow.
|
||||
// Update cscore to support BGRA for raw frames
|
||||
|
||||
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() * channels;
|
||||
frame.size = finalImage.total() * finalImage.channels();
|
||||
frame.width = finalImage.cols;
|
||||
frame.height = finalImage.rows;
|
||||
frame.stride = finalImage.cols;
|
||||
frame.pixelFormat = format;
|
||||
frame.stride = finalImage.step;
|
||||
frame.pixelFormat = pixelFormat;
|
||||
m_status = 0;
|
||||
PutSourceFrame(m_handle, frame, &m_status);
|
||||
}
|
||||
@@ -209,16 +309,21 @@ inline uint64_t CvSink::GrabFrameNoTimeout(cv::Mat& image) {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline constexpr int GetCvFormat(WPI_PixelFormat pixelFormat) {
|
||||
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:
|
||||
@@ -231,31 +336,33 @@ inline constexpr int GetCvFormat(WPI_PixelFormat pixelFormat) {
|
||||
inline uint64_t CvSink::GrabFrameDirect(cv::Mat& image, double timeout) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = pixelFormat;
|
||||
m_status = GrabSinkFrameTimeout(m_handle, rawFrame, timeout, &m_status);
|
||||
if (m_status <= 0) {
|
||||
return m_status;
|
||||
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};
|
||||
return m_status;
|
||||
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;
|
||||
m_status = GrabSinkFrame(m_handle, rawFrame, &m_status);
|
||||
if (m_status <= 0) {
|
||||
return m_status;
|
||||
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};
|
||||
return m_status;
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
@@ -80,6 +80,8 @@ static VideoMode::PixelFormat ToPixelFormat(__u32 pixelFormat) {
|
||||
return VideoMode::kRGB565;
|
||||
case V4L2_PIX_FMT_BGR24:
|
||||
return VideoMode::kBGR;
|
||||
case V4L2_PIX_FMT_ABGR32:
|
||||
return VideoMode::kBGRA;
|
||||
case V4L2_PIX_FMT_GREY:
|
||||
return VideoMode::kGray;
|
||||
case V4L2_PIX_FMT_Y16:
|
||||
@@ -102,6 +104,8 @@ static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) {
|
||||
return V4L2_PIX_FMT_RGB565;
|
||||
case VideoMode::kBGR:
|
||||
return V4L2_PIX_FMT_BGR24;
|
||||
case VideoMode::kBGRA:
|
||||
return V4L2_PIX_FMT_ABGR32;
|
||||
case VideoMode::kGray:
|
||||
return V4L2_PIX_FMT_GREY;
|
||||
case VideoMode::kY16:
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
|
||||
@implementation UsbCameraDelegate
|
||||
|
||||
- (id)init {
|
||||
@@ -24,6 +21,8 @@
|
||||
(void)sampleBuffer;
|
||||
(void)connection;
|
||||
|
||||
auto currentTime = wpi::Now();
|
||||
|
||||
auto sharedThis = self.cppImpl.lock();
|
||||
if (!sharedThis) {
|
||||
return;
|
||||
@@ -52,16 +51,12 @@
|
||||
return;
|
||||
}
|
||||
|
||||
size_t currSize = width * 3 * height;
|
||||
|
||||
auto tmpMat = cv::Mat(height, width, CV_8UC4, baseaddress, rowBytes);
|
||||
auto image = sharedThis->AllocImage(cs::VideoMode::PixelFormat::kBGR, width,
|
||||
height, currSize);
|
||||
cv::cvtColor(tmpMat, image->AsMat(), cv::COLOR_BGRA2BGR);
|
||||
std::unique_ptr<cs::Image> image = cs::CreateImageFromBGRA(
|
||||
sharedThis.get(), width, height, rowBytes, reinterpret_cast<uint8_t*>(baseaddress));
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
|
||||
|
||||
sharedThis->objcPutFrame(std::move(image), wpi::Now());
|
||||
sharedThis->objcPutFrame(std::move(image), currentTime);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "Instance.h"
|
||||
#include "c_util.h"
|
||||
#include "cscore_cpp.h"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "UsbCameraImpl.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
#include <Dbt.h>
|
||||
#include <Dshow.h>
|
||||
#include <Windows.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include <wpi/ConvertUTF.h>
|
||||
#include <wpi/MemAlloc.h>
|
||||
#include <wpi/SmallString.h>
|
||||
@@ -284,6 +281,8 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample,
|
||||
return;
|
||||
}
|
||||
|
||||
auto currentTime = wpi::Now();
|
||||
|
||||
ComPtr<IMFMediaBuffer> buf;
|
||||
|
||||
if (!SUCCEEDED(videoSample->ConvertToContiguousBuffer(buf.GetAddressOf()))) {
|
||||
@@ -339,56 +338,9 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample,
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat tmpMat;
|
||||
std::unique_ptr<Image> dest;
|
||||
bool doFinalSet = true;
|
||||
|
||||
switch (mode.pixelFormat) {
|
||||
case cs::VideoMode::PixelFormat::kMJPEG: {
|
||||
// Special case
|
||||
PutFrame(VideoMode::kMJPEG, mode.width, mode.height,
|
||||
{reinterpret_cast<char*>(ptr), length}, wpi::Now());
|
||||
doFinalSet = false;
|
||||
break;
|
||||
}
|
||||
case cs::VideoMode::PixelFormat::kGray:
|
||||
tmpMat = cv::Mat(mode.height, mode.width, CV_8UC1, ptr, pitch);
|
||||
dest = AllocImage(VideoMode::kGray, tmpMat.cols, tmpMat.rows,
|
||||
tmpMat.total());
|
||||
tmpMat.copyTo(dest->AsMat());
|
||||
break;
|
||||
case cs::VideoMode::PixelFormat::kY16:
|
||||
tmpMat = cv::Mat(mode.height, mode.width, CV_8UC2, ptr, pitch);
|
||||
dest =
|
||||
AllocImage(VideoMode::kY16, tmpMat.cols, tmpMat.rows, tmpMat.total());
|
||||
tmpMat.copyTo(dest->AsMat());
|
||||
break;
|
||||
case cs::VideoMode::PixelFormat::kBGR:
|
||||
tmpMat = cv::Mat(mode.height, mode.width, CV_8UC3, ptr, pitch);
|
||||
dest = AllocImage(VideoMode::kBGR, tmpMat.cols, tmpMat.rows,
|
||||
tmpMat.total() * 3);
|
||||
tmpMat.copyTo(dest->AsMat());
|
||||
break;
|
||||
case cs::VideoMode::PixelFormat::kYUYV:
|
||||
tmpMat = cv::Mat(mode.height, mode.width, CV_8UC2, ptr, pitch);
|
||||
dest = AllocImage(VideoMode::kYUYV, tmpMat.cols, tmpMat.rows,
|
||||
tmpMat.total() * 2);
|
||||
tmpMat.copyTo(dest->AsMat());
|
||||
break;
|
||||
case cs::VideoMode::PixelFormat::kUYVY:
|
||||
tmpMat = cv::Mat(mode.height, mode.width, CV_8UC2, ptr, pitch);
|
||||
dest = AllocImage(VideoMode::kUYVY, tmpMat.cols, tmpMat.rows,
|
||||
tmpMat.total() * 2);
|
||||
tmpMat.copyTo(dest->AsMat());
|
||||
break;
|
||||
default:
|
||||
doFinalSet = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (doFinalSet) {
|
||||
PutFrame(std::move(dest), wpi::Now());
|
||||
}
|
||||
std::string_view data_view{reinterpret_cast<char*>(ptr), length};
|
||||
SourceImpl::PutFrame(static_cast<VideoMode::PixelFormat>(mode.pixelFormat),
|
||||
mode.width, mode.height, data_view, currentTime);
|
||||
|
||||
if (buffer2d) {
|
||||
buffer2d->Unlock2D();
|
||||
@@ -480,8 +432,6 @@ static cs::VideoMode::PixelFormat GetFromGUID(const GUID& guid) {
|
||||
return cs::VideoMode::PixelFormat::kY16;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_YUY2)) {
|
||||
return cs::VideoMode::PixelFormat::kYUYV;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_RGB24)) {
|
||||
return cs::VideoMode::PixelFormat::kBGR;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_MJPG)) {
|
||||
return cs::VideoMode::PixelFormat::kMJPEG;
|
||||
} else if (IsEqualGUID(guid, MFVideoFormat_RGB565)) {
|
||||
|
||||
@@ -21,7 +21,9 @@ public enum PixelFormat {
|
||||
/** Grayscale, 16 bpp. */
|
||||
kY16(6),
|
||||
/** YUV 4:2:2, 16 bpp. */
|
||||
kUYVY(7);
|
||||
kUYVY(7),
|
||||
/** BGRA 8-8-8-8. 32 bpp. */
|
||||
kBGRA(8);
|
||||
|
||||
private final int value;
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ enum WPI_PixelFormat {
|
||||
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
|
||||
};
|
||||
|
||||
// Returns nonzero if the frame data was allocated/reallocated
|
||||
|
||||
Reference in New Issue
Block a user