@@ -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];
- 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;
+ 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;
- ycbcr[0] = y;
- ycbcr[1] = cb;
- ycbcr[2] = cr;
+#define CLAMP(x, low, high) \
+ ((x) < (low) ? (low) : ( (x) > (high) ? (high) : (x) ))
+
+ 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;
RGB to YCbCr conversion is only used at the moment to generate images for test cases that run fully YUV pipelines. The exact value of the RGB to YCbCr conversion matrices hasn't mattered much so far. However, this will change with introduction of tests that perform RGB to YCbCr conversion. To achieve pixel-perfect match between the reference and hardware frames, reference images need to be generated with the exact method used by the VSP. Replace the current coefficients by values obtained from reverse-engineering of the RGB to YCbCr conversion performed by the VSP1. Remove support for SMPTE240 and BT.2020, as the VSP hardware supports BT.601 and BT.709 only. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> --- src/gen-image.c | 114 +++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 64 deletions(-)