diff options
-rw-r--r-- | src/gen-image.c | 114 |
1 files changed, 50 insertions, 64 deletions
diff --git a/src/gen-image.c b/src/gen-image.c index d053d79..50c1010 100644 --- a/src/gen-image.c +++ b/src/gen-image.c @@ -700,56 +700,51 @@ static void image_format_yuv_planar(const struct image *input, struct image *out } /* ----------------------------------------------------------------------------- - * Colorspace handling - * - * The code is inspired by the v4l2-tpg Linux kernel driver. + * Format conversion (as performed by the Renesas VSP HST, HSI, RPF and WPF) */ static void colorspace_matrix(enum v4l2_ycbcr_encoding encoding, enum v4l2_quantization quantization, int (*matrix)[3][3]) { -#define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0)) - + /* + * The value of the coefficients has been reverse-engineered by + * analyzing the VSP1 YUV output values for carefully crafted input RGB + * data. The hardware precision of the coefficients appears to be Q1.11. + * + * The exact way to derive those fixed-point coefficients from the + * BT.601 and BT.709 standard values is not know, none of the tested + * rounding methods (rounding down, rounding up, rounding to the closest + * integer, or rounding to minimum the error on the sum of each line) + * produce the fixed-point values used by the hardware. + * + * While the coefficients for BT.601 in both ranges, and BT.709 in + * limited range, differ from the values listed in the respective + * standards by at most a single unit, some of the BT.709 full range + * coefficients differ more significantly. The first line of the matrix + * matches the standard, but the second and third lines seem to be + * multiplied a factor approximately equal to 1.072. The reason is not + * currently understood. + */ static const int bt601[3][3] = { - { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) }, - { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) }, - { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) }, + { 526, 1033, 201 }, + { -304, -596, 900 }, + { 900, -753, -146 }, }; static const int bt601_full[3][3] = { - { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) }, - { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) }, - { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) }, + { 612, 1202, 233 }, + { -346, -678, 1024 }, + { 1024, -857, -167 }, }; static const int rec709[3][3] = { - { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) }, - { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) }, - { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) }, + { 374, 1258, 127 }, + { -206, -693, 899 }, + { 899, -817, -83 }, }; static const int rec709_full[3][3] = { - { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) }, - { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) }, - { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) }, - }; - static const int smpte240m[3][3] = { - { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) }, - { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) }, - { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) }, - }; - static const int smpte240m_full[3][3] = { - { COEFF(0.212, 255), COEFF(0.701, 255), COEFF(0.087, 255) }, - { COEFF(-0.116, 255), COEFF(-0.384, 255), COEFF(0.5, 255) }, - { COEFF(0.5, 255), COEFF(-0.445, 255), COEFF(-0.055, 255) }, - }; - static const int bt2020[3][3] = { - { COEFF(0.2627, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) }, - { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) }, - { COEFF(0.5, 224), COEFF(-0.4598, 224), COEFF(-0.0402, 224) }, - }; - static const int bt2020_full[3][3] = { - { COEFF(0.2627, 255), COEFF(0.6780, 255), COEFF(0.0593, 255) }, - { COEFF(-0.1396, 255), COEFF(-0.3604, 255), COEFF(0.5, 255) }, - { COEFF(0.5, 255), COEFF(-0.4698, 255), COEFF(-0.0402, 255) }, + { 435, 1465, 148 }, + { -240, -807, 1047 }, + { 1047, -951, -96 }, }; bool full = quantization == V4L2_QUANTIZATION_FULL_RANGE; @@ -764,12 +759,6 @@ static void colorspace_matrix(enum v4l2_ycbcr_encoding encoding, case V4L2_YCBCR_ENC_709: m = full ? &rec709_full : &rec709; break; - case V4L2_YCBCR_ENC_BT2020: - m = full ? &bt2020_full : &bt2020; - break; - case V4L2_YCBCR_ENC_SMPTE240M: - m = full ? &smpte240m_full : &smpte240m; - break; } for (i = 0; i < ARRAY_SIZE(*m); ++i) @@ -781,23 +770,28 @@ static void colorspace_rgb2ycbcr(int m[3][3], const uint8_t rgb[3], uint8_t ycbcr[3]) { bool full = quantization == V4L2_QUANTIZATION_FULL_RANGE; - unsigned int y_offset = full ? 0 : 16; + int y_min = full ? 0 : 16; + int y_max = full ? 255 : 235; + int cbcr_min = full ? 0 : 16; + int cbcr_max = full ? 255 : 240; + int div = 1 << 11; int r, g, b; int y, cb, cr; - int div; - r = rgb[0] << 4; - g = rgb[1] << 4; - b = rgb[2] << 4; + r = rgb[0]; + g = rgb[1]; + b = rgb[2]; + + y = (m[0][0] * r + m[0][1] * g + m[0][2] * b + y_min * div + div / 2) / div; + cb = (m[1][0] * r + m[1][1] * g + m[1][2] * b + 128 * div + div / 2) / div; + cr = (m[2][0] * r + m[2][1] * g + m[2][2] * b + 128 * div + div / 2) / div; - div = (1 << (8 + 4)) * 255; - y = (m[0][0] * r + m[0][1] * g + m[0][2] * b + y_offset * div) / div; - cb = (m[1][0] * r + m[1][1] * g + m[1][2] * b + 128 * div) / div; - cr = (m[2][0] * r + m[2][1] * g + m[2][2] * b + 128 * div) / div; +#define CLAMP(x, low, high) \ + ((x) < (low) ? (low) : ( (x) > (high) ? (high) : (x) )) - ycbcr[0] = y; - ycbcr[1] = cb; - ycbcr[2] = cr; + ycbcr[0] = CLAMP(y, y_min, y_max); + ycbcr[1] = CLAMP(cb, cbcr_min, cbcr_max); + ycbcr[2] = CLAMP(cr, cbcr_min, cbcr_max); } static void image_colorspace_rgb_to_yuv(const struct image *input, @@ -851,10 +845,6 @@ static void image_convert_rgb_to_rgb(const struct image *input, } } -/* ----------------------------------------------------------------------------- - * RGB to HSV conversion (as performed by the Renesas VSP HST) - */ - #define K 4 static uint8_t hst_calc_h(uint8_t r, uint8_t g, uint8_t b) { @@ -1828,7 +1818,7 @@ static void usage(const char *argv0) printf("-C, --no-chroma-average Disable chroma averaging for odd pixels on output\n"); printf(" --crop (X,Y)/WxH Crop the input image\n"); printf("-e, --encoding enc Set the YCbCr encoding method. Valid values are\n"); - printf(" BT.601, REC.709, BT.2020 and SMPTE240M\n"); + printf(" BT.601 and REC.709\n"); printf("-f, --format format Set the output image format\n"); printf(" Defaults to RGB24 if not specified\n"); printf(" Use -f help to list the supported formats\n"); @@ -2062,10 +2052,6 @@ static int parse_args(struct options *options, int argc, char *argv[]) options->params.encoding = V4L2_YCBCR_ENC_601; } else if (!strcmp(optarg, "REC.709")) { options->params.encoding = V4L2_YCBCR_ENC_709; - } else if (!strcmp(optarg, "BT.2020")) { - options->params.encoding = V4L2_YCBCR_ENC_BT2020; - } else if (!strcmp(optarg, "SMPTE240M")) { - options->params.encoding = V4L2_YCBCR_ENC_SMPTE240M; } else { printf("Invalid encoding value '%s'\n", optarg); return 1; |