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 6f02cc82a6..012874dd98 100644 --- a/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java +++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java @@ -14,7 +14,8 @@ public class VideoMode { kRGB565(3), kBGR(4), kGray(5), - kUYVY(6); + kY16(6), + kUYVY(7); private final int value; diff --git a/cscore/src/main/native/cpp/Frame.cpp b/cscore/src/main/native/cpp/Frame.cpp index 77b93dff4e..881e82ec9a 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, UYVY, or Gray, need to convert to BGR first + // If source is YUYV, UYVY, Gray, or Y16, need to convert to BGR first if (cur->pixelFormat == VideoMode::kYUYV) { // Check to see if BGR version already exists... if (Image* newImage = @@ -242,9 +242,29 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, } 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 ConvertBGRToRGB565(cur); case VideoMode::kGray: + case VideoMode::kY16: + // If source is also grayscale, convert directly + if (pixelFormat == VideoMode::kGray && + cur->pixelFormat == VideoMode::kY16) { + return ConvertY16ToGray(cur); + } else if (pixelFormat == VideoMode::kY16 && + cur->pixelFormat == VideoMode::kGray) { + return ConvertGrayToY16(cur); + } // 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... @@ -271,7 +291,11 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, cur = ConvertRGB565ToBGR(cur); } } - return ConvertBGRToGray(cur); + cur = ConvertBGRToGray(cur); + if (pixelFormat == VideoMode::kY16) { + cur = ConvertGrayToY16(cur); + } + return cur; case VideoMode::kBGR: case VideoMode::kMJPEG: if (cur->pixelFormat == VideoMode::kYUYV) { @@ -286,6 +310,19 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, } else { return ConvertGrayToMJPEG(cur, defaultJpegQuality); } + } else if (cur->pixelFormat == VideoMode::kY16) { + // Check to see if Gray version already exists... + if (Image* newImage = + GetExistingImage(cur->width, cur->height, VideoMode::kGray)) { + cur = newImage; + } else { + cur = ConvertY16ToGray(cur); + } + if (pixelFormat == VideoMode::kBGR) { + return ConvertGrayToBGR(cur); + } else { + return ConvertGrayToMJPEG(cur, defaultJpegQuality); + } } break; case VideoMode::kYUYV: @@ -550,6 +587,50 @@ Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) { return rv; } +Image* Frame::ConvertGrayToY16(Image* image) { + if (!image || image->pixelFormat != VideoMode::kGray) { + return nullptr; + } + + // Allocate a Y16 image + auto newImage = + m_impl->source.AllocImage(VideoMode::kY16, image->width, image->height, + image->width * image->height * 2); + + // Convert with linear scaling + image->AsMat().convertTo(newImage->AsMat(), CV_16U, 256); + + // 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::ConvertY16ToGray(Image* image) { + if (!image || image->pixelFormat != VideoMode::kY16) { + return nullptr; + } + + // Allocate a Grayscale image + auto newImage = + m_impl->source.AllocImage(VideoMode::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); + + // 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) { diff --git a/cscore/src/main/native/cpp/Frame.h b/cscore/src/main/native/cpp/Frame.h index 1722a84845..ab6170ff06 100644 --- a/cscore/src/main/native/cpp/Frame.h +++ b/cscore/src/main/native/cpp/Frame.h @@ -202,6 +202,8 @@ class Frame { Image* ConvertGrayToBGR(Image* image); Image* ConvertBGRToMJPEG(Image* image, int quality); Image* ConvertGrayToMJPEG(Image* image, int quality); + Image* ConvertGrayToY16(Image* image); + Image* ConvertY16ToGray(Image* image); Image* GetImage(int width, int height, VideoMode::PixelFormat pixelFormat) { if (pixelFormat == VideoMode::kMJPEG) { diff --git a/cscore/src/main/native/cpp/Image.h b/cscore/src/main/native/cpp/Image.h index 0cace4a253..cb8df912dd 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::kY16: case VideoMode::kUYVY: type = CV_8UC2; break; diff --git a/cscore/src/main/native/cpp/MjpegServerImpl.cpp b/cscore/src/main/native/cpp/MjpegServerImpl.cpp index ab00d82a71..ea9ebe1134 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::kY16: + os << "Y16"; + break; case VideoMode::kUYVY: os << "UYVY"; break; @@ -572,6 +575,9 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os, case VideoMode::kGray: os << "gray"; break; + case VideoMode::kY16: + os << "Y16"; + break; case VideoMode::kUYVY: os << "UYVY"; break; @@ -749,6 +755,7 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) { case VideoMode::kUYVY: case VideoMode::kRGB565: case VideoMode::kYUYV: + case VideoMode::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)); diff --git a/cscore/src/main/native/cpp/RawSourceImpl.cpp b/cscore/src/main/native/cpp/RawSourceImpl.cpp index 6adb202773..bad26a05cf 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::kY16: case VideoMode::kUYVY: type = CV_8UC2; break; diff --git a/cscore/src/main/native/cpp/SourceImpl.cpp b/cscore/src/main/native/cpp/SourceImpl.cpp index f8cce0db4b..045bdd4692 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, "y16")) { + mode.pixelFormat = cs::VideoMode::kY16; } else if (wpi::equals_lower(str, "uyvy")) { mode.pixelFormat = cs::VideoMode::kUYVY; } else { @@ -362,6 +364,9 @@ wpi::json SourceImpl::GetConfigJsonObject(CS_Status* status) { case VideoMode::kGray: pixelFormat = "gray"; break; + case VideoMode::kY16: + pixelFormat = "y16"; + break; case VideoMode::kUYVY: pixelFormat = "uyvy"; break; diff --git a/cscore/src/main/native/include/cscore_c.h b/cscore/src/main/native/include/cscore_c.h index 5b3177f847..ba8c811923 100644 --- a/cscore/src/main/native/include/cscore_c.h +++ b/cscore/src/main/native/include/cscore_c.h @@ -94,6 +94,7 @@ enum CS_PixelFormat { CS_PIXFMT_RGB565, CS_PIXFMT_BGR, CS_PIXFMT_GRAY, + CS_PIXFMT_Y16, CS_PIXFMT_UYVY }; diff --git a/cscore/src/main/native/include/cscore_cpp.h b/cscore/src/main/native/include/cscore_cpp.h index 1dcffed15d..615b200399 100644 --- a/cscore/src/main/native/include/cscore_cpp.h +++ b/cscore/src/main/native/include/cscore_cpp.h @@ -69,6 +69,7 @@ struct VideoMode : public CS_VideoMode { kRGB565 = CS_PIXFMT_RGB565, kBGR = CS_PIXFMT_BGR, kGray = CS_PIXFMT_GRAY, + kY16 = CS_PIXFMT_Y16, kUYVY = CS_PIXFMT_UYVY }; VideoMode() { diff --git a/cscore/src/main/native/linux/UsbCameraImpl.cpp b/cscore/src/main/native/linux/UsbCameraImpl.cpp index fd9eeb40e1..270905b0e8 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_Y16: + return VideoMode::kY16; case V4L2_PIX_FMT_UYVY: return VideoMode::kUYVY; default: @@ -102,6 +104,8 @@ static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) { return V4L2_PIX_FMT_BGR24; case VideoMode::kGray: return V4L2_PIX_FMT_GREY; + case VideoMode::kY16: + return V4L2_PIX_FMT_Y16; case VideoMode::kUYVY: return V4L2_PIX_FMT_UYVY; default: diff --git a/cscore/src/main/native/windows/UsbCameraImpl.cpp b/cscore/src/main/native/windows/UsbCameraImpl.cpp index 016ea77f38..c6a8985734 100644 --- a/cscore/src/main/native/windows/UsbCameraImpl.cpp +++ b/cscore/src/main/native/windows/UsbCameraImpl.cpp @@ -356,6 +356,12 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample, 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, @@ -469,6 +475,8 @@ static cs::VideoMode::PixelFormat GetFromGUID(const GUID& guid) { // Compare GUID to one of the supported ones if (IsEqualGUID(guid, MFVideoFormat_L8)) { return cs::VideoMode::PixelFormat::kGray; + } else if (IsEqualGUID(guid, MFVideoFormat_L16)) { + return cs::VideoMode::PixelFormat::kY16; } else if (IsEqualGUID(guid, MFVideoFormat_YUY2)) { return cs::VideoMode::PixelFormat::kYUYV; } else if (IsEqualGUID(guid, MFVideoFormat_RGB24)) {