跳轉到內容

JPEG - 想法和實踐/彩色圖片的兩個程式

來自華夏公益教科書,開放的書籍,開放的世界

現在需要在檔案中寫入另外兩個元件。RGB 顏色值透過線性變換 RGB → YCbCr 轉換為 YCbCr 顏色值,這樣三個元件分別是 Y 元件、Cb 元件和 Cr 元件。但正如“幀段 SOF”部分所述,元件可以彼此之間進行子取樣,這種子取樣由三組元件的 (Hi, Vi) (i = 1, 2, 3) 對決定。通常 Y 元件不進行子取樣,而兩個顏色元件以相同的方式進行子取樣。我們這裡假設這種情況。這意味著 (Hi, Vi) = (1, 1) 用於顏色元件,而 (H1, V1) 則為 (1, 1)、(2, 1)、(1, 2) 或 (2, 2) 之一。我們首先假設 (H1, V1) = (1, 1),然後假設 (H1, V1) = (2, 2),並將最後一種情況公式化,以便公式和程式可以不加修改地應用於所有四種情況。

(H1, V1) = (1, 1)  在這種情況下,沒有子取樣。對於每個 8x8 方格,我們每個元件都有一個與用於灰度影像相同的編碼和寫入過程 - 唯一的區別是,我們對 Y 元件和兩個顏色元件使用不同的量化和霍夫曼表。寫入檔案由數字 cp 控制,它分別代表 Y 元件、Cb 元件和 Cr 元件的 1、2 和 3。

與灰度情況類似,檔案讀取和影像繪製在一個迴圈中進行,但由於在讀取三個資料序列之前無法繪製 8x8 方格,因此必須儲存一些內容,即每次讀取的結果 64 陣列。我們讓讀取由數字 cp 控制:對於 cp = 1、2 和 3,分別使用 Y 元件、Cb 元件和 Cr 元件的資料來形成 64 陣列 w,並將其儲存在變數 wy、wb 和 wr 中。然後將 cp 設定為 4,當 cp = 4 時,陣列 wy、wb 和 wr 將轉換為 8x8 矩陣,並進行反量化和逆離散餘弦變換,得到三個 8x8 矩陣(整數),這些矩陣可以看作是 8x8 的 YCbCr 三元組矩陣。YCbCr 三元組透過 RGB → YCbCr 變換的逆變換轉換為 RGB 三元組。如果我們設定 wid8 = width div 8 和 hei8 = height div 8,則 8x8 方格可以分配座標集 (i0, j0),i0 = 0, ..., wid8-1, j0 = 0, ..., hei8-1,並且要以 RGB 三元組(在 8x8 矩陣中)著色的點具有座標集 (i, j) (i, j = 0, ... 7),在影像中具有座標集 (i0*8 + i, j0*8 + j)。

(H1, V1) = (2, 2)  這意味著,對於兩個顏色元件,構成 2x2 方格的四個畫素透過取顏色的平均值被視為一個畫素。因此,對於顏色元件,一個 8x8 方格對應於影像中的一個 16x16 方格,並且必須與 Y 元件的四個 8x8 方格組合在一起。這些四個 8x8 方格的編碼資料按通常的順序(從左到右,從上到下)一個接一個地寫入檔案。之後,兩個顏色元件的 8x8 方格的資料被編碼並寫入檔案,然後我們轉到下一個 16x16 方格。我們現在假設影像的寬度和高度可以被 16 整除。我們設定 wid8 = width div (H1*8) 和 hei8 = height div (V1*8),這樣 Y 元件劃分(在本例中為 16x16 方格)的矩形具有座標集 (i0, j0),i0 = 0, ..., wid8-1, j0 = 0, ..., hei8-1。

此過程(建立檔案)很簡單,但相反的過程,即讀取檔案和繪製圖像並不那麼簡單,因為必須以正確的方式儲存和組合事物。讀取和解碼的結果是 64 個數字的陣列,現在必須儲存六個這樣的陣列,然後才能繪製一個 16x16 方格:四個陣列用於 Y 元件,每個顏色元件一個數組。為了以一致的方式進行組合(對於 (H1, V1) = (1, 1)、(2, 1)、(1, 2) 或 (2, 2)),我們讓 Y 元件的 64 陣列成為 64 陣列的矩陣,即(在我們當前假設 (H1, V1) = (2, 2) 的情況下)一個 2x2 的 64 陣列矩陣(或等效地:一個 64 陣列的 2x2 矩陣)。我們稱之為 wy,這樣四個 64 陣列就是 wy[0, 0][l]、wy[1, 0][l]、wy[0, 1][l] 和 wy[1, 1][l] (l = 1, ..., 64)。

