跳轉到內容

JPEG - 構思與實踐/製作灰度檔案的程式

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

現在來看看能夠生成灰度 JPEG 檔案的程式。我們假設圖片的寬度和高度都能被 8 整除,並設定 wid8 = width div 8 和 hei8 = height div 8。我們還假設顏色值以點陣圖記憶體塊 pb 的形式給出,因此螢幕座標為 (i, j)(i = 0, ..., width-1, j = 0, ..., height-1)的點的顏色值(位元組)為 pb[(height-1 - j) * width + i]。更準確地說:我們假設圖片以 BMP 格式給出為彩色圖片,我們只使用它在能被 8x8 方格均勻分割的最大區域內的部分,我們透過取 RGB 值的平均值來構造 pb。

我們按以下順序編寫了標記(及其段):SOI、APP、DQT、DQT、SOF、DHT、DHT、SOS(有兩個 DQT 和 DHT 段,因為有兩個量化表和兩個霍夫曼編碼)。最後一個段 - SOS - 標記編碼資料的流的開始,在此之後,檔案以標記 EOI 結束。我們已經針對 DC 和 AC 編號計算了 EHUFSI[val] 和 EHUFCO[val][i] 陣列,它們的大小分別為分配給霍夫曼值 val 的程式碼的大小和程式碼本身。在程式中,這些陣列分別稱為 ehufsid[val] 和 ehufcod[val](用於 DC 編號),以及 ehufsia[val] 和 ehufcoa[val](用於 AC 編號)。

對於具有座標集 (i0, j0)(i0 = 0, ..., wid8-1, j0 = 0, ..., hei8-1)的 8x8 方格,以及方格內具有座標集 (i, j)(i, j = 0, ..., 7)的點,螢幕座標集為 (i0 * 8 + i, j0 * 8 + j)。對於每個 8x8 方格,我們都有一個 8x8 矩陣 f,它包含顏色值(有符號位元組 - 我們從原始顏色值(級移)中減去了 128 以獲得更小的數值),透過離散餘弦變換、量化和舍入,我們得到一個 8x8 矩陣 g(u, v),它包含整數。這個過程(或更確切地說,函式)稱為 costrans(f):g = costrans(f)。反之字形變換(iz: (i, j) → [1, ..., 64])由兩個從 1 到 64 的陣列 zx[l] 和 zy[l] 組成(這樣 (zx[l], zy[l]) 的之字形變換為 l),由此,g 被轉換為一個 64 陣列 w(從 1 到 64)。w[1] 是 DC 編號,我們從它中減去前一個 DC 編號(儲存在變數 dc 中),得到差值 diff。我們透過函式 digit(n) 獲取整數 n 的二進位制數字表達式,並將這個陣列(從 1 到 size(n))插入到變數 c 陣列中(從 1 到 10)。將位(形式為 c[j],其中 c 是程式碼字或數字表達式)寫入檔案(稱為 fu)的過程表示為 wbit(bit) - 此過程使用(全域性)變數 b0、b 和 q。costranswbit 的程式在掃描過程的程式之後顯示。

b0 = 0
b = 0
q = 256
dc = 0
for j0 = 0 to hei8 - 1 do
for i0 = 0 to wid8 - 1 do
begin
for j = 0 to 7 do
for i = 0 to 7 do
f[i, j] = pb[(height - 1 - (j0 * 8 + j)) * width + (i0 * 8 + i)] - 128
g = costrans(f)
for l = 1 to 64 do
w[l] = g[zx[l], zy[l]]
diff = w[1] - dc
dc = w[1]
val = size(diff)
e = ehufsid[val]
c = ehufcod[val]
for j = 1 to e do
wbit(c[j])
if diff <> 0 then
begin
c = digit(diff)
for j = 1 to val do
wbit(c[j])
end
r = 64
while (r > 1) and (w[r] = 0) do
r = r - 1
if r > 1 then
begin
l = 1
m = 0
while l < r do
begin
l = l + 1
n = w[l]
if n = 0 then
begin
m = m + 1
if m = 16 then
begin
e = ehufsia[240]
c = ehufcoa[240]
for j = 1 to e do
wbit(c[j])
m = 0
end
end
else
begin
k = size(n)
val = m * 16 + k
e = ehufsia[val]
c = ehufcoa[val]
for j = 1 to e do
wbit(c[j])
c = digit(n)
for j = 1 to k do
wbit(c[j])
m = 0
end
end
end
if r < 64 then
begin
e = ehufsia[0]
c = ehufcoa[0]
for j = 1 to e do
wbit(c[j])
end
end

