summaryrefslogtreecommitdiff
path: root/src/gen-image.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gen-image.c')
-rw-r--r--src/gen-image.c114
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;