與之前一樣,解碼由數字 cp 控制,它分別代表三個元件的讀取的 1、2 和 3,以及 16x16 方格的計算和繪製的 4。

cp = 1  cp = 1 的讀取過程執行四次:分別針對 (i1, j1) = (0, 0)、(0, 1)、(1, 0) 和 (1, 1)。這樣的一對 (i1, j1) 被表示為 pos,找到下一對 pos 的函式被稱為nextpos(pos),因此,如果 pos = (1, 1),那麼nextpos(pos) 就是 (0, 0)。nextpos 的程式如下所示。

DC 數 dcy(用於 Y 元件)是透過將數字 m(透過decodedy(給出數字 val)後跟num(從 val 計算 m)找到)新增到儲存在 dcy0 中的先前 DC 數中得到的 - 這是針對先前對 pos 的,當 pos = (0, 0) 時,它是 (1, 1)(用於下一個 16x16 方格)。Y 元件的四個 DC 數構成一個 2x2 矩陣 wy1[i1, j1] (i1, j1 = 0, 1) - 被表示為 wy1,因為它是一個 2x2 矩陣的 64 陣列 wy 的 DC 項:wy[1] = wy1。

63 個 AC 數(用於 (i1, j1))是透過decodeay(給出數字 nz 和 val)後跟下面顯示的formac 過程找到的。formac 的結果是一個數組 w[l],l = 2, ..., 64(第一項未指定),該陣列儲存在 wy[i1, j1] 中:wy[i1, j1] = w。

wy[i1, j1] 的 DC 項是 wy1[i1, j1],但它的固定可以等到 cp = 4 時:wy[i1, j1][1] = wy1[i1, j1]。

完成四個 8x8 方格(構成 16x16 方格)的讀取後,將對 (i1, j1) 設定為 (0, 0),當 (i1, j1) = (0, 0) 時,將 cp 設定為 2 (= cp + 1),以讀取對應於 Y 元件 16x16 方格的 Cb 顏色元件 8x8 方格。

cp = 2, 3  對兩個顏色元件形成陣列 wb 和 wr 與適用於灰度過程的相似。例如,對於 wb,它以這種方式進行:透過將數字 m(透過decodedc(給出數字 val)後跟num(從 val 計算 m)找到)新增到儲存在 dcb0 中的先前 DC 數中,找到 DC 數 dcb。然後,透過decodeac(給出數字 nz 和 val)後跟下面顯示的formac 過程,找到 63 個 AC 數。formac 的結果是一個數組 w[l],l = 2, ..., 64(第一項未指定),該陣列儲存在 wb 中:wb = w。wb 的 DC 項是 dcb,但它的固定可以等到 cp = 4 時:wb[1] = dcb。

cp = 4  cp = 1 生成了一個 2x2 的 64 陣列矩陣 wy[i1, j1] (i1, j1 = 0, 1),cp = 2 生成了一個 64 陣列 wb,cp = 3 生成了一個 64 陣列 wr。此後,將 cp 設定為 4,當 cp = 4 時,這六個陣列將進行反量化和逆離散餘弦變換,結果數字是顏色值,需要以正確的方式組合以對 16x16 方格進行著色。16x16 方格的座標集是 (i0, j0) (i0 = 0, ..., wid8-1, j0 = 0, ..., hei8-1)。在這樣的 16x16 方格內,四個 8x8 方格的座標集是 (i1, j1),i1, j1 = 0, 1,因此,影像中 8x8 方格 (i1, j1) 的左上角具有座標集 (i2, j2),其中 i2 = (i0*H1 + i1) * 8 且 j2 = (j0*V1 + j1) * 8。在一個 8x8 方格內,座標集是 (i, j),i, j = 0, ..., 7。對於具有座標集 (i1, j1) 的 8x8 方格(在具有座標集 (i0, j0) 的 16x16 方格中),點 (i, j) 對應於 1)在影像中,對應於具有座標集 (i2 + i, j2 + j) 的點,以及 2)在對應於 16x16 方格的顏色元件的 8x8 方格中,對應於具有座標集 (i3, j3) 的點,其中 i3 = 4*i1 + i div H1 且 j3 = 4*j1 + j div V1。

