JPEG - Idea and Practice/The two programs for a colour picture

Two more components now need to be written in the file. The RGB colour values are converted to YCbCr colour values by the linear transform RGB → YCbCr, so that the three components are the Y component, the Cb component and the Cr component. But as explained in the section "The frame segment SOF" the components can be subsampled in relation to each other, and this subsampling is determined by pairs (Hi, Vi) (i = 1, 2, 3) for the three components. Usually the Y component is not submitted to subsampling and the two colour components are subsampled in the same way. We assume here that this is the case. It means that (Hi, Vi) = (1, 1) for the colour components, and that (H1, V1) is either (1, 1), (2, 1), (1, 2) or (2, 2). We assume first that (H1, V1) = (1, 1) and then that (H1, V1) = (2, 2), and we formulate the last case so that the formulas and the programs can be applied unaltered to the all the four cases.

(H1, V1) = (1, 1)  In this case there is no subsampling. For each 8x8-square we have for each component an encoding and writing procedure that is equal to the one used for the grey scale picture - the only difference is that we use different quantization and Huffman tables for the Y component and the two colour components. The writing into the file is controlled by a number cp, which is 1, 2 and 3, respectively, for the Y component, the Cb component and the Cr component.

Like in the grey scale case, the reading of the file and the drawing of the picture go on in a loop, but since an 8x8-square cannot be drawn until three sequences of data are read, we must store things, namely the 64-arrays that are the result of each reading. We let the reading be controlled by a number cp: for cp = 1, 2 and 3, the data of respectively the Y component, the Cb component and the Cr component are used to form 64-arrays w which are stored in the variables wy, wb and wr. Then cp is set to 4, and when cp = 4 the arrays wy, wb and wr are converted to 8x8-matrices and submitted to de-quantization and the inverse discrete cosine transform, giving three 8x8-matrices (of integers) which can be regarded as an 8x8-matrix of YCbCr triples. The YCbCr triples are converted to RGB triples by the inverse of the RGB → YCbCr transform. If we set wid8 = width div 8 and hei8 = height div 8, the 8x8-squares can be assigned coordinate sets (i0, j0), i0 = 0, ..., wid8-1, j0 = 0, ..., hei8-1, and the point to be coloured with the RGB triple (in the 8x8-matrix) having coordinate set (i, j) (i, j = 0, ... 7), has coordinate set (i0*8 + i, j0*8 + j) in the picture.

(H1, V1) = (2, 2)  This means that, for the two colour components, four pixels forming a 2x2-square are regarded as one pixel by taking the average value of the colours. For a colour component an 8x8-square therefore corresponds to a 16x16-square in the picture, and it must be combined with four 8x8-squares for the Y component. The encoded data for these four 8x8-squares are written in the file one just after the other in the usual order: left-to-right and top-to-bottom. After this the data for the 8x8-square for the two colour components are encoded and written in the file, and then we go to the next 16x16-square. We now assume that the width and the height of the picture are divisible by 16. We set wid8 = width div (H1*8) and hei8 = height div (V1*8), so that the rectangles of the dividing up of the Y component (in our concrete case, the 16x16-squares) have coordinate sets (i0, j0), i0 = 0, ..., wid8-1, j0 = 0, ..., hei8-1.

This procedure (the making of the file) is straightforward, but the converse procedure, the reading of the file and drawing of the picture is not as simple, because things must be stored and combined in the right way. The result of a reading and decoding is a 64-array of numbers, and such six arrays must now be stored before we can draw a 16x16-square: four arrays for the Y component and one array for each of the colour components. In order to have a uniform way of combining (for (H1, V1) = (1, 1), (2, 1), (1, 2) or (2, 2)) we let a 64-array for the Y component be a matrix of 64-arrays, namely (under our present assumption that (H1, V1) = (2, 2)) a 2x2-matrix of 64-arrays (or equivalent: a 64-array of 2x2-matrices). We call this wy, so that the four 64-arrays are wy[0, 0][l], wy[1, 0][l], wy[0, 1][l] and wy[1, 1][l] (l = 1, ..., 64).

