WPILibC++ 2025.0.0-alpha-1-24-g6478ba6
cscore_cv.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#ifndef CSCORE_CSCORE_CV_H_
6#define CSCORE_CSCORE_CV_H_
7
8#include <functional>
9
10#include <opencv2/core/mat.hpp>
11
12#include "cscore_oo.h"
13#include "cscore_raw.h"
14
15namespace cs {
16/**
17 * A source for user code to provide OpenCV images as video frames.
18 *
19 * This is not dependent on any opencv binary ABI, and can be used
20 * with versions of most versions of OpenCV.
21 */
22class CvSource : public ImageSource {
23 public:
24 CvSource() = default;
25
26 /**
27 * Create an OpenCV source.
28 *
29 * @param name Source name (arbitrary unique identifier)
30 * @param mode Video mode being generated
31 */
32 CvSource(std::string_view name, const VideoMode& mode);
33
34 /**
35 * Create an OpenCV source.
36 *
37 * @param name Source name (arbitrary unique identifier)
38 * @param pixelFormat Pixel format
39 * @param width width
40 * @param height height
41 * @param fps fps
42 */
43 CvSource(std::string_view name, VideoMode::PixelFormat pixelFormat, int width,
44 int height, int fps);
45
46 /**
47 * Put an OpenCV image and notify sinks
48 *
49 * <p>
50 * The image format is guessed from the number of channels. The channel
51 * mapping is as follows. 1: kGray 2: kYUYV 3: BGR 4: BGRA Any other channel
52 * numbers will throw an error. If your image is an in alternate format, use
53 * the overload that takes a PixelFormat.
54 *
55 * @param image OpenCV Image
56 */
57 void PutFrame(cv::Mat& image);
58
59 /**
60 * Put an OpenCV image and notify sinks.
61 *
62 * <p>
63 * The format of the Mat must match the PixelFormat. You will corrupt memory
64 * if they dont. With skipVerification false, we will verify the number of
65 * channels matches the pixel format. If skipVerification is true, this step
66 * is skipped and is passed straight through.
67 *
68 * @param image OpenCV image
69 * @param pixelFormat The pixel format of the image
70 * @param skipVerification skip verifying pixel format
71 */
72 void PutFrame(cv::Mat& image, VideoMode::PixelFormat pixelFormat,
73 bool skipVerification);
74
75 private:
76 static bool VerifyFormat(cv::Mat& image, VideoMode::PixelFormat pixelFormat);
77};
78
79/**
80 * A source for user code to accept video frames as OpenCV images.
81 *
82 * This is not dependent on any opencv binary ABI, and can be used
83 * with versions of most versions of OpenCV.
84 */
85class CvSink : public ImageSink {
86 public:
87 CvSink() = default;
88 CvSink(const CvSink& sink);
89
90 /**
91 * Create a sink for accepting OpenCV images.
92 *
93 * <p>WaitForFrame() must be called on the created sink to get each new
94 * image.
95 *
96 * @param name Source name (arbitrary unique identifier)
97 * @param pixelFormat The pixel format to read
98 */
99 explicit CvSink(std::string_view name, VideoMode::PixelFormat pixelFormat =
100 VideoMode::PixelFormat::kBGR);
101
102 /**
103 * Wait for the next frame and get the image.
104 * Times out (returning 0) after timeout seconds.
105 * The provided image will have the pixelFormat this class was constructed
106 * with.
107 *
108 * @return Frame time, or 0 on error (call GetError() to obtain the error
109 * message); the frame time is in the same time base as wpi::Now(),
110 * and is in 1 us increments.
111 */
112 [[nodiscard]]
113 uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
114
115 /**
116 * Wait for the next frame and get the image. May block forever.
117 * The provided image will have the pixelFormat this class was constructed
118 * with.
119 *
120 * @return Frame time, or 0 on error (call GetError() to obtain the error
121 * message); the frame time is in the same time base as wpi::Now(),
122 * and is in 1 us increments.
123 */
124 [[nodiscard]]
125 uint64_t GrabFrameNoTimeout(cv::Mat& image);
126
127 /**
128 * Wait for the next frame and get the image.
129 * Times out (returning 0) after timeout seconds.
130 * The provided image will have the pixelFormat this class was constructed
131 * with. The data is backed by data in the CvSink. It will be invalidated by
132 * any grabFrame*() call on the sink.
133 *
134 * @return Frame time, or 0 on error (call GetError() to obtain the error
135 * message); the frame time is in the same time base as wpi::Now(),
136 * and is in 1 us increments.
137 */
138 [[nodiscard]]
139 uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225);
140
141 /**
142 * Wait for the next frame and get the image. May block forever.
143 * The provided image will have the pixelFormat this class was constructed
144 * with. The data is backed by data in the CvSink. It will be invalidated by
145 * any grabFrame*() call on the sink.
146 *
147 * @return Frame time, or 0 on error (call GetError() to obtain the error
148 * message); the frame time is in the same time base as wpi::Now(),
149 * and is in 1 us increments.
150 */
151 [[nodiscard]]
152 uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
153
154 private:
155 constexpr int GetCvFormat(WPI_PixelFormat pixelFormat);
156
157 wpi::RawFrame rawFrame;
158 VideoMode::PixelFormat pixelFormat;
159};
160
162 m_handle = CreateRawSource(name, true, mode, &m_status);
163}
164
166 int width, int height, int fps) {
167 m_handle = CreateRawSource(name, true, VideoMode{format, width, height, fps},
168 &m_status);
169}
170
171inline bool CvSource::VerifyFormat(cv::Mat& image,
172 VideoMode::PixelFormat pixelFormat) {
173 int channels = image.channels();
174 switch (pixelFormat) {
175 case VideoMode::PixelFormat::kBGR:
176 if (channels == 3) {
177 return true;
178 }
179 break;
180 case VideoMode::PixelFormat::kBGRA:
181 if (channels == 4) {
182 return true;
183 }
184 break;
185 case VideoMode::PixelFormat::kGray:
186 if (channels == 1) {
187 return true;
188 }
189 break;
190 case VideoMode::PixelFormat::kRGB565:
191 if (channels == 2) {
192 return true;
193 }
194 break;
195 case VideoMode::PixelFormat::kUYVY:
196 if (channels == 2) {
197 return true;
198 }
199 break;
200 case VideoMode::PixelFormat::kY16:
201 if (channels == 2) {
202 return true;
203 }
204 break;
205 case VideoMode::PixelFormat::kYUYV:
206 if (channels == 2) {
207 return true;
208 }
209 break;
210 case VideoMode::PixelFormat::kMJPEG:
211 if (channels == 1) {
212 return true;
213 }
214 break;
215 default:
216 break;
217 }
218 return false;
219}
220
221inline void CvSource::PutFrame(cv::Mat& image) {
222 // We only support 8-bit images; convert if necessary.
223 cv::Mat finalImage;
224 if (image.depth() == CV_8U) {
225 finalImage = image;
226 } else {
227 image.convertTo(finalImage, CV_8U);
228 }
229
230 int channels = finalImage.channels();
232 if (channels == 1) {
233 // 1 channel is assumed Graysacle
234 format = VideoMode::PixelFormat::kGray;
235 } else if (channels == 2) {
236 // 2 channels is assumed YUYV
237 format = VideoMode::PixelFormat::kYUYV;
238 } else if (channels == 3) {
239 // 3 channels is assumed BGR
240 format = VideoMode::PixelFormat::kBGR;
241 } else if (channels == 4) {
242 // 4 channels is assumed BGRA
243 format = VideoMode::PixelFormat::kBGRA;
244 } else {
245 // TODO Error
246 return;
247 }
248
249 PutFrame(finalImage, format, true);
250}
251
252inline void CvSource::PutFrame(cv::Mat& image,
253 VideoMode::PixelFormat pixelFormat,
254 bool skipVerification) {
255 // We only support 8-bit images; convert if necessary.
256 cv::Mat finalImage;
257 if (image.depth() == CV_8U) {
258 finalImage = image;
259 } else {
260 image.convertTo(finalImage, CV_8U);
261 }
262
263 if (!skipVerification) {
264 if (!VerifyFormat(finalImage, pixelFormat)) {
265 // TODO Error
266 return;
267 }
268 }
269
270 WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
271 frame.data = finalImage.data;
272 frame.freeFunc = nullptr;
273 frame.freeCbData = nullptr;
274 frame.size = finalImage.total() * finalImage.channels();
275 frame.width = finalImage.cols;
276 frame.height = finalImage.rows;
277 frame.stride = finalImage.step;
278 frame.pixelFormat = pixelFormat;
279 m_status = 0;
281}
282
284 VideoMode::PixelFormat pixelFormat) {
285 m_handle = CreateRawSink(name, true, &m_status);
286 this->pixelFormat = pixelFormat;
287}
288
289inline CvSink::CvSink(const CvSink& sink)
290 : ImageSink{sink}, pixelFormat{sink.pixelFormat} {}
291
292inline uint64_t CvSink::GrabFrame(cv::Mat& image, double timeout) {
293 cv::Mat tmpnam;
294 auto retVal = GrabFrameDirect(tmpnam);
295 if (retVal <= 0) {
296 return retVal;
297 }
298 tmpnam.copyTo(image);
299 return retVal;
300}
301
302inline uint64_t CvSink::GrabFrameNoTimeout(cv::Mat& image) {
303 cv::Mat tmpnam;
304 auto retVal = GrabFrameNoTimeoutDirect(tmpnam);
305 if (retVal <= 0) {
306 return retVal;
307 }
308 tmpnam.copyTo(image);
309 return retVal;
310}
311
312inline constexpr int CvSink::GetCvFormat(WPI_PixelFormat pixelFormat) {
313 int type = 0;
314 switch (pixelFormat) {
315 case WPI_PIXFMT_YUYV:
317 case WPI_PIXFMT_Y16:
318 case WPI_PIXFMT_UYVY:
319 type = CV_8UC2;
320 break;
321 case WPI_PIXFMT_BGR:
322 type = CV_8UC3;
323 break;
324 case WPI_PIXFMT_BGRA:
325 type = CV_8UC4;
326 break;
327 case WPI_PIXFMT_GRAY:
328 case WPI_PIXFMT_MJPEG:
329 default:
330 type = CV_8UC1;
331 break;
332 }
333 return type;
334}
335
336inline uint64_t CvSink::GrabFrameDirect(cv::Mat& image, double timeout) {
337 rawFrame.height = 0;
338 rawFrame.width = 0;
339 rawFrame.stride = 0;
340 rawFrame.pixelFormat = pixelFormat;
341 auto timestamp = GrabSinkFrameTimeout(m_handle, rawFrame, timeout, &m_status);
342 if (m_status != CS_OK) {
343 return 0;
344 }
345 image =
346 cv::Mat{rawFrame.height, rawFrame.width,
347 GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
348 rawFrame.data, static_cast<size_t>(rawFrame.stride)};
349 return timestamp;
350}
351
352inline uint64_t CvSink::GrabFrameNoTimeoutDirect(cv::Mat& image) {
353 rawFrame.height = 0;
354 rawFrame.width = 0;
355 rawFrame.stride = 0;
356 rawFrame.pixelFormat = pixelFormat;
357 auto timestamp = GrabSinkFrame(m_handle, rawFrame, &m_status);
358 if (m_status != CS_OK) {
359 return 0;
360 }
361 image =
362 cv::Mat{rawFrame.height, rawFrame.width,
363 GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
364 rawFrame.data, static_cast<size_t>(rawFrame.stride)};
365 return timestamp;
366}
367
368} // namespace cs
369
370#endif // CSCORE_CSCORE_CV_H_
WPI_PixelFormat
Pixel formats.
Definition: RawFrame.h:49
@ WPI_PIXFMT_YUYV
Definition: RawFrame.h:52
@ WPI_PIXFMT_Y16
Definition: RawFrame.h:56
@ WPI_PIXFMT_UYVY
Definition: RawFrame.h:57
@ WPI_PIXFMT_BGRA
Definition: RawFrame.h:58
@ WPI_PIXFMT_MJPEG
Definition: RawFrame.h:51
@ WPI_PIXFMT_GRAY
Definition: RawFrame.h:55
@ WPI_PIXFMT_RGB565
Definition: RawFrame.h:53
@ WPI_PIXFMT_BGR
Definition: RawFrame.h:54
A source for user code to accept video frames as OpenCV images.
Definition: cscore_cv.h:85
CvSink()=default
uint64_t GrabFrame(cv::Mat &image, double timeout=0.225)
Wait for the next frame and get the image.
Definition: cscore_cv.h:292
uint64_t GrabFrameDirect(cv::Mat &image, double timeout=0.225)
Wait for the next frame and get the image.
Definition: cscore_cv.h:336
uint64_t GrabFrameNoTimeout(cv::Mat &image)
Wait for the next frame and get the image.
Definition: cscore_cv.h:302
uint64_t GrabFrameNoTimeoutDirect(cv::Mat &image)
Wait for the next frame and get the image.
Definition: cscore_cv.h:352
A source for user code to provide OpenCV images as video frames.
Definition: cscore_cv.h:22
void PutFrame(cv::Mat &image)
Put an OpenCV image and notify sinks.
Definition: cscore_cv.h:221
CvSource()=default
A base class for single image reading sinks.
Definition: cscore_oo.h:1095
A base class for single image providing sources.
Definition: cscore_oo.h:750
CS_Status m_status
Definition: cscore_oo.h:1008
CS_Sink m_handle
Definition: cscore_oo.h:1009
CS_Source m_handle
Video source handle.
Definition: cscore_oo.h:475
CS_Status m_status
Definition: cscore_oo.h:472
@ CS_OK
Definition: cscore_c.h:62
CS_Source CreateRawSource(std::string_view name, bool isCv, const VideoMode &mode, CS_Status *status)
CS_Sink CreateRawSink(std::string_view name, bool isCv, CS_Status *status)
uint64_t GrabSinkFrameTimeout(CS_Sink sink, WPI_RawFrame &image, double timeout, CS_Status *status)
uint64_t GrabSinkFrame(CS_Sink sink, WPI_RawFrame &image, CS_Status *status)
void PutSourceFrame(CS_Source source, const WPI_RawFrame &image, CS_Status *status)
CameraServer (cscore) namespace.
Definition: cscore_cpp.h:29
type
Definition: base.h:646
fps
Definition: velocity.h:46
Raw Frame.
Definition: RawFrame.h:32
int pixelFormat
Definition: RawFrame.h:40
void * freeCbData
Definition: RawFrame.h:37
void(* freeFunc)(void *cbdata, void *data, size_t capacity)
Definition: RawFrame.h:36
uint8_t * data
Definition: RawFrame.h:34
size_t size
Definition: RawFrame.h:39
int height
Definition: RawFrame.h:42
int stride
Definition: RawFrame.h:43
int width
Definition: RawFrame.h:41
Video mode.
Definition: cscore_cpp.h:62
PixelFormat
Definition: cscore_cpp.h:63
Definition: RawFrame.h:75
basic_string_view< char > string_view
Definition: base.h:601
auto format(wformat_string< T... > fmt, T &&... args) -> std::wstring
Definition: xchar.h:137