2 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
22 #include <sys/types.h>
24 #include <linux/videodev2.h>
26 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
28 #define min(a, b) ({ \
34 #define max(a, b) ({ \
40 #define clamp(val, low, high) max(low, min(high, val));
44 typeof(a) __tmp = (a); \
49 struct format_color_component {
54 struct format_rgb_info {
56 struct format_color_component red;
57 struct format_color_component green;
58 struct format_color_component blue;
59 struct format_color_component alpha;
62 enum format_yuv_order {
69 struct format_yuv_info {
70 unsigned int num_planes;
71 enum format_yuv_order order;
79 struct format_rgb_info rgb;
80 struct format_yuv_info yuv;
84 const struct format_info *format;
93 enum v4l2_ycbcr_encoding encoding;
94 enum v4l2_quantization quantization;
98 const char *input_filename;
99 const char *output_filename;
100 const char *histo_filename;
101 const char *clu_filename;
102 const char *lut_filename;
104 const struct format_info *output_format;
105 unsigned int output_height;
106 unsigned int output_width;
112 unsigned int compose;
113 struct params params;
116 /* -----------------------------------------------------------------------------
120 #define MAKE_RGB_INFO(rl, ro, gl, go, bl, bo, al, ao) \
121 .red = { (rl), (ro) }, .green = { (gl), (go) }, \
122 .blue = { (bl), (bo) }, .alpha = { (al), (ao) }
124 static const struct format_info format_info[] = {
126 * The alpha channel maps to the X (don't care) bits for the XRGB
129 { "RGB332", false, .rgb = { 8, MAKE_RGB_INFO(3, 5, 3, 2, 2, 0, 0, 0) } },
130 { "ARGB444", false, .rgb = { 16, MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 4, 12) } },
131 { "XRGB444", false, .rgb = { 16, MAKE_RGB_INFO(4, 8, 4, 4, 4, 0, 4, 12) } },
132 { "ARGB555", false, .rgb = { 16, MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 1, 15) } },
133 { "XRGB555", false, .rgb = { 16, MAKE_RGB_INFO(5, 10, 5, 5, 5, 0, 1, 15) } },
134 { "RGB565", false, .rgb = { 16, MAKE_RGB_INFO(5, 11, 6, 5, 5, 0, 0, 0) } },
135 { "BGR24", false, .rgb = { 24, MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 0, 0) } },
136 { "RGB24", false, .rgb = { 24, MAKE_RGB_INFO(8, 0, 8, 8, 8, 16, 0, 0) } },
137 { "ABGR32", false, .rgb = { 32, MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 8, 24) } },
138 { "XBGR32", false, .rgb = { 32, MAKE_RGB_INFO(8, 16, 8, 8, 8, 0, 8, 24) } },
139 { "ARGB32", false, .rgb = { 32, MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 8, 0) } },
140 { "XRGB32", false, .rgb = { 32, MAKE_RGB_INFO(8, 8, 8, 16, 8, 24, 8, 0) } },
141 { "UYVY", true, .yuv = { 1, YUV_YCbCr | YUV_CY, 2, 1 } },
142 { "VYUY", true, .yuv = { 1, YUV_YCrCb | YUV_CY, 2, 1 } },
143 { "YUYV", true, .yuv = { 1, YUV_YCbCr | YUV_YC, 2, 1 } },
144 { "YVYU", true, .yuv = { 1, YUV_YCrCb | YUV_YC, 2, 1 } },
145 { "NV12M", true, .yuv = { 2, YUV_YCbCr, 2, 2 } },
146 { "NV21M", true, .yuv = { 2, YUV_YCrCb, 2, 2 } },
147 { "NV16M", true, .yuv = { 2, YUV_YCbCr, 2, 1 } },
148 { "NV61M", true, .yuv = { 2, YUV_YCrCb, 2, 1 } },
149 { "YUV420M", true, .yuv = { 3, YUV_YCbCr, 2, 2 } },
150 { "YVU420M", true, .yuv = { 3, YUV_YCrCb, 2, 2 } },
151 { "YUV422M", true, .yuv = { 3, YUV_YCbCr, 2, 1 } },
152 { "YVU422M", true, .yuv = { 3, YUV_YCrCb, 2, 1 } },
153 { "YUV444M", true, .yuv = { 3, YUV_YCbCr, 1, 1 } },
154 { "YVU444M", true, .yuv = { 3, YUV_YCrCb, 1, 1 } },
155 { "YUV24", true, .yuv = { 1, YUV_YCbCr | YUV_YC, 1, 1 } },
158 static const struct format_info *format_by_name(const char *name)
162 for (i = 0; i < ARRAY_SIZE(format_info); i++) {
163 if (!strcmp(name, format_info[i].name))
164 return &format_info[i];
170 /* -----------------------------------------------------------------------------
174 static int file_read(int fd, void *buffer, size_t size)
176 unsigned int offset = 0;
178 while (offset < size) {
181 nbytes = read(fd, buffer + offset, size - offset);
198 static int file_write(int fd, const void *buffer, size_t size)
200 unsigned int offset = 0;
202 while (offset < size) {
205 nbytes = write(fd, buffer + offset, size - offset);
219 /* -----------------------------------------------------------------------------
220 * Image initialization
223 static struct image *image_new(const struct format_info *format,
224 unsigned int width, unsigned int height)
228 image = malloc(sizeof(*image));
232 memset(image, 0, sizeof(*image));
233 image->format = format;
234 image->width = width;
235 image->height = height;
238 image->size = image->width * image->height
239 * (8 + 2 * 8 / format->yuv.xsub / format->yuv.ysub)
242 image->size = image->width * image->height
243 * format->rgb.bpp / 8;
245 image->data = malloc(image->size);
247 printf("Not enough memory for image data\n");
255 static void image_delete(struct image *image)
264 /* -----------------------------------------------------------------------------
265 * Image read and write
268 static int pnm_read_bytes(int fd, char *buffer, size_t size)
272 ret = file_read(fd, buffer, size);
274 printf("Unable to read PNM file: %s (%d)\n", strerror(-ret),
278 if ((size_t)ret != size) {
279 printf("Invalid PNM file: file too short\n");
286 static int pnm_read_integer(int fd)
288 unsigned int value = 0;
293 ret = pnm_read_bytes(fd, &c, 1);
294 } while (!ret && isspace(c));
299 while (!ret && isdigit(c)) {
300 value = value * 10 + c - '0';
301 ret = pnm_read_bytes(fd, &c, 1);
313 static struct image *pnm_read(const char *filename)
322 fd = open(filename, O_RDONLY);
324 printf("Unable to open PNM file %s: %s (%d)\n", filename,
325 strerror(errno), errno);
329 /* Read and validate the header. */
330 ret = pnm_read_bytes(fd, buffer, 2);
334 if (buffer[0] != 'P' || buffer[1] != '6') {
335 printf("Invalid PNM file: invalid signature\n");
340 /* Read the width, height and depth. */
341 ret = pnm_read_integer(fd);
343 printf("Invalid PNM file: invalid width\n");
349 ret = pnm_read_integer(fd);
351 printf("Invalid PNM file: invalid height\n");
357 ret = pnm_read_integer(fd);
359 printf("Invalid PNM file: invalid depth\n");
364 printf("Invalid PNM file: unsupported depth %u\n", ret);
369 /* Allocate the image and read the data. */
370 image = image_new(format_by_name("RGB24"), width, height);
374 ret = pnm_read_bytes(fd, image->data, image->size);
383 return ret ? NULL : image;
386 static struct image *image_read(const char *filename)
388 return pnm_read(filename);
391 static int image_write(const struct image *image, const char *filename)
396 fd = open(filename, O_WRONLY | O_CREAT,
397 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
399 printf("Unable to open output file %s: %s (%d)\n", filename,
400 strerror(errno), errno);
404 ret = file_write(fd, image->data, image->size);
406 printf("Unable to write output image: %s (%d)\n",
407 strerror(-ret), ret);
409 ftruncate(fd, image->size);
415 /* -----------------------------------------------------------------------------
419 static void image_format_rgb8(const struct image *input, struct image *output,
420 const struct params *params)
422 const uint8_t *idata = input->data;
423 uint8_t *odata = output->data;
428 odata = output->data;
430 for (y = 0; y < input->height; ++y) {
431 for (x = 0; x < input->width; ++x) {
432 /* There's only one RGB8 variant supported, hardcode it. */
437 *odata++ = (r << 5) | (g << 2) | b;
442 static void image_format_rgb16(const struct image *input, struct image *output,
443 const struct params *params)
445 const struct format_info *format = output->format;
446 const uint8_t *idata = input->data;
447 uint16_t *odata = output->data;
451 for (y = 0; y < input->height; ++y) {
452 for (x = 0; x < input->width; ++x) {
453 r = *idata++ >> (8 - format->rgb.red.length);
454 g = *idata++ >> (8 - format->rgb.green.length);
455 b = *idata++ >> (8 - format->rgb.blue.length);
456 a = params->alpha >> (8 - format->rgb.alpha.length);
458 *odata++ = (r << format->rgb.red.offset)
459 | (g << format->rgb.green.offset)
460 | (b << format->rgb.blue.offset)
461 | (a << format->rgb.alpha.offset);
466 static void image_format_rgb24(const struct image *input, struct image *output,
467 const struct params *params)
470 unsigned int value:24;
471 } __attribute__((__packed__));
473 const struct format_info *format = output->format;
474 const uint8_t *idata = input->data;
475 struct color_rgb24 *odata = output->data;
480 odata = output->data;
482 for (y = 0; y < input->height; ++y) {
483 for (x = 0; x < input->width; ++x) {
484 r = *idata++ >> (8 - format->rgb.red.length);
485 g = *idata++ >> (8 - format->rgb.green.length);
486 b = *idata++ >> (8 - format->rgb.blue.length);
487 a = params->alpha >> (8 - format->rgb.alpha.length);
489 *odata++ = (struct color_rgb24){ .value =
490 (r << format->rgb.red.offset) |
491 (g << format->rgb.green.offset) |
492 (b << format->rgb.blue.offset) |
493 (a << format->rgb.alpha.offset) };
498 static void image_format_rgb32(const struct image *input, struct image *output,
499 const struct params *params)
501 const struct format_info *format = output->format;
502 const uint8_t *idata = input->data;
503 uint32_t *odata = output->data;
507 for (y = 0; y < input->height; ++y) {
508 for (x = 0; x < input->width; ++x) {
509 r = *idata++ >> (8 - format->rgb.red.length);
510 g = *idata++ >> (8 - format->rgb.green.length);
511 b = *idata++ >> (8 - format->rgb.blue.length);
512 a = params->alpha >> (8 - format->rgb.alpha.length);
514 *odata++ = (r << format->rgb.red.offset)
515 | (g << format->rgb.green.offset)
516 | (b << format->rgb.blue.offset)
517 | (a << format->rgb.alpha.offset);
523 * In YUV packed and planar formats, when subsampling horizontally average the
524 * chroma components of the two pixels to match the hardware behaviour.
526 static void image_format_yuv_packed(const struct image *input, struct image *output,
527 const struct params *params)
529 const struct format_info *format = output->format;
530 const uint8_t *idata = input->data;
531 uint8_t *o_y = output->data + ((format->yuv.order & YUV_YC) ? 0 : 1);
532 uint8_t *o_c = output->data + ((format->yuv.order & YUV_CY) ? 0 : 1);
533 unsigned int u_offset = (format->yuv.order & YUV_YCrCb) ? 2 : 0;
534 unsigned int v_offset = (format->yuv.order & YUV_YCbCr) ? 2 : 0;
538 for (y = 0; y < output->height; ++y) {
539 for (x = 0; x < output->width; x += 2) {
540 o_y[2*x] = idata[3*x];
541 o_y[2*x + 2] = idata[3*x + 3];
542 o_c[2*x + u_offset] = (idata[3*x + 1] + idata[3*x + 4]) / 2;
543 o_c[2*x + v_offset] = (idata[3*x + 2] + idata[3*x + 5]) / 2;
546 o_y += input->width * 2;
547 o_c += input->width * 2;
548 idata += input->width * 3;
552 static void image_format_yuv_planar(const struct image *input, struct image *output,
553 const struct params *params)
555 const struct format_info *format = output->format;
556 const uint8_t *idata;
557 uint8_t *o_y = output->data;
558 uint8_t *o_c = o_y + output->width * output->height;
561 unsigned int xsub = format->yuv.xsub;
562 unsigned int ysub = format->yuv.ysub;
563 unsigned int c_stride;
567 if (format->yuv.num_planes == 2) {
568 o_u = (format->yuv.order & YUV_YCbCr) ? o_c : o_c + 1;
569 o_v = (format->yuv.order & YUV_YCrCb) ? o_c : o_c + 1;
572 unsigned int c_size = output->width * output->height
575 o_u = (format->yuv.order & YUV_YCbCr) ? o_c : o_c + c_size;
576 o_v = (format->yuv.order & YUV_YCrCb) ? o_c : o_c + c_size;
581 for (y = 0; y < output->height; ++y) {
582 for (x = 0; x < output->width; ++x)
585 idata += input->width * 3;
589 for (y = 0; y < output->height / ysub; ++y) {
591 for (x = 0; x < output->width; x += xsub) {
592 o_u[x*c_stride/xsub] = idata[3*x + 1];
593 o_v[x*c_stride/xsub] = idata[3*x + 2];
596 for (x = 0; x < output->width; x += xsub) {
597 o_u[x*c_stride/xsub] = (idata[3*x + 1] + idata[3*x + 4]) / 2;
598 o_v[x*c_stride/xsub] = (idata[3*x + 2] + idata[3*x + 5]) / 2;
602 o_u += output->width * c_stride / xsub;
603 o_v += output->width * c_stride / xsub;
604 idata += input->width * 3 * ysub;
608 /* -----------------------------------------------------------------------------
609 * Colorspace handling
611 * The code is inspired by the v4l2-tpg Linux kernel driver.
614 static void colorspace_matrix(enum v4l2_ycbcr_encoding encoding,
615 enum v4l2_quantization quantization,
618 #define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0))
620 static const int bt601[3][3] = {
621 { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) },
622 { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) },
623 { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) },
625 static const int bt601_full[3][3] = {
626 { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) },
627 { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) },
628 { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) },
630 static const int rec709[3][3] = {
631 { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) },
632 { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) },
633 { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) },
635 static const int rec709_full[3][3] = {
636 { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) },
637 { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) },
638 { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) },
640 static const int smpte240m[3][3] = {
641 { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) },
642 { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) },
643 { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) },
645 static const int smpte240m_full[3][3] = {
646 { COEFF(0.212, 255), COEFF(0.701, 255), COEFF(0.087, 255) },
647 { COEFF(-0.116, 255), COEFF(-0.384, 255), COEFF(0.5, 255) },
648 { COEFF(0.5, 255), COEFF(-0.445, 255), COEFF(-0.055, 255) },
650 static const int bt2020[3][3] = {
651 { COEFF(0.2627, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) },
652 { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) },
653 { COEFF(0.5, 224), COEFF(-0.4598, 224), COEFF(-0.0402, 224) },
655 static const int bt2020_full[3][3] = {
656 { COEFF(0.2627, 255), COEFF(0.6780, 255), COEFF(0.0593, 255) },
657 { COEFF(-0.1396, 255), COEFF(-0.3604, 255), COEFF(0.5, 255) },
658 { COEFF(0.5, 255), COEFF(-0.4698, 255), COEFF(-0.0402, 255) },
661 bool full = quantization == V4L2_QUANTIZATION_FULL_RANGE;
663 const int (*m)[3][3];
666 case V4L2_YCBCR_ENC_601:
668 m = full ? &bt601_full : &bt601;
670 case V4L2_YCBCR_ENC_709:
671 m = full ? &rec709_full : &rec709;
673 case V4L2_YCBCR_ENC_BT2020:
674 m = full ? &bt2020_full : &bt2020;
676 case V4L2_YCBCR_ENC_SMPTE240M:
677 m = full ? &smpte240m_full : &smpte240m;
681 for (i = 0; i < ARRAY_SIZE(*m); ++i)
682 memcpy((*matrix)[i], (*m)[i], sizeof((*m)[i]));
685 static void colorspace_rgb2ycbcr(int m[3][3],
686 enum v4l2_quantization quantization,
687 const uint8_t rgb[3], uint8_t ycbcr[3])
689 bool full = quantization == V4L2_QUANTIZATION_FULL_RANGE;
690 unsigned int y_offset = full ? 0 : 16;
699 div = (1 << (8 + 4)) * 255;
700 y = (m[0][0] * r + m[0][1] * g + m[0][2] * b + y_offset * div) / div;
701 cb = (m[1][0] * r + m[1][1] * g + m[1][2] * b + 128 * div) / div;
702 cr = (m[2][0] * r + m[2][1] * g + m[2][2] * b + 128 * div) / div;
709 static void image_colorspace_rgb_to_yuv(const struct image *input,
710 struct image *output,
711 const struct params *params)
714 const uint8_t *idata = input->data;
715 uint8_t *odata = output->data;
719 colorspace_matrix(params->encoding, params->quantization, &matrix);
721 for (y = 0; y < output->height; ++y) {
722 for (x = 0; x < output->width; ++x) {
723 colorspace_rgb2ycbcr(matrix, params->quantization, idata, odata);
730 /* -----------------------------------------------------------------------------
734 static void image_scale_bilinear(const struct image *input, struct image *output)
736 #define _C0(x, y) (idata[0][((y)*input->width+(x)) * 3])
737 #define _C1(x, y) (idata[1][((y)*input->width+(x)) * 3])
738 #define _C2(x, y) (idata[2][((y)*input->width+(x)) * 3])
739 const uint8_t *idata[3] = { input->data, input->data + 1, input->data + 2 };
740 uint8_t *odata = output->data;
744 for (v = 0; v < output->height; ++v) {
745 double v_input = (double)v / (output->height - 1) * (input->height - 1);
746 unsigned int y = floor(v_input);
747 double v_ratio = v_input - y;
749 for (u = 0; u < output->width; ++u) {
750 double u_input = (double)u / (output->width - 1) * (input->width - 1);
751 unsigned int x = floor(u_input);
752 double u_ratio = u_input - x;
754 c0 = (_C0(x, y) * (1 - u_ratio) + _C0(x+1, y) * u_ratio) * (1 - v_ratio)
755 + (_C0(x, y+1) * (1 - u_ratio) + _C0(x+1, y+1) * u_ratio) * v_ratio;
756 c1 = (_C1(x, y) * (1 - u_ratio) + _C1(x+1, y) * u_ratio) * (1 - v_ratio)
757 + (_C1(x, y+1) * (1 - u_ratio) + _C1(x+1, y+1) * u_ratio) * v_ratio;
758 c2 = (_C2(x, y) * (1 - u_ratio) + _C2(x+1, y) * u_ratio) * (1 - v_ratio)
759 + (_C2(x, y+1) * (1 - u_ratio) + _C2(x+1, y+1) * u_ratio) * v_ratio;
771 static void image_scale(const struct image *input, struct image *output,
772 const struct params *params)
774 image_scale_bilinear(input, output);
777 /* -----------------------------------------------------------------------------
781 static void image_compose(const struct image *input, struct image *output,
782 unsigned int num_inputs)
784 const uint8_t *idata = input->data;
785 uint8_t *odata = output->data;
786 unsigned int offset = 50;
790 memset(odata, 0, output->size);
792 for (i = 0; i < num_inputs; ++i) {
793 unsigned int dst_offset = (offset * output->width + offset) * 3;
795 if (offset >= output->width || offset >= output->height)
798 for (y = 0; y < output->height - offset; ++y)
799 memcpy(odata + y * output->width * 3 + dst_offset,
800 idata + y * output->width * 3,
801 (output->width - offset) * 3);
807 /* -----------------------------------------------------------------------------
808 * Image rotation and flipping
811 static void image_rotate(const struct image *input, struct image *output)
813 const uint8_t *idata = input->data;
815 unsigned int stride = output->width * 3;
818 odata = output->data + stride - 3;
820 for (y = 0; y < input->height; ++y) {
821 for (x = 0; x < input->width; ++x) {
822 odata[x*stride+0] = *idata++;
823 odata[x*stride+1] = *idata++;
824 odata[x*stride+2] = *idata++;
831 static void image_flip(const struct image *input, struct image *output,
832 bool hflip, bool vflip)
834 const uint8_t *idata = input->data;
835 uint8_t *odata = output->data;
836 unsigned int stride = output->width * 3;
842 y_step = !vflip ? 0 : -2 * stride;
843 odata += !vflip ? 0 : stride * (output->height - 1);
846 y_step = !vflip ? 2 * stride : 0;
847 odata += !vflip ? stride - 3 : stride * output->height - 3;
850 for (y = 0; y < output->height; ++y) {
851 for (x = 0; x < output->width; ++x) {
863 /* -----------------------------------------------------------------------------
867 static int image_lut_1d(const struct image *input, struct image *output,
868 const char *filename)
870 const uint8_t *idata = input->data;
871 uint8_t *odata = output->data;
872 unsigned int comp_map[3];
879 fd = open(filename, O_RDONLY);
881 printf("Unable to open LUT file %s: %s (%d)\n", filename,
882 strerror(errno), errno);
886 ret = file_read(fd, lut, sizeof(lut));
889 printf("Unable to read 1D LUT file: %s (%d)\n", strerror(-ret),
893 if ((size_t)ret != sizeof(lut)) {
894 printf("Invalid 1D LUT file: file too short\n");
898 if (input->format->is_yuv)
899 memcpy(comp_map, (unsigned int[3]){ 1, 0, 2 },
902 memcpy(comp_map, (unsigned int[3]){ 2, 1, 0 },
905 for (y = 0; y < input->height; ++y) {
906 for (x = 0; x < input->width; ++x) {
911 *odata++ = lut[c0*4 + comp_map[0]];
912 *odata++ = lut[c1*4 + comp_map[1]];
913 *odata++ = lut[c2*4 + comp_map[2]];
920 static int image_lut_3d(const struct image *input, struct image *output,
921 const char *filename)
923 const uint8_t *idata = input->data;
924 uint8_t *odata = output->data;
925 unsigned int comp_map[3];
927 uint32_t lut[17*17*17];
931 fd = open(filename, O_RDONLY);
933 printf("Unable to open 3D LUT file %s: %s (%d)\n", filename,
934 strerror(errno), errno);
938 ret = file_read(fd, lut, sizeof(lut));
941 printf("Unable to read 3D LUT file: %s (%d)\n", strerror(-ret),
945 if ((size_t)ret != sizeof(lut)) {
946 printf("Invalid 3D LUT file: file too short\n");
950 if (input->format->is_yuv)
951 memcpy(comp_map, (unsigned int[3]){ 2, 0, 1 },
954 memcpy(comp_map, (unsigned int[3]){ 0, 1, 2 },
957 for (y = 0; y < input->height; ++y) {
958 for (x = 0; x < input->width; ++x) {
959 double a1_ratio, a2_ratio, a3_ratio;
960 unsigned int a1, a2, a3;
964 c[0] = idata[comp_map[0]];
965 c[1] = idata[comp_map[1]];
966 c[2] = idata[comp_map[2]];
973 * Implement the hardware MVS (Max Value Stretch)
974 * behaviour: move the point by one step towards the
975 * upper limit of the grid if we're closer than 0.5 to
978 a1_ratio = ((c[0] & 0xf) + (c[0] >= 0xf8 ? 1 : 0)) / 16.;
979 a2_ratio = ((c[1] & 0xf) + (c[1] >= 0xf8 ? 1 : 0)) / 16.;
980 a3_ratio = ((c[2] & 0xf) + (c[2] >= 0xf8 ? 1 : 0)) / 16.;
982 #define _LUT(a1, a2, a3, offset) ((lut[(a1)+(a2)*17+(a3)*17*17] >> (offset)) & 0xff)
983 c0 = _LUT(a1, a2, a3, 16) * (1 - a1_ratio) * (1 - a2_ratio) * (1 - a3_ratio)
984 + _LUT(a1, a2, a3+1, 16) * (1 - a1_ratio) * (1 - a2_ratio) * a3_ratio
985 + _LUT(a1, a2+1, a3, 16) * (1 - a1_ratio) * a2_ratio * (1 - a3_ratio)
986 + _LUT(a1, a2+1, a3+1, 16) * (1 - a1_ratio) * a2_ratio * a3_ratio
987 + _LUT(a1+1, a2, a3, 16) * a1_ratio * (1 - a2_ratio) * (1 - a3_ratio)
988 + _LUT(a1+1, a2, a3+1, 16) * a1_ratio * (1 - a2_ratio) * a3_ratio
989 + _LUT(a1+1, a2+1, a3, 16) * a1_ratio * a2_ratio * (1 - a3_ratio)
990 + _LUT(a1+1, a2+1, a3+1, 16) * a1_ratio * a2_ratio * a3_ratio;
991 c1 = _LUT(a1, a2, a3, 8) * (1 - a1_ratio) * (1 - a2_ratio) * (1 - a3_ratio)
992 + _LUT(a1, a2, a3+1, 8) * (1 - a1_ratio) * (1 - a2_ratio) * a3_ratio
993 + _LUT(a1, a2+1, a3, 8) * (1 - a1_ratio) * a2_ratio * (1 - a3_ratio)
994 + _LUT(a1, a2+1, a3+1, 8) * (1 - a1_ratio) * a2_ratio * a3_ratio
995 + _LUT(a1+1, a2, a3, 8) * a1_ratio * (1 - a2_ratio) * (1 - a3_ratio)
996 + _LUT(a1+1, a2, a3+1, 8) * a1_ratio * (1 - a2_ratio) * a3_ratio
997 + _LUT(a1+1, a2+1, a3, 8) * a1_ratio * a2_ratio * (1 - a3_ratio)
998 + _LUT(a1+1, a2+1, a3+1, 8) * a1_ratio * a2_ratio * a3_ratio;
999 c2 = _LUT(a1, a2, a3, 0) * (1 - a1_ratio) * (1 - a2_ratio) * (1 - a3_ratio)
1000 + _LUT(a1, a2, a3+1, 0) * (1 - a1_ratio) * (1 - a2_ratio) * a3_ratio
1001 + _LUT(a1, a2+1, a3, 0) * (1 - a1_ratio) * a2_ratio * (1 - a3_ratio)
1002 + _LUT(a1, a2+1, a3+1, 0) * (1 - a1_ratio) * a2_ratio * a3_ratio
1003 + _LUT(a1+1, a2, a3, 0) * a1_ratio * (1 - a2_ratio) * (1 - a3_ratio)
1004 + _LUT(a1+1, a2, a3+1, 0) * a1_ratio * (1 - a2_ratio) * a3_ratio
1005 + _LUT(a1+1, a2+1, a3, 0) * a1_ratio * a2_ratio * (1 - a3_ratio)
1006 + _LUT(a1+1, a2+1, a3+1, 0) * a1_ratio * a2_ratio * a3_ratio;
1009 odata[comp_map[0]] = round(c0);
1010 odata[comp_map[1]] = round(c1);
1011 odata[comp_map[2]] = round(c2);
1021 /* -----------------------------------------------------------------------------
1025 static void histogram_compute(const struct image *image, void *histo)
1027 const uint8_t *data = image->data;
1028 uint8_t comp_min[3] = { 255, 255, 255 };
1029 uint8_t comp_max[3] = { 0, 0, 0 };
1030 uint32_t comp_sums[3] = { 0, 0, 0 };
1031 uint32_t comp_bins[3][64];
1032 unsigned int comp_map[3];
1036 if (image->format->is_yuv)
1037 memcpy(comp_map, (unsigned int[3]){ 2, 0, 1 }, sizeof(comp_map));
1039 memcpy(comp_map, (unsigned int[3]){ 0, 1, 2 }, sizeof(comp_map));
1041 memset(comp_bins, 0, sizeof(comp_bins));
1043 for (y = 0; y < image->height; ++y) {
1044 for (x = 0; x < image->width; ++x) {
1045 for (i = 0; i < 3; ++i) {
1046 comp_min[i] = min(*data, comp_min[i]);
1047 comp_max[i] = max(*data, comp_max[i]);
1048 comp_sums[i] += *data;
1049 comp_bins[i][*data >> 2]++;
1055 for (i = 0; i < ARRAY_SIZE(comp_min); ++i) {
1056 *(uint8_t *)histo++ = comp_min[comp_map[i]];
1057 *(uint8_t *)histo++ = 0;
1058 *(uint8_t *)histo++ = comp_max[comp_map[i]];
1059 *(uint8_t *)histo++ = 0;
1062 for (i = 0; i < ARRAY_SIZE(comp_sums); ++i) {
1063 *(uint32_t *)histo = comp_sums[comp_map[i]];
1067 for (i = 0; i < ARRAY_SIZE(comp_bins); ++i) {
1068 for (j = 0; j < ARRAY_SIZE(comp_bins[i]); ++j) {
1069 *(uint32_t *)histo = comp_bins[comp_map[i]][j];
1075 static int histogram(const struct image *image, const char *filename)
1077 uint8_t data[3*4 + 3*4 + 3*64*4];
1081 histogram_compute(image, data);
1083 fd = open(filename, O_WRONLY | O_CREAT,
1084 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1086 printf("Unable to open histogram file %s: %s (%d)\n", filename,
1087 strerror(errno), errno);
1091 ret = file_write(fd, data, sizeof(data));
1093 printf("Unable to write histogram: %s (%d)\n",
1094 strerror(-ret), ret);
1096 ftruncate(fd, sizeof(data));
1102 /* -----------------------------------------------------------------------------
1103 * Processing pipeline
1106 static int process(const struct options *options)
1108 struct image *input = NULL;
1109 struct image *output = NULL;
1110 unsigned int output_width;
1111 unsigned int output_height;
1114 /* Read the input image */
1115 input = image_read(options->input_filename);
1121 /* Convert colorspace */
1122 if (options->process_yuv) {
1125 yuv = image_new(format_by_name("YUV24"), input->width,
1132 image_colorspace_rgb_to_yuv(input, yuv, &options->params);
1133 image_delete(input);
1138 if (options->output_width && options->output_height) {
1139 output_width = options->output_width;
1140 output_height = options->output_height;
1142 output_width = input->width;
1143 output_height = input->height;
1146 if (options->rotate)
1147 swap(output_width, output_height);
1149 if (input->width != output_width ||
1150 input->height != output_height) {
1151 struct image *scaled;
1153 scaled = image_new(input->format, output_width, output_height);
1159 image_scale(input, scaled, &options->params);
1160 image_delete(input);
1165 if (options->compose) {
1166 struct image *composed;
1168 composed = image_new(input->format, input->width, input->height);
1174 image_compose(input, composed, options->compose);
1175 image_delete(input);
1179 /* Look-up tables */
1180 if (options->lut_filename) {
1183 lut = image_new(input->format, input->width, input->height);
1189 image_lut_1d(input, lut, options->lut_filename);
1190 image_delete(input);
1194 if (options->clu_filename) {
1197 clu = image_new(input->format, input->width, input->height);
1203 image_lut_3d(input, clu, options->clu_filename);
1204 image_delete(input);
1208 /* Compute the histogram */
1209 if (options->histo_filename) {
1210 ret = histogram(input, options->histo_filename);
1215 /* Rotation and flipping */
1216 if (options->rotate) {
1217 struct image *rotated;
1219 rotated = image_new(input->format, input->height, input->width);
1225 image_rotate(input, rotated);
1226 image_delete(input);
1230 if (options->hflip || options->vflip) {
1231 struct image *flipped;
1233 flipped = image_new(input->format, input->width, input->height);
1239 image_flip(input, flipped, options->hflip, options->vflip);
1240 image_delete(input);
1244 /* Format the output */
1245 if (input->format->is_yuv && !options->output_format->is_yuv) {
1246 printf("RGB output with YUV processing not supported\n");
1251 if (!input->format->is_yuv && options->output_format->is_yuv) {
1252 const struct format_info *format = format_by_name("YUV24");
1253 struct image *converted;
1255 converted = image_new(format, input->width, input->height);
1261 image_colorspace_rgb_to_yuv(input, converted, &options->params);
1262 image_delete(input);
1266 output = image_new(options->output_format, input->width, input->height);
1272 if (output->format->is_yuv) {
1273 switch (output->format->yuv.num_planes) {
1275 image_format_yuv_packed(input, output, &options->params);
1279 image_format_yuv_planar(input, output, &options->params);
1286 switch (output->format->rgb.bpp) {
1288 image_format_rgb8(input, output, &options->params);
1291 image_format_rgb16(input, output, &options->params);
1294 image_format_rgb24(input, output, &options->params);
1297 image_format_rgb32(input, output, &options->params);
1306 printf("Output formatting failed\n");
1310 /* Write the output image */
1311 if (options->output_filename) {
1312 ret = image_write(output, options->output_filename);
1320 image_delete(input);
1321 image_delete(output);
1325 /* -----------------------------------------------------------------------------
1326 * Usage, argument parsing and main
1329 static void usage(const char *argv0)
1331 printf("Usage: %s [options] <infile.pnm>\n\n", argv0);
1332 printf("Convert the input image stored in <infile> in PNM format to\n");
1333 printf("the target format and resolution and store the resulting\n");
1334 printf("image in raw binary form\n\n");
1335 printf("Supported options:\n");
1336 printf("-a, --alpha value Set the alpha value. Valid syntaxes are floating\n");
1337 printf(" point values ([0.0 - 1.0]), fixed point values ([0-255])\n");
1338 printf(" or percentages ([0%% - 100%%]). Defaults to 1.0\n");
1339 printf("-c, --compose n Compose n copies of the image offset by (50,50) over a black background\n");
1340 printf("-e, --encoding enc Set the YCbCr encoding method. Valid values are\n");
1341 printf(" BT.601, REC.709, BT.2020 and SMPTE240M\n");
1342 printf("-f, --format format Set the output image format\n");
1343 printf(" Defaults to RGB24 if not specified\n");
1344 printf(" Use -f help to list the supported formats\n");
1345 printf("-h, --help Show this help screen\n");
1346 printf(" --hflip Flip the image horizontally\n");
1347 printf("-H, --histogram file Compute histogram on the output image and store it to file\n");
1348 printf("-l, --lut file Apply 1D Look Up Table from file\n");
1349 printf("-L, --clu file Apply 3D Look Up Table from file\n");
1350 printf("-o, --output file Store the output image to file\n");
1351 printf("-q, --quantization q Set the quantization method. Valid values are\n");
1352 printf(" limited or full\n");
1353 printf("-r, --rotate Rotate the image clockwise by 90°\n");
1354 printf("-s, --size WxH Set the output image size\n");
1355 printf(" Defaults to the input size if not specified\n");
1356 printf(" --vflip Flip the image vertically\n");
1357 printf("-y, --yuv Perform all processing in YUV space\n");
1360 static void list_formats(void)
1364 for (i = 0; i < ARRAY_SIZE(format_info); i++)
1365 printf("%s\n", format_info[i].name);
1368 #define OPT_HFLIP 256
1369 #define OPT_VFLIP 257
1371 static struct option opts[] = {
1372 {"alpha", 1, 0, 'a'},
1374 {"compose", 1, 0, 'c'},
1375 {"encoding", 1, 0, 'e'},
1376 {"format", 1, 0, 'f'},
1377 {"help", 0, 0, 'h'},
1378 {"hflip", 0, 0, OPT_HFLIP},
1379 {"histogram", 1, 0, 'H'},
1381 {"output", 1, 0, 'o'},
1382 {"quantization", 1, 0, 'q'},
1383 {"rotate", 0, 0, 'r'},
1384 {"size", 1, 0, 's'},
1385 {"vflip", 0, 0, OPT_VFLIP},
1390 static int parse_args(struct options *options, int argc, char *argv[])
1400 memset(options, 0, sizeof(*options));
1401 options->output_format = format_by_name("RGB24");
1402 options->params.alpha = 255;
1403 options->params.encoding = V4L2_YCBCR_ENC_601;
1404 options->params.quantization = V4L2_QUANTIZATION_LIM_RANGE;
1407 while ((c = getopt_long(argc, argv, "a:c:e:f:hH:l:L:o:q:rs:y", opts, NULL)) != -1) {
1413 if (strchr(optarg, '.')) {
1414 alpha = strtod(optarg, &endptr) * 255;
1418 alpha = strtoul(optarg, &endptr, 10);
1420 alpha = alpha * 255 / 100;
1421 else if (*endptr != 0)
1425 if (alpha < 0 || alpha > 255) {
1426 printf("Invalid alpha value '%s'\n", optarg);
1430 options->params.alpha = alpha;
1435 options->compose = strtoul(optarg, &endptr, 10);
1437 printf("Invalid compose value '%s'\n", optarg);
1443 if (!strcmp(optarg, "BT.601")) {
1444 options->params.encoding = V4L2_YCBCR_ENC_601;
1445 } else if (!strcmp(optarg, "REC.709")) {
1446 options->params.encoding = V4L2_YCBCR_ENC_709;
1447 } else if (!strcmp(optarg, "BT.2020")) {
1448 options->params.encoding = V4L2_YCBCR_ENC_BT2020;
1449 } else if (!strcmp(optarg, "SMPTE240M")) {
1450 options->params.encoding = V4L2_YCBCR_ENC_SMPTE240M;
1452 printf("Invalid encoding value '%s'\n", optarg);
1458 if (!strcmp("help", optarg)) {
1463 options->output_format = format_by_name(optarg);
1464 if (!options->output_format) {
1465 printf("Unsupported output format '%s'\n", optarg);
1477 options->histo_filename = optarg;
1481 options->lut_filename = optarg;
1485 options->clu_filename = optarg;
1489 options->output_filename = optarg;
1493 if (!strcmp(optarg, "limited")) {
1494 options->params.quantization = V4L2_QUANTIZATION_LIM_RANGE;
1495 } else if (!strcmp(optarg, "full")) {
1496 options->params.quantization = V4L2_QUANTIZATION_FULL_RANGE;
1498 printf("Invalid quantization value '%s'\n", optarg);
1506 options->rotate = true;
1510 options->output_width = strtol(optarg, &endptr, 10);
1511 if (*endptr != 'x' || endptr == optarg) {
1512 printf("Invalid size '%s'\n", optarg);
1516 options->output_height = strtol(endptr + 1, &endptr, 10);
1518 printf("Invalid size '%s'\n", optarg);
1524 options->process_yuv = true;
1528 options->hflip = true;
1532 options->vflip = true;
1536 printf("Invalid option -%c\n", c);
1537 printf("Run %s -h for help.\n", argv[0]);
1542 if (optind != argc - 1) {
1547 options->input_filename = argv[optind];
1552 int main(int argc, char *argv[])
1554 struct options options;
1557 ret = parse_args(&options, argc, argv);
1561 ret = process(&options);