As before, the decoding is controlled by a number cp that is 1, 2 and 3 for the readings of the three components, and 4 for the calculations and the drawing of the 16x16-square.

cp = 1  The reading procedure for cp = 1 is run through four times: for (i1, j1) = (0, 0), (0, 1), (1, 0) and (1, 1), respectively. Such a pair (i1, j1) is denoted pos, and the function that finds the next pair pos is called nextpos(pos), so that if pos = (1, 1) then nextpos(pos) is (0, 0). The program for nextpos is shown below.

A DC number dcy (for the Y component) is found by adding the number m (found by decodedy (giving the number val) followed by num (calculating m from val)) to the previous DC number stored in dcy0 - that for the previous pair pos, which is (1, 1) when pos = (0, 0) (for the next 16x16-square). The four DC numbers for the Y component make up a 2x2-matrix wy1[i1, j1] (i1, j1 = 0, 1) - denoted wy1 because it is the DC term of the 64-array wy of 2x2-matrices: wy[1] = wy1.

The 63 AC numbers (for the (i1, j1)) are found by decodeay (giving the numbers nz and val) followed by the procedure formac shown below. The result of formac is an array w[l], l = 2, ..., 64 (with the first term unspecified), and this array is stored in wy[i1, j1]: wy[i1, j1] = w.

The DC term of wy[i1, j1] is wy1[i1, j1], but the fixing of this can wait until cp = 4: wy[i1, j1][1] = wy1[i1, j1].

After the readings for the four 8x8-squares (making up the 16x16-square) are finished, the pair (i1, j1) is set to (0, 0), and when (i1, j1) = (0, 0), cp is set to 2 (= cp + 1) for the reading of the Cb colour component 8x8-square corresponding to the Y component 16x16-square.

cp = 2, 3  The forming of arrays wb and wr for the two colour components is similar to the one applying to the grey scale procedure. For wb (for instance) it goes on in this way: The DC number dcb is found by adding the number m (found by decodedc (giving the number val) followed by num (calculating m from val)) to the previous DC number stored in dcb0. Then the 63 AC numbers are found by decodeac (giving the numbers nz and val) followed by the procedure formac shown below. The result of formac is an array w[l], l = 2, ..., 64 (with the first term unspecified), and this array is stored in wb: wb = w. The DC term of wb is dcb, but the fixing of this can wait until cp = 4: wb[1] = dcb.

cp = 4  cp = 1 has produced a 2x2-matrix of 64-arrays wy[i1, j1] (i1, j1 = 0, 1), cp = 2 has produced a 64-array wb and cp = 3 has produced a 64-array wr. After this cp is set to 4, and when cp = 4 these six arrays are submitted to de-quantization and the inverse discrete cosine transform, and the resulting numbers are colour values to be combined in the right way to colour the 16x16-square. The coordinate set of the 16x16-square is (i0, j0) (i0 = 0, ..., wid8-1, j0 = 0, ..., hei8-1). And within such a 16x16-square, the coordinate sets for the four 8x8-squares are (i1, j1), i1, j1 = 0, 1, so that the left top corner of the 8x8-square (i1, j1) in the picture has coordinate set (i2, j2), where i2 = (i0*H1 + i1) * 8 and j2 = (j0*V1 + j1) * 8. Within an 8x8-square the coordinate sets are (i, j), i, j = 0, ..., 7. For the 8x8-square with coordinate set (i1, j1) in the 16x16-square with coordinate set (i0, j0), the point (i, j) corresponds 1) in the picture, to the point having coordinate set (i2 + i, j2 + j), and 2) in the 8x8-square of the colour components corresponding to the 16x16-square, to the point having coordinate set (i3, j3), where i3 = 4*i1 + i div H1 and j3 = 4*j1 + j div V1.

