JeVois  1.0
JeVois Smart Embedded Machine Vision Toolkit
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 // ####################################################################################################
204 {
205  cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
206  cv::Mat result;
207 
208  switch (src.fmt)
209  {
210  case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, CV_YUV2GRAY_YUYV); return result;
211  case V4L2_PIX_FMT_GREY: return rawimgcv;
212  case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, CV_BayerBG2GRAY); return result;
213 
214  case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
215  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC1);
216  cv::parallel_for_(cv::Range(0, src.height), rgb565ToGray(rawimgcv, result.data, result.cols));
217  return result;
218 
219  case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
220  case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, CV_BGR2GRAY); return result;
221  }
222  LFATAL("Unknown RawImage pixel format");
223 }
224 
225 // ####################################################################################################
227 {
228  cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
229  cv::Mat result;
230 
231  switch (src.fmt)
232  {
233  case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, CV_YUV2BGR_YUYV); return result;
234  case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, CV_GRAY2BGR); return result;
235  case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, CV_BayerBG2BGR); return result;
236 
237  case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
238  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC3);
239  cv::parallel_for_(cv::Range(0, src.height), rgb565ToBGR(rawimgcv, result.data, result.cols));
240  return result;
241 
242  case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
243  case V4L2_PIX_FMT_BGR24: return rawimgcv;
244  }
245  LFATAL("Unknown RawImage pixel format");
246 }
247 
248 // ####################################################################################################
250 {
251  cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
252  cv::Mat result;
253 
254  switch (src.fmt)
255  {
256  case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, CV_YUV2RGB_YUYV); return result;
257  case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, CV_GRAY2RGB); return result;
258  case V4L2_PIX_FMT_SRGGB8: cv::cvtColor(rawimgcv, result, CV_BayerBG2RGB); return result;
259 
260  case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
261  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC3);
262  cv::parallel_for_(cv::Range(0, src.height), rgb565ToRGB(rawimgcv, result.data, result.cols));
263  return result;
264 
265  case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
266  case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, CV_BGR2RGB); return result;
267  }
268  LFATAL("Unknown RawImage pixel format");
269 }
270 
271 // ####################################################################################################
273 {
274  cv::Mat rawimgcv = jevois::rawimage::cvImage(src);
275  cv::Mat result;
276 
277  switch (src.fmt)
278  {
279  case V4L2_PIX_FMT_YUYV: cv::cvtColor(rawimgcv, result, CV_YUV2RGBA_YUYV); return result;
280 
281  case V4L2_PIX_FMT_GREY: cv::cvtColor(rawimgcv, result, CV_GRAY2RGBA); return result;
282 
283  case V4L2_PIX_FMT_SRGGB8:
284  {
285  // FIXME: we do two conversions, should get a hold of the opencv source for bayer conversions and make an RGBA
286  // version of it:
287  cv::Mat fixme;
288  cv::cvtColor(rawimgcv, fixme, CV_BayerBG2RGB);
289  cv::cvtColor(fixme, result, CV_RGB2RGBA);
290  return result;
291  }
292 
293  case V4L2_PIX_FMT_RGB565: // camera outputs big-endian pixels, cv::cvtColor() assumes little-endian
294  result = cv::Mat(cv::Size(src.width, src.height), CV_8UC4);
295  cv::parallel_for_(cv::Range(0, src.height), rgb565ToRGBA(rawimgcv, result.data, result.cols));
296  return result;
297 
298  case V4L2_PIX_FMT_MJPEG: LFATAL("MJPEG not supported");
299 
300  case V4L2_PIX_FMT_BGR24: cv::cvtColor(rawimgcv, result, CV_BGR2RGBA); return result;
301  }
302  LFATAL("Unknown RawImage pixel format");
303 }
304 
305 // ####################################################################################################
307 {
308  if (img.bytesperpix() != 2) LFATAL("Can only byteswap images with 2 bytes/pixel");
309 
310 #ifdef JEVOIS_PLATFORM
311  // Use neon acceleration, in parallel threads:
312  unsigned int ncores = std::min(4U, std::thread::hardware_concurrency());
313  size_t const nbc = img.bytesize() / ncores;
314  unsigned char * ptr = img.pixelsw<unsigned char>();
315 
316  // FIXME check for possible size rounding problems
317 
318  // Launch ncores-1 threads and we will do the last chunk in the current thread:
319  std::vector<std::future<void> > fut;
320  for (unsigned int core = 0; core < ncores-1; ++core)
321  fut.push_back(std::async(std::launch::async, [&ptr, &nbc](int core) -> void {
322  unsigned char * cptr = ptr + core * nbc;
323  for (size_t i = 0; i < nbc; i += 16) vst1q_u8(cptr + i, vrev16q_u8(vld1q_u8(cptr + i)));
324  }, core));
325 
326  // Last chunk:
327  size_t const sz = img.bytesize();
328  for (size_t i = (ncores-1) * nbc; i < sz; i += 16) vst1q_u8(ptr + i, vrev16q_u8(vld1q_u8(ptr + i)));
329 
330  // Wait for all the threads to complete:
331  for (auto & f : fut) f.get();
332 #else
333  // Use CPU:
334  size_t const sz = img.width * img.height; // size in shorts
335  unsigned short * ptr = img.pixelsw<unsigned short>();
336  for (size_t i = 0; i < sz; ++i) ptr[i] = __builtin_bswap16(ptr[i]);
337 #endif
338 }
339 
340 // ####################################################################################################
341 void jevois::rawimage::paste(jevois::RawImage const & src, jevois::RawImage & dest, int x, int y)
342 {
343  if (src.fmt != dest.fmt) LFATAL("src and dest must have the same pixel format");
344  if (x < 0 || y < 0 || x + src.width > dest.width || y + src.height > dest.height)
345  LFATAL("src does not fit within dest");
346 
347  unsigned int const bpp = src.bytesperpix();
348 
349  unsigned char const * sptr = src.pixels<unsigned char>();
350  unsigned char * dptr = dest.pixelsw<unsigned char>() + (x + y * dest.width) * bpp;
351  size_t const srclinelen = src.width * bpp;
352  size_t const dstlinelen = dest.width * bpp;
353 
354  for (unsigned int j = 0; j < src.height; ++j)
355  {
356  memcpy(dptr, sptr, srclinelen);
357  sptr += srclinelen;
358  dptr += dstlinelen;
359  }
360 }
361 
362 // ####################################################################################################
363 void jevois::rawimage::roipaste(jevois::RawImage const & src, int x, int y, unsigned int w, unsigned int h,
364  jevois::RawImage & dest, int dx, int dy)
365 {
366  if (src.fmt != dest.fmt) LFATAL("src and dest must have the same pixel format");
367  if (x < 0 || y < 0 || x + w > src.width || y + h > src.height) LFATAL("roi not within source image");
368  if (dx < 0 || dy < 0 || dx + w > dest.width || dy + h > dest.height) LFATAL("roi not within dest image");
369 
370  unsigned int const bpp = src.bytesperpix();
371 
372  unsigned char const * sptr = src.pixels<unsigned char>() + (x + y * src.width) * bpp;
373  unsigned char * dptr = dest.pixelsw<unsigned char>() + (dx + dy * dest.width) * bpp;
374  size_t const srclinelen = src.width * bpp;
375  size_t const dstlinelen = dest.width * bpp;
376 
377  for (unsigned int j = 0; j < h; ++j)
378  {
379  memcpy(dptr, sptr, w * bpp);
380  sptr += srclinelen;
381  dptr += dstlinelen;
382  }
383 }
384 
385 // ####################################################################################################
386 void jevois::rawimage::pasteGreyToYUYV(cv::Mat const & src, jevois::RawImage & dest, int x, int y)
387 {
388  if (x + src.cols > int(dest.width) || y + src.rows > int(dest.height)) LFATAL("src does not fit within dest");
389  unsigned int const bpp = dest.bytesperpix();
390 
391  unsigned char const * sptr = src.data;
392  unsigned char * dptr = dest.pixelsw<unsigned char>() + (x + y * dest.width) * bpp;
393  size_t const dststride = (dest.width - src.cols) * bpp;
394 
395  for (int j = 0; j < src.rows; ++j)
396  {
397  for (int i = 0; i < src.cols; ++i) { *dptr++ = *sptr++; *dptr++ = 0x80; }
398  dptr += dststride;
399  }
400 }
401 
402 // ####################################################################################################
403 void jevois::rawimage::drawDisk(jevois::RawImage & img, int cx, int cy, unsigned int rad, unsigned int col)
404 {
405  // From the iLab Neuromorphic Vision C++ Toolkit
406  unsigned short * const dptr = img.pixelsw<unsigned short>();
407  int const w = int(img.width);
408 
409  if (rad == 0) { if (img.coordsOk(cx, cy)) dptr[cx + w * cy] = col; return; }
410 
411  int const intrad = rad;
412  for (int y = -intrad; y <= intrad; ++y)
413  {
414  int bound = int(std::sqrt(float(intrad * intrad - y * y)));
415  for (int x = -bound; x <= bound; ++x)
416  if (img.coordsOk(x + cx, y + cy)) dptr[x + cx + w * (y + cy)] = col;
417  }
418 }
419 
420 // ####################################################################################################
421 void jevois::rawimage::drawCircle(jevois::RawImage & img, int cx, int cy, unsigned int rad,
422  unsigned int thick, unsigned int col)
423 {
424  // From the iLab Neuromorphic Vision C++ Toolkit
425  if (rad == 0) { jevois::rawimage::drawDisk(img, cx, cy, thick, col); return; }
426 
427  jevois::rawimage::drawDisk(img, cx - rad, cy, thick, col);
428  jevois::rawimage::drawDisk(img, cx + rad, cy, thick, col);
429  int bound1 = rad, bound2;
430 
431  for (unsigned int dy = 1; dy <= rad; ++dy)
432  {
433  bound2 = bound1;
434  bound1 = int(0.4999F + sqrtf(rad*rad - dy*dy));
435  for (int dx = bound1; dx <= bound2; ++dx)
436  {
437  jevois::rawimage::drawDisk(img, cx - dx, cy - dy, thick, col);
438  jevois::rawimage::drawDisk(img, cx + dx, cy - dy, thick, col);
439  jevois::rawimage::drawDisk(img, cx + dx, cy + dy, thick, col);
440  jevois::rawimage::drawDisk(img, cx - dx, cy + dy, thick, col);
441  }
442  }
443 }
444 
445 // ####################################################################################################
446 void jevois::rawimage::drawLine(jevois::RawImage & img, int x1, int y1, int x2, int y2, unsigned int thick,
447  unsigned int col)
448 {
449  // From the iLab Neuromorphic Vision C++ Toolkit
450  // from Graphics Gems / Paul Heckbert
451  int const dx = x2 - x1; int const ax = std::abs(dx) << 1; int const sx = dx < 0 ? -1 : 1;
452  int const dy = y2 - y1; int const ay = std::abs(dy) << 1; int const sy = dy < 0 ? -1 : 1;
453  int const w = img.width; int const h = img.height;
454  int x = x1, y = y1;
455 
456  if (ax > ay)
457  {
458  int d = ay - (ax >> 1);
459  for (;;)
460  {
461  if (x >= 0 && x < w && y >= 0 && y < h) jevois::rawimage::drawDisk(img, x, y, thick, col);
462 
463  if (x == x2) return;
464  if (d >= 0) { y += sy; d -= ax; }
465  x += sx; d += ay;
466  }
467  }
468  else
469  {
470  int d = ax - (ay >> 1);
471  for (;;)
472  {
473  if (x >= 0 && x < w && y >= 0 && y < h) jevois::rawimage::drawDisk(img, x, y, thick, col);
474  if (y == y2) return;
475  if (d >= 0) { x += sx; d -= ay; }
476  y += sy; d += ax;
477  }
478  }
479 }
480 
481 // ####################################################################################################
482 void jevois::rawimage::drawRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
483  unsigned int thick, unsigned int col)
484 {
485  if (thick == 0)
486  jevois::rawimage::drawRect(img, x, y, w, h, col);
487  else
488  {
489  jevois::rawimage::drawLine(img, x, y, x+w, y, thick, col);
490  jevois::rawimage::drawLine(img, x, y+h, x+w, y+h, thick, col);
491  jevois::rawimage::drawLine(img, x, y, x, y+h, thick, col);
492  jevois::rawimage::drawLine(img, x+w, y, x+w, y+h, thick, col);
493  }
494 }
495 // ####################################################################################################
496 void jevois::rawimage::drawRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
497  unsigned int col)
498 {
499  if (x > int(img.width)) x = img.width;
500  if (y > int(img.height)) y = img.height;
501  if (x + w > img.width) w = img.width - x;
502  if (y + h > img.height) h = img.height - y;
503 
504  unsigned int const imgw = img.width;
505  unsigned short * b = img.pixelsw<unsigned short>() + x + y * imgw;
506 
507  // Two horizontal lines:
508  unsigned int const offy = (h-1) * imgw;
509  for (unsigned int xx = 0; xx < w; ++xx) { b[xx] = col; b[xx + offy] = col; }
510 
511  // Two vertical lines:
512  unsigned int const offx = w-1;
513  for (unsigned int yy = 0; yy < h * imgw; yy += imgw) { b[yy] = col; b[yy + offx] = col; }
514 }
515 // ####################################################################################################
516 void jevois::rawimage::drawFilledRect(jevois::RawImage & img, int x, int y, unsigned int w, unsigned int h,
517  unsigned int col)
518 {
519  if (x > int(img.width)) x = img.width;
520  if (y > int(img.height)) y = img.height;
521  if (x + w > img.width) w = img.width - x;
522  if (y + h > img.height) h = img.height - y;
523 
524  unsigned int const stride = img.width - w;
525  unsigned short * b = img.pixelsw<unsigned short>() + x + y * img.width;
526 
527  for (unsigned int yy = 0; yy < h; ++yy)
528  {
529  for (unsigned int xx = 0; xx < w; ++xx) *b++ = col;
530  b += stride;
531  }
532 }
533 
534 // ####################################################################################################
535 // Font pattern definitions:
536 namespace jevois
537 {
538  namespace font
539  {
540  extern const unsigned char font10x20[95][200];
541  extern const unsigned char font11x22[95][242];
542  extern const unsigned char font12x22[95][264];
543  extern const unsigned char font14x26[95][364];
544  extern const unsigned char font15x28[95][420];
545  extern const unsigned char font16x29[95][464];
546  extern const unsigned char font20x38[95][760];
547  extern const unsigned char font5x7[95][35];
548  extern const unsigned char font6x10[95][60];
549  extern const unsigned char font7x13[95][91];
550  extern const unsigned char font8x13bold[95][104];
551  extern const unsigned char font9x15bold[95][135];
552  } // namespace font
553 } // namespace jevois
554 
555 
556 // ####################################################################################################
557 void jevois::rawimage::writeText(jevois::RawImage & img, std::string const & txt, int x, int y, unsigned int col,
559 {
560  jevois::rawimage::writeText(img, txt.c_str(), x, y, col, font);
561 }
562 
563 // ####################################################################################################
564 void jevois::rawimage::writeText(jevois::RawImage & img, char const * txt, int x, int y, unsigned int col,
566 {
567  int len = int(strlen(txt));
568  unsigned int const imgw = img.width;
569 
570  int fontw, fonth; unsigned char const * fontptr;
571  switch (font)
572  {
573  case Font5x7: fontw = 5; fonth = 7; fontptr = &jevois::font::font5x7[0][0]; break;
574  case Font6x10: fontw = 6; fonth = 10; fontptr = &jevois::font::font6x10[0][0]; break;
575  case Font7x13: fontw = 7; fonth = 13; fontptr = &jevois::font::font7x13[0][0]; break;
576  case Font8x13bold: fontw = 8; fonth = 13; fontptr = &jevois::font::font8x13bold[0][0]; break;
577  case Font9x15bold: fontw = 9; fonth = 15; fontptr = &jevois::font::font9x15bold[0][0]; break;
578  case Font10x20: fontw = 10; fonth = 20; fontptr = &jevois::font::font10x20[0][0]; break;
579  case Font11x22: fontw = 11; fonth = 22; fontptr = &jevois::font::font11x22[0][0]; break;
580  case Font12x22: fontw = 12; fonth = 22; fontptr = &jevois::font::font12x22[0][0]; break;
581  case Font14x26: fontw = 14; fonth = 26; fontptr = &jevois::font::font14x26[0][0]; break;
582  case Font15x28: fontw = 15; fonth = 28; fontptr = &jevois::font::font15x28[0][0]; break;
583  case Font16x29: fontw = 16; fonth = 29; fontptr = &jevois::font::font16x29[0][0]; break;
584  case Font20x38: fontw = 20; fonth = 38; fontptr = &jevois::font::font20x38[0][0]; break;
585  default: LFATAL("Invalid font");
586  }
587 
588  // Clip the text so that it does not go outside the image:
589  while (x + len * fontw > int(imgw)) { --len; if (len <= 0) return; }
590 
591  // Be nice and handle various pixel formats:
592  switch (img.bytesperpix())
593  {
594  case 2:
595  {
596  unsigned short * b = img.pixelsw<unsigned short>() + x + y * imgw;
597 
598  for (int i = 0; i < len; ++i)
599  {
600  int idx = txt[i] - 32; if (idx >= 95) idx = 0;
601  unsigned char const * ptr = fontptr + fontw * fonth * idx;
602  unsigned short * bb = b;
603  for (int yy = 0; yy < fonth; ++yy)
604  {
605  // Draw one line of this letter, note the transparent background:
606  for (int xx = 0; xx < fontw; ++xx) if (*ptr++) ++bb; else *bb++ = col;
607  bb += imgw - fontw;
608  }
609  b += fontw;
610  }
611  }
612  break;
613 
614  case 1:
615  {
616  unsigned char * b = img.pixelsw<unsigned char>() + x + y * imgw;
617 
618  for (int i = 0; i < len; ++i)
619  {
620  int idx = txt[i] - 32; if (idx >= 95) idx = 0;
621  unsigned char const * ptr = fontptr + fontw * fonth * idx;
622  unsigned char * bb = b;
623  for (int yy = 0; yy < fonth; ++yy)
624  {
625  // Draw one line of this letter, note the transparent background:
626  for (int xx = 0; xx < fontw; ++xx) if (*ptr++) ++bb; else *bb++ = col;
627  bb += imgw - fontw;
628  }
629  b += fontw;
630  }
631  }
632  break;
633 
634  default:
635  LFATAL("Sorry, only 1 and 2 bytes/pixel images are supported for now");
636  }
637 }
638 
639 // ####################################################################################################
640 namespace
641 {
642  class bgrToBayer : public cv::ParallelLoopBody
643  {
644  public:
645  bgrToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
646  inImg(inputImage), outImg(outImage)
647  {
648  inlinesize = inputImage.cols * 3; // 3 bytes/pix for BGR
649  outlinesize = outw * 1; // 1 byte/pix for Bayer
650  }
651 
652  virtual void operator()(const cv::Range & range) const
653  {
654  for (int j = range.start; j < range.end; ++j)
655  {
656  int const inoff = j * inlinesize;
657  int const outoff = j * outlinesize;
658 
659  for (int i = 0; i < inImg.cols; i += 2)
660  {
661  int const in = inoff + i * 3;
662  int const out = outoff + i;
663 
664  if ( (j & 1) == 0) { outImg[out + 0] = inImg.data[in + 2]; outImg[out + 1] = inImg.data[in + 4]; }
665  else { outImg[out + 0] = inImg.data[in + 1]; outImg[out + 1] = inImg.data[in + 3]; }
666  }
667  }
668  }
669 
670  private:
671  cv::Mat const & inImg;
672  unsigned char * outImg;
673  int inlinesize, outlinesize;
674  };
675 
676  // ####################################################################################################
677  void convertCvBGRtoBayer(cv::Mat const & src, jevois::RawImage & dst)
678  {
679  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and BGR pixels");
680  if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
681  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
682 
683  cv::parallel_for_(cv::Range(0, src.rows), bgrToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
684  }
685 } // anonymous namespace
686 
687 // ####################################################################################################
688 namespace
689 {
690  class rgbaToBayer : public cv::ParallelLoopBody
691  {
692  public:
693  rgbaToBayer(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
694  inImg(inputImage), outImg(outImage)
695  {
696  inlinesize = inputImage.cols * 4; // 4 bytes/pix for RGBA
697  outlinesize = outw * 1; // 1 byte/pix for Bayer
698  }
699 
700  virtual void operator()(const cv::Range & range) const
701  {
702  for (int j = range.start; j < range.end; ++j)
703  {
704  int const inoff = j * inlinesize;
705  int const outoff = j * outlinesize;
706 
707  for (int i = 0; i < inImg.cols; i += 2)
708  {
709  int const in = inoff + i * 4;
710  int const out = outoff + i;
711 
712  if ( (j & 1) == 0) { outImg[out + 0] = inImg.data[in + 0]; outImg[out + 1] = inImg.data[in + 5]; }
713  else { outImg[out + 0] = inImg.data[in + 1]; outImg[out + 1] = inImg.data[in + 6]; }
714  }
715  }
716  }
717 
718  private:
719  cv::Mat const & inImg;
720  unsigned char * outImg;
721  int inlinesize, outlinesize;
722  };
723 
724  // ####################################################################################################
725  void convertCvRGBAtoBayer(cv::Mat const & src, jevois::RawImage & dst)
726  {
727  if (src.type() != CV_8UC4) LFATAL("src must have type CV_8UC4 and RGBA pixels");
728  if (dst.fmt != V4L2_PIX_FMT_SRGGB8) LFATAL("dst format must be V4L2_PIX_FMT_SRGGB8");
729  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
730 
731  cv::parallel_for_(cv::Range(0, src.rows), rgbaToBayer(src, dst.pixelsw<unsigned char>(), dst.width));
732  }
733 } // anonymous namespace
734 
735 // ####################################################################################################
736 namespace
737 {
738  class bgrToYUYV : public cv::ParallelLoopBody
739  {
740  public:
741  bgrToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
742  inImg(inputImage), outImg(outImage)
743  {
744  inlinesize = inputImage.cols * 3; // 3 bytes/pix for BGR
745  outlinesize = outw * 2; // 2 bytes/pix for YUYV
746  }
747 
748  virtual void operator()(const cv::Range & range) const
749  {
750  for (int j = range.start; j < range.end; ++j)
751  {
752  int const inoff = j * inlinesize;
753  int const outoff = j * outlinesize;
754 
755  for (int i = 0; i < inImg.cols; i += 2)
756  {
757  int mc = inoff + i * 3;
758  unsigned char const B1 = inImg.data[mc + 0];
759  unsigned char const G1 = inImg.data[mc + 1];
760  unsigned char const R1 = inImg.data[mc + 2];
761  unsigned char const B2 = inImg.data[mc + 3];
762  unsigned char const G2 = inImg.data[mc + 4];
763  unsigned char const R2 = inImg.data[mc + 5];
764 
765  float const Y1 = (0.257F * R1) + (0.504F * G1) + (0.098F * B1) + 16.0F;
766  //float const V1 = (0.439F * R1) - (0.368F * G1) - (0.071F * B1) + 128.0F;
767  float const U1 = -(0.148F * R1) - (0.291F * G1) + (0.439F * B1) + 128.0F;
768  float const Y2 = (0.257F * R2) + (0.504F * G2) + (0.098F * B2) + 16.0F;
769  float const V2 = (0.439F * R2) - (0.368F * G2) - (0.071F * B2) + 128.0F;
770  //float const U2 = -(0.148F * R2) - (0.291F * G2) + (0.439F * B2) + 128.0F;
771 
772  mc = outoff + i * 2;
773  outImg[mc + 0] = Y1;
774  outImg[mc + 1] = U1;
775  outImg[mc + 2] = Y2;
776  outImg[mc + 3] = V2;
777  }
778  }
779  }
780 
781  private:
782  cv::Mat const & inImg;
783  unsigned char * outImg;
784  int inlinesize, outlinesize;
785  };
786 
787  // ####################################################################################################
788  void convertCvBGRtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
789  {
790  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and BGR pixels");
791  if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
792  if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
793 
794  cv::parallel_for_(cv::Range(0, src.rows), bgrToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
795  }
796 } // anonymous namespace
797 
798 // ####################################################################################################
799 namespace
800 {
801  class rgbaToYUYV : public cv::ParallelLoopBody
802  {
803  public:
804  rgbaToYUYV(cv::Mat const & inputImage, unsigned char * outImage, size_t outw) :
805  inImg(inputImage), outImg(outImage)
806  {
807  inlinesize = inputImage.cols * 4; // 4 bytes/pix for RGBA
808  outlinesize = outw * 2; // 2 bytes/pix for YUYV
809  }
810 
811  virtual void operator()(const cv::Range & range) const
812  {
813  for (int j = range.start; j < range.end; ++j)
814  {
815  int const inoff = j * inlinesize;
816  int const outoff = j * outlinesize;
817 
818  for (int i = 0; i < inImg.cols; i += 2)
819  {
820  int mc = inoff + i * 4;
821  unsigned char const R1 = inImg.data[mc + 0];
822  unsigned char const G1 = inImg.data[mc + 1];
823  unsigned char const B1 = inImg.data[mc + 2];
824  unsigned char const R2 = inImg.data[mc + 4];
825  unsigned char const G2 = inImg.data[mc + 5];
826  unsigned char const B2 = inImg.data[mc + 6];
827 
828  float const Y1 = (0.257F * R1) + (0.504F * G1) + (0.098F * B1) + 16.0F;
829  float const U1 = -(0.148F * R1) - (0.291F * G1) + (0.439F * B1) + 128.0F;
830  float const Y2 = (0.257F * R2) + (0.504F * G2) + (0.098F * B2) + 16.0F;
831  float const V2 = (0.439F * R2) - (0.368F * G2) - (0.071F * B2) + 128.0F;
832 
833  mc = outoff + i * 2;
834  outImg[mc + 0] = Y1;
835  outImg[mc + 1] = U1;
836  outImg[mc + 2] = Y2;
837  outImg[mc + 3] = V2;
838  }
839  }
840  }
841 
842  private:
843  cv::Mat const & inImg;
844  unsigned char * outImg;
845  int inlinesize, outlinesize;
846  };
847 
848  // ####################################################################################################
849  void convertCvRGBAtoYUYV(cv::Mat const & src, jevois::RawImage & dst)
850  {
851  if (src.type() != CV_8UC4) LFATAL("src must have type CV_8UC4 and RGBA pixels");
852  if (dst.fmt != V4L2_PIX_FMT_YUYV) LFATAL("dst format must be V4L2_PIX_FMT_YUYV");
853  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
854 
855  cv::parallel_for_(cv::Range(0, src.rows), rgbaToYUYV(src, dst.pixelsw<unsigned char>(), dst.width));
856  }
857 } // anonymous namespace
858 
859 // ####################################################################################################
860 void jevois::rawimage::convertCvBGRtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
861 {
862  if (src.type() != CV_8UC3) LFATAL("src must have type CV_8UC3 and BGR pixels");
863  if (int(dst.width) != src.cols || int(dst.height) < src.rows) LFATAL("src and dst dims must match");
864 
865  // Note how the destination opencv image dstcv here is just a shell, the actual pixel data is in dst:
866  cv::Mat dstcv = jevois::rawimage::cvImage(dst);
867 
868  switch (dst.fmt)
869  {
870  case V4L2_PIX_FMT_SRGGB8: convertCvBGRtoBayer(src, dst); break;
871  case V4L2_PIX_FMT_YUYV: convertCvBGRtoYUYV(src, dst); break;
872  case V4L2_PIX_FMT_GREY: cv::cvtColor(src, dstcv, CV_BGR2GRAY); break;
873  case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, CV_BGR2BGR565); break;
874  case V4L2_PIX_FMT_MJPEG: jevois::compressBGRtoJpeg(src, dst, quality); break;
875  case V4L2_PIX_FMT_BGR24: memcpy(dst.pixelsw<void>(), src.data, dst.width * dst.height * dst.bytesperpix()); break;
876  default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
877  }
878 }
879 
880 // ####################################################################################################
882 {
883  if (src.type() != CV_8UC4) LFATAL("src must have type CV_8UC4 and RGBA pixels");
884  if (dst.fmt != V4L2_PIX_FMT_GREY) LFATAL("dst must have pixel type V4L2_PIX_FMT_GREY");
885  int const w = src.cols, h = src.rows;
886  if (int(dst.width) < w || int(dst.height) < 4 * h) LFATAL("dst must be at least as wide and 4x as tall as src");
887 
888  unsigned char const * sptr = src.data; unsigned char * dptr = dst.pixelsw<unsigned char>();
889  int const stride = int(dst.width) - w;
890 
891  // Do R, G, B in 3 threads then A in the current thread:
892  std::vector<std::future<void> > fut;
893  for (int i = 0; i < 3; ++i) fut.push_back(std::async(std::launch::async, [&](int offset) {
894  unsigned char const * s = sptr + offset; unsigned char * d = dptr + offset * w * h;
895  for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { *d++ = *s; s += 4; } d += stride; } }, i));
896 
897  unsigned char const * s = sptr + 3; unsigned char * d = dptr + 3 * w * h;
898  for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { *d++ = *s; s += 4; } d += stride; }
899 
900  // Wait for all threads to complete (those should never throw):
901  for (auto & f : fut) f.get();
902 }
903 
904 // ####################################################################################################
905 void jevois::rawimage::convertCvRGBAtoRawImage(cv::Mat const & src, RawImage & dst, int quality)
906 {
907  if (src.type() != CV_8UC4) LFATAL("src must have type CV_8UC4 and RGBA pixels");
908  if (int(dst.width) != src.cols || int(dst.height) != src.rows) LFATAL("src and dst dims must match");
909 
910  cv::Mat dstcv = jevois::rawimage::cvImage(dst);
911 
912  switch (dst.fmt)
913  {
914  case V4L2_PIX_FMT_SRGGB8: convertCvRGBAtoBayer(src, dst); break;
915  case V4L2_PIX_FMT_YUYV: convertCvRGBAtoYUYV(src, dst); break;
916  case V4L2_PIX_FMT_GREY: cv::cvtColor(src, dstcv, CV_RGBA2GRAY); break;
917  case V4L2_PIX_FMT_RGB565: cv::cvtColor(src, dstcv, CV_BGRA2BGR565); break;
918  case V4L2_PIX_FMT_MJPEG: jevois::compressRGBAtoJpeg(src, dst, quality); break;
919  case V4L2_PIX_FMT_BGR24: cv::cvtColor(src, dstcv, CV_RGBA2BGR); break;
920  default: LFATAL("Unsupported output pixel format " << jevois::fccstr(dst.fmt) << std::hex <<' '<< dst.fmt);
921  }
922 
923 }
924 
925 // ####################################################################################################
926 namespace
927 {
928  class hflipYUYV : public cv::ParallelLoopBody
929  {
930  public:
931  hflipYUYV(unsigned char * outImage, size_t outw) :
932  outImg(outImage), linesize(outw * 2) // 2 bytes/pix for YUYV
933  { }
934 
935  virtual void operator()(const cv::Range & range) const
936  {
937  for (int j = range.start; j < range.end; ++j)
938  {
939  int const off = j * linesize;
940 
941  for (int i = 0; i < linesize / 2; i += 4)
942  {
943  unsigned char * ptr1 = outImg + off + i;
944  unsigned char * ptr2 = outImg + off + linesize - 4 - i;
945  std::swap(ptr1[0], ptr2[2]);
946  std::swap(ptr1[1], ptr2[1]);
947  std::swap(ptr1[2], ptr2[0]);
948  std::swap(ptr1[3], ptr2[3]);
949  }
950  }
951  }
952 
953  private:
954  unsigned char * outImg;
955  int linesize;
956  };
957 
958 } // anonymous namespace
959 
960 // ####################################################################################################
962 {
963  if (img.fmt != V4L2_PIX_FMT_YUYV) LFATAL("img format must be V4L2_PIX_FMT_YUYV");
964  cv::parallel_for_(cv::Range(0, img.height), hflipYUYV(img.pixelsw<unsigned char>(), img.width));
965 }
966 
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:249
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:272
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:421
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:557
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:226
unsigned int height
Image height in pixels.
Definition: RawImage.H:140
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:446
T * pixelsw()
Shortcut access to pixels, read-write.
Definition: RawImageImpl.H:24
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:141
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition: RawImage.H:142
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:363
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:43
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:136
void hFlipYUYV(RawImage &img)
Flip a YUYV RawImage horizontally while preserving color information.
Definition: RawImageOps.C:961
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition: Utils.C:30
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:203
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:860
unsigned int bytesize() const
Helper function to get the total number of bytes in the RawImage, i.e., width * height * bytesperpix(...
Definition: RawImage.C:35
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level. ...
Definition: Log.H:205
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:66
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:516
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:905
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:881
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:386
unsigned int bytesperpix() const
Helper function to get the number of bytes/pixel given the RawImage pixel format. ...
Definition: RawImage.C:31
void drawDisk(RawImage &img, int x, int y, unsigned int rad, unsigned int col)
Draw a disk in a YUYV image.
Definition: RawImageOps.C:403
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:482
void byteSwap(RawImage &img)
Swap pairs of bytes in a RawImage.
Definition: RawImageOps.C:306
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:55
unsigned int width
Image width in pixels.
Definition: RawImage.H:139
void paste(RawImage const &src, RawImage &dest, int dx, int dy)
Paste an image within another of same pixel type.
Definition: RawImageOps.C:341