我們分別用idcty(w) 和idctc(w) 表示對 Y 元件和顏色元件的 8x8 方格的 64 陣列 w 進行反量化和逆離散餘弦變換的函式。對於 8x8 方格 (i1, j1)(Y 元件的 16x16 方格),idcty 應用於 64 陣列 wy[i1, j1]。我們稱結果 8x8 矩陣為 fy (fy = idcty(wy[i1, j1])),並讓 yy 是 fy 在點 (i, j) 中的值:yy = fy[i, j]。對於顏色元件的 8x8 方格(對應於 16x16 方格),idctc 應用於 64 陣列 wb 和 wr。我們稱結果 8x8 矩陣為 fb 和 fr (fb = idctc(wb) 且 fr = idctc(wr)),並讓 cb 和 br 是 fb 和 fr 在對應於 (i, j)(和 (i1, j1))的點 (i3, j3) 中的值:cb = fb[i3, j3] 且 cr = fr[i3, j3]。

YCbCr 三元組 (yy, cb, cr) 透過 RGB → YCbCr 變換的逆變換轉換為 RGB 三元組 (tr, tg, tb)。並且要以該 RGB 三元組著色的點具有座標集 (i2 + i, j2 + j)

如果 cp = 1 那麼
開始
如果 l = 1 那麼
開始
dcy0 = dcy
decodedy
num
dcy = m + dcy0
wy1[i1, j1] = dcy
結束
decodeay
formac
如果 l = 64 那麼
開始
l = 1
wy[i1, j1] = w
pos[0] = i1
pos[1] = j1
i1 = nextpos(pos)[0]
j1 = nextpos(pos)[1]
如果 (i1 = 0) 且 (j1 = 0) 那麼
cp = cp + 1
結束
結束
如果 cp = 2 那麼
開始
如果 l = 1 那麼
開始
dcb0 = dcb
decodedc
num
dcb = m + dcb0
結束
decodeac
formac
如果 l = 64 那麼
開始
l = 1
wb = w
cp = cp + 1
結束
結束
如果 cp = 3 那麼
開始
如果 l = 1 那麼
開始
dcr0 = dcr
decodedc
num
dcr = m + dcr0
結束
decodeac
formac
如果 l = 64 那麼
開始
l = 1
wr = w
cp = cp + 1
結束
結束
如果 cp = 4 那麼
開始
cp = 1
wb[1] = dcb
wr[1] = dcr
fb = idctc(wb)
fr = idctc(wr)
對於 j1 = 0 到 v1 - 1 做
對於 i1 = 0 到 h1 - 1 做
開始
wy[i1, j1][1] = wy1[i1, j1]
fy = idcty(wy[i1, j1])
i2 = (i0 * h1 + i1) * 8
j2 = (j0 * v1 + j1) * 8
對於 j = 0 到 7 做
對於 i = 0 到 7 做
開始
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)
如果 tr > 255 那麼
tr = 255
如果 tr < 0 那麼
tr = 0
如果 tg > 255 那麼
tg = 255
如果 tg < 0 那麼
tg = 0
如果 tb > 255 那麼
tb = 255
如果 tb < 0 那麼
tb = 0
setpixel(i2 + i, j2 + j, tr, tg, tb)
結束
結束
i1 = 0
j1 = 0
i0 = i0 + 1
如果 i0 * h1 * 8 >= width 那麼
開始
i0 = 0
j0 = j0 + 1
結束
結束

函式nextpos(pos) 可以透過以下程式計算

i = pos[0]
j = pos[1]
i = i + 1
如果 (v1 = 2) 且 (j = 0) 且 (i = h1) 那麼
開始
j = 1
i = 0
結束
如果 (j = v1 - 1) 且 (i = h1) 那麼
開始
i = 0
j = 0
結束
nextpos[0] = i
nextpos[1] = j

對 Y 分量的 AC 部分和顏色分量的 AC 部分分別進行 decodeaydecodeac 解碼後,用於 formac 的程式將形成 64 陣列 w 的 AC 部分(即 l > 1 的 w[l]),生成兩個數字 nz(零的個數)和 val(num 使用的數字個數),程式可能如下所示:

如果 val > 0 則
開始
如果 nz > 0 則
從 i = 1 到 nz 執行迴圈
開始
l = l + 1
w[l] = 0
結束
num
l = l + 1
w[l] = m
結束
如果 (nz = 15) 且 (val = 0) 則
從 i = 1 到 16 執行迴圈
開始
l = l + 1
w[l] = 0
結束
如果 (nz = 0) 且 (val = 0) 則
當 l < 64 時執行迴圈
開始
l = l + 1
w[l] = 0
結束
華夏公益教科書