We denote by idcty(w) and idctc(w), respectively, the function that de-quantizes and takes the inverse discrete cosine transform of a 64-array w of an 8x8-square of the Y component and of the colour components. For the 8x8-square (i1, j1) (of the 16x16-square of the Y component), idcty is applied to the 64-array wy[i1, j1]. We call the resulting 8x8-matrix fy (fy = idcty(wy[i1, j1])) and let yy be the value of fy in the point (i, j): yy = fy[i, j]. For the 8x8-square of the colour components (corresponding to the 16x16-square), idctc is applied to the 64-arrays wb and wr. We call the resulting 8x8-matrices fb and fr (fb = idctc(wb) and fr = idctc(wr)) and let cb and br be the values of fb and fr in the point (i3, j3) corresponding to (i, j) (and (i1, j1)): cb = fb[i3, j3] and cr = fr[i3, j3].

The YCbCr triple (yy, cb, cr) is converted to the RGB triple (tr, tg, tb) by the inverse of the RGB → YCbCr transform. And the point to be coloured with this RGB triple has coordinate set (i2 + i, j2 + j):

if cp = 1 then
begin
if l = 1 then
begin
dcy0 = dcy
decodedy
num
dcy = m + dcy0
wy1[i1, j1] = dcy
end
decodeay
formac
if l = 64 then
begin
l = 1
wy[i1, j1] = w
pos[0] = i1
pos[1] = j1
i1 = nextpos(pos)[0]
j1 = nextpos(pos)[1]
if (i1 = 0) and (j1 = 0) then
cp = cp + 1
end
end
if cp = 2 then
begin
if l = 1 then
begin
dcb0 = dcb
decodedc
num
dcb = m + dcb0
end
decodeac
formac
if l = 64 then
begin
l = 1
wb = w
cp = cp + 1
end
end
if cp = 3 then
begin
if l = 1 then
begin
dcr0 = dcr
decodedc
num
dcr = m + dcr0
end
decodeac
formac
if l = 64 then
begin
l = 1
wr = w
cp = cp + 1
end
end
if cp = 4 then
begin
cp = 1
wb[1] = dcb
wr[1] = dcr
fb = idctc(wb)
fr = idctc(wr)
for j1 = 0 to v1 - 1 do
for i1 = 0 to h1 - 1 do
begin
wy[i1, j1][1] = wy1[i1, j1]
fy = idcty(wy[i1, j1])
i2 = (i0 * h1 + i1) * 8
j2 = (j0 * v1 + j1) * 8
for j = 0 to 7 do
for i = 0 to 7 do
begin
i3 = 4 * i1 + i div h1
j3 = 4 * j1 + j div v1
yy = fy[i, j]
cb = fb[i3, j3]
cr = fr[i3, j3]
tr = round(yy + 1.402 * cr + 128)
tg = round(yy - 0.3441 * cb - 0.71414 * cr + 128)
tb = round(yy + 1.772 * cb + 128)
if tr > 255 then
tr = 255
if tr < 0 then
tr = 0
if tg > 255 then
tg = 255
if tg < 0 then
tg = 0
if tb > 255 then
tb = 255
if tb < 0 then
tb = 0
setpixel(i2 + i, j2 + j, tr, tg, tb)
end
end
i1 = 0
j1 = 0
i0 = i0 + 1
if i0 * h1 * 8 >= width then
begin
i0 = 0
j0 = j0 + 1
end
end

The function nextpos(pos) can be calculated by this program:

i = pos[0]
j = pos[1]
i = i + 1
if (v1 = 2) and (j = 0) and (i = h1) then
begin
j = 1
i = 0
end
if (j = v1 - 1) and (i = h1) then
begin
i = 0
j = 0
end
nextpos[0] = i
nextpos[1] = j

The program for formac which, after the decoding decodeay and decodeac of the AC part of the Y component and the colour components, respectively, forms the AC part of the 64-array w (that is, the w[l]'s for l > 1), producing two numbers nz (number of zeros) and val (number of digits to be used by num), could look like this:

if val > 0 then
begin
if nz > 0 then
for i = 1 to nz do
begin
l = l + 1
w[l] = 0
end
num
l = l + 1
w[l] = m
end
if (nz = 15) and (val = 0) then
for i = 1 to 16 do
begin
l = l + 1
w[l] = 0
end
if (nz = 0) and (val = 0) then
while l < 64 do
begin
l = l + 1
w[l] = 0
end