JeVois  1.3
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
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 
19 #include <jevois/Core/VideoBuf.H>
20 #include <jevois/Util/Utils.H>
21 #include <jevois/Debug/Log.H>
22 #include <jevois/Image/Jpeg.H>
23 #include <future>
24 
25 #include <linux/videodev2.h>
26 #include <cmath>
27 #include <opencv2/imgproc/imgproc.hpp>
28 
29 // ####################################################################################################
31 {
32  unsigned int bpp = jevois::v4l2BytesPerPix(src.fmt);
33 
34  switch (bpp)
35  {
36  case 3: return cv::Mat(src.height, src.width, CV_8UC3, src.buf->data());
37  case 2: return cv::Mat(src.height, src.width, CV_8UC2, src.buf->data());
38  case 1: return cv::Mat(src.height, src.width, CV_8UC1, src.buf->data());
39  default: LFATAL("Unsupported RawImage format");
40  }
41 }
42 
43 // ####################################################################################################
44 namespace
45 {
46  inline void rgb565pixrgb(unsigned short rgb565, unsigned char & r, unsigned char & g, unsigned char & b)
47  {
48  r = ((((rgb565 >> 11) & 0x1F) * 527) + 23) >> 6;
49  g = ((((rgb565 >> 5) & 0x3F) * 259) + 33) >> 6;
50  b = (((rgb565 & 0x1F) * 527) + 23) >> 6;
51  }
52 
53  class rgb565ToGray : public cv::ParallelLoopBody
54  {
55  public:
56  rgb565ToGray(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
57  inImg(inputImage), outImg(outImage)
58  {
59  inlinesize = inputImage.cols * 2; // 2 bytes/pix for RGB565
60  outlinesize = outw * 1; // 1 byte/pix for Gray
61  }
62 
63  virtual void operator()(const cv::Range & range) const
64  {
65  for (int j = range.start; j < range.end; ++j)
66  {
67  int const inoff = j * inlinesize;
68  int const outoff = j * outlinesize;
69 
70  for (int i = 0; i < inImg.cols; ++i)
71  {
72  int const in = inoff + i * 2;
73  int const out = outoff + i;
74  unsigned short const rgb565 = ((unsigned short)(inImg.data[in + 0]) << 8) | inImg.data[in + 1];
75  unsigned char r, g, b;
76  rgb565pixrgb(rgb565, r, g, b);
77  int lum = int(r + g + b) / 3;
78  outImg[out] = lum;
79  }
80  }
81  }
82 
83  private:
84  cv::Mat const & inImg;
85  unsigned char * outImg;
86  int inlinesize, outlinesize;
87  };
88 
89  class rgb565ToBGR : public cv::ParallelLoopBody
90  {
91  public:
92  rgb565ToBGR(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
93  inImg(inputImage), outImg(outImage)
94  {
95  inlinesize = inputImage.cols * 2; // 2 bytes/pix for RGB565
96  outlinesize = outw * 3; // 3 bytes/pix for BGR
97  }
98 
99  virtual void operator()(const cv::Range & range) const
100  {
101  for (int j = range.start; j < range.end; ++j)
102  {
103  int const inoff = j * inlinesize;
104  int const outoff = j * outlinesize;
105 
106  for (int i = 0; i < inImg.cols; ++i)
107  {
108  int const in = inoff + i * 2;
109  int const out = outoff + i * 3;
110  unsigned short const rgb565 = ((unsigned short)(inImg.data[in + 0]) << 8) | inImg.data[in + 1];
111 
112  unsigned char r, g, b; rgb565pixrgb(rgb565, r, g, b);
113  outImg[out + 0] = b;
114  outImg[out + 1] = g;
115  outImg[out + 2] = r;
116  }
117  }
118  }
119 
120  private:
121  cv::Mat const & inImg;
122  unsigned char * outImg;
123  int inlinesize, outlinesize;
124  };
125 
126  class rgb565ToRGB : public cv::ParallelLoopBody
127  {
128  public:
129  rgb565ToRGB(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
130  inImg(inputImage), outImg(outImage)
131  {
132  inlinesize = inputImage.cols * 2; // 2 bytes/pix for RGB565
133  outlinesize = outw * 3; // 3 bytes/pix for RGB
134  }
135 
136  virtual void operator()(const cv::Range & range) const
137  {
138  for (int j = range.start; j < range.end; ++j)
139  {
140  int const inoff = j * inlinesize;
141  int const outoff = j * outlinesize;
142 
143  for (int i = 0; i < inImg.cols; ++i)
144  {
145  int const in = inoff + i * 2;
146  int const out = outoff + i * 3;
147  unsigned short const rgb565 = ((unsigned short)(inImg.data[in + 0]) << 8) | inImg.data[in + 1];
148 
149  unsigned char r, g, b; rgb565pixrgb(rgb565, r, g, b);
150  outImg[out + 0] = r;
151  outImg[out + 1] = g;
152  outImg[out + 2] = b;
153  }
154  }
155  }
156 
157  private:
158  cv::Mat const & inImg;
159  unsigned char * outImg;
160  int inlinesize, outlinesize;
161  };
162 
163  class rgb565ToRGBA : public cv::ParallelLoopBody
164  {
165  public:
166  rgb565ToRGBA(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
167  inImg(inputImage), outImg(outImage)
168  {
169  inlinesize = inputImage.cols * 2; // 2 bytes/pix for RGB565
170  outlinesize = outw * 4; // 4 bytes/pix for RGBA
171  }
172 
173  virtual void operator()(const cv::Range & range) const
174  {
175  for (int j = range.start; j < range.end; ++j)
176  {
177  int const inoff = j * inlinesize;
178  int const outoff = j * outlinesize;
179 
180  for (int i = 0; i < inImg.cols; ++i)
181  {
182  int const in = inoff + i * 2;
183  int const out = outoff + i * 4;
184  unsigned short const rgb565 = ((unsigned short)(inImg.data[in + 0]) << 8) | inImg.data[in + 1];
185 
186  unsigned char r, g, b; rgb565pixrgb(rgb565, r, g, b);
187  outImg[out + 0] = r;
188  outImg[out + 1] = g;
189  outImg[out + 2] = b;
190  outImg[out + 3] = (unsigned char)(255);
191  }
192  }
193  }
194 
195  private:
196  cv::Mat const & inImg;
197  unsigned char * outImg;
198  int inlinesize, outlinesize;
199  };
200 } // anonymous namespace
201 
202 #ifdef JEVOIS_PLATFORM
203 // NEON accelerated YUYV to Gray:
204 namespace
205 {
206  class yuyvToGrayNEON : public cv::ParallelLoopBody
207  {
208  public:
209  yuyvToGrayNEON(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
210  inImg(inputImage), outImg(outImage)
211  {
212  inlinesize = inputImage.cols * 2; // 2 bytes/pix for YUYV
213  outlinesize = outw * 1; // 1 byte/pix for Gray
214  initer = (inputImage.cols >> 4); // we process 16 pixels (32 input bytes) at a time
215  }
216 
217  virtual void operator()(const cv::Range & range) const
218  {
219  unsigned char const * inptr = inImg.data + range.start * inlinesize;
220  unsigned char * outptr = outImg + range.start * outlinesize;
221 
222  for (int j = range.start; j < range.end; ++j)
223  {
224  unsigned char const * ip = inptr; unsigned char * op = outptr;
225 
226  for (int i = 0; i < initer; ++i)
227  {
228  uint8x16x2_t const pixels = vld2q_u8(ip); // load 16 YUYV pixels
229  vst1q_u8(op, pixels.val[0]); // store the 16 Y values
230  ip += 32; op += 16;
231  }
232  inptr += inlinesize; outptr += outlinesize;
233  }
234  }
235 
236  private:
237  cv::Mat const & inImg;
238  unsigned char * outImg;
239  int inlinesize, outlinesize, initer;
240  };
241 } // anonymous namespace
242 #endif
243 
244 // ####################################################################################################
246 {
247  cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
248  cv::Mat result;
249 
250  switch (src.fmt)
251  {
252  case V4L2_PIX_FMT_YUYV:
253 #if 0
254  //#ifdef JEVOIS_PLATFORM
255  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC1);
256  cv::parallel_for_(cv::Range(0, src.height), yuyvToGrayNEON(rawimgcv, result.data, result.cols));
257 #else
258  cv::cvtColor(rawimgcv, result, CV_YUV2GRAY_YUYV);
259 #endif
260  return result;
261 
262  case V4L2_PIX_FMT_GREY: return rawimgcv;
263 
264  case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, CV_BayerBG2GRAY); return result;
265 
266  case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
267  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC1);
268  cv::parallel_for_(cv::Range(0, src.height), rgb565ToGray(rawimgcv, result.data, result.cols));
269  return result;
270 
271  case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
272 
273  case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, CV_BGR2GRAY); return result;
274  }
275  LFATAL("Unknown RawImage pixel format");
276 }
277 
278 // ####################################################################################################
280 {
281  cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
282  cv::Mat result;
283 
284  switch (src.fmt)
285  {
286  case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, CV_YUV2BGR_YUYV); return result;
287  case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, CV_GRAY2BGR); return result;
288  case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, CV_BayerBG2BGR); return result;
289 
290  case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
291  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC3);
292  cv::parallel_for_(cv::Range(0, src.height), rgb565ToBGR(rawimgcv, result.data, result.cols));
293  return result;
294 
295  case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
296  case V4L2_PIX_FMT_BGR24: return rawimgcv;
297  }
298  LFATAL("Unknown RawImage pixel format");
299 }
300 
301 // ####################################################################################################
303 {
304  cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
305  cv::Mat result;
306 
307  switch (src.fmt)
308  {
309  case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, CV_YUV2RGB_YUYV); return result;
310  case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, CV_GRAY2RGB); return result;
311  case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, CV_BayerBG2RGB); return result;
312 
313  case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
314  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC3);
315  cv::parallel_for_(cv::Range(0, src.height), rgb565ToRGB(rawimgcv, result.data, result.cols));
316  return result;
317 
318  case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
319  case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, CV_BGR2RGB); return result;
320  }
321  LFATAL("Unknown RawImage pixel format");
322 }
323 
324 // ####################################################################################################
326 {
327  cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
328  cv::Mat result;
329 
330  switch (src.fmt)
331  {
332  case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, CV_YUV2RGBA_YUYV); return result;
333 
334  case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, CV_GRAY2RGBA); return result;
335 
336  case V4L2_PIX_FMT_SRGGB8:
337  {
338  // FIXME: we do two conversions, should get a hold of the opencv source for bayer conversions and make an RGBA
339  // version of it:
340  cv::Mat fixme;
341  cv::cvtColor(rawimgcv, fixme, CV_BayerBG2RGB);
342  cv::cvtColor(fixme, result, CV_RGB2RGBA);
343  return result;
344  }
345 
346  case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
347  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC4);
348  cv::parallel_for_(cv::Range(0, src.height), rgb565ToRGBA(rawimgcv, result.data, result.cols));
349  return result;
350 
351  case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
352 
353  case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, CV_BGR2RGBA); return result;
354  }
355  LFATAL("Unknown RawImage pixel format");
356 }
357 
358 // ####################################################################################################
360 {
361  if (img.bytesperpix() != 2) LFATAL("Can only byteswap images with 2 bytes/pixel");
362 
363 #ifdef JEVOIS_PLATFORM
364  // Use neon acceleration, in parallel threads:
365  unsigned int ncores = std::min(4U, std::thread::hardware_concurrency());
366  size_t const nbc = img.bytesize() / ncores;
367  unsigned char * ptr = img.pixelsw<unsigned char>();
368 
369  // FIXME check for possible size rounding problems
370 
371  // Launch ncores-1 threads and we will do the last chunk in the current thread:
372  std::vector<std::future<void> > fut;
373  for (unsigned int core = 0; core < ncores-1; ++core)
374  fut.push_back(std::async(std::launch::async, [&ptr, &nbc](int core) -> void {
375  unsigned char * cptr = ptr + core * nbc;
376  for (size_t i = 0; i < nbc; i += 16) vst1q_u8(cptr + i, vrev16q_u8(vld1q_u8(cptr + i)));
377  }, core));
378 
379  // Last chunk:
380  size_t const sz = img.bytesize();
381  for (size_t i = (ncores-1) * nbc; i < sz; i += 16) vst1q_u8(ptr + i, vrev16q_u8(vld1q_u8(ptr + i)));
382 
383  // Wait for all the threads to complete:
384  for (auto & f : fut) f.get();
385 #else
386  // Use CPU:
387  size_t const sz = img.width * img.height; // size in shorts
388  unsigned short * ptr = img.pixelsw<unsigned short>();
389  for (size_t i = 0; i < sz; ++i) ptr[i] = __builtin_bswap16(ptr[i]);
390 #endif
391 }
392 
393 // ####################################################################################################
394 void jevois::rawimage::paste(jevois::RawImage const & src, jevois::RawImage & dest, int x, int y)
395 {
396  if (src.fmt != dest.fmt) LFATAL("src and dest must have the same pixel format");
397  if (x < 0 || y < 0 || x + src.width > dest.width || y + src.height > dest.height)
398  LFATAL("src does not fit within dest");
399 
400  unsigned int const bpp = src.bytesperpix();
401 
402  unsigned char const * sptr = src.pixels<unsigned char>();
403  unsigned char * dptr = dest.pixelsw<unsigned char>() + (x + y * dest.width) * bpp;
404  size_t const srclinelen = src.width * bpp;
405  size_t const dstlinelen = dest.width * bpp;
406 
407  for (unsigned int j = 0; j < src.height; ++j)
408  {
409  memcpy(dptr, sptr, srclinelen);
410  sptr += srclinelen;
411  dptr += dstlinelen;
412  }
413 }
414 
415 // ####################################################################################################
416 void jevois::rawimage::roipaste(jevois::RawImage const & src, int x, int y, unsigned int w, unsigned int h,
417  jevois::RawImage & dest, int dx, int dy)
418 {
419  if (src.fmt != dest.fmt) LFATAL("src and dest must have the same pixel format");
420  if (x < 0 || y < 0 || x + w > src.width || y + h > src.height) LFATAL("roi not within source image");
421  if (dx < 0 || dy < 0 || dx + w > dest.width || dy + h > dest.height) LFATAL("roi not within dest image");
422 
423  unsigned int const bpp = src.bytesperpix();
424 
425  unsigned char const * sptr = src.pixels<unsigned char>() + (x + y * src.width) * bpp;
426  unsigned char * dptr = dest.pixelsw<unsigned char>() + (dx + dy * dest.width) * bpp;
427  size_t const srclinelen = src.width * bpp;
428  size_t const dstlinelen = dest.width * bpp;
429 
430  for (unsigned int j = 0; j < h; ++j)
431  {
432  memcpy(dptr, sptr, w * bpp);
433  sptr += srclinelen;
434  dptr += dstlinelen;
435  }
436 }
437 
438 // ####################################################################################################
439 void jevois::rawimage::pasteGreyToYUYV(cv::Mat const & src, jevois::RawImage & dest, int x, int y)
440 {
441  if (x + src.cols > int(dest.width) || y + src.rows > int(dest.height)) LFATAL("src does not fit within dest");
442  unsigned int const bpp = dest.bytesperpix();
443 
444  unsigned char const * sptr = src.data;
445  unsigned char * dptr = dest.pixelsw<unsigned char>() + (x + y * dest.width) * bpp;
446  size_t const dststride = (dest.width - src.cols) * bpp;
447 
448  for (int j = 0; j < src.rows; ++j)
449  {
450  for (int i = 0; i < src.cols; ++i) { *dptr++ = *sptr++; *dptr++ = 0x80; }
451  dptr += dststride;
452  }
453 }
454 
455 // ####################################################################################################
456 void jevois::rawimage::drawDisk(jevois::RawImage & img, int cx, int cy, unsigned int rad, unsigned int col)
457 {
458  // From the iLab Neuromorphic Vision C++ Toolkit
459  unsigned short * const dptr = img.pixelsw<unsigned short>();
460  int const w = int(img.width);
461 
462  if (rad == 0) { if (img.coordsOk(cx, cy)) dptr[cx + w * cy] = col; return; }
463 
464  int const intrad = rad;
465  for (int y = -intrad; y <= intrad; ++y)
466  {
467  int bound = int(std::sqrt(float(intrad * intrad - y * y)));
468  for (int x = -bound; x <= bound; ++x)
469  if (img.coordsOk(x + cx, y + cy)) dptr[x + cx + w * (y + cy)] = col;
470  }
471 }
472 
473 // ####################################################################################################
474 void jevois::rawimage::drawCircle(jevois::RawImage & img, int cx, int cy, unsigned int rad,
475  unsigned int thick, unsigned int col)
476 {
477  // From the iLab Neuromorphic Vision C++ Toolkit
478  if (rad == 0) { jevois::rawimage::drawDisk(img, cx, cy, thick, col); return; }
479 
480  jevois::rawimage::drawDisk(img, cx - rad, cy, thick, col);
481  jevois::rawimage::drawDisk(img, cx + rad, cy, thick, col);
482  int bound1 = rad, bound2;
483 
484  for (unsigned int dy = 1; dy <= rad; ++dy)
485  {
486  bound2 = bound1;
487  bound1 = int(0.4999F + sqrtf(rad*rad - dy*dy));
488  for (int dx = bound1; dx <= bound2; ++dx)
489  {
490  jevois::rawimage::drawDisk(img, cx - dx, cy - dy, thick, col);
491  jevois::rawimage::drawDisk(img, cx + dx, cy - dy, thick, col);
492  jevois::rawimage::drawDisk(img, cx + dx, cy + dy, thick, col);
493  jevois::rawimage::drawDisk(img, cx - dx, cy + dy, thick, col);
494  }
495  }
496 }
497 
498 // ####################################################################################################
499 void jevois::rawimage::drawLine(jevois::RawImage & img, int x1, int y1, int x2, int y2, unsigned int thick,
500  unsigned int col)
501 {
502  // From the iLab Neuromorphic Vision C++ Toolkit
503  // from Graphics Gems / Paul Heckbert
504  int const dx = x2 - x1; int const ax = std::abs(dx) << 1; int const sx = dx < 0 ? -1 : 1;
505  int const dy = y2 - y1; int const ay = std::abs(dy) << 1; int const sy = dy < 0 ? -1 : 1;
506  int const w = img.width; int const h = img.height;
507  int x = x1, y = y1;
508 
509  if (ax > ay)
510  {
511  int d = ay - (ax >> 1);
512  for (;;)
513  {
514  if (x >= 0 && x < w && y >= 0 && y < h) jevois::rawimage::drawDisk(img, x, y, thick, col);
515 
516  if (x == x2) return;
517  if (d >= 0) { y += sy; d -= ax; }
518  x += sx; d += ay;
519  }
520  }
521  else
522  {
523  int d = ax - (ay >> 1);
524  for (;;)
525  {
526  if (x >= 0 && x < w && y >= 0 && y < h) jevois::rawimage::drawDisk(img, x, y, thick, col);
527  if (y == y2) return;
528  if (d >= 0) { x += sx; d -= ay; }
529  y += sy; d += ax;
530  }
531  }
532 }
533 
534 // ####################################################################################################
535 void jevois::rawimage::drawRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
536  unsigned int thick, unsigned int col)
537 {
538  if (thick == 0)
539  jevois::rawimage::drawRect(img, x, y, w, h, col);
540  else
541  {
542  jevois::rawimage::drawLine(img, x, y, x+w, y, thick, col);
543  jevois::rawimage::drawLine(img, x, y+h, x+w, y+h, thick, col);
544  jevois::rawimage::drawLine(img, x, y, x, y+h, thick, col);
545  jevois::rawimage::drawLine(img, x+w, y, x+w, y+h, thick, col);
546  }
547 }
548 // ####################################################################################################
549 void jevois::rawimage::drawRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
550  unsigned int col)
551 {
552  if (x > int(img.width)) x = img.width;
553  if (y > int(img.height)) y = img.height;
554  if (x + w > img.width) w = img.width - x;
555  if (y + h > img.height) h = img.height - y;
556 
557  unsigned int const imgw = img.width;
558  unsigned short * b = img.pixelsw<unsigned short>() + x + y * imgw;
559 
560  // Two horizontal lines:
561  unsigned int const offy = (h-1) * imgw;
562  for (unsigned int xx = 0; xx < w; ++xx) { b[xx] = col; b[xx + offy] = col; }
563 
564  // Two vertical lines:
565  unsigned int const offx = w-1;
566  for (unsigned int yy = 0; yy < h * imgw; yy += imgw) { b[yy] = col; b[yy + offx] = col; }
567 }
568 // ####################################################################################################
569 void jevois::rawimage::drawFilledRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
570  unsigned int col)
571 {
572  if (x > int(img.width)) x = img.width;
573  if (y > int(img.height)) y = img.height;
574  if (x + w > img.width) w = img.width - x;
575  if (y + h > img.height) h = img.height - y;
576 
577  unsigned int const stride = img.width - w;
578  unsigned short * b = img.pixelsw<unsigned short>() + x + y * img.width;
579 
580  for (unsigned int yy = 0; yy < h; ++yy)
581  {
582  for (unsigned int xx = 0; xx < w; ++xx) *b++ = col;
583  b += stride;
584  }
585 }
586 
587 // ####################################################################################################
588 // Font pattern definitions:
589 namespace jevois
590 {
591  namespace font
592  {
593  extern const unsigned char font10x20[95][200];
594  extern const unsigned char font11x22[95][242];
595  extern const unsigned char font12x22[95][264];
596  extern const unsigned char font14x26[95][364];
597  extern const unsigned char font15x28[95][420];
598  extern const unsigned char font16x29[95][464];
599  extern const unsigned char font20x38[95][760];
600  extern const unsigned char font5x7[95][35];
601  extern const unsigned char font6x10[95][60];
602  extern const unsigned char font7x13[95][91];
603  extern const unsigned char font8x13bold[95][104];
604  extern const unsigned char font9x15bold[95][135];
605  } // namespace font
606 } // namespace jevois
607 
608 
609 // ####################################################################################################
610 void jevois::rawimage::writeText(jevois::RawImage & img, std::string const & txt, int x, int y, unsigned int col,
612 {
613  jevois::rawimage::writeText(img, txt.c_str(), x, y, col, font);
614 }
615 
616 // ####################################################################################################
617 void jevois::rawimage::writeText(jevois::RawImage & img, char const * txt, int x, int y, unsigned int col,
619 {
620  int len = int(strlen(txt));
621  unsigned int const imgw = img.width;
622 
623  int fontw, fonth; unsigned char const * fontptr;
624  switch (font)
625  {
626  case Font5x7: fontw = 5; fonth = 7; fontptr = &jevois::font::font5x7[0][0]; break;
627  case Font6x10: fontw = 6; fonth = 10; fontptr = &jevois::font::font6x10[0][0]; break;
628  case Font7x13: fontw = 7; fonth = 13; fontptr = &jevois::font::font7x13[0][0]; break;
629  case Font8x13bold: fontw = 8; fonth = 13; fontptr = &jevois::font::font8x13bold[0][0]; break;
630  case Font9x15bold: fontw = 9; fonth = 15; fontptr = &jevois::font::font9x15bold[0][0]; break;
631  case Font10x20: fontw = 10; fonth = 20; fontptr = &jevois::font::font10x20[0][0]; break;
632  case Font11x22: fontw = 11; fonth = 22; fontptr = &jevois::font::font11x22[0][0]; break;
633  case Font12x22: fontw = 12; fonth = 22; fontptr = &jevois::font::font12x22[0][0]; break;
634  case Font14x26: fontw = 14; fonth = 26; fontptr = &jevois::font::font14x26[0][0]; break;
635  case Font15x28: fontw = 15; fonth = 28; fontptr = &jevois::font::font15x28[0][0]; break;
636  case Font16x29: fontw = 16; fonth = 29; fontptr = &jevois::font::font16x29[0][0]; break;
637  case Font20x38: fontw = 20; fonth = 38; fontptr = &jevois::font::font20x38[0][0]; break;
638  default: LFATAL("Invalid font");
639  }
640 
641  // Clip the text so that it does not go outside the image:
642  if (y < 0 || y + fonth > int(img.height)) return;
643  while (x + len * fontw > int(imgw)) { --len; if (len <= 0) return; }
644 
645  // Be nice and handle various pixel formats:
646  switch (img.bytesperpix())
647  {
648  case 2:
649  {
650  unsigned short * b = img.pixelsw<unsigned short>() + x + y * imgw;
651 
652  for (int i = 0; i < len; ++i)
653  {
654  int idx = txt[i] - 32; if (idx >= 95) idx = 0;
655  unsigned char const * ptr = fontptr + fontw * fonth * idx;
656  unsigned short * bb = b;
657  for (int yy = 0; yy < fonth; ++yy)
658  {
659  // Draw one line of this letter, note the transparent background:
660  for (int xx = 0; xx < fontw; ++xx) if (*ptr++) ++bb; else *bb++ = col;
661  bb += imgw - fontw;
662  }
663  b += fontw;
664  }
665  }
666  break;
667 
668  case 1:
669  {
670  unsigned char * b = img.pixelsw<unsigned char>() + x + y * imgw;
671 
672  for (int i = 0; i < len; ++i)
673  {
674  int idx = txt[i] - 32; if (idx >= 95) idx = 0;
675  unsigned char const * ptr = fontptr + fontw * fonth * idx;
676  unsigned char * bb = b;
677  for (int yy = 0; yy < fonth; ++yy)
678  {
679  // Draw one line of this letter, note the transparent background:
680  for (int xx = 0; xx < fontw; ++xx) if (*ptr++) ++bb; else *bb++ = col;
681  bb += imgw - fontw;
682  }
683  b += fontw;
684  }
685  }
686  break;
687 
688  default:
689  LFATAL("Sorry, only 1 and 2 bytes/pixel images are supported for now");
690  }
691 }
692 
693 // ####################################################################################################
694 namespace
695 {
696  class bgrToBayer : public cv::ParallelLoopBody
697  {
698  public:
699  bgrToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
700  inImg(inputImage), outImg(outImage)
701  {
702  inlinesize = inputImage.cols * 3; // 3 bytes/pix for BGR
703  outlinesize = outw * 1; // 1 byte/pix for Bayer
704  }
705 
706  virtual void operator()(const cv::Range & range) const
707  {
708  for (int j = range.start; j < range.end; ++j)
709  {
710  int const inoff = j * inlinesize;
711  int const outoff = j * outlinesize;
712 
713  for (int i = 0; i < inImg.cols; i += 2)
714  {
715  int const in = inoff + i * 3;
716  int const out = outoff + i;
717 
718  if ( (j & 1) == 0) { outImg[out + 0] = inImg.data[in + 2]; outImg[out + 1] = inImg.data[in + 4]; }
719  else { outImg[out + 0] = inImg.data[in + 1]; outImg[out + 1] = inImg.data[in + 3]; }
720  }
721  }
722  }
723 
724  private:
725  cv::Mat const & inImg;
726  unsigned char * outImg;
727  int inlinesize, outlinesize;
728  };
729 
730  // ####################################################################################################
731  void convertCvBGRtoBayer(cv::Mat const & src, jevois::RawImage & dst)
732  {
733  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and BGR pixels");
734  if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
735  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
736 
737  cv::parallel_for_(cv::Range(0, src.rows), bgrToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
738  }
739 } // anonymous namespace
740 
741 // ####################################################################################################
742 namespace
743 {
744  class rgbToBayer : public cv::ParallelLoopBody
745  {
746  public:
747  rgbToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
748  inImg(inputImage), outImg(outImage)
749  {
750  inlinesize = inputImage.cols * 3; // 3 bytes/pix for RGB
751  outlinesize = outw * 1; // 1 byte/pix for Bayer
752  }
753 
754  virtual void operator()(const cv::Range & range) const
755  {
756  for (int j = range.start; j < range.end; ++j)
757  {
758  int const inoff = j * inlinesize;
759  int const outoff = j * outlinesize;
760 
761  for (int i = 0; i < inImg.cols; i += 2)
762  {
763  int const in = inoff + i * 3;
764  int const out = outoff + i;
765 
766  if ( (j & 1) == 0) { outImg[out + 0] = inImg.data[in + 0]; outImg[out + 1] = inImg.data[in + 4]; }
767  else { outImg[out + 0] = inImg.data[in + 1]; outImg[out + 1] = inImg.data[in + 5]; }
768  }
769  }
770  }
771 
772  private:
773  cv::Mat const & inImg;
774  unsigned char * outImg;
775  int inlinesize, outlinesize;
776  };
777 
778  // ####################################################################################################
779  void convertCvRGBtoBayer(cv::Mat const & src, jevois::RawImage & dst)
780  {
781  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and RGB pixels");
782  if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
783  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
784 
785  cv::parallel_for_(cv::Range(0, src.rows), rgbToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
786  }
787 } // anonymous namespace
788 
789 // ####################################################################################################
790 namespace
791 {
792  class grayToBayer : public cv::ParallelLoopBody
793  {
794  public:
795  grayToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
796  inImg(inputImage), outImg(outImage)
797  {
798  inlinesize = inputImage.cols * 1; // 1 bytes/pix for GRAY
799  outlinesize = outw * 1; // 1 byte/pix for Bayer
800  }
801 
802  virtual void operator()(const cv::Range & range) const
803  {
804  for (int j = range.start; j < range.end; ++j)
805  {
806  int const inoff = j * inlinesize;
807  int const outoff = j * outlinesize;
808 
809  memcpy(&outImg[outoff], &inImg.data[inoff], inlinesize);
810  }
811  }
812 
813  private:
814  cv::Mat const & inImg;
815  unsigned char * outImg;
816  int inlinesize, outlinesize;
817  };
818 
819  // ####################################################################################################
820  void convertCvGRAYtoBayer(cv::Mat const & src, jevois::RawImage & dst)
821  {
822  if (src.type() != CV_8UC1) LFATAL("src must have type CV_8UC1 and GRAY pixels");
823  if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
824  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
825 
826  cv::parallel_for_(cv::Range(0, src.rows), grayToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
827  }
828 } // anonymous namespace
829 
830 // ####################################################################################################
831 namespace
832 {
833  class rgbaToBayer : public cv::ParallelLoopBody
834  {
835  public:
836  rgbaToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
837  inImg(inputImage), outImg(outImage)
838  {
839  inlinesize = inputImage.cols * 4; // 4 bytes/pix for RGBA
840  outlinesize = outw * 1; // 1 byte/pix for Bayer
841  }
842 
843  virtual void operator()(const cv::Range & range) const
844  {
845  for (int j = range.start; j < range.end; ++j)
846  {
847  int const inoff = j * inlinesize;
848  int const outoff = j * outlinesize;
849 
850  for (int i = 0; i < inImg.cols; i += 2)
851  {
852  int const in = inoff + i * 4;
853  int const out = outoff + i;
854 
855  if ( (j & 1) == 0) { outImg[out + 0] = inImg.data[in + 0]; outImg[out + 1] = inImg.data[in + 5]; }
856  else { outImg[out + 0] = inImg.data[in + 1]; outImg[out + 1] = inImg.data[in + 6]; }
857  }
858  }
859  }
860 
861  private:
862  cv::Mat const & inImg;
863  unsigned char * outImg;
864  int inlinesize, outlinesize;
865  };
866 
867  // ####################################################################################################
868  void convertCvRGBAtoBayer(cv::Mat const & src, jevois::RawImage & dst)
869  {
870  if (src.type() != CV_8UC4) LFATAL("src must have type CV_8UC4 and RGBA pixels");
871  if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
872  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
873 
874  cv::parallel_for_(cv::Range(0, src.rows), rgbaToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
875  }
876 } // anonymous namespace
877 
878 // ####################################################################################################
879 namespace
880 {
881  class bgrToYUYV : public cv::ParallelLoopBody
882  {
883  public:
884  bgrToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
885  inImg(inputImage), outImg(outImage)
886  {
887  inlinesize = inputImage.cols * 3; // 3 bytes/pix for BGR
888  outlinesize = outw * 2; // 2 bytes/pix for YUYV
889  }
890 
891  virtual void operator()(const cv::Range & range) const
892  {
893  for (int j = range.start; j < range.end; ++j)
894  {
895  int const inoff = j * inlinesize;
896  int const outoff = j * outlinesize;
897 
898  for (int i = 0; i < inImg.cols; i += 2)
899  {
900  int mc = inoff + i * 3;
901  unsigned char const B1 = inImg.data[mc + 0];
902  unsigned char const G1 = inImg.data[mc + 1];
903  unsigned char const R1 = inImg.data[mc + 2];
904  unsigned char const B2 = inImg.data[mc + 3];
905  unsigned char const G2 = inImg.data[mc + 4];
906  unsigned char const R2 = inImg.data[mc + 5];
907 
908  float const Y1 = (0.257F * R1) + (0.504F * G1) + (0.098F * B1) + 16.0F;
909  //float const V1 = (0.439F * R1) - (0.368F * G1) - (0.071F * B1) + 128.0F;
910  float const U1 = -(0.148F * R1) - (0.291F * G1) + (0.439F * B1) + 128.0F;
911  float const Y2 = (0.257F * R2) + (0.504F * G2) + (0.098F * B2) + 16.0F;
912  float const V2 = (0.439F * R2) - (0.368F * G2) - (0.071F * B2) + 128.0F;
913  //float const U2 = -(0.148F * R2) - (0.291F * G2) + (0.439F * B2) + 128.0F;
914 
915  mc = outoff + i * 2;
916  outImg[mc + 0] = Y1;
917  outImg[mc + 1] = U1;
918  outImg[mc + 2] = Y2;
919  outImg[mc + 3] = V2;
920  }
921  }
922  }
923 
924  private:
925  cv::Mat const & inImg;
926  unsigned char * outImg;
927  int inlinesize, outlinesize;
928  };
929 
930  // ####################################################################################################
931  void convertCvBGRtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
932  {
933  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and BGR pixels");
934  if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
935  if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
936 
937  cv::parallel_for_(cv::Range(0, src.rows), bgrToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
938  }
939 } // anonymous namespace
940 
941 // ####################################################################################################
942 void jevois::rawimage::pasteBGRtoYUYV(cv::Mat const & src, jevois::RawImage & dst, int x, int y)
943 {
944  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and BGR pixels");
945  if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
946  if (x + src.cols > int(dst.width) || y + src.rows > int(dst.height)) LFATAL("src does not fit within dst");
947 
948  cv::parallel_for_(cv::Range(0, src.rows), bgrToYUYV(src, dst.pixelsw<unsigned char>() +
949  (x + y * dst.width) * dst.bytesperpix(), dst.width));
950 }
951 
952 // ####################################################################################################
953 namespace
954 {
955  class rgbToYUYV : public cv::ParallelLoopBody
956  {
957  public:
958  rgbToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
959  inImg(inputImage), outImg(outImage)
960  {
961  inlinesize = inputImage.cols * 3; // 3 bytes/pix for RGB
962  outlinesize = outw * 2; // 2 bytes/pix for YUYV
963  }
964 
965  virtual void operator()(const cv::Range & range) const
966  {
967  for (int j = range.start; j < range.end; ++j)
968  {
969  int const inoff = j * inlinesize;
970  int const outoff = j * outlinesize;
971 
972  for (int i = 0; i < inImg.cols; i += 2)
973  {
974  int mc = inoff + i * 3;
975  unsigned char const R1 = inImg.data[mc + 0];
976  unsigned char const G1 = inImg.data[mc + 1];
977  unsigned char const B1 = inImg.data[mc + 2];
978  unsigned char const R2 = inImg.data[mc + 3];
979  unsigned char const G2 = inImg.data[mc + 4];
980  unsigned char const B2 = inImg.data[mc + 5];
981 
982  float const Y1 = (0.257F * R1) + (0.504F * G1) + (0.098F * B1) + 16.0F;
983  //float const V1 = (0.439F * R1) - (0.368F * G1) - (0.071F * B1) + 128.0F;
984  float const U1 = -(0.148F * R1) - (0.291F * G1) + (0.439F * B1) + 128.0F;
985  float const Y2 = (0.257F * R2) + (0.504F * G2) + (0.098F * B2) + 16.0F;
986  float const V2 = (0.439F * R2) - (0.368F * G2) - (0.071F * B2) + 128.0F;
987  //float const U2 = -(0.148F * R2) - (0.291F * G2) + (0.439F * B2) + 128.0F;
988 
989  mc = outoff + i * 2;
990  outImg[mc + 0] = Y1;
991  outImg[mc + 1] = U1;
992  outImg[mc + 2] = Y2;
993  outImg[mc + 3] = V2;
994  }
995  }
996  }
997 
998  private:
999  cv::Mat const & inImg;
1000  unsigned char * outImg;
1001  int inlinesize, outlinesize;
1002  };
1003 
1004  // ####################################################################################################
1005  void convertCvRGBtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
1006  {
1007  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and RGB pixels");
1008  if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1009  if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1010 
1011  cv::parallel_for_(cv::Range(0, src.rows), rgbToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
1012  }
1013 } // anonymous namespace
1014 
1015 // ####################################################################################################
1016 namespace
1017 {
1018  class grayToYUYV : public cv::ParallelLoopBody
1019  {
1020  public:
1021  grayToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
1022  inImg(inputImage), outImg(outImage)
1023  {
1024  inlinesize = inputImage.cols * 1; // 1 bytes/pix for GRAY
1025  outlinesize = outw * 2; // 2 bytes/pix for YUYV
1026  }
1027 
1028  virtual void operator()(const cv::Range & range) const
1029  {
1030  for (int j = range.start; j < range.end; ++j)
1031  {
1032  int const inoff = j * inlinesize;
1033  int const outoff = j * outlinesize;
1034 
1035  for (int i = 0; i < inImg.cols; ++i)
1036  {
1037  int mc = inoff + i;
1038  unsigned char const G = inImg.data[mc + 0];
1039 
1040  mc = outoff + i * 2;
1041  outImg[mc + 0] = G;
1042  outImg[mc + 1] = 0x80;
1043  }
1044  }
1045  }
1046 
1047  private:
1048  cv::Mat const & inImg;
1049  unsigned char * outImg;
1050  int inlinesize, outlinesize;
1051  };
1052 
1053  // ####################################################################################################
1054  void convertCvGRAYtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
1055  {
1056  if (src.type() != CV_8UC1) LFATAL("src must have type CV_8UC1 and GRAY pixels");
1057  if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1058  if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1059 
1060  cv::parallel_for_(cv::Range(0, src.rows), grayToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
1061  }
1062 } // anonymous namespace
1063 
1064 // ####################################################################################################
1065 namespace
1066 {
1067  class rgbaToYUYV : public cv::ParallelLoopBody
1068  {
1069  public:
1070  rgbaToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
1071  inImg(inputImage), outImg(outImage)
1072  {
1073  inlinesize = inputImage.cols * 4; // 4 bytes/pix for RGBA
1074  outlinesize = outw * 2; // 2 bytes/pix for YUYV
1075  }
1076 
1077  virtual void operator()(const cv::Range & range) const
1078  {
1079  for (int j = range.start; j < range.end; ++j)
1080  {
1081  int const inoff = j * inlinesize;
1082  int const outoff = j * outlinesize;
1083 
1084  for (int i = 0; i < inImg.cols; i += 2)
1085  {
1086  int mc = inoff + i * 4;
1087  unsigned char const R1 = inImg.data[mc + 0];
1088  unsigned char const G1 = inImg.data[mc + 1];
1089  unsigned char const B1 = inImg.data[mc + 2];
1090  unsigned char const R2 = inImg.data[mc + 4];
1091  unsigned char const G2 = inImg.data[mc + 5];
1092  unsigned char const B2 = inImg.data[mc + 6];
1093 
1094  float const Y1 = (0.257F * R1) + (0.504F * G1) + (0.098F * B1) + 16.0F;
1095  float const U1 = -(0.148F * R1) - (0.291F * G1) + (0.439F * B1) + 128.0F;
1096  float const Y2 = (0.257F * R2) + (0.504F * G2) + (0.098F * B2) + 16.0F;
1097  float const V2 = (0.439F * R2) - (0.368F * G2) - (0.071F * B2) + 128.0F;
1098 
1099  mc = outoff + i * 2;
1100  outImg[mc + 0] = Y1;
1101  outImg[mc + 1] = U1;
1102  outImg[mc + 2] = Y2;
1103  outImg[mc + 3] = V2;
1104  }
1105  }
1106  }
1107 
1108  private:
1109  cv::Mat const & inImg;
1110  unsigned char * outImg;
1111  int inlinesize, outlinesize;
1112  };
1113 
1114  // ####################################################################################################
1115  void convertCvRGBAtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
1116  {
1117  if (src.type() != CV_8UC4) LFATAL("src must have type CV_8UC4 and RGBA pixels");
1118  if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
1119  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
1120 
1121  cv::parallel_for_(cv::Range(0, src.rows), rgbaToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
1122  }
1123 } // anonymous namespace
1124 
1125 // ####################################################################################################
1126 void jevois::rawimage::convertCvBGRtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
1127 {
1128  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and BGR pixels");
1129  if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1130 
1131  // Note how the destination opencv image dstcv here is just a shell, the actual pixel data is in dst:
1132  cv::Mat dstcv = jevois::rawimage::cvImage(dst);
1133 
1134  switch (dst.fmt)
1135  {
1136  case V4L2_PIX_FMT_SRGGB8: convertCvBGRtoBayer(src, dst); break;
1137  case V4L2_PIX_FMT_YUYV: convertCvBGRtoYUYV(src, dst); break;
1138  case V4L2_PIX_FMT_GREY: cv::cvtColor(src, dstcv, CV_BGR2GRAY); break;
1139  case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, CV_BGR2BGR565); break;
1140  case V4L2_PIX_FMT_MJPEG: jevois::compressBGRtoJpeg(src, dst, quality); break;
1141  case V4L2_PIX_FMT_BGR24: memcpy(dst.pixelsw<void>(), src.data, dst.width * dst.height * dst.bytesperpix()); break;
1142  default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
1143  }
1144 }
1145 
1146 // ####################################################################################################
1147 void jevois::rawimage::convertCvRGBtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
1148 {
1149  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and RGB pixels");
1150  if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
1151 
1152  // Note how the destination opencv image dstcv here is just a shell, the actual pixel data is in dst:
1153  cv::Mat dstcv = jevois::rawimage::cvImage(dst);
1154 
1155  switch (dst.fmt)
1156  {
1157  case V4L2_PIX_FMT_SRGGB8: convertCvRGBtoBayer(src, dst); break;
1158  case V4L2_PIX_FMT_YUYV: convertCvRGBtoYUYV(src, dst); break;
1159  case V4L2_PIX_FMT_GREY: cv::cvtColor(src, dstcv, CV_RGB2GRAY); break;
1160  case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, CV_RGB2BGR565); break;
1161  case V4L2_PIX_FMT_MJPEG: jevois::compressRGBtoJpeg(src, dst, quality); break;
1162  case V4L2_PIX_FMT_BGR24: cv::cvtColor(src, dstcv, CV_RGB2BGR); break;
1163  default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
1164  }
1165 }
1166 
1167 // ####################################################################################################
1169 {
1170  if (src.type() != CV_8UC4) LFATAL("src must have type CV_8UC4 and RGBA pixels");
1171  if (dst.fmt != V4L2_PIX_FMT_GREY) LFATAL("dst must have pixel type V4L2_PIX_FMT_GREY");
1172  int const w = src.cols, h = src.rows;
1173  if (int(dst.width) < w || int(dst.height) < 4 * h) LFATAL("dst must be at least as wide and 4x as tall as src");
1174 
1175  unsigned char const * sptr = src.data; unsigned char * dptr = dst.pixelsw<unsigned char>();
1176  int const stride = int(dst.width) - w;
1177 
1178  // Do R, G, B in 3 threads then A in the current thread:
1179  std::vector<std::future<void> > fut;
1180  for (int i = 0; i < 3; ++i) fut.push_back(std::async(std::launch::async, [&](int offset) {
1181  unsigned char const * s = sptr + offset; unsigned char * d = dptr + offset * w * h;
1182  for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { *d++ = *s; s += 4; } d += stride; } }, i));
1183 
1184  unsigned char const * s = sptr + 3; unsigned char * d = dptr + 3 * w * h;
1185  for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { *d++ = *s; s += 4; } d += stride; }
1186 
1187  // Wait for all threads to complete (those should never throw):
1188  for (auto & f : fut) f.get();
1189 }
1190 
1191 // ####################################################################################################
1192 void jevois::rawimage::convertCvRGBAtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
1193 {
1194  if (src.type() != CV_8UC4) LFATAL("src must have type CV_8UC4 and RGBA pixels");
1195  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
1196 
1197  cv::Mat dstcv = jevois::rawimage::cvImage(dst);
1198 
1199  switch (dst.fmt)
1200  {
1201  case V4L2_PIX_FMT_SRGGB8: convertCvRGBAtoBayer(src, dst); break;
1202  case V4L2_PIX_FMT_YUYV: convertCvRGBAtoYUYV(src, dst); break;
1203  case V4L2_PIX_FMT_GREY: cv::cvtColor(src, dstcv, CV_RGBA2GRAY); break;
1204  case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, CV_BGRA2BGR565); break;
1205  case V4L2_PIX_FMT_MJPEG: jevois::compressRGBAtoJpeg(src, dst, quality); break;
1206  case V4L2_PIX_FMT_BGR24: cv::cvtColor(src, dstcv, CV_RGBA2BGR); break;
1207  default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
1208  }
1209 }
1210 
1211 // ####################################################################################################
1212 void jevois::rawimage::convertCvGRAYtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
1213 {
1214  if (src.type() != CV_8UC1) LFATAL("src must have type CV_8UC1 and Gray pixels");
1215  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
1216 
1217  cv::Mat dstcv = jevois::rawimage::cvImage(dst);
1218 
1219  switch (dst.fmt)
1220  {
1221  case V4L2_PIX_FMT_SRGGB8: convertCvGRAYtoBayer(src, dst); break;
1222  case V4L2_PIX_FMT_YUYV: convertCvGRAYtoYUYV(src, dst); break;
1223  case V4L2_PIX_FMT_GREY: memcpy(dst.pixelsw<void>(), src.data, dst.width * dst.height * dst.bytesperpix()); break;
1224  case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, CV_GRAY2BGR565); break;
1225  case V4L2_PIX_FMT_MJPEG: jevois::compressGRAYtoJpeg(src, dst, quality); break;
1226  case V4L2_PIX_FMT_BGR24: cv::cvtColor(src, dstcv, CV_GRAY2BGR); break;
1227  default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
1228  }
1229 }
1230 
1231 // ####################################################################################################
1232 namespace
1233 {
1234  class hflipYUYV : public cv::ParallelLoopBody
1235  {
1236  public:
1237  hflipYUYV(unsigned char * outImage, size_t outw) :
1238  outImg(outImage), linesize(outw * 2) // 2 bytes/pix for YUYV
1239  { }
1240 
1241  virtual void operator()(const cv::Range & range) const
1242  {
1243  for (int j = range.start; j < range.end; ++j)
1244  {
1245  int const off = j * linesize;
1246 
1247  for (int i = 0; i < linesize / 2; i += 4)
1248  {
1249  unsigned char * ptr1 = outImg + off + i;
1250  unsigned char * ptr2 = outImg + off + linesize - 4 - i;
1251  std::swap(ptr1[0], ptr2[2]);
1252  std::swap(ptr1[1], ptr2[1]);
1253  std::swap(ptr1[2], ptr2[0]);
1254  std::swap(ptr1[3], ptr2[3]);
1255  }
1256  }
1257  }
1258 
1259  private:
1260  unsigned char * outImg;
1261  int linesize;
1262  };
1263 
1264 } // anonymous namespace
1265 
1266 // ####################################################################################################
1268 {
1269  if (img.fmt != V4L2_PIX_FMT_YUYV) LFATAL("img format must be V4L2_PIX_FMT_YUYV");
1270  cv::parallel_for_(cv::Range(0, img.height), hflipYUYV(img.pixelsw<unsigned char>(), img.width));
1271 }
1272 
cv::Mat convertToCvRGB(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV RGB byte...
Definition: RawImageOps.C:302
cv::Mat convertToCvRGBA(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV RGB-A byte...
Definition: RawImageOps.C:325
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:30
const unsigned char font7x13[95][91]
Definition: Font7x13.C:8
const unsigned char font16x29[95][464]
Definition: Font16x29.C:8
void drawCircle(RawImage &img, int x, int y, unsigned int rad, unsigned int thick, unsigned int col)
Draw a circle in a YUYV image.
Definition: RawImageOps.C:474
void writeText(RawImage &img, std::string const &txt, int x, int y, unsigned int col, Font font=Font6x10)
Write some text in an image.
Definition: RawImageOps.C:610
const unsigned char font12x22[95][264]
Definition: Font12x22.C:8
cv::Mat convertToCvBGR(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV BGR byte...
Definition: RawImageOps.C:279
unsigned int height
Image height in pixels.
Definition: RawImage.H:146
const unsigned char font9x15bold[95][135]
Definition: Font9x15bold.C:8
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.
Definition: RawImageOps.C:499
T * pixelsw()
Shortcut access to pixels, read-write.
Definition: RawImageImpl.H:24
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
const unsigned char font20x38[95][760]
Definition: Font20x38.C:8
const unsigned char font11x22[95][242]
Definition: Font11x22.C:8
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:147
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition: RawImage.H:149
const unsigned char font10x20[95][200]
Definition: Font10x20.C:8
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.
Definition: RawImageOps.C:416
const unsigned char font5x7[95][35]
Definition: Font5x7.C:7
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 int v4l2BytesPerPix(unsigned int fcc)
Return the number of bytes per pixel for a given V4L2_PIX_FMT_...
Definition: Utils.C:63
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
Font
Available fonts for writeText()
Definition: RawImageOps.H:140
void hFlipYUYV(RawImage &img)
Flip a YUYV RawImage horizontally while preserving color information.
Definition: RawImageOps.C:1267
void pasteBGRtoYUYV(cv::Mat const &src, RawImage &dst, int dx, int dy)
Paste a BGR byte image into a YUYV image.
Definition: RawImageOps.C:942
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition: Utils.C:37
cv::Mat convertToCvGray(RawImage const &src)
Convert RawImage to OpenCV doing color conversion from any RawImage source pixel to OpenCV gray byte...
Definition: RawImageOps.C:245
void convertCvBGRtoRawImage(cv::Mat const &src, RawImage &dst, int quality)
Convert a BGR cv::Mat to RawImage with already-allocated pixels and pixel type.
Definition: RawImageOps.C:1126
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
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level. ...
Definition: Log.H:212
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
T const * pixels() const
Shortcut access to pixels, read-only.
Definition: RawImageImpl.H:29
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.
Definition: RawImageOps.C:569
void convertCvRGBAtoRawImage(cv::Mat const &src, RawImage &dst, int quality)
Convert an RGBA cv::Mat to RawImage with already-allocated pixels and pixel type. ...
Definition: RawImageOps.C:1192
const unsigned char font15x28[95][420]
Definition: Font15x28.C:8
void unpackCvRGBAtoGrayRawImage(cv::Mat const &src, RawImage &dst)
Split an RGBA cv::Mat into a 4x taller grey RawImage with already-allocated pixels.
Definition: RawImageOps.C:1168
const unsigned char font8x13bold[95][104]
Definition: Font8x13bold.C:8
const unsigned char font6x10[95][60]
Definition: Font6x10.C:8
void pasteGreyToYUYV(cv::Mat const &src, RawImage &dest, int dx, int dy)
Paste a grey byte image into a YUYV image.
Definition: RawImageOps.C:439
void convertCvGRAYtoRawImage(cv::Mat const &src, RawImage &dst, int quality)
Convert a Gray cv::Mat to RawImage with already-allocated pixels and pixel type.
Definition: RawImageOps.C:1212
unsigned int bytesperpix() const
Helper function to get the number of bytes/pixel given the RawImage pixel format. ...
Definition: RawImage.C:34
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
void drawDisk(RawImage &img, int x, int y, unsigned int rad, unsigned int col)
Draw a disk in a YUYV image.
Definition: RawImageOps.C:456
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.
Definition: RawImageOps.C:535
void byteSwap(RawImage &img)
Swap pairs of bytes in a RawImage.
Definition: RawImageOps.C:359
const unsigned char font14x26[95][364]
Definition: Font14x26.C:8
bool coordsOk(int x, int y) const
Helper function to check that coords are within image bounds.
Definition: RawImage.C:76
unsigned int width
Image width in pixels.
Definition: RawImage.H:145
void convertCvRGBtoRawImage(cv::Mat const &src, RawImage &dst, int quality)
Convert a RGB cv::Mat to RawImage with already-allocated pixels and pixel type.
Definition: RawImageOps.C:1147
void paste(RawImage const &src, RawImage &dest, int dx, int dy)
Paste an image within another of same pixel type.
Definition: RawImageOps.C:394