diff options
-rw-r--r-- | src/gen-image.c | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/src/gen-image.c b/src/gen-image.c index 2f85463..cf6b6bc 100644 --- a/src/gen-image.c +++ b/src/gen-image.c @@ -979,6 +979,101 @@ static void image_convert_rgb_to_hsv(const struct image *input, } } +/* YUV ro RGB */ +static void csc_yuv_to_rgb(const int ccm[3][3], bool full_range, + const uint8_t ycbcr[3], uint8_t rgb[3]) +{ + int y_min = full_range ? 0 : 16; + int div = 1 << 11; + int y, cb, cr; + int r, g, b; + + y = ycbcr[0] - y_min; + cb = ycbcr[1] - 128; + cr = ycbcr[2] - 128; + + r = (ccm[0][0] * y + ccm[0][1] * cb + ccm[0][2] * cr + div / 2) / div; + g = (ccm[1][0] * y + ccm[1][1] * cb + ccm[1][2] * cr + div / 2) / div; + b = (ccm[2][0] * y + ccm[2][1] * cb + ccm[2][2] * cr + div / 2) / div; + + rgb[0] = CLAMP(r, 0, 255); + rgb[1] = CLAMP(g, 0, 255); + rgb[2] = CLAMP(b, 0, 255); +} + +static void image_convert_yuv_to_rgb(const struct image *input, + struct image *output, + const struct format_info *format, + const struct csc_params *params) +{ + /* + * The value of the coefficients has been reverse-engineered by + * analyzing the VSP1 RGB output values for carefully crafted input YUV + * 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 column of the + * matrix matches the standard, but the second and third columns seem to + * be divided by a factor equal to (240-16)/(235-16). The reason is not + * currently understood, but the value of the factor strongly hints that + * this isn't a random difference. + */ + static const int bt601[3][3] = { + { 2384, 0, 3269 }, + { 2384, -803, -1665 }, + { 2384, 4131, 0 }, + }; + static const int bt601_full[3][3] = { + { 2048, 0, 2871 }, + { 2048, -705, -1463 }, + { 2048, 3629, 0 }, + }; + static const int rec709[3][3] = { + { 2385, 0, 3672 }, + { 2385, -437, -1092 }, + { 2385, 4326, 0 }, + }; + static const int rec709_full[3][3] = { + { 2048, 0, 3153 }, + { 2048, -375, -937 }, + { 2048, 3715, 0 }, + }; + + bool full_range = params->quantization == V4L2_QUANTIZATION_FULL_RANGE; + const int (*matrix)[3][3]; + const uint8_t *idata = input->data; + uint8_t *odata = output->data; + unsigned int x; + unsigned int y; + + switch (params->encoding) { + case V4L2_YCBCR_ENC_601: + default: + matrix = full_range ? &bt601_full : &bt601; + break; + case V4L2_YCBCR_ENC_709: + matrix = full_range ? &rec709_full : &rec709; + break; + } + + for (y = 0; y < output->height; ++y) { + for (x = 0; x < output->width; ++x) + csc_yuv_to_rgb(*matrix, full_range, + &idata[3*x], &odata[3*x]); + + idata += 3 * output->width; + odata += 3 * output->width; + } +} + typedef void (*image_convert_func)(const struct image *input, struct image *output, const struct format_info *format, @@ -994,6 +1089,7 @@ static const struct image_converter image_converters[] = { { FORMAT_RGB, FORMAT_HSV, image_convert_rgb_to_hsv }, { FORMAT_RGB, FORMAT_RGB, image_convert_rgb_to_rgb }, { FORMAT_RGB, FORMAT_YUV, image_convert_rgb_to_yuv }, + { FORMAT_YUV, FORMAT_RGB, image_convert_yuv_to_rgb }, }; static int image_convert(struct image *input, const struct format_info *format, |