WPILibC++ 2025.2.1
Loading...
Searching...
No Matches
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#include <wpi/RawFrame.h>
12
13#include "cscore_oo.h"
14#include "cscore_raw.h"
15
16namespace cs {
17/**
18 * A source for user code to provide OpenCV images as video frames.
19 *
20 * This is not dependent on any opencv binary ABI, and can be used
21 * with versions of most versions of OpenCV.
22 */
23class CvSource : public ImageSource {
24 public:
25 CvSource() = default;
26
27 /**
28 * Create an OpenCV source.
29 *
30 * @param name Source name (arbitrary unique identifier)
31 * @param mode Video mode being generated
32 */
33 CvSource(std::string_view name, const VideoMode& mode);
34
35 /**
36 * Create an OpenCV source.
37 *
38 * @param name Source name (arbitrary unique identifier)
39 * @param pixelFormat Pixel format
40 * @param width width
41 * @param height height
42 * @param fps fps
43 */
44 CvSource(std::string_view name, VideoMode::PixelFormat pixelFormat, int width,
45 int height, int fps);
46
47 /**
48 * Put an OpenCV image and notify sinks
49 *
50 * <p>
51 * The image format is guessed from the number of channels. The channel
52 * mapping is as follows. 1: kGray 2: kYUYV 3: BGR 4: BGRA Any other channel
53 * numbers will throw an error. If your image is an in alternate format, use
54 * the overload that takes a PixelFormat.
55 *
56 * @param image OpenCV Image
57 */
58 void PutFrame(cv::Mat& image);
59
60 /**
61 * Put an OpenCV image and notify sinks.
62 *
63 * <p>
64 * The format of the Mat must match the PixelFormat. You will corrupt memory
65 * if they dont. With skipVerification false, we will verify the number of
66 * channels matches the pixel format. If skipVerification is true, this step
67 * is skipped and is passed straight through.
68 *
69 * @param image OpenCV image
70 * @param pixelFormat The pixel format of the image
71 * @param skipVerification skip verifying pixel format
72 */
73 void PutFrame(cv::Mat& image, VideoMode::PixelFormat pixelFormat,
74 bool skipVerification);
75
76 private:
77 static bool VerifyFormat(cv::Mat& image, VideoMode::PixelFormat pixelFormat);
78};
79
80/**
81 * A source for user code to accept video frames as OpenCV images.
82 *
83 * This is not dependent on any opencv binary ABI, and can be used
84 * with versions of most versions of OpenCV.
85 */
86class CvSink : public ImageSink {
87 public:
88 CvSink() = default;
89 CvSink(const CvSink& sink);
90
91 /**
92 * Create a sink for accepting OpenCV images.
93 *
94 * <p>WaitForFrame() must be called on the created sink to get each new
95 * image.
96 *
97 * @param name Source name (arbitrary unique identifier)
98 * @param pixelFormat The pixel format to read
99 */
100 explicit CvSink(std::string_view name, VideoMode::PixelFormat pixelFormat =
102
103 /**
104 * Wait for the next frame and get the image.
105 * Times out (returning 0) after timeout seconds.
106 * The provided image will have the pixelFormat this class was constructed
107 * with.
108 *
109 * @return Frame time, or 0 on error (call GetError() to obtain the error
110 * message); the frame time is in the same time base as wpi::Now(),
111 * and is in 1 us increments.
112 */
113 [[nodiscard]]
114 uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
115
116 /**
117 * Wait for the next frame and get the image. May block forever.
118 * The provided image will have the pixelFormat this class was constructed
119 * with.
120 *
121 * @return Frame time, or 0 on error (call GetError() to obtain the error
122 * message); the frame time is in the same time base as wpi::Now(),
123 * and is in 1 us increments.
124 */
125 [[nodiscard]]
126 uint64_t GrabFrameNoTimeout(cv::Mat& image);
127
128 /**
129 * Wait for the next frame and get the image.
130 * Times out (returning 0) after timeout seconds.
131 * The provided image will have the pixelFormat this class was constructed
132 * with. The data is backed by data in the CvSink. It will be invalidated by
133 * any grabFrame*() call on the sink.
134 *
135 * @return Frame time, or 0 on error (call GetError() to obtain the error
136 * message); the frame time is in the same time base as wpi::Now(),
137 * and is in 1 us increments.
138 */
139 [[nodiscard]]
140 uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225);
141
142 /**
143 * Wait for the next frame and get the image. May block forever.
144 * The provided image will have the pixelFormat this class was constructed
145 * with. The data is backed by data in the CvSink. It will be invalidated by
146 * any grabFrame*() call on the sink.
147 *
148 * @return Frame time, or 0 on error (call GetError() to obtain the error
149 * message); the frame time is in the same time base as wpi::Now(),
150 * and is in 1 us increments.
151 */
152 [[nodiscard]]
153 uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
154
155 /**
156 * Wait for the next frame and get the image.
157 * Times out (returning 0) after timeout seconds.
158 * The provided image will have the pixelFormat this class was constructed
159 * with. The data is backed by data in the CvSink. It will be invalidated by
160 * any grabFrame*() call on the sink.
161 *
162 * <p>If lastFrameTime is provided and non-zero, the sink will fill image with
163 * the first frame from the source that is not equal to lastFrameTime. If
164 * lastFrameTime is zero, the time of the current frame owned by the CvSource
165 * is used, and this function will block until the connected CvSource provides
166 * a new frame.
167 *
168 * @return Frame time, or 0 on error (call GetError() to obtain the error
169 * message); the frame time is in the same time base as wpi::Now(),
170 * and is in 1 us increments.
171 */
172 [[nodiscard]]
173 uint64_t GrabFrameDirectLastTime(cv::Mat& image, uint64_t lastFrameTime,
174 double timeout = 0.225);
175
176 /**
177 * Get the last time a frame was grabbed. This uses the same time base as
178 * wpi::Now().
179 *
180 * @return Time in 1 us increments.
181 */
182 [[nodiscard]]
183 uint64_t LastFrameTime();
184
185 /**
186 * Get the time source for the timestamp the last frame was grabbed at.
187 *
188 * @return Time source
189 */
190 [[nodiscard]]
192
193 private:
194 constexpr int GetCvFormat(WPI_PixelFormat pixelFormat);
195
196 wpi::RawFrame rawFrame;
197 VideoMode::PixelFormat pixelFormat;
198};
199
200inline CvSource::CvSource(std::string_view name, const VideoMode& mode) {
201 m_handle = CreateRawSource(name, true, mode, &m_status);
202}
203
205 int width, int height, int fps) {
206 m_handle = CreateRawSource(name, true, VideoMode{format, width, height, fps},
207 &m_status);
208}
209
210inline bool CvSource::VerifyFormat(cv::Mat& image,
211 VideoMode::PixelFormat pixelFormat) {
212 int channels = image.channels();
213 switch (pixelFormat) {
215 if (channels == 3) {
216 return true;
217 }
218 break;
220 if (channels == 4) {
221 return true;
222 }
223 break;
225 if (channels == 1) {
226 return true;
227 }
228 break;
230 if (channels == 2) {
231 return true;
232 }
233 break;
235 if (channels == 2) {
236 return true;
237 }
238 break;
240 if (channels == 2) {
241 return true;
242 }
243 break;
245 if (channels == 2) {
246 return true;
247 }
248 break;
250 if (channels == 1) {
251 return true;
252 }
253 break;
254 default:
255 break;
256 }
257 return false;
258}
259
260inline void CvSource::PutFrame(cv::Mat& image) {
261 // We only support 8-bit images; convert if necessary.
262 cv::Mat finalImage;
263 if (image.depth() == CV_8U) {
264 finalImage = image;
265 } else {
266 image.convertTo(finalImage, CV_8U);
267 }
268
269 int channels = finalImage.channels();
271 if (channels == 1) {
272 // 1 channel is assumed Grayscale
274 } else if (channels == 2) {
275 // 2 channels is assumed YUYV
277 } else if (channels == 3) {
278 // 3 channels is assumed BGR
280 } else if (channels == 4) {
281 // 4 channels is assumed BGRA
283 } else {
284 // TODO Error
285 return;
286 }
287
288 PutFrame(finalImage, format, true);
289}
290
291inline void CvSource::PutFrame(cv::Mat& image,
292 VideoMode::PixelFormat pixelFormat,
293 bool skipVerification) {
294 // We only support 8-bit images; convert if necessary.
295 cv::Mat finalImage;
296 if (image.depth() == CV_8U) {
297 finalImage = image;
298 } else {
299 image.convertTo(finalImage, CV_8U);
300 }
301
302 if (!skipVerification) {
303 if (!VerifyFormat(finalImage, pixelFormat)) {
304 // TODO Error
305 return;
306 }
307 }
308
309 WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
310 frame.data = finalImage.data;
311 frame.freeFunc = nullptr;
312 frame.freeCbData = nullptr;
313 frame.size = finalImage.total() * finalImage.channels();
314 frame.width = finalImage.cols;
315 frame.height = finalImage.rows;
316 frame.stride = finalImage.step;
317 frame.pixelFormat = pixelFormat;
318 m_status = 0;
320}
321
322inline CvSink::CvSink(std::string_view name,
323 VideoMode::PixelFormat pixelFormat) {
324 m_handle = CreateRawSink(name, true, &m_status);
325 this->pixelFormat = pixelFormat;
326}
327
328inline CvSink::CvSink(const CvSink& sink)
329 : ImageSink{sink}, pixelFormat{sink.pixelFormat} {}
330
331inline uint64_t CvSink::GrabFrame(cv::Mat& image, double timeout) {
332 cv::Mat tmpnam;
333 auto retVal = GrabFrameDirect(tmpnam);
334 if (retVal <= 0) {
335 return retVal;
336 }
337 tmpnam.copyTo(image);
338 return retVal;
339}
340
341inline uint64_t CvSink::GrabFrameNoTimeout(cv::Mat& image) {
342 cv::Mat tmpnam;
343 auto retVal = GrabFrameNoTimeoutDirect(tmpnam);
344 if (retVal <= 0) {
345 return retVal;
346 }
347 tmpnam.copyTo(image);
348 return retVal;
349}
350
351inline constexpr int CvSink::GetCvFormat(WPI_PixelFormat pixelFormat) {
352 int type = 0;
353 switch (pixelFormat) {
354 case WPI_PIXFMT_YUYV:
356 case WPI_PIXFMT_Y16:
357 case WPI_PIXFMT_UYVY:
358 type = CV_8UC2;
359 break;
360 case WPI_PIXFMT_BGR:
361 type = CV_8UC3;
362 break;
363 case WPI_PIXFMT_BGRA:
364 type = CV_8UC4;
365 break;
366 case WPI_PIXFMT_GRAY:
367 case WPI_PIXFMT_MJPEG:
368 default:
369 type = CV_8UC1;
370 break;
371 }
372 return type;
373}
374
375inline uint64_t CvSink::GrabFrameDirect(cv::Mat& image, double timeout) {
376 rawFrame.height = 0;
377 rawFrame.width = 0;
378 rawFrame.stride = 0;
379 rawFrame.pixelFormat = pixelFormat;
380 auto timestamp = GrabSinkFrameTimeout(m_handle, rawFrame, timeout, &m_status);
381 if (m_status != CS_OK) {
382 return 0;
383 }
384 image =
385 cv::Mat{rawFrame.height, rawFrame.width,
386 GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
387 rawFrame.data, static_cast<size_t>(rawFrame.stride)};
388 return timestamp;
389}
390
391inline uint64_t CvSink::GrabFrameNoTimeoutDirect(cv::Mat& image) {
392 rawFrame.height = 0;
393 rawFrame.width = 0;
394 rawFrame.stride = 0;
395 rawFrame.pixelFormat = pixelFormat;
396 auto timestamp = GrabSinkFrame(m_handle, rawFrame, &m_status);
397 if (m_status != CS_OK) {
398 return 0;
399 }
400 image =
401 cv::Mat{rawFrame.height, rawFrame.width,
402 GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
403 rawFrame.data, static_cast<size_t>(rawFrame.stride)};
404 return timestamp;
405}
406
407inline uint64_t CvSink::GrabFrameDirectLastTime(cv::Mat& image,
408 uint64_t lastFrameTime,
409 double timeout) {
410 rawFrame.height = 0;
411 rawFrame.width = 0;
412 rawFrame.stride = 0;
413 rawFrame.pixelFormat = pixelFormat;
414 auto timestamp = GrabSinkFrameTimeoutLastTime(m_handle, rawFrame, timeout,
415 lastFrameTime, &m_status);
416 if (m_status != CS_OK) {
417 return 0;
418 }
419 image =
420 cv::Mat{rawFrame.height, rawFrame.width,
421 GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
422 rawFrame.data, static_cast<size_t>(rawFrame.stride)};
423 return timestamp;
424}
425
426inline uint64_t CvSink::LastFrameTime() {
427 return rawFrame.timestamp;
428}
429
431 return static_cast<WPI_TimestampSource>(rawFrame.timestampSrc);
432}
433
434} // namespace cs
435
436#endif // CSCORE_CSCORE_CV_H_
WPI_PixelFormat
Pixel formats.
Definition RawFrame.h:51
@ WPI_PIXFMT_YUYV
Definition RawFrame.h:54
@ WPI_PIXFMT_Y16
Definition RawFrame.h:58
@ WPI_PIXFMT_UYVY
Definition RawFrame.h:59
@ WPI_PIXFMT_BGRA
Definition RawFrame.h:60
@ WPI_PIXFMT_MJPEG
Definition RawFrame.h:53
@ WPI_PIXFMT_GRAY
Definition RawFrame.h:57
@ WPI_PIXFMT_RGB565
Definition RawFrame.h:55
@ WPI_PIXFMT_BGR
Definition RawFrame.h:56
WPI_TimestampSource
Timestamp metadata.
Definition RawFrame.h:66
A source for user code to accept video frames as OpenCV images.
Definition cscore_cv.h:86
CvSink()=default
WPI_TimestampSource LastFrameTimeSource()
Get the time source for the timestamp the last frame was grabbed at.
Definition cscore_cv.h:430
uint64_t GrabFrame(cv::Mat &image, double timeout=0.225)
Wait for the next frame and get the image.
Definition cscore_cv.h:331
uint64_t GrabFrameDirect(cv::Mat &image, double timeout=0.225)
Wait for the next frame and get the image.
Definition cscore_cv.h:375
uint64_t LastFrameTime()
Get the last time a frame was grabbed.
Definition cscore_cv.h:426
uint64_t GrabFrameDirectLastTime(cv::Mat &image, uint64_t lastFrameTime, double timeout=0.225)
Wait for the next frame and get the image.
Definition cscore_cv.h:407
uint64_t GrabFrameNoTimeout(cv::Mat &image)
Wait for the next frame and get the image.
Definition cscore_cv.h:341
uint64_t GrabFrameNoTimeoutDirect(cv::Mat &image)
Wait for the next frame and get the image.
Definition cscore_cv.h:391
A source for user code to provide OpenCV images as video frames.
Definition cscore_cv.h:23
void PutFrame(cv::Mat &image)
Put an OpenCV image and notify sinks.
Definition cscore_cv.h:260
CvSource()=default
A base class for single image reading sinks.
Definition cscore_oo.h:1451
A base class for single image providing sources.
Definition cscore_oo.h:984
CS_Status m_status
Definition cscore_oo.h:1339
CS_Sink m_handle
Definition cscore_oo.h:1340
CS_Source m_handle
Video source handle.
Definition cscore_oo.h:603
CS_Status m_status
Definition cscore_oo.h:600
FMT_INLINE auto format(detail::locale_ref loc, format_string< T... > fmt, T &&... args) -> std::string
Definition format.h:4146
@ CS_OK
Definition cscore_c.h:62
uint64_t GrabSinkFrameTimeoutLastTime(CS_Sink sink, WPI_RawFrame &image, double timeout, uint64_t lastFrameTime, CS_Status *status)
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 ShuffleboardContainer.h:25
Raw Frame.
Definition RawFrame.h:32
int pixelFormat
Definition RawFrame.h:40
uint64_t timestamp
Definition RawFrame.h:44
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
int timestampSrc
Definition RawFrame.h:45
Video mode.
Definition cscore_cpp.h:62
PixelFormat
Definition cscore_cpp.h:63
@ kYUYV
Definition cscore_cpp.h:66
@ kGray
Definition cscore_cpp.h:69
@ kBGR
Definition cscore_cpp.h:68
@ kBGRA
Definition cscore_cpp.h:72
@ kMJPEG
Definition cscore_cpp.h:65
@ kY16
Definition cscore_cpp.h:70
@ kUYVY
Definition cscore_cpp.h:71
@ kRGB565
Definition cscore_cpp.h:67
Definition RawFrame.h:92