diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java index 57bd5fd253..6f02cc82a6 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java @@ -13,7 +13,8 @@ public class VideoMode { kYUYV(2), kRGB565(3), kBGR(4), - kGray(5); + kGray(5), + kUYVY(6); private final int value; diff --git a/cscore/src/main/native/cpp/Frame.cpp b/cscore/src/main/native/cpp/Frame.cpp index ddf356a892..77b93dff4e 100644 --- a/cscore/src/main/native/cpp/Frame.cpp +++ b/cscore/src/main/native/cpp/Frame.cpp @@ -217,7 +217,7 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, // Color convert switch (pixelFormat) { case VideoMode::kRGB565: - // If source is YUYV or Gray, need to convert to BGR first + // If source is YUYV, UYVY, or Gray, need to convert to BGR first if (cur->pixelFormat == VideoMode::kYUYV) { // Check to see if BGR version already exists... if (Image* newImage = @@ -226,6 +226,14 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, } 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 = @@ -237,7 +245,7 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, } return ConvertBGRToRGB565(cur); case VideoMode::kGray: - // If source is YUYV or RGB565, need to convert to BGR first + // If source is YUYV, UYVY, or RGB565, need to convert to BGR first if (cur->pixelFormat == VideoMode::kYUYV) { // Check to see if BGR version already exists... if (Image* newImage = @@ -246,6 +254,14 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, } 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::kRGB565) { // Check to see if BGR version already exists... if (Image* newImage = @@ -260,6 +276,8 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, case VideoMode::kMJPEG: if (cur->pixelFormat == VideoMode::kYUYV) { cur = ConvertYUYVToBGR(cur); + } else if (cur->pixelFormat == VideoMode::kUYVY) { + cur = ConvertUYVYToBGR(cur); } else if (cur->pixelFormat == VideoMode::kRGB565) { cur = ConvertRGB565ToBGR(cur); } else if (cur->pixelFormat == VideoMode::kGray) { @@ -271,6 +289,7 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, } break; case VideoMode::kYUYV: + case VideoMode::kUYVY: default: return nullptr; // Unsupported } @@ -351,6 +370,28 @@ Image* Frame::ConvertYUYVToBGR(Image* image) { return rv; } +Image* Frame::ConvertUYVYToBGR(Image* image) { + if (!image || image->pixelFormat != VideoMode::kUYVY) { + return nullptr; + } + + // Allocate a BGR image + auto newImage = + m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height, + image->width * image->height * 3); + + // Convert + cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2BGR_UYVY); + + // 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::ConvertBGRToRGB565(Image* image) { if (!image || image->pixelFormat != VideoMode::kBGR) { return nullptr; diff --git a/cscore/src/main/native/cpp/Frame.h b/cscore/src/main/native/cpp/Frame.h index 9a16824021..1722a84845 100644 --- a/cscore/src/main/native/cpp/Frame.h +++ b/cscore/src/main/native/cpp/Frame.h @@ -195,6 +195,7 @@ class Frame { Image* ConvertMJPEGToBGR(Image* image); Image* ConvertMJPEGToGray(Image* image); Image* ConvertYUYVToBGR(Image* image); + Image* ConvertUYVYToBGR(Image* image); Image* ConvertBGRToRGB565(Image* image); Image* ConvertRGB565ToBGR(Image* image); Image* ConvertBGRToGray(Image* image); diff --git a/cscore/src/main/native/cpp/Image.h b/cscore/src/main/native/cpp/Image.h index 98d86759b0..0cace4a253 100644 --- a/cscore/src/main/native/cpp/Image.h +++ b/cscore/src/main/native/cpp/Image.h @@ -74,6 +74,7 @@ class Image { switch (pixelFormat) { case VideoMode::kYUYV: case VideoMode::kRGB565: + case VideoMode::kUYVY: type = CV_8UC2; break; case VideoMode::kBGR: diff --git a/cscore/src/main/native/cpp/MjpegServerImpl.cpp b/cscore/src/main/native/cpp/MjpegServerImpl.cpp index 2a82bb8901..ab00d82a71 100644 --- a/cscore/src/main/native/cpp/MjpegServerImpl.cpp +++ b/cscore/src/main/native/cpp/MjpegServerImpl.cpp @@ -460,6 +460,9 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os, case VideoMode::kGray: os << "gray"; break; + case VideoMode::kUYVY: + os << "UYVY"; + break; default: os << "unknown"; break; @@ -569,6 +572,9 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os, case VideoMode::kGray: os << "gray"; break; + case VideoMode::kUYVY: + os << "UYVY"; + break; default: os << "unknown"; break; @@ -740,8 +746,9 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) { // for adding it if required. addDHT = JpegNeedsDHT(data, &size, &locSOF); break; - case VideoMode::kYUYV: + case VideoMode::kUYVY: case VideoMode::kRGB565: + case VideoMode::kYUYV: default: // Bad frame; sleep for 10 ms so we don't consume all processor time. std::this_thread::sleep_for(std::chrono::milliseconds(10)); diff --git a/cscore/src/main/native/cpp/RawSourceImpl.cpp b/cscore/src/main/native/cpp/RawSourceImpl.cpp index 9ce0628102..6adb202773 100644 --- a/cscore/src/main/native/cpp/RawSourceImpl.cpp +++ b/cscore/src/main/native/cpp/RawSourceImpl.cpp @@ -26,6 +26,7 @@ void RawSourceImpl::PutFrame(const CS_RawFrame& image) { switch (image.pixelFormat) { case VideoMode::kYUYV: case VideoMode::kRGB565: + case VideoMode::kUYVY: type = CV_8UC2; break; case VideoMode::kBGR: diff --git a/cscore/src/main/native/cpp/SourceImpl.cpp b/cscore/src/main/native/cpp/SourceImpl.cpp index 7a02fcbd24..f8cce0db4b 100644 --- a/cscore/src/main/native/cpp/SourceImpl.cpp +++ b/cscore/src/main/native/cpp/SourceImpl.cpp @@ -198,6 +198,8 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) { mode.pixelFormat = cs::VideoMode::kBGR; } else if (wpi::equals_lower(str, "gray")) { mode.pixelFormat = cs::VideoMode::kGray; + } else if (wpi::equals_lower(str, "uyvy")) { + mode.pixelFormat = cs::VideoMode::kUYVY; } else { SWARNING("SetConfigJson: could not understand pixel format value '{}'", str); @@ -360,6 +362,9 @@ wpi::json SourceImpl::GetConfigJsonObject(CS_Status* status) { case VideoMode::kGray: pixelFormat = "gray"; break; + case VideoMode::kUYVY: + pixelFormat = "uyvy"; + break; default: break; } diff --git a/cscore/src/main/native/include/cscore_c.h b/cscore/src/main/native/include/cscore_c.h index 76bd8360b4..5b3177f847 100644 --- a/cscore/src/main/native/include/cscore_c.h +++ b/cscore/src/main/native/include/cscore_c.h @@ -93,7 +93,8 @@ enum CS_PixelFormat { CS_PIXFMT_YUYV, CS_PIXFMT_RGB565, CS_PIXFMT_BGR, - CS_PIXFMT_GRAY + CS_PIXFMT_GRAY, + CS_PIXFMT_UYVY }; /** diff --git a/cscore/src/main/native/include/cscore_cpp.h b/cscore/src/main/native/include/cscore_cpp.h index f20a0664b1..1dcffed15d 100644 --- a/cscore/src/main/native/include/cscore_cpp.h +++ b/cscore/src/main/native/include/cscore_cpp.h @@ -68,7 +68,8 @@ struct VideoMode : public CS_VideoMode { kYUYV = CS_PIXFMT_YUYV, kRGB565 = CS_PIXFMT_RGB565, kBGR = CS_PIXFMT_BGR, - kGray = CS_PIXFMT_GRAY + kGray = CS_PIXFMT_GRAY, + kUYVY = CS_PIXFMT_UYVY }; VideoMode() { pixelFormat = 0; diff --git a/cscore/src/main/native/linux/UsbCameraImpl.cpp b/cscore/src/main/native/linux/UsbCameraImpl.cpp index 60421eb50d..fd9eeb40e1 100644 --- a/cscore/src/main/native/linux/UsbCameraImpl.cpp +++ b/cscore/src/main/native/linux/UsbCameraImpl.cpp @@ -82,6 +82,8 @@ static VideoMode::PixelFormat ToPixelFormat(__u32 pixelFormat) { return VideoMode::kBGR; case V4L2_PIX_FMT_GREY: return VideoMode::kGray; + case V4L2_PIX_FMT_UYVY: + return VideoMode::kUYVY; default: return VideoMode::kUnknown; } @@ -100,6 +102,8 @@ static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) { return V4L2_PIX_FMT_BGR24; case VideoMode::kGray: return V4L2_PIX_FMT_GREY; + case VideoMode::kUYVY: + return V4L2_PIX_FMT_UYVY; default: return 0; } diff --git a/cscore/src/main/native/windows/UsbCameraImpl.cpp b/cscore/src/main/native/windows/UsbCameraImpl.cpp index 5e1b36b1b7..c291212c01 100644 --- a/cscore/src/main/native/windows/UsbCameraImpl.cpp +++ b/cscore/src/main/native/windows/UsbCameraImpl.cpp @@ -368,6 +368,12 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample, 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; @@ -472,6 +478,8 @@ static cs::VideoMode::PixelFormat GetFromGUID(const GUID& guid) { return cs::VideoMode::PixelFormat::kMJPEG; } else if (IsEqualGUID(guid, MFVideoFormat_RGB565)) { return cs::VideoMode::PixelFormat::kRGB565; + } else if (IsEqualGUID(guid, MFVideoFormat_UYVY)) { + return cs::VideoMode::PixelFormat::kUYVY; } else { return cs::VideoMode::PixelFormat::kUnknown; }