third-party/leptonica/prog/yuvtest.c
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*====================================================================*/
/*
* yuvtest.c
*
* Test the yuv to rgb conversion.
*
* Note that the yuv gamut is greater than rgb, so although any
* rgb image can be converted to yuv (and back), any possible
* yuv value does not necessarily represent a valid rgb value.
*/
#include "allheaders.h"
void AddTransformsRGB(PIXA *pixa, L_BMF *bmf, l_int32 gval);
void AddTransformsYUV(PIXA *pixa, L_BMF *bmf, l_int32 yval);
l_int32 main(int argc,
char **argv)
{
l_int32 i, rval, gval, bval, yval, uval, vval;
l_float32 *a[3], b[3];
L_BMF *bmf;
PIX *pixd;
PIXA *pixa;
/* Explore the range of rgb --> yuv transforms. All rgb
* values transform to a valid value of yuv, so when transforming
* back we get the same rgb values that we started with. */
pixa = pixaCreate(0);
bmf = bmfCreate("fonts", 6);
for (gval = 0; gval <= 255; gval += 20)
AddTransformsRGB(pixa, bmf, gval);
pixd = pixaDisplayTiledAndScaled(pixa, 32, 755, 1, 0, 20, 2);
pixDisplay(pixd, 100, 0);
pixWrite("/tmp/yuv1.png", pixd, IFF_PNG);
pixDestroy(&pixd);
pixaDestroy(&pixa);
/* Now start with all "valid" yuv values, not all of which are
* related to a valid rgb value. Our yuv --> rgb transform
* clips the rgb components to [0 ... 255], so when transforming
* back we get different values whenever the initial yuv
* value is out of the rgb gamut. */
pixa = pixaCreate(0);
for (yval = 16; yval <= 235; yval += 16)
AddTransformsYUV(pixa, bmf, yval);
pixd = pixaDisplayTiledAndScaled(pixa, 32, 755, 1, 0, 20, 2);
pixDisplay(pixd, 600, 0);
pixWrite("/tmp/yuv2.png", pixd, IFF_PNG);
pixDestroy(&pixd);
pixaDestroy(&pixa);
bmfDestroy(&bmf);
/* --------- Try out a special case by hand, and show that --------- *
* ------- the transform matrices we are using are inverses ---------*/
/* First, use our functions for the transform */
fprintf(stderr, "Start with: yval = 143, uval = 79, vval = 103\n");
convertYUVToRGB(143, 79, 103, &rval, &gval, &bval);
fprintf(stderr, " ==> rval = %d, gval = %d, bval = %d\n", rval, gval, bval);
convertRGBToYUV(rval, gval, bval, &yval, &uval, &vval);
fprintf(stderr, " ==> yval = %d, uval = %d, vval = %d\n", yval, uval, vval);
/* Next, convert yuv --> rbg by solving for rgb --> yuv transform.
* [ a00 a01 a02 ] r = b0 (y - 16)
* [ a10 a11 a12 ] * g = b1 (u - 128)
* [ a20 a21 a22 ] b = b2 (v - 128)
*/
b[0] = 143.0 - 16.0; /* y - 16 */
b[1] = 79.0 - 128.0; /* u - 128 */
b[2] = 103.0 - 128.0; /* v - 128 */
for (i = 0; i < 3; i++)
a[i] = (l_float32 *)lept_calloc(3, sizeof(l_float32));
a[0][0] = 65.738 / 256.0;
a[0][1] = 129.057 / 256.0;
a[0][2] = 25.064 / 256.0;
a[1][0] = -37.945 / 256.0;
a[1][1] = -74.494 / 256.0;
a[1][2] = 112.439 / 256.0;
a[2][0] = 112.439 / 256.0;
a[2][1] = -94.154 / 256.0;
a[2][2] = -18.285 / 256.0;
fprintf(stderr, "Here's the original matrix: yuv --> rgb:\n");
for (i = 0; i < 3; i++)
fprintf(stderr, " %7.3f %7.3f %7.3f\n", 256.0 * a[i][0],
256.0 * a[i][1], 256.0 * a[i][2]);
gaussjordan(a, b, 3);
fprintf(stderr, "\nInput (yuv) = (143,79,103); solve for rgb:\n"
"rval = %7.3f, gval = %7.3f, bval = %7.3f\n",
b[0], b[1], b[2]);
fprintf(stderr, "Here's the inverse matrix: rgb --> yuv:\n");
for (i = 0; i < 3; i++)
fprintf(stderr, " %7.3f %7.3f %7.3f\n", 256.0 * a[i][0],
256.0 * a[i][1], 256.0 * a[i][2]);
/* Now, convert back: rgb --> yuv;
* Do this by solving for yuv --> rgb transform.
* Use the b[] found previously (the rgb values), and
* the a[][] which now holds the rgb --> yuv transform. */
gaussjordan(a, b, 3);
fprintf(stderr, "\nInput rgb; solve for yuv:\n"
"yval = %7.3f, uval = %7.3f, vval = %7.3f\n",
b[0] + 16.0, b[1] + 128.0, b[2] + 128.0);
fprintf(stderr, "Inverting the matrix again: yuv --> rgb:\n");
for (i = 0; i < 3; i++)
fprintf(stderr, " %7.3f %7.3f %7.3f\n", 256.0 * a[i][0],
256.0 * a[i][1], 256.0 * a[i][2]);
for (i = 0; i < 3; i++) lept_free(a[i]);
return 0;
}
void
AddTransformsRGB(PIXA *pixa,
L_BMF *bmf,
l_int32 gval)
{
char textbuf[256];
l_int32 i, j, wpls;
l_uint32 *datas, *lines;
PIX *pixs, *pixt1, *pixt2, *pixt3, *pixt4;
PIXA *pixat;
pixs = pixCreate(255, 255, 32);
wpls = pixGetWpl(pixs);
datas = pixGetData(pixs);
for (i = 0; i < 255; i++) { /* r */
lines = datas + i * wpls;
for (j = 0; j < 255; j++) /* b */
composeRGBPixel(i, gval, j, lines + j);
}
pixat = pixaCreate(3);
pixaAddPix(pixat, pixs, L_INSERT);
pixt1 = pixConvertRGBToYUV(NULL, pixs);
pixaAddPix(pixat, pixt1, L_INSERT);
pixt2 = pixConvertYUVToRGB(NULL, pixt1);
pixaAddPix(pixat, pixt2, L_INSERT);
pixt3 = pixaDisplayTiledAndScaled(pixat, 32, 255, 3, 0, 20, 2);
snprintf(textbuf, sizeof(textbuf), "gval = %d", gval);
pixt4 = pixAddSingleTextblock(pixt3, bmf, textbuf, 0xff000000,
L_ADD_BELOW, NULL);
pixaAddPix(pixa, pixt4, L_INSERT);
pixDestroy(&pixt3);
pixaDestroy(&pixat);
return;
}
void
AddTransformsYUV(PIXA *pixa,
L_BMF *bmf,
l_int32 yval)
{
char textbuf[256];
l_int32 i, j, wpls;
l_uint32 *datas, *lines;
PIX *pixs, *pixt1, *pixt2, *pixt3, *pixt4;
PIXA *pixat;
pixs = pixCreate(225, 225, 32);
wpls = pixGetWpl(pixs);
datas = pixGetData(pixs);
for (i = 0; i < 225; i++) { /* v */
lines = datas + i * wpls;
for (j = 0; j < 225; j++) /* u */
composeRGBPixel(yval + 16, j + 16, i + 16, lines + j);
}
pixat = pixaCreate(3);
pixaAddPix(pixat, pixs, L_INSERT);
pixt1 = pixConvertYUVToRGB(NULL, pixs);
pixaAddPix(pixat, pixt1, L_INSERT);
pixt2 = pixConvertRGBToYUV(NULL, pixt1);
pixaAddPix(pixat, pixt2, L_INSERT);
pixt3 = pixaDisplayTiledAndScaled(pixat, 32, 225, 3, 0, 20, 2);
snprintf(textbuf, sizeof(textbuf), "yval = %d", yval);
pixt4 = pixAddSingleTextblock(pixt3, bmf, textbuf, 0xff000000,
L_ADD_BELOW, NULL);
pixaAddPix(pixa, pixt4, L_INSERT);
pixDestroy(&pixt3);
pixaDestroy(&pixat);
return;
}