JeVois  1.21
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
RawImageOps.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4// California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5//
6// This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7// redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8// Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10// License for more details. You should have received a copy of the GNU General Public License along with this program;
11// if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12//
13// Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14// Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16/*! \file */
17
20#include <jevois/Util/Utils.H>
21#include <jevois/Util/Async.H>
22#include <jevois/Debug/Log.H>
23#include <jevois/Image/Jpeg.H>
24#include <future>
25
26#include <linux/videodev2.h>
27#include <cmath>
28#include <opencv2/imgproc/imgproc.hpp>
29
30// ####################################################################################################
32{
33 unsigned int bpp = jevois::v4l2BytesPerPix(src.fmt);
34
35 switch (bpp)
36 {
37 case 3: return cv::Mat(src.height, src.width, CV_8UC3, src.buf->data());
38 case 2: return cv::Mat(src.height, src.width, CV_8UC2, src.buf->data());
39 case 1: return cv::Mat(src.height, src.width, CV_8UC1, src.buf->data());
40 default: LFATAL("Unsupported RawImage format");
41 }
42}
43
44// ####################################################################################################
45namespace
46{
47 inline void rgb565pixrgb(unsigned short rgb565, unsigned char & r, unsigned char & g, unsigned char & b)
48 {
49 r = ((((rgb565 >> 11) & 0x1F) * 527) + 23) >> 6;
50 g = ((((rgb565 >> 5) & 0x3F) * 259) + 33) >> 6;
51 b = (((rgb565 & 0x1F) * 527) + 23) >> 6;
52 }
53
54 class rgb565ToGray : public cv::ParallelLoopBody
55 {
56 public:
57 rgb565ToGray(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
58 inImg(inputImage), outImg(outImage)
59 {
60 inlinesize = inputImage.cols * 2; // 2 bytes/pix for RGB565
61 outlinesize = outw * 1; // 1 byte/pix for Gray
62 }
63
64 virtual void operator()(const cv::Range & range) const
65 {
66 for (int j = range.start; j < range.end; ++j)
67 {
68 int const inoff = j * inlinesize;
69 int const outoff = j * outlinesize;
70
71 for (int i = 0; i < inImg.cols; ++i)
72 {
73 int const in = inoff + i * 2;
74 int const out = outoff + i;
75 unsigned short const rgb565 = ((unsigned short)(inImg.data[in + 0]) << 8) | inImg.data[in + 1];
76 unsigned char r, g, b;
77 rgb565pixrgb(rgb565, r, g, b);
78 int lum = int(r + g + b) / 3;
79 outImg[out] = lum;
80 }
81 }
82 }
83
84 private:
85 cv::Mat const & inImg;
86 unsigned char * outImg;
87 int inlinesize, outlinesize;
88 };
89
90 class rgb565ToBGR : public cv::ParallelLoopBody
91 {
92 public:
93 rgb565ToBGR(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
94 inImg(inputImage), outImg(outImage)
95 {
96 inlinesize = inputImage.cols * 2; // 2 bytes/pix for RGB565
97 outlinesize = outw * 3; // 3 bytes/pix for BGR
98 }
99
100 virtual void operator()(const cv::Range & range) const
101 {
102 for (int j = range.start; j < range.end; ++j)
103 {
104 int const inoff = j * inlinesize;
105 int const outoff = j * outlinesize;
106
107 for (int i = 0; i < inImg.cols; ++i)
108 {
109 int const in = inoff + i * 2;
110 int const out = outoff + i * 3;
111 unsigned short const rgb565 = ((unsigned short)(inImg.data[in + 0]) << 8) | inImg.data[in + 1];
112
113 unsigned char r, g, b; rgb565pixrgb(rgb565, r, g, b);
114 outImg[out + 0] = b;
115 outImg[out + 1] = g;
116 outImg[out + 2] = r;
117 }
118 }
119 }
120
121 private:
122 cv::Mat const & inImg;
123 unsigned char * outImg;
124 int inlinesize, outlinesize;
125 };
126
127 class rgb565ToRGB : public cv::ParallelLoopBody
128 {
129 public:
130 rgb565ToRGB(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
131 inImg(inputImage), outImg(outImage)
132 {
133 inlinesize = inputImage.cols * 2; // 2 bytes/pix for RGB565
134 outlinesize = outw * 3; // 3 bytes/pix for RGB
135 }
136
137 virtual void operator()(const cv::Range & range) const
138 {
139 for (int j = range.start; j < range.end; ++j)
140 {
141 int const inoff = j * inlinesize;
142 int const outoff = j * outlinesize;
143
144 for (int i = 0; i < inImg.cols; ++i)
145 {
146 int const in = inoff + i * 2;
147 int const out = outoff + i * 3;
148 unsigned short const rgb565 = ((unsigned short)(inImg.data[in + 0]) << 8) | inImg.data[in + 1];
149
150 unsigned char r, g, b; rgb565pixrgb(rgb565, r, g, b);
151 outImg[out + 0] = r;
152 outImg[out + 1] = g;
153 outImg[out + 2] = b;
154 }
155 }
156 }
157
158 private:
159 cv::Mat const & inImg;
160 unsigned char * outImg;
161 int inlinesize, outlinesize;
162 };
163
164 class rgb565ToRGBA : public cv::ParallelLoopBody
165 {
166 public:
167 rgb565ToRGBA(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
168 inImg(inputImage), outImg(outImage)
169 {
170 inlinesize = inputImage.cols * 2; // 2 bytes/pix for RGB565
171 outlinesize = outw * 4; // 4 bytes/pix for RGBA
172 }
173
174 virtual void operator()(const cv::Range & range) const
175 {
176 for (int j = range.start; j < range.end; ++j)
177 {
178 int const inoff = j * inlinesize;
179 int const outoff = j * outlinesize;
180
181 for (int i = 0; i < inImg.cols; ++i)
182 {
183 int const in = inoff + i * 2;
184 int const out = outoff + i * 4;
185 unsigned short const rgb565 = ((unsigned short)(inImg.data[in + 0]) << 8) | inImg.data[in + 1];
186
187 unsigned char r, g, b; rgb565pixrgb(rgb565, r, g, b);
188 outImg[out + 0] = r;
189 outImg[out + 1] = g;
190 outImg[out + 2] = b;
191 outImg[out + 3] = (unsigned char)(255);
192 }
193 }
194 }
195
196 private:
197 cv::Mat const & inImg;
198 unsigned char * outImg;
199 int inlinesize, outlinesize;
200 };
201} // anonymous namespace
202
203#ifdef JEVOIS_PLATFORM
204// NEON accelerated YUYV to Gray:
205namespace
206{
207 class yuyvToGrayNEON : public cv::ParallelLoopBody
208 {
209 public:
210 yuyvToGrayNEON(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
211 inImg(inputImage), outImg(outImage)
212 {
213 inlinesize = inputImage.cols * 2; // 2 bytes/pix for YUYV
214 outlinesize = outw * 1; // 1 byte/pix for Gray
215 initer = (inputImage.cols >> 4); // we process 16 pixels (32 input bytes) at a time
216 }
217
218 virtual void operator()(const cv::Range & range) const
219 {
220 unsigned char const * inptr = inImg.data + range.start * inlinesize;
221 unsigned char * outptr = outImg + range.start * outlinesize;
222
223 for (int j = range.start; j < range.end; ++j)
224 {
225 unsigned char const * ip = inptr; unsigned char * op = outptr;
226
227 for (int i = 0; i < initer; ++i)
228 {
229 uint8x16x2_t const pixels = vld2q_u8(ip); // load 16 YUYV pixels
230 vst1q_u8(op, pixels.val[0]); // store the 16 Y values
231 ip += 32; op += 16;
232 }
233 inptr += inlinesize; outptr += outlinesize;
234 }
235 }
236
237 private:
238 cv::Mat const & inImg;
239 unsigned char * outImg;
240 int inlinesize, outlinesize, initer;
241 };
242} // anonymous namespace
243#endif
244
245// ####################################################################################################
247{
248 cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
249 cv::Mat result;
250
251 switch (src.fmt)
252 {
253 case V4L2_PIX_FMT_GREY: return rawimgcv;
254
255 case V4L2_PIX_FMT_YUYV:
256#if 0
257 //#ifdef JEVOIS_PLATFORM
258 result = cv::Mat(cv::Size(src.width, src.height), CV_8UC1);
259 cv::parallel_for_(cv::Range(0, src.height), yuyvToGrayNEON(rawimgcv, result.data, result.cols));
260#else
261 cv::cvtColor(rawimgcv, result, cv::COLOR_YUV2GRAY_YUYV);
262#endif
263 return result;
264
265 case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, cv::COLOR_BayerBG2GRAY); return result;
266
267 case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
268 result = cv::Mat(cv::Size(src.width, src.height), CV_8UC1);
269 cv::parallel_for_(cv::Range(0, src.height), rgb565ToGray(rawimgcv, result.data, result.cols));
270 return result;
271
272 case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
273
274 case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, cv::COLOR_BGR2GRAY); return result;
275
276 case V4L2_PIX_FMT_RGB24: cv::cvtColor(rawimgcv, result, cv::COLOR_RGB2GRAY); return result;
277 }
278 LFATAL("Unknown RawImage pixel format");
279}
280
281// ####################################################################################################
283{
284 cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
285 cv::Mat result;
286
287 switch (src.fmt)
288 {
289 case V4L2_PIX_FMT_BGR24: return rawimgcv;
290
291 case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, cv::COLOR_YUV2BGR_YUYV); return result;
292 case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, cv::COLOR_GRAY2BGR); return result;
293 case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, cv::COLOR_BayerBG2BGR); return result;
294
295 case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
296 result = cv::Mat(cv::Size(src.width, src.height), CV_8UC3);
297 cv::parallel_for_(cv::Range(0, src.height), rgb565ToBGR(rawimgcv, result.data, result.cols));
298 return result;
299
300 case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
301 case V4L2_PIX_FMT_RGB24: cv::cvtColor(rawimgcv, result, cv::COLOR_RGB2BGR); return result;
302 }
303 LFATAL("Unknown RawImage pixel format");
304}
305
306// ####################################################################################################
308{
309 cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
310 cv::Mat result;
311
312 switch (src.fmt)
313 {
314 case V4L2_PIX_FMT_RGB24: return rawimgcv;
315
316 case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, cv::COLOR_YUV2RGB_YUYV); return result;
317 case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, cv::COLOR_GRAY2RGB); return result;
318 case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, cv::COLOR_BayerBG2RGB); return result;
319
320 case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
321 result = cv::Mat(cv::Size(src.width, src.height), CV_8UC3);
322 cv::parallel_for_(cv::Range(0, src.height), rgb565ToRGB(rawimgcv, result.data, result.cols));
323 return result;
324
325 case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
326 case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, cv::COLOR_BGR2RGB); return result;
327 }
328 LFATAL("Unknown RawImage pixel format");
329}
330
331// ####################################################################################################
333{
334 cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
335 cv::Mat result;
336
337 switch (src.fmt)
338 {
339 case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, cv::COLOR_YUV2RGBA_YUYV); return result;
340
341 case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, cv::COLOR_GRAY2RGBA); return result;
342
343 case V4L2_PIX_FMT_SRGGB8:
344 {
345 // FIXME: we do two conversions, should get a hold of the opencv source for bayer conversions and make an RGBA
346 // version of it:
347 cv::Mat fixme;
348 cv::cvtColor(rawimgcv, fixme, cv::COLOR_BayerBG2RGB);
349 cv::cvtColor(fixme, result, cv::COLOR_RGB2RGBA);
350 return result;
351 }
352
353 case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
354 result = cv::Mat(cv::Size(src.width, src.height), CV_8UC4);
355 cv::parallel_for_(cv::Range(0, src.height), rgb565ToRGBA(rawimgcv, result.data, result.cols));
356 return result;
357
358 case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
359
360 case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, cv::COLOR_BGR2RGBA); return result;
361
362 case V4L2_PIX_FMT_RGB24: cv::cvtColor(rawimgcv, result, cv::COLOR_RGB2RGBA); return result;
363 }
364 LFATAL("Unknown RawImage pixel format");
365}
366
367// ####################################################################################################
369{
370 if (img.bytesperpix() != 2) LFATAL("Can only byteswap images with 2 bytes/pixel");
371
372#ifdef JEVOIS_PLATFORM
373 // Use neon acceleration, in parallel threads:
374 unsigned int ncores = 4; //std::min(4U, std::thread::hardware_concurrency());
375 size_t const nbc = img.bytesize() / ncores;
376 unsigned char * ptr = img.pixelsw<unsigned char>();
377
378 // FIXME check for possible size rounding problems
379
380 // Launch ncores-1 threads and we will do the last chunk in the current thread:
381 std::vector<std::future<void> > fut;
382 for (unsigned int core = 0; core < ncores-1; ++core)
383 fut.push_back(jevois::async([&ptr, &nbc](int core) -> void {
384 unsigned char * cptr = ptr + core * nbc;
385 for (size_t i = 0; i < nbc; i += 16) vst1q_u8(cptr + i, vrev16q_u8(vld1q_u8(cptr + i)));
386 }, core));
387
388 // Last chunk:
389 size_t const sz = img.bytesize();
390 for (size_t i = (ncores-1) * nbc; i < sz; i += 16) vst1q_u8(ptr + i, vrev16q_u8(vld1q_u8(ptr + i)));
391
392 // Wait for all the threads to complete:
393 for (auto & f : fut) f.get();
394#else
395 // Use CPU:
396 size_t const sz = img.width * img.height; // size in shorts
397 unsigned short * ptr = img.pixelsw<unsigned short>();
398 for (size_t i = 0; i < sz; ++i) ptr[i] = __builtin_bswap16(ptr[i]);
399#endif
400}
401
402// ####################################################################################################
403void jevois::rawimage::paste(jevois::RawImage const & src, jevois::RawImage & dest, int x, int y)
404{
405 if (src.fmt != dest.fmt) LFATAL("src and dest must have the same pixel format");
406 if (x < 0 || y < 0 || x + src.width > dest.width || y + src.height > dest.height)
407 LFATAL("src does not fit within dest");
408
409 unsigned int const bpp = src.bytesperpix();
410
411 unsigned char const * sptr = src.pixels<unsigned char>();
412 unsigned char * dptr = dest.pixelsw<unsigned char>() + (x + y * dest.width) * bpp;
413 size_t const srclinelen = src.width * bpp;
414 size_t const dstlinelen = dest.width * bpp;
415
416 for (unsigned int j = 0; j < src.height; ++j)
417 {
418 memcpy(dptr, sptr, srclinelen);
419 sptr += srclinelen;
420 dptr += dstlinelen;
421 }
422}
423
424// ####################################################################################################
425void jevois::rawimage::roipaste(jevois::RawImage const & src, int x, int y, unsigned int w, unsigned int h,
426 jevois::RawImage & dest, int dx, int dy)
427{
428 if (src.fmt != dest.fmt) LFATAL("src and dest must have the same pixel format");
429 if (x < 0 || y < 0 || x + w > src.width || y + h > src.height) LFATAL("roi not within source image");
430 if (dx < 0 || dy < 0 || dx + w > dest.width || dy + h > dest.height) LFATAL("roi not within dest image");
431
432 unsigned int const bpp = src.bytesperpix();
433
434 unsigned char const * sptr = src.pixels<unsigned char>() + (x + y * src.width) * bpp;
435 unsigned char * dptr = dest.pixelsw<unsigned char>() + (dx + dy * dest.width) * bpp;
436 size_t const srclinelen = src.width * bpp;
437 size_t const dstlinelen = dest.width * bpp;
438
439 for (unsigned int j = 0; j < h; ++j)
440 {
441 memcpy(dptr, sptr, w * bpp);
442 sptr += srclinelen;
443 dptr += dstlinelen;
444 }
445}
446
447// ####################################################################################################
448void jevois::rawimage::pasteGreyToYUYV(cv::Mat const & src, jevois::RawImage & dest, int x, int y)
449{
450 if (x + src.cols > int(dest.width) || y + src.rows > int(dest.height)) LFATAL("src does not fit within dest");
451 unsigned int const bpp = dest.bytesperpix();
452
453 unsigned char const * sptr = src.data;
454 unsigned char * dptr = dest.pixelsw<unsigned char>() + (x + y * dest.width) * bpp;
455 size_t const dststride = (dest.width - src.cols) * bpp;
456
457 for (int j = 0; j < src.rows; ++j)
458 {
459 for (int i = 0; i < src.cols; ++i) { *dptr++ = *sptr++; *dptr++ = 0x80; }
460 dptr += dststride;
461 }
462}
463
464// ####################################################################################################
465void jevois::rawimage::drawDisk(jevois::RawImage & img, int cx, int cy, unsigned int rad, unsigned int col)
466{
467 // From the iLab Neuromorphic Vision C++ Toolkit
468 unsigned short * const dptr = img.pixelsw<unsigned short>();
469 int const w = int(img.width);
470
471 if (rad == 0) { if (img.coordsOk(cx, cy)) dptr[cx + w * cy] = col; return; }
472
473 int const intrad = rad;
474 for (int y = -intrad; y <= intrad; ++y)
475 {
476 int bound = int(std::sqrt(float(intrad * intrad - y * y)));
477 for (int x = -bound; x <= bound; ++x)
478 if (img.coordsOk(x + cx, y + cy)) dptr[x + cx + w * (y + cy)] = col;
479 }
480}
481
482// ####################################################################################################
483void jevois::rawimage::drawCircle(jevois::RawImage & img, int cx, int cy, unsigned int rad,
484 unsigned int thick, unsigned int col)
485{
486 // From the iLab Neuromorphic Vision C++ Toolkit
487 if (rad == 0) { jevois::rawimage::drawDisk(img, cx, cy, thick, col); return; }
488
489 jevois::rawimage::drawDisk(img, cx - rad, cy, thick, col);
490 jevois::rawimage::drawDisk(img, cx + rad, cy, thick, col);
491 int bound1 = rad, bound2;
492
493 for (unsigned int dy = 1; dy <= rad; ++dy)
494 {
495 bound2 = bound1;
496 bound1 = int(0.4999F + sqrtf(rad*rad - dy*dy));
497 for (int dx = bound1; dx <= bound2; ++dx)
498 {
499 jevois::rawimage::drawDisk(img, cx - dx, cy - dy, thick, col);
500 jevois::rawimage::drawDisk(img, cx + dx, cy - dy, thick, col);
501 jevois::rawimage::drawDisk(img, cx + dx, cy + dy, thick, col);
502 jevois::rawimage::drawDisk(img, cx - dx, cy + dy, thick, col);
503 }
504 }
505}
506
507// ####################################################################################################
508namespace
509{
510 // Liang-Barsky algo from http://hinjang.com/articles/04.html#eight
511 inline bool isZero(double a)
512 { return (a < 0.0001 && a > -0.0001 ); }
513
514 bool clipT(double num, double denom, double & tE, double & tL)
515 {
516 if (isZero(denom)) return (num <= 0.0);
517
518 double t = num / denom;
519
520 if (denom > 0.0) {
521 if (t > tL) return false;
522 if (t > tE) tE = t;
523 } else {
524 if (t < tE) return false;
525 if (t < tL) tL = t;
526 }
527 return true;
528 }
529}
530
531// ####################################################################################################
532// Liang-Barsky algo from http://hinjang.com/articles/04.html#eight
533bool jevois::rawimage::clipLine(int wxmin, int wymin, int wxmax, int wymax, int & x1, int & y1, int & x2, int & y2)
534{
535 // This algo does not handle lines completely outside the window? quick test here that should work for most lines (but
536 // not all, may need to fix later):
537 if (x1 < wxmin && x2 < wxmin) return false;
538 if (x1 >= wxmax && x2 >= wxmax) return false;
539 if (y1 < wymin && y2 < wymin) return false;
540 if (y1 >= wymax && y2 >= wymax) return false;
541
542 int const toofar = 5000;
543 if (x1 < -toofar || x1 > toofar || y1 < -toofar || y1 > toofar) return false;
544 if (x2 < -toofar || x2 > toofar || y2 < -toofar || y2 > toofar) return false;
545
546 --wxmax; --wymax; // exclude right and bottom edges of the window
547
548 double dx = x2 - x1, dy = y2 - y1;
549 if (isZero(dx) && isZero(dy)) return true;
550
551 double tE = 0.0, tL = 1.0;
552
553 if (clipT(wxmin - x1, dx, tE, tL) && clipT(x1 - wxmax, -dx, tE, tL) &&
554 clipT(wymin - y1, dy, tE, tL) && clipT(y1 - wymax, -dy, tE, tL))
555 {
556 if (tL < 1) { x2 = x1 + tL * dx; y2 = y1 + tL * dy; }
557 if (tE > 0) { x1 += tE * dx; y1 += tE * dy; }
558 }
559
560 return true;
561}
562
563// ####################################################################################################
564void jevois::rawimage::drawLine(jevois::RawImage & img, int x1, int y1, int x2, int y2, unsigned int thick,
565 unsigned int col)
566{
567 // If thickness is very large, refuse to draw, as it will hang for a very long time and be confusing:
568 if (thick > 500) LFATAL("Thickness " << thick << " too large. Did you mistakenly swap thick and col?");
569
570 // First clip the line so we don't waste time trying to sometimes draw very long lines that may result from singular
571 // 3D projections:
572 if (jevois::rawimage::clipLine(0, 0, img.width, img.height, x1, y1, x2, y2) == false) return; // line fully outside
573
574 // From the iLab Neuromorphic Vision C++ Toolkit
575 // from Graphics Gems / Paul Heckbert
576 int const dx = x2 - x1; int const ax = std::abs(dx) << 1; int const sx = dx < 0 ? -1 : 1;
577 int const dy = y2 - y1; int const ay = std::abs(dy) << 1; int const sy = dy < 0 ? -1 : 1;
578 int const w = img.width; int const h = img.height;
579 int x = x1, y = y1;
580
581 if (ax > ay)
582 {
583 int d = ay - (ax >> 1);
584 for (;;)
585 {
586 if (x >= 0 && x < w && y >= 0 && y < h) jevois::rawimage::drawDisk(img, x, y, thick, col);
587
588 if (x == x2) return;
589 if (d >= 0) { y += sy; d -= ax; }
590 x += sx; d += ay;
591 }
592 }
593 else
594 {
595 int d = ax - (ay >> 1);
596 for (;;)
597 {
598 if (x >= 0 && x < w && y >= 0 && y < h) jevois::rawimage::drawDisk(img, x, y, thick, col);
599 if (y == y2) return;
600 if (d >= 0) { x += sx; d -= ay; }
601 y += sy; d += ax;
602 }
603 }
604}
605
606// ####################################################################################################
607void jevois::rawimage::drawRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
608 unsigned int thick, unsigned int col)
609{
610 if (thick == 0)
611 jevois::rawimage::drawRect(img, x, y, w, h, col);
612 else
613 {
614 // Draw so that the lines are drawn on top of the bottom-right corner at (x+w-1, y+h-1):
615 if (w) --w;
616 if (h) --h;
617 jevois::rawimage::drawLine(img, x, y, x+w, y, thick, col);
618 jevois::rawimage::drawLine(img, x, y+h, x+w, y+h, thick, col);
619 jevois::rawimage::drawLine(img, x, y, x, y+h, thick, col);
620 jevois::rawimage::drawLine(img, x+w, y, x+w, y+h, thick, col);
621 }
622}
623// ####################################################################################################
624void jevois::rawimage::drawRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
625 unsigned int col)
626{
627 if (w == 0) w = 1;
628 if (h == 0) h = 1;
629 if (x >= int(img.width)) x = img.width - 1;
630 if (y >= int(img.height)) y = img.height - 1;
631 if (x + w > img.width) w = img.width - x;
632 if (y + h > img.height) h = img.height - y;
633
634 unsigned int const imgw = img.width;
635 unsigned short * b = img.pixelsw<unsigned short>() + x + y * imgw;
636
637 // Two horizontal lines:
638 unsigned int const offy = (h-1) * imgw;
639 for (unsigned int xx = 0; xx < w; ++xx) { b[xx] = col; b[xx + offy] = col; }
640
641 // Two vertical lines:
642 unsigned int const offx = w-1;
643 for (unsigned int yy = 0; yy < h * imgw; yy += imgw) { b[yy] = col; b[yy + offx] = col; }
644}
645// ####################################################################################################
646void jevois::rawimage::drawFilledRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
647 unsigned int col)
648{
649 if (w == 0) w = 1;
650 if (h == 0) h = 1;
651 if (x >= int(img.width)) x = img.width - 1;
652 if (y >= int(img.height)) y = img.height - 1;
653 if (x + w > img.width) w = img.width - x;
654 if (y + h > img.height) h = img.height - y;
655
656 unsigned int const stride = img.width - w;
657 unsigned short * b = img.pixelsw<unsigned short>() + x + y * img.width;
658
659 for (unsigned int yy = 0; yy < h; ++yy)
660 {
661 for (unsigned int xx = 0; xx < w; ++xx) *b++ = col;
662 b += stride;
663 }
664}
665
666// ####################################################################################################
667// Font pattern definitions:
668namespace jevois
669{
670 //! Fonts available for drawing text strings into RawImage
671 namespace font
672 {
673 extern const unsigned char font10x20[95][200];
674 extern const unsigned char font11x22[95][242];
675 extern const unsigned char font12x22[95][264];
676 extern const unsigned char font14x26[95][364];
677 extern const unsigned char font15x28[95][420];
678 extern const unsigned char font16x29[95][464];
679 extern const unsigned char font20x38[95][760];
680 extern const unsigned char font5x7[95][35];
681 extern const unsigned char font6x10[95][60];
682 extern const unsigned char font7x13[95][91];
683 extern const unsigned char font8x13bold[95][104];
684 extern const unsigned char font9x15bold[95][135];
685 } // namespace font
686} // namespace jevois
687
688
689// ####################################################################################################
690void jevois::rawimage::writeText(jevois::RawImage & img, std::string const & txt, int x, int y, unsigned int col,
692{
693 jevois::rawimage::writeText(img, txt.c_str(), x, y, col, font);
694}
695
696// ####################################################################################################
697void jevois::rawimage::writeText(jevois::RawImage & img, char const * txt, int x, int y, unsigned int col,
699{
700 int len = int(strlen(txt));
701 unsigned int const imgw = img.width;
702
703 int fontw, fonth; unsigned char const * fontptr;
704 switch (font)
705 {
706 case Font5x7: fontw = 5; fonth = 7; fontptr = &jevois::font::font5x7[0][0]; break;
707 case Font6x10: fontw = 6; fonth = 10; fontptr = &jevois::font::font6x10[0][0]; break;
708 case Font7x13: fontw = 7; fonth = 13; fontptr = &jevois::font::font7x13[0][0]; break;
709 case Font8x13bold: fontw = 8; fonth = 13; fontptr = &jevois::font::font8x13bold[0][0]; break;
710 case Font9x15bold: fontw = 9; fonth = 15; fontptr = &jevois::font::font9x15bold[0][0]; break;
711 case Font10x20: fontw = 10; fonth = 20; fontptr = &jevois::font::font10x20[0][0]; break;
712 case Font11x22: fontw = 11; fonth = 22; fontptr = &jevois::font::font11x22[0][0]; break;
713 case Font12x22: fontw = 12; fonth = 22; fontptr = &jevois::font::font12x22[0][0]; break;
714 case Font14x26: fontw = 14; fonth = 26; fontptr = &jevois::font::font14x26[0][0]; break;
715 case Font15x28: fontw = 15; fonth = 28; fontptr = &jevois::font::font15x28[0][0]; break;
716 case Font16x29: fontw = 16; fonth = 29; fontptr = &jevois::font::font16x29[0][0]; break;
717 case Font20x38: fontw = 20; fonth = 38; fontptr = &jevois::font::font20x38[0][0]; break;
718 default: LFATAL("Invalid font");
719 }
720
721 // Clip the text so that it does not go outside the image:
722 if (y < 0 || y + fonth > int(img.height)) return;
723 while (x + len * fontw > int(imgw)) { --len; if (len <= 0) return; }
724
725 // Be nice and handle various pixel formats:
726 switch (img.bytesperpix())
727 {
728 case 2:
729 {
730 unsigned short * b = img.pixelsw<unsigned short>() + x + y * imgw;
731
732 for (int i = 0; i < len; ++i)
733 {
734 int idx = txt[i] - 32; if (idx >= 95) idx = 0;
735 unsigned char const * ptr = fontptr + fontw * fonth * idx;
736 unsigned short * bb = b;
737 for (int yy = 0; yy < fonth; ++yy)
738 {
739 // Draw one line of this letter, note the transparent background:
740 for (int xx = 0; xx < fontw; ++xx) if (*ptr++) ++bb; else *bb++ = col;
741 bb += imgw - fontw;
742 }
743 b += fontw;
744 }
745 }
746 break;
747
748 case 1:
749 {
750 unsigned char * b = img.pixelsw<unsigned char>() + x + y * imgw;
751
752 for (int i = 0; i < len; ++i)
753 {
754 int idx = txt[i] - 32; if (idx >= 95) idx = 0;
755 unsigned char const * ptr = fontptr + fontw * fonth * idx;
756 unsigned char * bb = b;
757 for (int yy = 0; yy < fonth; ++yy)
758 {
759 // Draw one line of this letter, note the transparent background:
760 for (int xx = 0; xx < fontw; ++xx) if (*ptr++) ++bb; else *bb++ = col;
761 bb += imgw - fontw;
762 }
763 b += fontw;
764 }
765 }
766 break;
767
768 default:
769 LFATAL("Sorry, only 1 and 2 bytes/pixel images are supported for now");
770 }
771}
772
773// ####################################################################################################
774int jevois::rawimage::itext(RawImage & img, std::string const & txt, int y, unsigned int col, Font font)
775{
776 return jevois::rawimage::itext(img, txt.c_str(), y, col, font);
777}
778
779// ####################################################################################################
780int jevois::rawimage::itext(RawImage & img, char const * txt, int y, unsigned int col, Font font)
781{
782 if (y < 3) y = 3;
783
784 jevois::rawimage::writeText(img, txt, 3, y, col, font);
785
786 // Keep this in exact sync with writeText():
787 int fonth;
788 switch (font)
789 {
790 case Font5x7: fonth = 7; break;
791 case Font6x10: fonth = 10; break;
792 case Font7x13: fonth = 13; break;
793 case Font8x13bold: fonth = 13; break;
794 case Font9x15bold: fonth = 15; break;
795 case Font10x20: fonth = 20; break;
796 case Font11x22: fonth = 22; break;
797 case Font12x22: fonth = 22; break;
798 case Font14x26: fonth = 26; break;
799 case Font15x28: fonth = 28; break;
800 case Font16x29: fonth = 29; break;
801 case Font20x38: fonth = 38; break;
802 default: LFATAL("Invalid font");
803 }
804
805 return y + fonth + 2;
806}
807
808// ####################################################################################################
809namespace
810{
811 class bgrToBayer : public cv::ParallelLoopBody
812 {
813 public:
814 bgrToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
815 inImg(inputImage), outImg(outImage)
816 {
817 inlinesize = inputImage.cols * 3; // 3 bytes/pix for BGR
818 outlinesize = outw * 1; // 1 byte/pix for Bayer
819 }
820
821 virtual void operator()(const cv::Range & range) const
822 {
823 for (int j = range.start; j < range.end; ++j)
824 {
825 int const inoff = j * inlinesize;
826 int const outoff = j * outlinesize;
827
828 for (int i = 0; i < inImg.cols; i += 2)
829 {
830 int const in = inoff + i * 3;
831 int const out = outoff + i;
832
833 if ( (j & 1) == 0) { outImg[out + 0] = inImg.data[in + 2]; outImg[out + 1] = inImg.data[in + 4]; }
834 else { outImg[out + 0] = inImg.data[in + 1]; outImg[out + 1] = inImg.data[in + 3]; }
835 }
836 }
837 }
838
839 private:
840 cv::Mat const & inImg;
841 unsigned char * outImg;
842 int inlinesize, outlinesize;
843 };
844
845 // ####################################################################################################
846 void convertCvBGRtoBayer(cv::Mat const & src, jevois::RawImage & dst)
847 {
848 if (src.type() != CV_8UC3)
849 LFATAL("src must have type CV_8UC3 and BGR pixels; your image has " << jevois::cvtypestr(src.type()));
850 if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
851 if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
852
853 cv::parallel_for_(cv::Range(0, src.rows), bgrToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
854 }
855} // anonymous namespace
856
857// ####################################################################################################
858namespace
859{
860 class rgbToBayer : public cv::ParallelLoopBody
861 {
862 public:
863 rgbToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
864 inImg(inputImage), outImg(outImage)
865 {
866 inlinesize = inputImage.cols * 3; // 3 bytes/pix for RGB
867 outlinesize = outw * 1; // 1 byte/pix for Bayer
868 }
869
870 virtual void operator()(const cv::Range & range) const
871 {
872 for (int j = range.start; j < range.end; ++j)
873 {
874 int const inoff = j * inlinesize;
875 int const outoff = j * outlinesize;
876
877 for (int i = 0; i < inImg.cols; i += 2)
878 {
879 int const in = inoff + i * 3;
880 int const out = outoff + i;
881
882 if ( (j & 1) == 0) { outImg[out + 0] = inImg.data[in + 0]; outImg[out + 1] = inImg.data[in + 4]; }
883 else { outImg[out + 0] = inImg.data[in + 1]; outImg[out + 1] = inImg.data[in + 5]; }
884 }
885 }
886 }
887
888 private:
889 cv::Mat const & inImg;
890 unsigned char * outImg;
891 int inlinesize, outlinesize;
892 };
893
894 // ####################################################################################################
895 void convertCvRGBtoBayer(cv::Mat const & src, jevois::RawImage & dst)
896 {
897 if (src.type() != CV_8UC3)
898 LFATAL("src must have type CV_8UC3 and RGB pixels; your image has " << jevois::cvtypestr(src.type()));
899 if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
900 if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
901
902 cv::parallel_for_(cv::Range(0, src.rows), rgbToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
903 }
904} // anonymous namespace
905
906// ####################################################################################################
907namespace
908{
909 class grayToBayer : public cv::ParallelLoopBody
910 {
911 public:
912 grayToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
913 inImg(inputImage), outImg(outImage)
914 {
915 inlinesize = inputImage.cols * 1; // 1 bytes/pix for GRAY
916 outlinesize = outw * 1; // 1 byte/pix for Bayer
917 }
918
919 virtual void operator()(const cv::Range & range) const
920 {
921 for (int j = range.start; j < range.end; ++j)
922 {
923 int const inoff = j * inlinesize;
924 int const outoff = j * outlinesize;
925
926 memcpy(&outImg[outoff], &inImg.data[inoff], inlinesize);
927 }
928 }
929
930 private:
931 cv::Mat const & inImg;
932 unsigned char * outImg;
933 int inlinesize, outlinesize;
934 };
935
936 // ####################################################################################################
937 void convertCvGRAYtoBayer(cv::Mat const & src, jevois::RawImage & dst)
938 {
939 if (src.type() != CV_8UC1)
940 LFATAL("src must have type CV_8UC1 and GRAY pixels; your image has " << jevois::cvtypestr(src.type()));
941 if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
942 if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
943
944 cv::parallel_for_(cv::Range(0, src.rows), grayToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
945 }
946} // anonymous namespace
947
948// ####################################################################################################
949namespace
950{
951 class rgbaToBayer : public cv::ParallelLoopBody
952 {
953 public:
954 rgbaToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
955 inImg(inputImage), outImg(outImage)
956 {
957 inlinesize = inputImage.cols * 4; // 4 bytes/pix for RGBA
958 outlinesize = outw * 1; // 1 byte/pix for Bayer
959 }
960
961 virtual void operator()(const cv::Range & range) const
962 {
963 for (int j = range.start; j < range.end; ++j)
964 {
965 int const inoff = j * inlinesize;
966 int const outoff = j * outlinesize;
967
968 for (int i = 0; i < inImg.cols; i += 2)
969 {
970 int const in = inoff + i * 4;
971 int const out = outoff + i;
972
973 if ( (j & 1) == 0) { outImg[out + 0] = inImg.data[in + 0]; outImg[out + 1] = inImg.data[in + 5]; }
974 else { outImg[out + 0] = inImg.data[in + 1]; outImg[out + 1] = inImg.data[in + 6]; }
975 }
976 }
977 }
978
979 private:
980 cv::Mat const & inImg;
981 unsigned char * outImg;
982 int inlinesize, outlinesize;
983 };
984
985 // ####################################################################################################
986 void convertCvRGBAtoBayer(cv::Mat const & src, jevois::RawImage & dst)
987 {
988 if (src.type() != CV_8UC4)
989 LFATAL("src must have type CV_8UC4 and RGBA pixels; your image has " << jevois::cvtypestr(src.type()));
990 if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
991 if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
992
993 cv::parallel_for_(cv::Range(0, src.rows), rgbaToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
994 }
995} // anonymous namespace
996
997// ####################################################################################################
998namespace
999{
1000 class bgrToYUYV : public cv::ParallelLoopBody
1001 {
1002 public:
1003 bgrToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
1004 inImg(inputImage), outImg(outImage)
1005 {
1006 inlinesize = inputImage.cols * 3; // 3 bytes/pix for BGR
1007 outlinesize = outw * 2; // 2 bytes/pix for YUYV
1008 }
1009
1010 virtual void operator()(const cv::Range & range) const
1011 {
1012 for (int j = range.start; j < range.end; ++j)
1013 {
1014 int const inoff = j * inlinesize;
1015 int const outoff = j * outlinesize;
1016
1017 for (int i = 0; i < inImg.cols; i += 2)
1018 {
1019 int mc = inoff + i * 3;
1020 unsigned char const B1 = inImg.data[mc + 0];
1021 unsigned char const G1 = inImg.data[mc + 1];
1022 unsigned char const R1 = inImg.data[mc + 2];
1023 unsigned char const B2 = inImg.data[mc + 3];
1024 unsigned char const G2 = inImg.data[mc + 4];
1025 unsigned char const R2 = inImg.data[mc + 5];
1026
1027 float const Y1 = (0.257F * R1) + (0.504F * G1) + (0.098F * B1) + 16.0F;
1028 //float const V1 = (0.439F * R1) - (0.368F * G1) - (0.071F * B1) + 128.0F;
1029 float const U1 = -(0.148F * R1) - (0.291F * G1) + (0.439F * B1) + 128.0F;
1030 float const Y2 = (0.257F * R2) + (0.504F * G2) + (0.098F * B2) + 16.0F;
1031 float const V2 = (0.439F * R2) - (0.368F * G2) - (0.071F * B2) + 128.0F;
1032 //float const U2 = -(0.148F * R2) - (0.291F * G2) + (0.439F * B2) + 128.0F;
1033
1034 mc = outoff + i * 2;
1035 outImg[mc + 0] = Y1;
1036 outImg[mc + 1] = U1;
1037 outImg[mc + 2] = Y2;
1038 outImg[mc + 3] = V2;
1039 }
1040 }
1041 }
1042
1043 private:
1044 cv::Mat const & inImg;
1045 unsigned char * outImg;
1046 int inlinesize, outlinesize;
1047 };
1048
1049 // ####################################################################################################
1050 void convertCvBGRtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
1051 {
1052 if (src.type() != CV_8UC3)
1053 LFATAL("src must have type CV_8UC3 and BGR pixels; your image has " << jevois::cvtypestr(src.type()));
1054 if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1055 if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1056
1057 cv::parallel_for_(cv::Range(0, src.rows), bgrToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
1058 }
1059} // anonymous namespace
1060
1061// ####################################################################################################
1062void jevois::rawimage::convertCvBGRtoCvYUYV(cv::Mat const & src, cv::Mat & dst)
1063{
1064 if (src.type() != CV_8UC3)
1065 LFATAL("src must have type CV_8UC3 and BGR pixels; your image has " << jevois::cvtypestr(src.type()));
1066 dst = cv::Mat(src.rows, src.cols, CV_8UC2);
1067
1068 cv::parallel_for_(cv::Range(0, src.rows), bgrToYUYV(src, dst.data, dst.cols));
1069}
1070
1071// ####################################################################################################
1072void jevois::rawimage::pasteBGRtoYUYV(cv::Mat const & src, jevois::RawImage & dst, int x, int y)
1073{
1074 if (src.type() != CV_8UC3)
1075 LFATAL("src must have type CV_8UC3 and BGR pixels; your image has " << jevois::cvtypestr(src.type()));
1076 if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1077 if (x + src.cols > int(dst.width) || y + src.rows > int(dst.height)) LFATAL("src does not fit within dst");
1078
1079 cv::parallel_for_(cv::Range(0, src.rows), bgrToYUYV(src, dst.pixelsw<unsigned char>() +
1080 (x + y * dst.width) * dst.bytesperpix(), dst.width));
1081}
1082
1083// ####################################################################################################
1084namespace
1085{
1086 class rgbToYUYV : public cv::ParallelLoopBody
1087 {
1088 public:
1089 rgbToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
1090 inImg(inputImage), outImg(outImage)
1091 {
1092 inlinesize = inputImage.cols * 3; // 3 bytes/pix for RGB
1093 outlinesize = outw * 2; // 2 bytes/pix for YUYV
1094 }
1095
1096 virtual void operator()(const cv::Range & range) const
1097 {
1098 for (int j = range.start; j < range.end; ++j)
1099 {
1100 int const inoff = j * inlinesize;
1101 int const outoff = j * outlinesize;
1102
1103 for (int i = 0; i < inImg.cols; i += 2)
1104 {
1105 int mc = inoff + i * 3;
1106 unsigned char const R1 = inImg.data[mc + 0];
1107 unsigned char const G1 = inImg.data[mc + 1];
1108 unsigned char const B1 = inImg.data[mc + 2];
1109 unsigned char const R2 = inImg.data[mc + 3];
1110 unsigned char const G2 = inImg.data[mc + 4];
1111 unsigned char const B2 = inImg.data[mc + 5];
1112
1113 float const Y1 = (0.257F * R1) + (0.504F * G1) + (0.098F * B1) + 16.0F;
1114 //float const V1 = (0.439F * R1) - (0.368F * G1) - (0.071F * B1) + 128.0F;
1115 float const U1 = -(0.148F * R1) - (0.291F * G1) + (0.439F * B1) + 128.0F;
1116 float const Y2 = (0.257F * R2) + (0.504F * G2) + (0.098F * B2) + 16.0F;
1117 float const V2 = (0.439F * R2) - (0.368F * G2) - (0.071F * B2) + 128.0F;
1118 //float const U2 = -(0.148F * R2) - (0.291F * G2) + (0.439F * B2) + 128.0F;
1119
1120 mc = outoff + i * 2;
1121 outImg[mc + 0] = Y1;
1122 outImg[mc + 1] = U1;
1123 outImg[mc + 2] = Y2;
1124 outImg[mc + 3] = V2;
1125 }
1126 }
1127 }
1128
1129 private:
1130 cv::Mat const & inImg;
1131 unsigned char * outImg;
1132 int inlinesize, outlinesize;
1133 };
1134
1135 // ####################################################################################################
1136 void convertCvRGBtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
1137 {
1138 if (src.type() != CV_8UC3)
1139 LFATAL("src must have type CV_8UC3 and RGB pixels; your image has " << jevois::cvtypestr(src.type()));
1140 if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1141 if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1142
1143 cv::parallel_for_(cv::Range(0, src.rows), rgbToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
1144 }
1145} // anonymous namespace
1146
1147// ####################################################################################################
1148void jevois::rawimage::convertCvRGBtoCvYUYV(cv::Mat const & src, cv::Mat & dst)
1149{
1150 if (src.type() != CV_8UC3)
1151 LFATAL("src must have type CV_8UC3 and RGB pixels; your image has " << jevois::cvtypestr(src.type()));
1152 dst = cv::Mat(src.rows, src.cols, CV_8UC2);
1153
1154 cv::parallel_for_(cv::Range(0, src.rows), rgbToYUYV(src, dst.data, dst.cols));
1155}
1156
1157// ####################################################################################################
1158void jevois::rawimage::pasteRGBtoYUYV(cv::Mat const & src, jevois::RawImage & dst, int x, int y)
1159{
1160 if (src.type() != CV_8UC3)
1161 LFATAL("src must have type CV_8UC3 and RGB pixels; your image has " << jevois::cvtypestr(src.type()));
1162 if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1163 if (x + src.cols > int(dst.width) || y + src.rows > int(dst.height)) LFATAL("src does not fit within dst");
1164
1165 cv::parallel_for_(cv::Range(0, src.rows), rgbToYUYV(src, dst.pixelsw<unsigned char>() +
1166 (x + y * dst.width) * dst.bytesperpix(), dst.width));
1167}
1168
1169// ####################################################################################################
1170namespace
1171{
1172 class grayToYUYV : public cv::ParallelLoopBody
1173 {
1174 public:
1175 grayToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
1176 inImg(inputImage), outImg(outImage)
1177 {
1178 inlinesize = inputImage.cols * 1; // 1 bytes/pix for GRAY
1179 outlinesize = outw * 2; // 2 bytes/pix for YUYV
1180 }
1181
1182 virtual void operator()(const cv::Range & range) const
1183 {
1184 for (int j = range.start; j < range.end; ++j)
1185 {
1186 int const inoff = j * inlinesize;
1187 int const outoff = j * outlinesize;
1188
1189 for (int i = 0; i < inImg.cols; ++i)
1190 {
1191 int mc = inoff + i;
1192 unsigned char const G = inImg.data[mc + 0];
1193
1194 mc = outoff + i * 2;
1195 outImg[mc + 0] = G;
1196 outImg[mc + 1] = 0x80;
1197 }
1198 }
1199 }
1200
1201 private:
1202 cv::Mat const & inImg;
1203 unsigned char * outImg;
1204 int inlinesize, outlinesize;
1205 };
1206
1207 // ####################################################################################################
1208 void convertCvGRAYtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
1209 {
1210 if (src.type() != CV_8UC1)
1211 LFATAL("src must have type CV_8UC1 and GRAY pixels; your image has " << jevois::cvtypestr(src.type()));
1212 if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1213 if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1214
1215 cv::parallel_for_(cv::Range(0, src.rows), grayToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
1216 }
1217} // anonymous namespace
1218
1219// ####################################################################################################
1220void jevois::rawimage::convertCvGRAYtoCvYUYV(cv::Mat const & src, cv::Mat & dst)
1221{
1222 if (src.type() != CV_8UC1)
1223 LFATAL("src must have type CV_8UC1 and GRAY pixels; your image has " << jevois::cvtypestr(src.type()));
1224 dst = cv::Mat(src.rows, src.cols, CV_8UC2);
1225
1226 cv::parallel_for_(cv::Range(0, src.rows), grayToYUYV(src, dst.data, dst.cols));
1227}
1228
1229// ####################################################################################################
1230namespace
1231{
1232 class rgbaToYUYV : public cv::ParallelLoopBody
1233 {
1234 public:
1235 rgbaToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
1236 inImg(inputImage), outImg(outImage)
1237 {
1238 inlinesize = inputImage.cols * 4; // 4 bytes/pix for RGBA
1239 outlinesize = outw * 2; // 2 bytes/pix for YUYV
1240 }
1241
1242 virtual void operator()(const cv::Range & range) const
1243 {
1244 for (int j = range.start; j < range.end; ++j)
1245 {
1246 int const inoff = j * inlinesize;
1247 int const outoff = j * outlinesize;
1248
1249 for (int i = 0; i < inImg.cols; i += 2)
1250 {
1251 int mc = inoff + i * 4;
1252 unsigned char const R1 = inImg.data[mc + 0];
1253 unsigned char const G1 = inImg.data[mc + 1];
1254 unsigned char const B1 = inImg.data[mc + 2];
1255 unsigned char const R2 = inImg.data[mc + 4];
1256 unsigned char const G2 = inImg.data[mc + 5];
1257 unsigned char const B2 = inImg.data[mc + 6];
1258
1259 float const Y1 = (0.257F * R1) + (0.504F * G1) + (0.098F * B1) + 16.0F;
1260 float const U1 = -(0.148F * R1) - (0.291F * G1) + (0.439F * B1) + 128.0F;
1261 float const Y2 = (0.257F * R2) + (0.504F * G2) + (0.098F * B2) + 16.0F;
1262 float const V2 = (0.439F * R2) - (0.368F * G2) - (0.071F * B2) + 128.0F;
1263
1264 mc = outoff + i * 2;
1265 outImg[mc + 0] = Y1;
1266 outImg[mc + 1] = U1;
1267 outImg[mc + 2] = Y2;
1268 outImg[mc + 3] = V2;
1269 }
1270 }
1271 }
1272
1273 private:
1274 cv::Mat const & inImg;
1275 unsigned char * outImg;
1276 int inlinesize, outlinesize;
1277 };
1278
1279 // ####################################################################################################
1280 void convertCvRGBAtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
1281 {
1282 if (src.type() != CV_8UC4)
1283 LFATAL("src must have type CV_8UC4 and RGBA pixels; your image has " << jevois::cvtypestr(src.type()));
1284 if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1285 if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
1286
1287 cv::parallel_for_(cv::Range(0, src.rows), rgbaToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
1288 }
1289} // anonymous namespace
1290
1291// ####################################################################################################
1292void jevois::rawimage::convertCvRGBAtoCvYUYV(cv::Mat const & src, cv::Mat & dst)
1293{
1294 if (src.type() != CV_8UC4)
1295 LFATAL("src must have type CV_8UC4 and RGBA pixels; your image has " << jevois::cvtypestr(src.type()));
1296 dst = cv::Mat(src.rows, src.cols, CV_8UC2);
1297
1298 cv::parallel_for_(cv::Range(0, src.rows), rgbaToYUYV(src, dst.data, dst.cols));
1299}
1300
1301// ####################################################################################################
1302void jevois::rawimage::convertCvBGRtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
1303{
1304 if (src.type() != CV_8UC3)
1305 LFATAL("src must have type CV_8UC3 and BGR pixels; your image has " << jevois::cvtypestr(src.type()));
1306 if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1307
1308 // Note how the destination opencv image dstcv here is just a shell, the actual pixel data is in dst:
1309 cv::Mat dstcv = jevois::rawimage::cvImage(dst);
1310
1311 switch (dst.fmt)
1312 {
1313 case V4L2_PIX_FMT_SRGGB8: convertCvBGRtoBayer(src, dst); break;
1314 case V4L2_PIX_FMT_YUYV: convertCvBGRtoYUYV(src, dst); break;
1315 case V4L2_PIX_FMT_GREY: cv::cvtColor(src, dstcv, cv::COLOR_BGR2GRAY); break;
1316 case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, cv::COLOR_BGR2BGR565); break;
1317 case V4L2_PIX_FMT_MJPEG: jevois::compressBGRtoJpeg(src, dst, quality); break;
1318 case V4L2_PIX_FMT_BGR24: memcpy(dst.pixelsw<void>(), src.data, dst.width * dst.height * dst.bytesperpix()); break;
1319 case V4L2_PIX_FMT_RGB24: cv::cvtColor(src, dstcv, cv::COLOR_BGR2RGB); break;
1320 default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
1321 }
1322}
1323
1324// ####################################################################################################
1325void jevois::rawimage::convertCvRGBtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
1326{
1327 if (src.type() != CV_8UC3)
1328 LFATAL("src must have type CV_8UC3 and RGB pixels; your image has " << jevois::cvtypestr(src.type()));
1329 if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1330
1331 // Note how the destination opencv image dstcv here is just a shell, the actual pixel data is in dst:
1332 cv::Mat dstcv = jevois::rawimage::cvImage(dst);
1333
1334 switch (dst.fmt)
1335 {
1336 case V4L2_PIX_FMT_SRGGB8: convertCvRGBtoBayer(src, dst); break;
1337 case V4L2_PIX_FMT_YUYV: convertCvRGBtoYUYV(src, dst); break;
1338 case V4L2_PIX_FMT_GREY: cv::cvtColor(src, dstcv, cv::COLOR_RGB2GRAY); break;
1339 case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, cv::COLOR_RGB2BGR565); break;
1340 case V4L2_PIX_FMT_MJPEG: jevois::compressRGBtoJpeg(src, dst, quality); break;
1341 case V4L2_PIX_FMT_BGR24: cv::cvtColor(src, dstcv, cv::COLOR_RGB2BGR); break;
1342 case V4L2_PIX_FMT_RGB24: memcpy(dst.pixelsw<void>(), src.data, dst.width * dst.height * dst.bytesperpix()); break;
1343 default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
1344 }
1345}
1346
1347// ####################################################################################################
1349{
1350 if (src.type() != CV_8UC4)
1351 LFATAL("src must have type CV_8UC4 and RGBA pixels; your image has " << jevois::cvtypestr(src.type()));
1352 if (dst.fmt != V4L2_PIX_FMT_GREY) LFATAL("dst must have pixel type V4L2_PIX_FMT_GREY");
1353 int const w = src.cols, h = src.rows;
1354 if (int(dst.width) < w || int(dst.height) < 4 * h) LFATAL("dst must be at least as wide and 4x as tall as src");
1355
1356 unsigned char const * sptr = src.data; unsigned char * dptr = dst.pixelsw<unsigned char>();
1357 int const stride = int(dst.width) - w;
1358
1359 // Do R, G, B in 3 threads then A in the current thread:
1360 std::vector<std::future<void> > fut;
1361 for (int i = 0; i < 3; ++i) fut.push_back(jevois::async([&](int offset) {
1362 unsigned char const * s = sptr + offset; unsigned char * d = dptr + offset * w * h;
1363 for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { *d++ = *s; s += 4; } d += stride; } }, i));
1364
1365 unsigned char const * s = sptr + 3; unsigned char * d = dptr + 3 * w * h;
1366 for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { *d++ = *s; s += 4; } d += stride; }
1367
1368 // Wait for all threads to complete (those should never throw):
1369 for (auto & f : fut) f.get();
1370}
1371
1372// ####################################################################################################
1373void jevois::rawimage::convertCvRGBAtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
1374{
1375 if (src.type() != CV_8UC4)
1376 LFATAL("src must have type CV_8UC4 and RGBA pixels; your image has " << jevois::cvtypestr(src.type()));
1377 if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
1378
1379 cv::Mat dstcv = jevois::rawimage::cvImage(dst);
1380
1381 switch (dst.fmt)
1382 {
1383 case V4L2_PIX_FMT_SRGGB8: convertCvRGBAtoBayer(src, dst); break;
1384 case V4L2_PIX_FMT_YUYV: convertCvRGBAtoYUYV(src, dst); break;
1385 case V4L2_PIX_FMT_GREY: cv::cvtColor(src, dstcv, cv::COLOR_RGBA2GRAY); break;
1386 case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, cv::COLOR_BGRA2BGR565); break;
1387 case V4L2_PIX_FMT_MJPEG: jevois::compressRGBAtoJpeg(src, dst, quality); break;
1388 case V4L2_PIX_FMT_BGR24: cv::cvtColor(src, dstcv, cv::COLOR_RGBA2BGR); break;
1389 case V4L2_PIX_FMT_RGB24: cv::cvtColor(src, dstcv, cv::COLOR_RGBA2RGB); break;
1390 default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
1391 }
1392}
1393
1394// ####################################################################################################
1395void jevois::rawimage::convertCvGRAYtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
1396{
1397 if (src.type() != CV_8UC1)
1398 LFATAL("src must have type CV_8UC1 and GRAY pixels; your image has " << jevois::cvtypestr(src.type()));
1399 if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
1400
1401 cv::Mat dstcv = jevois::rawimage::cvImage(dst);
1402
1403 switch (dst.fmt)
1404 {
1405 case V4L2_PIX_FMT_SRGGB8: convertCvGRAYtoBayer(src, dst); break;
1406 case V4L2_PIX_FMT_YUYV: convertCvGRAYtoYUYV(src, dst); break;
1407 case V4L2_PIX_FMT_GREY: memcpy(dst.pixelsw<void>(), src.data, dst.width * dst.height * dst.bytesperpix()); break;
1408 case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, cv::COLOR_GRAY2BGR565); break;
1409 case V4L2_PIX_FMT_MJPEG: jevois::compressGRAYtoJpeg(src, dst, quality); break;
1410 case V4L2_PIX_FMT_BGR24: cv::cvtColor(src, dstcv, cv::COLOR_GRAY2BGR); break;
1411 case V4L2_PIX_FMT_RGB24: cv::cvtColor(src, dstcv, cv::COLOR_GRAY2RGB); break;
1412 default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
1413 }
1414}
1415
1416// ####################################################################################################
1417namespace
1418{
1419 class hflipYUYV : public cv::ParallelLoopBody
1420 {
1421 public:
1422 hflipYUYV(unsigned char * outImage, size_t outw) :
1423 outImg(outImage), linesize(outw * 2) // 2 bytes/pix for YUYV
1424 { }
1425
1426 virtual void operator()(const cv::Range & range) const
1427 {
1428 for (int j = range.start; j < range.end; ++j)
1429 {
1430 int const off = j * linesize;
1431
1432 for (int i = 0; i < linesize / 2; i += 4)
1433 {
1434 unsigned char * ptr1 = outImg + off + i;
1435 unsigned char * ptr2 = outImg + off + linesize - 4 - i;
1436 std::swap(ptr1[0], ptr2[2]);
1437 std::swap(ptr1[1], ptr2[1]);
1438 std::swap(ptr1[2], ptr2[0]);
1439 std::swap(ptr1[3], ptr2[3]);
1440 }
1441 }
1442 }
1443
1444 private:
1445 unsigned char * outImg;
1446 int linesize;
1447 };
1448
1449} // anonymous namespace
1450
1451// ####################################################################################################
1453{
1454 if (img.fmt != V4L2_PIX_FMT_YUYV) LFATAL("img format must be V4L2_PIX_FMT_YUYV");
1455 cv::parallel_for_(cv::Range(0, img.height), hflipYUYV(img.pixelsw<unsigned char>(), img.width));
1456}
1457
1458// ####################################################################################################
1459#ifdef __ARM_NEON__
1460namespace
1461{
1462 class bayerToYUYV : public cv::ParallelLoopBody
1463 {
1464 public:
1465 bayerToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
1466 inImg(inputImage), outImg(outImage)
1467 {
1468 inlinesize = inputImage.cols; // 1 bytes/pix for bayer
1469 outlinesize = outw * 2; // 2 bytes/pix for YUYV
1470 }
1471
1472 virtual void operator()(const cv::Range & range) const
1473 {
1474 uint16x8_t const masklo = vdupq_n_u16(255);
1475 uint8x16x3_t pix;
1476 const uint8x8_t u8_zero = vdup_n_u8(0);
1477 const uint16x8_t u16_rounding = vdupq_n_u16(128);
1478 const int16x8_t s16_rounding = vdupq_n_s16(128);
1479 const int8x16_t s8_rounding = vdupq_n_s8(128);
1480
1481 for (int j = range.start; j < range.end; ++j)
1482 {
1483 int const inoff = j * inlinesize;
1484 int const outoff = j * outlinesize;
1485
1486 unsigned char const * bayer = inImg.data + inoff;
1487 unsigned char const * bayer_end = bayer + inlinesize;
1488 int const bayer_step = inlinesize;
1489 unsigned char * dst = outImg + outoff;
1490
1491 /* This code from opencv demosaicing:
1492 B G B G | B G B G | B G B G | B G B G
1493 G R G R | G R G R | G R G R | G R G R
1494 B G B G | B G B G | B G B G | B G B G */
1495
1496 for( ; bayer <= bayer_end - 18; bayer += 14, dst += 28 )
1497 {
1498 uint16x8_t r0 = vld1q_u16((const ushort*)bayer);
1499 uint16x8_t r1 = vld1q_u16((const ushort*)(bayer + bayer_step));
1500 uint16x8_t r2 = vld1q_u16((const ushort*)(bayer + bayer_step*2));
1501
1502 uint16x8_t b1 = vaddq_u16(vandq_u16(r0, masklo), vandq_u16(r2, masklo));
1503 uint16x8_t nextb1 = vextq_u16(b1, b1, 1);
1504 uint16x8_t b0 = vaddq_u16(b1, nextb1);
1505 // b0 b1 b2 ...
1506 uint8x8x2_t bb = vzip_u8(vrshrn_n_u16(b0, 2), vrshrn_n_u16(nextb1, 1));
1507 pix.val[2] = vcombine_u8(bb.val[0], bb.val[1]);
1508
1509 uint16x8_t g0 = vaddq_u16(vshrq_n_u16(r0, 8), vshrq_n_u16(r2, 8));
1510 uint16x8_t g1 = vandq_u16(r1, masklo);
1511 g0 = vaddq_u16(g0, vaddq_u16(g1, vextq_u16(g1, g1, 1)));
1512 g1 = vextq_u16(g1, g1, 1);
1513 // g0 g1 g2 ...
1514 uint8x8x2_t gg = vzip_u8(vrshrn_n_u16(g0, 2), vmovn_u16(g1));
1515 pix.val[1] = vcombine_u8(gg.val[0], gg.val[1]);
1516
1517 r0 = vshrq_n_u16(r1, 8);
1518 r1 = vaddq_u16(r0, vextq_u16(r0, r0, 1));
1519 // r0 r1 r2 ...
1520 uint8x8x2_t rr = vzip_u8(vmovn_u16(r0), vrshrn_n_u16(r1, 1));
1521 pix.val[0] = vcombine_u8(rr.val[0], rr.val[1]);
1522
1523
1524 // Ok, we have rgb values in pix, now convert to YUV:
1525 // code from: https://github.com/yszheda/rgb2yuv-neon/blob/master/yuv444.cpp
1526 uint8x8_t high_r = vget_high_u8(pix.val[2]);
1527 uint8x8_t low_r = vget_low_u8(pix.val[2]);
1528 uint8x8_t high_g = vget_high_u8(pix.val[1]);
1529 uint8x8_t low_g = vget_low_u8(pix.val[1]);
1530 uint8x8_t high_b = vget_high_u8(pix.val[0]);
1531 uint8x8_t low_b = vget_low_u8(pix.val[0]);
1532 int16x8_t signed_high_r = vreinterpretq_s16_u16(vaddl_u8(high_r, u8_zero));
1533 int16x8_t signed_low_r = vreinterpretq_s16_u16(vaddl_u8(low_r, u8_zero));
1534 int16x8_t signed_high_g = vreinterpretq_s16_u16(vaddl_u8(high_g, u8_zero));
1535 int16x8_t signed_low_g = vreinterpretq_s16_u16(vaddl_u8(low_g, u8_zero));
1536 int16x8_t signed_high_b = vreinterpretq_s16_u16(vaddl_u8(high_b, u8_zero));
1537 int16x8_t signed_low_b = vreinterpretq_s16_u16(vaddl_u8(low_b, u8_zero));
1538
1539 // NOTE:
1540 // declaration may not appear after executable statement in block
1541 uint16x8_t high_y;
1542 uint16x8_t low_y;
1543 uint8x8_t scalar = vdup_n_u8(76);
1544 int16x8_t high_u;
1545 int16x8_t low_u;
1546 int16x8_t signed_scalar = vdupq_n_s16(-43);
1547 int16x8_t high_v;
1548 int16x8_t low_v;
1549 uint8x16x3_t pixel_yuv;
1550 int8x16_t u;
1551 int8x16_t v;
1552
1553 // 1. Multiply transform matrix (Y′: unsigned, U/V: signed)
1554 high_y = vmull_u8(high_r, scalar);
1555 low_y = vmull_u8(low_r, scalar);
1556
1557 high_u = vmulq_s16(signed_high_r, signed_scalar);
1558 low_u = vmulq_s16(signed_low_r, signed_scalar);
1559
1560 signed_scalar = vdupq_n_s16(127);
1561 high_v = vmulq_s16(signed_high_r, signed_scalar);
1562 low_v = vmulq_s16(signed_low_r, signed_scalar);
1563
1564 scalar = vdup_n_u8(150);
1565 high_y = vmlal_u8(high_y, high_g, scalar);
1566 low_y = vmlal_u8(low_y, low_g, scalar);
1567
1568 signed_scalar = vdupq_n_s16(-84);
1569 high_u = vmlaq_s16(high_u, signed_high_g, signed_scalar);
1570 low_u = vmlaq_s16(low_u, signed_low_g, signed_scalar);
1571
1572 signed_scalar = vdupq_n_s16(-106);
1573 high_v = vmlaq_s16(high_v, signed_high_g, signed_scalar);
1574 low_v = vmlaq_s16(low_v, signed_low_g, signed_scalar);
1575
1576 scalar = vdup_n_u8(29);
1577 high_y = vmlal_u8(high_y, high_b, scalar);
1578 low_y = vmlal_u8(low_y, low_b, scalar);
1579
1580 signed_scalar = vdupq_n_s16(127);
1581 high_u = vmlaq_s16(high_u, signed_high_b, signed_scalar);
1582 low_u = vmlaq_s16(low_u, signed_low_b, signed_scalar);
1583
1584 signed_scalar = vdupq_n_s16(-21);
1585 high_v = vmlaq_s16(high_v, signed_high_b, signed_scalar);
1586 low_v = vmlaq_s16(low_v, signed_low_b, signed_scalar);
1587 // 2. Scale down (">>8") to 8-bit values with rounding ("+128") (Y′: unsigned, U/V: signed)
1588 // 3. Add an offset to the values to eliminate any negative values (all results are 8-bit unsigned)
1589
1590 high_y = vaddq_u16(high_y, u16_rounding);
1591 low_y = vaddq_u16(low_y, u16_rounding);
1592
1593 high_u = vaddq_s16(high_u, s16_rounding);
1594 low_u = vaddq_s16(low_u, s16_rounding);
1595
1596 high_v = vaddq_s16(high_v, s16_rounding);
1597 low_v = vaddq_s16(low_v, s16_rounding);
1598
1599 pixel_yuv.val[0] = vcombine_u8(vqshrn_n_u16(low_y, 8), vqshrn_n_u16(high_y, 8));
1600
1601 u = vcombine_s8(vqshrn_n_s16(low_u, 8), vqshrn_n_s16(high_u, 8));
1602
1603 v = vcombine_s8(vqshrn_n_s16(low_v, 8), vqshrn_n_s16(high_v, 8));
1604
1605 u = vaddq_s8(u, s8_rounding);
1606 pixel_yuv.val[1] = vreinterpretq_u8_s8(u);
1607
1608 v = vaddq_s8(v, s8_rounding);
1609 pixel_yuv.val[2] = vreinterpretq_u8_s8(v);
1610
1611 // Store
1612
1613 //FIXME: instead of storing all 3x16 YUV values, we need to interleave into YUYV...
1614 vst3q_u8(dst, pixel_yuv);
1615 }
1616 }
1617 }
1618
1619 private:
1620 cv::Mat const & inImg;
1621 unsigned char * outImg;
1622 int inlinesize, outlinesize;
1623 };
1624} // anonymous namespace
1625#endif
1626
1627
1628// ####################################################################################################
1630{
1631 if (src.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("src format must be V4L2_PIX_FMT_SRGGB8");
1632 if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1633 if (dst.width != src.width || dst.height < src.height) LFATAL("src and dst dims must match");
1634
1635 auto cvsrc = jevois::rawimage::cvImage(src);
1636
1637#ifdef FIXME__ARM_NEON__
1638 // FIXME: Neon code not working yet, needs more work...
1639 cv::parallel_for_(cv::Range(0, cvsrc.rows), bayerToYUYV(cvsrc, dst.pixelsw<unsigned char>(), dst.width));
1640#else
1641 auto cvdst = jevois::rawimage::cvImage(dst);
1642 cv::Mat xx;
1643 cv::cvtColor(cvsrc, xx, cv::COLOR_BayerBG2BGR);
1644 cv::parallel_for_(cv::Range(0, xx.rows), bgrToYUYV(xx, cvdst.data, cvdst.cols));
1645#endif
1646}
1647
1648// ####################################################################################################
1650{
1651 if (src.fmt != V4L2_PIX_FMT_GREY) LFATAL("src format must be V4L2_PIX_FMT_GREY");
1652 if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1653 if (dst.width != src.width || dst.height < src.height) LFATAL("src and dst dims must match");
1654
1655 auto cvsrc = jevois::rawimage::cvImage(src);
1656 convertCvGRAYtoYUYV(cvsrc, dst);
1657}
1658
1659// ####################################################################################################
1660cv::Mat jevois::rescaleCv(cv::Mat const & img, cv::Size const & newdims)
1661{
1662 cv::Mat scaled;
1663
1664 if (newdims.width == img.cols && newdims.height == img.rows)
1665 scaled = img;
1666 else if (newdims.width > img.cols || newdims.height > img.rows)
1667 cv::resize(img, scaled, newdims, 0, 0, cv::INTER_LINEAR);
1668 else
1669 cv::resize(img, scaled, newdims, 0, 0, cv::INTER_AREA);
1670
1671 return scaled;
1672}
int h
Definition GUIhelper.C:2491
TinyImGUIdemo G[999]
Definition GUIhelper.C:2491
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition RawImage.H:111
bool coordsOk(int x, int y) const
Helper function to check that coords are within image bounds.
Definition RawImage.C:88
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition RawImage.H:147
unsigned int bytesize() const
Helper function to get the total number of bytes in the RawImage, i.e., width * height * bytesperpix(...
Definition RawImage.C:38
T const * pixels() const
Shortcut access to pixels, read-only.
T * pixelsw()
Shortcut access to pixels, read-write.
unsigned int width
Image width in pixels.
Definition RawImage.H:145
unsigned int bytesperpix() const
Helper function to get the number of bytes/pixel given the RawImage pixel format.
Definition RawImage.C:34
unsigned int height
Image height in pixels.
Definition RawImage.H:146
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition RawImage.H:149
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
cv::Mat convertToCvRGBA(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV RGB-A byte...
void paste(RawImage const &src, RawImage &dest, int dx, int dy)
Paste an image within another of same pixel type.
bool clipLine(int wxmin, int wymin, int wxmax, int wymax, int &x1, int &y1, int &x2, int &y2)
Clip a line to fit inside a viewport [wxmin...wxmax[ x [wymin...wymax[.
void convertCvGRAYtoCvYUYV(cv::Mat const &src, cv::Mat &dst)
OpenCV does not provide conversion from GRAY to YUYV in cvtColor(), so this function provides it.
cv::Mat cvImage(RawImage const &src)
Create an OpenCV image from the existing RawImage data, sharing the pixel memory rather than copying ...
Definition RawImageOps.C:31
void writeText(RawImage &img, std::string const &txt, int x, int y, unsigned int col, Font font=Font6x10)
Write some text in an image.
cv::Mat convertToCvGray(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV gray byte.
void convertCvBGRtoCvYUYV(cv::Mat const &src, cv::Mat &dst)
OpenCV does not provide conversion from BGR to YUYV in cvtColor(), so this function provides it.
void pasteRGBtoYUYV(cv::Mat const &src, RawImage &dst, int dx, int dy)
Paste a RGB byte image into a YUYV image.
cv::Mat convertToCvRGB(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV RGB byte.
void drawFilledRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int col)
Draw a filled rectangle in a YUYV image.
void drawDisk(RawImage &img, int x, int y, unsigned int rad, unsigned int col)
Draw a disk in a YUYV image.
void convertCvRGBAtoRawImage(cv::Mat const &src, RawImage &dst, int quality)
Convert an RGBA cv::Mat to RawImage with already-allocated pixels and pixel type.
cv::Mat rescaleCv(cv::Mat const &img, cv::Size const &newdims)
Rescale an OpenCV image, choosing the right kind of interpolation.
Font
Available fonts for writeText()
cv::Mat convertToCvBGR(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV BGR byte.
void convertCvBGRtoRawImage(cv::Mat const &src, RawImage &dst, int quality)
Convert a BGR cv::Mat to RawImage with already-allocated pixels and pixel type.
void drawLine(RawImage &img, int x1, int y1, int x2, int y2, unsigned int thick, unsigned int col)
Draw a line in a YUYV image.
void convertCvRGBtoCvYUYV(cv::Mat const &src, cv::Mat &dst)
OpenCV does not provide conversion from RGB to YUYV in cvtColor(), so this function provides it.
void convertCvRGBtoRawImage(cv::Mat const &src, RawImage &dst, int quality)
Convert a RGB cv::Mat to RawImage with already-allocated pixels and pixel type.
void unpackCvRGBAtoGrayRawImage(cv::Mat const &src, RawImage &dst)
Split an RGBA cv::Mat into a 4x taller grey RawImage with already-allocated pixels.
void drawRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int thick, unsigned int col)
Draw a rectangle in a YUYV image.
void hFlipYUYV(RawImage &img)
Flip a YUYV RawImage horizontally while preserving color information.
void roipaste(RawImage const &src, int x, int y, unsigned int w, unsigned int h, RawImage &dest, int dx, int dy)
Paste an ROI from an image to within another of same pixel type.
void byteSwap(RawImage &img)
Swap pairs of bytes in a RawImage.
void convertCvRGBAtoCvYUYV(cv::Mat const &src, cv::Mat &dst)
OpenCV does not provide conversion from RGBA to YUYV in cvtColor(), so this function provides it.
void convertCvGRAYtoRawImage(cv::Mat const &src, RawImage &dst, int quality)
Convert a Gray cv::Mat to RawImage with already-allocated pixels and pixel type.
int itext(RawImage &img, std::string const &txt, int y=3, unsigned int col=jevois::yuyv::White, Font font=Font6x10)
Shorthand to write some text in an image, with x = 3 and return the next y position.
void drawCircle(RawImage &img, int x, int y, unsigned int rad, unsigned int thick, unsigned int col)
Draw a circle in a YUYV image.
void pasteGreyToYUYV(cv::Mat const &src, RawImage &dest, int dx, int dy)
Paste a grey byte image into a YUYV image.
void pasteBGRtoYUYV(cv::Mat const &src, RawImage &dst, int dx, int dy)
Paste a BGR byte image into a YUYV image.
unsigned long compressGRAYtoJpeg(unsigned char const *src, int width, int height, unsigned char *dst, int quality=75)
Compress raw pixel buffer to jpeg.
Definition Jpeg.C:94
unsigned long compressBGRtoJpeg(unsigned char const *src, int width, int height, unsigned char *dst, int quality=75)
Compress raw pixel buffer to jpeg.
Definition Jpeg.C:52
unsigned long compressRGBAtoJpeg(unsigned char const *src, int width, int height, unsigned char *dst, int quality=75)
Compress raw pixel buffer to jpeg.
Definition Jpeg.C:80
unsigned long compressRGBtoJpeg(unsigned char const *src, int width, int height, unsigned char *dst, int quality=75)
Compress raw pixel buffer to jpeg.
Definition Jpeg.C:66
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async(Function &&f, Args &&... args)
Async execution using a thread pool.
unsigned int v4l2BytesPerPix(unsigned int fcc)
Return the number of bytes per pixel for a given V4L2_PIX_FMT_...
Definition Utils.C:141
std::string cvtypestr(unsigned int cvtype)
Convert cv::Mat::type() code to to a string (e.g., CV_8UC1, CV_32SC3, etc)
Definition Utils.C:58
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition Utils.C:45
const unsigned char font10x20[95][200]
Definition Font10x20.C:8
const unsigned char font6x10[95][60]
Definition Font6x10.C:8
const unsigned char font15x28[95][420]
Definition Font15x28.C:8
const unsigned char font12x22[95][264]
Definition Font12x22.C:8
const unsigned char font20x38[95][760]
Definition Font20x38.C:8
const unsigned char font7x13[95][91]
Definition Font7x13.C:8
const unsigned char font16x29[95][464]
Definition Font16x29.C:8
const unsigned char font8x13bold[95][104]
Definition Font8x13bold.C:8
const unsigned char font14x26[95][364]
Definition Font14x26.C:8
const unsigned char font11x22[95][242]
Definition Font11x22.C:8
const unsigned char font9x15bold[95][135]
Definition Font9x15bold.C:8
const unsigned char font5x7[95][35]
Definition Font5x7.C:7
void convertBayerToYUYV(RawImage const &src, RawImage &dst)
Convert from Bayer to YUYV, only used internally by Camera class.
void convertGreyToYUYV(RawImage const &src, RawImage &dst)
Convert from Grey (monochrome) to YUYV, only used internally by Camera class.
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2