函式 costrans(f) 的程式,該程式對 8x8 矩陣 f[i, j](有符號位元組)進行餘弦變換和量化,得到 8x8 矩陣 g[u, v](整數),分為四種情況:u = 0 和 v = 0、u = 0 和 v > 0、u > 0 和 v = 0 以及 u > 0 和 v > 0。如果量化表的 64 陣列稱為 quant[k],之字形函式稱為 iz(i, j),我們事先計算了矩陣 cq[i, j] = 4 * quant[iz(i, j)](i, j = 0, 1, ..., 7)(整數)和矩陣 cs[i, j] = cos((2 * i + 1) * j * pi / 16)(i, j = 0, 1, ..., 7)(實數)。g[u, v] 的四種情況的程式可以如下所示

s = 0
for i = 0 to 7 do
for j = 0 to 7 do
s = s + f[i, j]
g[0, 0] = round(s / (2 * cq[0, 0]))
for v = 1 to 7 do
begin
s = 0
for j = 0 to 7 do
begin
t = 0
for i = 0 to 7 do
t = t + f[i, j]
s = s + cs[j, v] * t
end
g[0, v] = round(s / (sqrt(2) * cq[0, v]))
end
for u = 1 to 7 do
begin
s = 0
for i = 0 to 7 do
begin
t = 0
for j = 0 to 7 do
t = t + f[i, j]
s = s + cs[i, u] * t
end
g[u, 0] = round(s / (sqrt(2) * cq[u, 0]))
end
for u = 1 to 7 do
for v = 1 to 7 do
begin
s = 0
for i = 0 to 7 do
begin
t = 0
for j = 0 to 7 do
t = t + cs[j, v] * f[i, j]
s = s + cs[i, u] * t
end
g[u, v] = round(s / cq[u, v])
end

最後是過程 wbit(bit),它將位“bit”(定義為位元組,因為程式不處理位)寫入檔案 fu。我們從程式碼字或數字的數字中獲取位,在插入檔案之前,這些位被收集到 8 個塊中,並轉換為位元組。我們稱當前位元組為 b(最初設定為 0),如果我們有一個從 256 開始的整數 q,並且在將每個位插入 b 之前,它被除以 2,那麼新增(新)位意味著 b 必須增加 bit * q:b = b + bit * q。當 q = 1 時,b 被寫入檔案,q 再次設定為 256。如果 b = 255(8 個數字 1),則寫入必須緊隨其後的是寫入零位元組 b0(8 個數字 0)(位元組填充),這樣 255(在解碼期間)就不會被誤認為是標記的開始。寫入過程 wbit 可能如下所示

procedure wbit(bit: byte)
begin
q = q div 2
b = b + bit * q
if q = 1 then
begin
write(fu, b)
if b = 255 then
write(fu, b0)
b = 0
q = 256
end
end

程式以這個過程結束,這個過程在 q 未設定為 256(表示 b 尚未寫入)時寫入最後一個位元組 b,並將 b 的其餘位設定為 1(位填充

e = size(q) - 1
p = 1
for i = 1 to e do
begin
b = b + p
p = 2 * p
end
write(fu, b)

如果最後一個位元組 b 為 255,則它必須緊隨其後的是零位元組 b0。最後,我們寫入標記 EOI = (255, 217)(影像結束)並關閉檔案。

華夏公益教科書