Tải bản đầy đủ (.doc) (86 trang)

Nén dữ liệu ảnh trong xử lý ảnh số

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (464.63 KB, 86 trang )

Ch-
ơng
13
Nén dữ liệu ảnh
13.1 Chỉ dẫn
Nén ảnh là một kỹ thuật mã hoá hiệu suất cao ảnh số nhằm làm giảm số bit cần
cho biểu diễn ảnh. Chức năng của kỹ thuật này là giảm độ lớn dữ liệu phải lu trữ
cùng với thời gian truyền trong khi vẫn giữ nguyên chất lợng của ảnh. Để đánh giá
sự cần thiết của nén ảnh, chúng ta xem xét về yêu cầu bộ nhớ và thời gian truyền khi
dùng một modem 9600 baud (bit/s) cho các ảnh sau đây:
Một ảnh 512 ì 512 điểm, 8 bit cho một điểm, ảnh mức xám yêu cầu 2,097,152
bit cho lu giữ và mất 3.64 phút để truyền.
Một ảnh màu RGB có cùng các bớc xử lý nh trờng hợp trên yêu cầu xấp xỉ 6
triệu bít cho lu trữ và mất gần 11 phút để truyền.
Một phim âm bản có kích thớc 24 ì 36 mm (35 mm) chia bằng các khoảng
cách nhau 12 àm, vào khoảng 3000 ì 2000 điểm, 8 bit cho một điểm, yêu cầu 48
triệu bit cho lu giữ ảnh và 83 phút để truyền. Một phim âm bản màu sẽ yêu cầu một
số lớn gấp ba lần cho lu giữ và truyền.
Rõ ràng, việc truyền và lu giữ các ảnh sẽ có nhiều vấn đề. Có rất nhiều ví dụ khác
mà sẽ dễ dàng làm sáng tỏ vai trò của nén ảnh, và rất có nhiều nghiên cứu tập trung
vào lĩnh vực này. Fax, một tài liệu đồ hoạ đợc truyền qua đờng dây điện thoại, nén
dữ liệu ảnh y học, truyền hình là một vài trong số nhiều ứng dụng tiềm tàng của nén
ảnh. Sự phát triển của kỹ thuật vi điện tử và sự phát triển của rất nhiều ứng dụng th -
ơng mại dẫn dắt sự phát triển cho các tiêu chuẩn và phần cứng của bộ nén ảnh theo
thời gian thực.
Nén ảnh là đạt đợc bâừng cách loại bỏ các phần thừa trong ảnh. Các phần thừa
này có thể ở trong miền không gian, miền phổ, hoặc là phần thừa trong thời gian.
Phần thừa không gian là kết quả do mối quan hệ tơng quan giữa các điểm gần
nhau.
Phần thừa phổ là kết quả do mối tơng quan giữa các mặt phẳng màu khác
nhau.


Phần thừa thời gian là kết quả mối tơng quan của các khung khác nhau một
dãy các ảnh nh trong truyền hình .

Trong chơng này tôi sẽ trình bày với các bạn một số thuật toán nén ảnh có kết
quả tốt đợc thừa nhận rộng rãi. Chúng ta sẽ phát triển thuật toán thành mã chơng
trình C, sau đó kiểm tra kết quả của các kỹ thuật này qua các ví dụ chạy thử. Bạn sẽ
có nhiều kinh nghiệm bằng cách tự chạy các chơng trình này.
53
13.2 Mã thống kê
Những ảnh mà chúng ta thu nhận đợc mã hoá và lu giữ dới dạng "mã tự nhiên".
Một mức xám của giá trị đợc mã hoá bằng 8 bit nhị phân bằng nhau. Ví dụ một mức
xám giá trị 6 đợc mã hoá là 0000 0110. Một sự sắp xếp mã hoá luân phiên nhau đợc
dùng trong mã mức xám. Loại mã này có đặc tính là bất kỳ hai từ mã liền nhau nào
cũng chỉ khác nhau một vị trí. Bảng 13.1 trình bày hai kiểu mã khác nhau cho một
tín hiệu mẫu có giá trị vào khoảng từ 0 đến 7. Một kiểu cho ta thấy rằng tín hiệu
không nhất thiết phải có giá trị thực sự từ 0 đến 7, nhng phải có 8 mức riêng biệt.
Bảng 13.1 Các mã khoảng cách bằng nhau
Vào Mã tự nhiên Mã mức xám
0
1
2
3
4
5
6
7
000
001
010
011

100
101
110
111
111
110
100
101
001
000
010
011
Những loại mã này thờng gọi là mã khoảng cách bằng nhau. Mã khoảng cách
bằng nhau không đợc dùng trong trong thống kê dữ liệu. Sự thừa nhận này đợc tạo
ra khi ta coi rằng tất cả các mức xám (hoặc giá trị tín hiệu chói) có cùng số lần xuất
hiện trong ảnh. Nếu điều này không đúng, dạng mã này không phải tốt nhất. Nếu
chúng ta phát triển một mã mà một số ít bít hơn đợc kí hiệu cho các từ mã biểu diễn
các mức xám có khả năng xuất hiện cao hơn, thì trung bình độ dài từ mã sẽ nhỏ nhất
và loại mã mà chúng ta vừa phát triển là cơ bản cho mã phần thừa tối thiểu. Tất cả
các loại mã này đợc biết với tên mã có độ dài thay đổi hoặc đôi khi gọi là mã
entropy. Câu hỏi đặt ra cho chúng ta lúc này là :
Chiều dài từ mã trung bình tối thiểu mà có thể dùng giải mã để sửa lại mã
chính xác là gì?
Làm cách nào chúng ta tạo ra mã này?
Câu trả lời cho câu hỏi đầu tiên có thể tìm thấy trong lý thuyết thông tin. Nếu ta
cho rằng một mức xám g của xác suất p(g) đợc cho bằng từ mã dài L(g) bit. Chiều
dài từ mã trung bình, trong một ảnh mức xám 8 bit, đợc cho bởi


=

255
0
)()( gLgpL
bit/ pixel (13.1)
Một thừa nhận hợp lý nữa có thể suy ra là sự kiện có số lần xuất hiện ít, thì sẽ
cung cấp nhiều thông tin hơn sự kiện số lần xuất hiện nhiều hơn. Sự thừa nhận này
dẫn chúng ta đến mối quan hệ
54

)(log
1
)(
2
gp
gL =
(13.2)
Cơ số 2 dùng khi L(g) đợc cho dới dạng đơn vị nhị phân hoặc bit. Chiều dài từ
nhỏ nhất mà có thể đợc dùng cho bởi


=
=
255
0
2min
)(log)(
g
gpgpL
bít/pixel (13.3)
Biểu thức này gọi là entropy của tín hiệu. Entropy thì không bao giờ âm vì p(g)

nằm trong khoảng [0,1]. Đạo hàm của biểu thức entropy có thể tìm thấy trong các
sách nói về tin học hoặc thông tin. Chú ý rằng cho một ảnh 256 mức xám mà tất cả
các mức có khả năng xuất hiện bằng nhau khi dùng biểu thức (13.3) chúng ta có:

==
255
0
2min
8)
256
1
(log
256
1
L
bit/pixel
Điều này có nghĩa là một mã có độ dài bằng nhau có thể dùng trên một ảnh mà
có hàm phân bố cờng độ sáng đồng đều.
Câu trả lời cho câu hỏi thứ hai đề cập đến mã có phần thừa nhỏ nhất (mã tối u)
đợc Huffman tìm ra. Loại mã này gọi là mã Huffman và đợc áp dụng rộng rãi trong
các kỹ thuật mã hoá bằng phần cứng cũng nh bằng phần mềm trong các ứng dụng
thơng mại. Bây giờ chúng ta sẽ xem xét sơ đồ mã hoá Huffman.
Thuật toán mã hoá Huffman tuân theo các giới hạn sau:
1. Không có hai thông báo nào có sự sắp xếp của từ mã giống nhau.
2. Từ mã của thông báo đợc mã hóa theo cách mà không cần một sự chỉ dẫn nào
thêm để chỉ ra đâu là nơi bắt đầu và đâu là nơi kết thúc của từ mã.

Hạn chế thứ hai chỉ ra rằng không có thông báo nào đợc mã hoá theo cách mà khi
từ mã xuất hiện, bit nối bit, nh là một phần của từ mã lớn hơn. Ví dụ, 01, 102, và
202 là các từ mã hợp lệ. Một dãy của các từ mã xuất hiện có dạng

1111022020101111102 có thể tách ra thành 111-102-202-01-01-111-102. Tất cả các
vấn đề mà chúng ta cần quan tâm khi giải mã là bộ mã gốc. Nếu nh một bộ mã bao
gồm 11, 111, 102, 02 thì khi một thông báo bắt đầu vói 11, ta sẽ không biết liệu đây
là thông báo 11 hay đây là phần bắt đầu của thông báo 111. Nếu một thông báo
11102 xuất hiện thì ta sẽ không biết liệu đây là 111-02 hoặc là 11-102 đợc truyền đi.
Mã Huffman đợc mã hoá theo hai hạn chế trên đây và gọi là mã có độ d thừa tối
thiểu hay gọi là mã tối u. Phơng pháp mã hoá này theo hai bớc: bớc thu gọn và bớc
mở rộng. Để xem xét phơng pháp mã hoá này ta coi rằng các thông báo để xây dựng
từ mã đợc sắp xếp theo thứ tự xác suất xuất hiện giảm dần.
p(0) p(1) p(2) p(N - 1)
ở đây N là số của các thông báo. Nh tôi đã chỉ ra ban đầu, cho bộ mã hoá tối u thì
độ dài của từ mã đợc xắp xếp theo thứ tự
L(0) L(1) L(2) L(N - 1)
Các bớc dới đây trình bày giải thuật mã hoá Huffman. Giải thuật này, cũng nh
phần lớn các giải thuật khác trong cuốn sách này, đợc phát triển bởi chính tác giả.
55
13.2.1 Giải thuật thu gọn
Các bớc của giải thuật thu gọn đợc trình bày tốt nhất theo các bớc sau đây:
Đặt M = N và coi đây là một mảng tuyến tính có kích thớc N - 2.
Cho i = 0 đến N - 3 lặp lại các bớc sau:
{
Cộng p(M) và p(M - 1). Thay p(M - 1) bằng kết quả thu đợc.
Xác định vị trí, loc, trong M - 1 vị trí đầu tiên của mảng p trong đó p(M - 1) >
p(loc).
Đặt temp = p(M - 1).
Chuyển các giá trị từ p(loc) đến p(M - 2) xuống một vị trí.
Đặt giá trị trung gian vào loc.
Lu giá trị trung gian trong mảng tuyến tính v theo
v(N - 2 - i) = loc
Giảm M đi một giá trị.

}
Để hiểu giải thuật thu gọn ta xem xét ví dụ sau. Giả sử rằng khả năng xuất hiện
của các thông tin là:
p = {0.25, 0.25, 0.125, 0.125, 0.0625, 0.0625, 0.0625, 0.0625}
Giải thuật thu gọn đợc trình bày ở trên, cho trờng hợp này, trong hình 13.1, từ đó
chúng ta có thể viết:















=
5
6
3
4
1
2
v
Hình 13.1 Giải thuật thu gọn.

Mảng tuyến tính v đợc dùng để xây dựng mã hoá Huffman theo bớc mở rộng đợc
trình bày ở dới đây.
56
0.0625
0.0625
0.0625
0.25
0.25
0.25
0.25
0.25
1
0.5
0.5
2
3
4
5
6
7
8
0.25
0.25
0.25
0.25
0.25
0.5
0.125
0.125
0.125

0.25
0.125
0.125
0.125
0.0625
0.125
0.0625
0.0625
0.125
0.125
0.125
0.125
0.25
0.25
0.25
0.25
13.3.2 Bớc mở rộng
Bớc giải thuật này có thể trình bày theo các bớc sau:
1) Gán các giá trị ban đầu cho một mảng H theo:
H
H
H
H
H N
=























=























0
1
0
0
0
1
2
3
.
.
( )
( )
( )
.
.
.
( )
Chú ý là phần tử thứ hai bằng 1 còn các phần tử khác bằng 0.
Véc tơ tuyến tính
v = [v[1] v[2] v[3] v[N - 2]]

T
đợc tính trong bớc thu gọn. Chú ý là vị trí của các phần tử đợc cho bằng 1,2,3
2) Đặt M = 2 và i = 1.
3) Lu phần tử ở vị trí v[i] trong mảng H vào một biến trung gian temp.
temp = H[v(i)]
4) Dịch chuyển các phần tử các phần tử ở vị trí dới H[v(i)] một vị trí lên phía
trên. Chèn giá trị 0 vào vị trí cuối cùng.
5) Sao chép temp tới vị trí thứ M trong H.
6) Dịch trái H(M) một bít.
1 0 0 1 0 1 0 1 0 1 0 1 0
2 1 1 0 1 1 1 1 1 1 1 1 1 1
3 1 1 0 0 0 0 0 1 0 0 1 0 0 1 0
4 0 1 0 1 0 0 1 1 0 1 1 0 1 1
5 0 1 1 0 0 0 0 0 0 0 0 1 0
6 0 0 1 0 0 1 0 0 0 1 1
7 0 0 1 1 0 0 0 0
8 0 0 0 1
Hình 13.2 Giải thuật mở rộng.
7) H[M + 1] = H[M] + 1.
8) Tăng M và i lên một.
9) Nếu i

(N - 2) quay lại bớc thứ ba.
Nếu không thì đã hoàn thành, và mã nằm trong bảng H.
Các bớc trên giải thích qua hình 13.2 dùng ví dụ hình 13.1.
57
Các bớc trên đợc lập ra bởi Huffman. Lu và Chen đã nhận ra rằng thuật toán của
Huffman không phải lúc nào cũng tạo ra một mã có độ dài đơn diệu tăng dần qua ví
dụ của họ dới đây:
Xem xét khả năng xuất hiện thông tin dới đây:

P =


















0 5
01
01
01
01
01
.
.
.
.
.

.

Theo giải thuật thu gọn ở trên chúng ta có

v =












2
2
3
2
và tạo bộ mã
H =



















0
101
1000
1001
110
111
Bộ mã trên không thoả mãn điều kiện về chiều dài của từ mã đơn điệu tăng dần;
tuy nhiên, bộ mã này có thể sử dụng nếu nó thoả mãn điều kiện có khả năng giải mã
đợc. Lỗi này có thể sửa đợc bằng một thay đổi nhỏ theo Lu và Chen trong bớc thứ ba
từ
p(M - 1) > p(loc) thành p(M - 1) p(loc)
Theo phơng pháp này cho ta kết quả
v =













1
2
2
2
và tạo ra một bộ mã
58
H =



















1
001
010
011
0000
0001
Chú ý là thuật toán Lu và Chen tạo ra bộ mã thì hoàn thiện hơn thuật toán mô tả ở
trên.
Chơng trình dới đây tạo ra bộ mã dùng các bớc thu gọn và mở rộng ở trên. Chơng
trình này sử dụng thuật toán Lu và Chen mô tả ở trên. Chơng trình cũng tính trung
bình độ dài từ. Chơng trình không dùng trên ảnh, mà chỉ minh hoạ các bớc thực hiện
việc sinh mã Huffman. Chú ý điều kiện dùng trong bớc 3 của quá trình thu nhỏ đợc
thay bằng điều kiện của Lu và Chen.
Chơng trình 13.1 Chơng trình ví dụ sinh mã Huffman.
/*Program 13.1 "HUFFMAN.C". Example program for generating the
Huffman code.*/
/* Example program to demonstrate the
algorithm for the generation procedure
of the Huffman code. */
#include <stdio.h>
#include <conio.h>
/*float p[]={0.2, 0.18, 0.1, 0.1, 0.1, 0.06, 0.06,
0.04, 0.04, 0.04, 0.04, 0.03, 0.01};
int N=13;*/
float p[]={0.5,0.1,0.1,0.1,0.1,0.1};
int N=6;
void main()
{
int i,M,j,loc;

unsigned char v[11],L[13],code[13];
unsigned char ctemp,Ltemp;
float temp,sum,pt[13];
for(j=0;j<N;j++)
pt[i]=p[j];
clrscr();
for(i=0;i<(N-2);i++)
v[i]=0;
/* Contraction. */
M=N;
for(i=0;i<(N-2);i++)
{
p[M-2]=p[M-1]+p[M-2];
loc=M-2;
for(j=0;j<(M-1);j++)
{
if(p[M-2]>=p[j])
{
59
loc=j;
break;
}
}
temp=p[M-2];
for(j=M-2;j>=loc;j )
p[j]=p[j-1];
p[loc]=temp;
M ;
v[(N-3)-i]=loc;
}

printf("\n The v vector is given by:\n");
for(j=0; j<(N-2); j++)
printf ("%d\n", v[j]+1);
/*Expansion.*/
for(j=0; j<N; j++)
{
code[j]=0 ;
L[j]=1;
}
printf("\n\n ");
code[0]=0;
code[1]=1;
M=1;
for(i=0; i<(N-2);i++)
{
if(v[i]==M)
{
code[M+1]=(code[M]<<1)+1;
code[M]=(code[M])<<1;
L[M]++;
L[M+1]=L[M];
}
else
{
ctemp=code[v[i]];
Ltemp=L[v[i]];
for(j=v[i];j<M;j++)
{
code[j]=code[j+1];
L[j]=L[j+1];

}
code[M]=ctemp;
L[M]=Ltemp;
code[M+1]=(code[M]<<1)+1;
code[M]=code[M]<<1;
L[M]++;
L[M+1]=L[M];
}
M++;
}
printf("Code words Length\n");
for(j=0;j<N;j++)
printf(" %d %d\n",code[j],L[j]);
sum=0.0;
for(j=0;j<N;j++)
sum+=pt[j]*(float)L[j];
60
printf("Lav.=%f",sum);
getch();
}
Độ dài từ trung bình đợc tính bởi công thức (13.1). Chú ý rằng chơng trình cũng
tính độ dài các bit của mỗi mã và độ dài từ trung bình. Độ dài sẽ mã cần thiết cho
việc mã hoá và giải mã các tín hiệu.
Bài tâp 13.1
1. Chạy chơng trình 13.1 dùng ví dụ cho trong tài liệu của Huffman:
p = {0.2, 0.18, 0.1, 0.1, 0.1, 0.06, 0.06, 0.04, 0.04, 0.04, 0.04, 0.03, 0.01}
2. Đổi giả thiết:
if (p[M - 1] p[j]
thành
if (p[M - 1] > p[j])

trong chơng trình. Chạy chơng trình và so sánh với bộ mã cho bởi Lu và Chen.
3. Dùng ví dụ cho bởi Lu và Chen, chú ý sự khác nhau trong chiều dài từ mã vói
hai điều kiện khác nhau.
Nhiệm vụ tiếp theo của chúng ta thật rõ ràng. Chúng ta cần viết một chơng trình
C để mã hoá và giải mã ảnh số dùng lợc đồ mã Huffman. Chơng trình sẽ bao gồm:
tính toán khả năng xảy ra của mỗi mức xám và xắp xếp lại theo thứ tự giảm dần của
khả năng có thể. Tất nhiên, chơng trình cũng bao gồm một vector để lu giữ các thông
tin cần thiết về các mức xám theo khả năng của các sự kiện, ví dụ, một bảng tra cứu.
Chơng trình cũng sẽ loại trừ các sự kiện không có khả năng xảy ra. Khi các bớc ở
trên đợc thực hiện, mã Huffman đợc sinh ra theo giải thuật đợc mô tả trên. Trong
việc mã hoá ảnh số bạn cần nhớ rằng chơng trình sẽ cần nhiều byte trên đĩa. Khi
viết một thủ tục giải mã ta cần chú ý đến giới hạn này.
13.2.3 Mã hoá ảnh số
Trớc khi mã hoá ta cần tạo ra hai bảng tra cứu:
1. Một bảng tra cứu (LUT) chứa quan hệ các giá trị mức xám trên ảnh vói bộ mã
hoá Huffman. Bảng LUT này có thể phát triển dùng quan hệ:
table_code[gray[i]] = code[i]
ở đây code[i] là mã Huffman.
2. Một bảng tra cứu xác lập quan hệ giữa các mức xám trên ảnh vói chiều dài từ
mã Huffman. LUT có thể tạo ra theo mối quan hệ sau:
table_length [ gray[i]] = L[i]
Mã hoá theo các bớc sau:
a. Mở một file để chứa ảnh đã đợc mã hoá.
b. Đặt Len = 0 và aux = 0.
aux là một thanh ghi bốn byte để truyền mã.
Len chứa số các bít còn lại trong aux.
61
c. Đọc giá trị điểm ảnh.
d. Xác định mã Huffman của nó, code[i], và độ dài của từ mã L[i], dùng hai
bảng LUT để tạo ra giá trị ban đầu.

e. Dịch aux sang trái L[i] bit.
f. Thực hiện phép logic OR code[i] với aux.
g. Len = Len + L[i].
h. Nếu Len 8 thì chuyển 8 bit cuối đến file đầu ra và giảm Len đi 8.
i. Nếu cha hết file thì chuyển đến bớc c.
k. Chuyển các bít còn lại trong thanh ghi aux ra file đầu ra.
Để giải mã đợc ảnh chúng ta cần phải tạo thêm phần header của file. Header của
file bao gồm cả chiều dài thực sự của file tính theo bit. Chú ý là chiều dài thực sự có
thể lớn hơn hoặc bằng chiều dài thực sự của file. Điều này bởi vì chúng ta chỉ có thể
chứa dới dạng đơn vị byte, còn file ảnh đã mã hoá phải có chiều dài không chia hết
cho 8. Phần này chứa đủ thông tin để thiết lập các bảng LUT, dùng cho việc giải mã
ảnh. Nó bao gồm các mức xám trên ảnh, dạng mã Huffman và chiều dài tơng đơng
của chúng. Chú ý rằng các từ mã đợc sắp xếp theo thứ tự giảm dần theo xác suất
xuất hiện của chúng.
Chơng trình C sau đây sẽ trình bày các bớc trên.
Chơng trình 13.2 "HUCODING" . Mã hoá Huffman ảnh số.
/*Program 13.2 "HUCODING.C". Huffman coding of digital images.*/
/* This program is for coding a binary file
using Huffman codes. */
#include <stdio.h>
#include <conio.h>
#include <io.h>
#include <alloc.h>
#include <string.h>
#include <process.h>
void main()
{
int i,j,N,M,loc,k,ind,xt,yt;
unsigned char *v,*L,*gray, *table_length;
unsigned long int *code, *table_code, ctemp2;

unsigned long int aux1,aux2,Lmask,act_len,flength;
unsigned char mask,Len;
int ch;
unsigned char ctemp,Ltemp;
float temp, sum, *pt, *p, big;
unsigned long int *histo;
double nsq;
char file_name1[14], file_name2[14];
FILE *fptr,*fptro;
clrscr();
printf("Enter file name of image >");
scanf("%s",file_name1);
fptr=fopen(file_name1,"rb");
if(fptr==NULL)
{
62
printf("%s does not exist. ",file_name1 );
exit(1);
}
printf("\nEnter file name for compressed image >");
scanf("%s",file_name2);
k=stricmp(file_name1 ,file_name2);
if (k==0)
{
printf("\nInput and output files cannot share the same name.");
fcloseall();
exit(1);
}
ind=access(file_name2,0);
while(!ind)

{
k=stricmp(file_name1, file_name2);
if(k==0)
{
gotoxy(1,8);
printf( "Input and output files cannot share the same name.");
exit(1);
}
gotoxy(1,4);
printf("File exists. Wish to overwrite? (y or n) >");
while(((ch=getch())!='y')&&(ch!='n'));
putch(ch);
switch(ch)
{
case 'y':
ind=1;
break;
case 'n':
gotoxy (1,4);
printf( " ");
gotoxy (1,3);
printf ( " ");
gotoxy (1,3);
printf ( " Enter file name >");
scanf ( "%s " , file_name2 ) ;
ind=access ( file_name2, 0);
}
}
fptro=fopen(file_name2, "wb");
xt=wherex();

yt=wherey();
gotoxy(70, 25);
textattr (RED+(LIGHTGRAY<<4) +BLINK);
cputs ( "WAIT" ) ;
/* Generate histogram. */
histo=( unsigned long int *) malloc(256*sizeof(long int));
for(i=0; i<256; i++)
histo[i]=0;
while((ch=getc(fptr))!=EOF)
histo[ch]++;
p=(float *)malloc(256*sizeof(float));
gray=(unsigned char *)malloc(256*sizeof(char));
63
k=0;
for(i=0;i<256;i++)
{
if(histo[i]!=0)
{
p[k]=(float)histo[j];
gray[k]=i ;
k++;
}
}
free(histo);
N=k;
/* Normalize.*/
sum=0.0;
for(i=0;i<N;i++)
sum+=p[i];
for(i=0;i<N;i++);

p[i]/=sum;
/* Rearranging the probability values in ascending order. */
for(i=0;i<(N-1);i++)
{
big=p[i];
loc=i;
for(j=i+1;j<N;j++)
{
if(p[j]>big)
{
big=p[i];
loc=j;
}
}
if(loc==i) continue;
else
{
temp=p[i];
ctemp=gray[j];
p[i]=p[loc];
gray[i]=gray[loc];
p[loc]=temp;
gray[loc]=ctemp;
}
}
pt=(float *)malloc(N*sizeof(float));
for(j=0;j<N;j++)
pt[j]=p[j];
v=(unsigned char *)malloc((N-2)*sizeof(char));
code=(unsigned long int *)malloc(N*sizeof(unsigned long int));

L=(unsigned char *)malloc(N*sizeof(char));
for(i=0; i<(N-2); i++)
v[i]=0;
/* Contraction steps in the generation of the Huffman's codes. */
M=N ;
for(i=0; i<(N-2) ;i++)
{
p[M-2]=p[M-1]+p[M-2];
loc=M-2;
for(j=0;j<(M-1);j++)
64
{
if(p[M-2]>=p[j])
{
loc=j;
break;
}
}
temp=p[M-2];
for(j=M-2;j>=loc;j )
p[j]=p[j-1];
p[loc]=temp;
M ;
v[(N-3)-i]=loc;
}
/*Expansion steps in the generation of the Huffman's codes.*/
for(j=0;j<N;j++)
{
code[j]=0;
L[j]=1;

}
code[0]=0;
code[1]=1 ;
M=1;
for(i=0; i<(N-2);i++)
{
if(v[i]==M)
{
code[M+1]=(code[M]<<1)+1;
code[M]=(code[M])<<1;
L[M]++;
L[M+1]=L[M];
}
else
{
ctemp2=code[v[i]];
Ltemp=L[v[i]];
for(j=v[i];j<M;j++)
{
code[j]=code[j+1];
L[j]=L[j+1];
}
code[M]=ctemp2;
L[M]=Ltemp;
code[M+1]=(code[M]<<1)+1;
code[M]=code[M]<<1;
L[M]++;
L[M+1]=L[M];
}
M++;

}
/*printf("Code words Length\n");
for(j=0;j<N;j++)
printf(" %lu %u\n",code[j],L[j]);*/
sum=0.0;
for(j=0;j<N;j++)
sum+=pt[j]*(float)L[j];
gotoxy(xt,yt);
65
printf("\nAverage number of bits/pixel.=%f",sum);
free(v);
free(p) ;
free(pt);
/* Coding */
/* Writing the header in the output file.
The first 4 bytes stores the true length in
bits of the stored image with the MSB stored
first. The 5th byte is the number of Huffman
codes=N. The following N bytes contain the N
natural codes for the gray levels, followed by N
bytes containing the Huffman code lengths. These
are then followed by the actual Huffman codes
stored in packed form. */
for(i=0;i<4;i++)
putc((int)0,fptro);
/* reserve the first 4 bytes for the true length of
the file in bits.*/
putc(N,fptro);
for(i=0;i<N;i++)
putc(gray[i],fptro);

for(i=0;i<N;i++)
putc(L[i],fptro);
aux1=0;
Len=0;
for(i=0;i<N;i++)
{
Len+=L[i];
aux1=(aux1<<L[i])|code[i];
if(Len<8) continue;
else
while(Len>=8)
{
aux2=aux1;
Lmask=255;
Lmask<<=(Len-8);
aux2=(aux2&Lmask);
aux2>>=(Len-8);
ch=(char)aux2;
putc(ch,fptro);
Lmask=~Lmask;
aux1=aux1&Lmask;
Len-=8;
}
}
if(aux1!=0)
{
ch=(char)(aux1<<(8-Len));
putc(ch,fptro);
}
table_code=(unsigned long int *)malloc(256*sizeof(long int));

table_length=(unsigned char *)malloc(256*sizeof(char));
for(i=0; i<N; i++)
{
table_code[gray[i]]=code[i];
table_length[gray[i]]=L[i];
66
}
rewind(fptr);
Len=0 ;
act_len=0; /*variable to save true length of file in bits. */
aux1=aux2=0;
k=0;
while((ch=getc(fptr))!=EOF)
{
k++;
Len+=table_length[ch];
act_len+=table_length[ch];
aux1=(aux1<<table_length[ch])|table_code[ch];
if(Len<8) continue;
else
while(Len>=8)
{
aux2=aux1;
Lmask=255;
Lmask<<=(Len-8);
aux2=aux2&Lmask;
Lmask=~Lmask;
aux1=aux1&Lmask;
aux2>>=(Len-8);
ch=(char)aux2;

Len-=8 ;
putc(ch,fptro);
}
}
if(aux1!=0) /* Transmit remaining bits.*/
{
ch=(char)(aux1<<(8-Len));
putc(ch,fptro);
}
rewind(fptro); /* Write true length of file. */
M=8*sizeof(long int)-8;
for(i=0;i<4;i++)
{
Lmask=255;
Lmask<<=M;
aux1=(act_len&Lmask);
aux1>>=M;
ch=(int)aux1;
putc(ch,fptro);
M-=8;
}
fclose(fptro);
fclose(fptr);
gotoxy(70,25);
textattr(WHITE+(BLACK<<4));
cputs(" ");
gotoxy(1,yt+2);
getch( );
}
Bµi tËp 13.2

67
1. áp dụng chơng trình 13.2 cho IKRAM.IMG. Tên file ra là IKRHUFF.IMG.
Bạn sẽ cần đến file này để kiểm tra chơng trình giải mã, chơng trình này sẽ đ-
ợc trình bày sau.
2. Tính độ dài từ trung bình từ kích thớc file ra. So sánh với độ dài từ đã đợc tính
bằng chơng trình.
13.2.4 Giải mã
Giải mã của một ảnh mã hoá bằng mã Huffman thực hiện qua các bớc sau:
1. Đặt Len = 0, flength = 0; aux = 0.
Len là số đếm của các bít đợc giải mã.
Flength là một số đếm của các để so sánh với chiều dài thực sự của file.
Aux là một thanh ghi bốn byte chứa các từ mã sẽ đợc giải mã.
Lặp lại các bớc sau đây cho tới khi chiều dài flength > true_length
{
2. Chuyển một byte từ file đến thanh ghi aux.
Lặp lại các bớc sau 8 lần:
{
3. Dịch trái thanh ghi aux đi một bít.
4. Dịch chuyển bit dấu lớn nhất của ch đến vị trí bít dấu nhỏ nhất của ch.
5. Dịch trái 1 bit ch.
6. Tăng Len lên 1.
7. Tăng flength lên 1.
8. Kiểm tra lại tất cả các mã Huffman có chiều dài length = Len.
Nếu một mã đợc tìm ra:
a. Sao chép mức xám tơng ứng ra file xuất ra.
b. Đặt aux bằng không.
c. Đặt Len bằng không.
} ( cụ thể chuyển tói bớc 3).
} ( cụ thể chuyển tới bớc 2).
Các thủ tục trên tạo ra các bớc giả mã. Bạn cần nhớ rằng header của file chứa các

thông tin cho việc giải mã. Một chơng trình C cho việc giải mã đợc trình bày ở phần
dới đây.
Chơng trình 13.3 "HUDECDNG". Chơng trình giải mã ảnh đợc mã hoá
Huffman.
/*Program 13.3 "HUDECDNG.C". Program for decoding a Huffman-coded
image.*/
/*This program is for decoding binary files
coded in Huffman codes. */
#include <stdio.h>
#include <conio.h>
#include <math.h>
#include <io.h>
#include <alloc.h>
#include <string.h>
#include <process.h>
68
void main()
{
unsigned int i,j,N,M,k,xt,yt;
unsigned char *L,*gray;
unsigned long int *code;
unsigned long int aux1,aux2,Lmask,act_len,flength;
unsigned char mask,Len;
int ch,ind;
unsigned char ctemp,Ltemp;
char file_name1 [14],file_name2[14];
FILE *fptr,*fptro;
clrscr();
printf("Enter input file name for compressed image >");
scanf("%s", file_name1);

fptr=fopen(file_name1,"rb");
if(fptr==NULL)
{
printf("\n No such file exists.");
exit(1);
}
printf("\nEnter file name for uncompressed image >");
scanf("%s",file_name2);
k=stricmp(file_name1,file_name2);
if(k==0)
{
printf
("\nInput and output files cannot share the same name.");
exit(1);
}
ind=access(file_name2,0);
while(!ind)
{
k=stricmp(file_name1 ,file_name2);
if(k==0)
{
gotoxy(1,8);
printf("Input and output files cannot share the same name.");
exit(1);
}
gotoxy(1,4);
printf("File exists. Wish to overwrite? (y or n) >");
while(((ch=getch())!='y')&&(ch!='n'));
putch(ch);
switch(ch)

{
case 'y':
ind=1;
break;
case 'n':
gotoxy(1,4);
printf(" ");
gotoxy(1,3);
printf(" ");
gotoxy(1,3);
printf(" Enter file name >");
69
scanf ( "%s", file_name2);
ind=access(file_name2, 0);
}
}
fptro=fopen(file_name2, "wb");
xt=wherex();
yt=wherey();
gotoxy(70,25);
textattr(RED+(LIGHTGRAY<<4)+BLINK);
cputs("WAIT");
/*Reading the header.*/
act_len=0;
M=8*sizeof(long int)-8;
for(j=0;i<4;i++)
{
ch=getc(fptr);
aux1=(unsigned long int)ch;
aux1<<=M;

act_len|=aux1;
aux1=(long int)0;
M-=8;
}
N=getc(fptr);
gray=( unsigned char * )malloc(N*sizeof(char));
L=(unsigned char *)malloc(N*sizeof(char));
code=( unsigned long int *)malloc ( N*sizeof(long int));
for(i=0; i<N; i++)
gray[i]=getc(fptr);
for(i=0; i<N ; i++)
L[i]=getc(fptr);
mask=128;
Len=0;
aux2=0;
aux1=0;
ind=1;
i=0;
while(ind)
{
ch=getc(fptr);
for(k=0;k<8;k++)
{
aux1=ch&mask;
ch<<=1;
aux2<<=1;
if(aux1!=0)
aux2|=1;
Len++;
if(Len==L[i])

{
code[i]=aux2;
aux2=0;
Len=0;
i++;
if(i==N){
ind=0;
break;
}
70
}
}
}
Len=0;
aux1=aux2=0;
flength=0; /*a parameter to measure against actual file length.*/
mask=128;
ind=1;
while(ind)
{
ch=getc(fptr);
for(k=0;k<8;k++)
{
/* test if the actual end of file has been reached.*/
if(flength>act_len){ind=0; break;}
aux1=(ch&mask);
ch<<=1;
aux2<<=1;
if(aux1!=0)
aux2|=1;

Len++;
for(j=0;j<N;j++)
{
if(L[j]>Len) break;
if(L[j]==Len)
{
if(code[j]==aux2)
{
aux2=0;
putc((int)gray[j],fptro);
Len=0 ;
break;
}
}
}
flength++;
}
}
gotoxy(70, 25);
textattr(WHITE+(BLACK<<4));
cputs( " ");
gotoxy (xt, yt);
printf(" \ n Done decoding. ");
fcloseall();
getch();
}
71
0
1
1

1
12
3
4
5
6 78
0
0
0
0
0
0
1
1
Nót gèc
Nót l¸
C¸c nót gèc
Hình 13.3 Cây nhị phân giải mã Huffman hình 13.2.
Bài tập 13.3
1. áp dụng chơng trình 13.3 cho việc giải mã ảnh đã mã hoá ở bài tập 13.2.
2. Mã hoá Huffman có thể đạt đợc kết quả hơn nhờ sử dụng cây nhị phân. Cây
nhị phân trong hình 13.3 biểu diễn mã Huffman ở hình 13.2.
a.Viết chơng trình C sử dụng mã Huffman đặt trong phần header của file ảnh
đã mã hoá để tạo một cây nhị phân.
b.Mở rộng cho chơng trình giải mã dùng cây nhị phân. Chơng trình phải đạt đ-
ợc một vài yêu cầu quan trọng nhanh hơn phơng pháp đã mô tả trong phần
này. giải thích tại sao.
13.3 Mã chiều dài thay đổi
Mã chiều dài thay đổi (RLC) là một phơng pháp nén ảnh dựa trên sự cắt bớt các
d thừa không gian. Cho mã hoá chiều dài thay đổi một chiều, một mã chiều dài thay

đổi đợc định nghĩa là một số các phần tử điểm ảnh liên tục có chung một giá trị. Một
ảnh có thể mã hoá dùng một cặp (mã chiều dài thay đổi, mã mức xám). Một chơng
trình nh vậy sẽ không thể làm giảm kích thớc của ảnh nếu ảnh không chứa các điểm
có cùng các giá trị mức xám. Điều kiện này xuất hiện trong một ảnh nhiều chi tiết.
Dù có thế đi chăng nữa thì định nghĩa của RLC có thể là một phơng pháp tốt để mã
hoá mà có thể khắc phục các vấn đề xuất hiện dựa theo các điều kiện sau:
1. Một mã chiều dài thay đổi đợc xác định bằng ba bít cuối cùng có ý nghĩa của
nó đợc xác lập bằng 1. Còn 5 bít thấp của nó cung cấp một bộ đếm từ 1 đến
31 cho byte đi theo nó.
2. Nếu giá trị một điểm có mã chiều dài thay đổi bằng không, thì nó đợc mã hoá
nh sau:
a.Nếu 3 bit cuối của nó đều xác lập lên 1, châửng hạn, 224, thì nó đợc mã
hoá thành (11100000, giá trị điểm), cụ thể, mã chiều dài thay đổi bằng
không theo sau bằng giá trị điểm.
b.Cho các trờng hợp còn lại, nó đợc mã hoá nh giá trị điểm.
Các bớc trên giả thiết rằng trong một ảnh bình thờng, mã chiều dài thay đổi lớn
hơn 31 ít xuất hiện, và các điểm có giá trị lớn hơn 224 cũng ít xuất hiện. Ch ơng trình
C sau sẽ thực hiện các bớc trên.
Chơng trình 13.4 "RLC.C". Chơng trình cho giải thuật RLC.
/*Program 13.4 "RLC.C". Program for RLC.*/
/* Run length code. */
/* This program can be used for either
coding images in RLC or decoding them. */
#include <stdio.h>
#include <stdlib.h>
72
#include <conio.h>
#include <math.h>
#define MASK 224 /* MASK=11100000 */
void main()

{
int buff[256],p;
int N,i,k;
FILE *fptr,*fptro;
char file_name[14],ch;
int NMASK=(~MASK);
clrscr();
printf("This program can be used for both coding");
printf("\nand decoding images using 1-D RLC.");
printf("\n\n Enter choice:");
printf("\n 1.Coding.");
printf("\n 2.Decoding >" );
while(((ch=getch())!='1')&&(ch!='2'));
putch(ch);
printf("\n\nEnter input file name >");
scanf("%s",file_name);
fptr=fopen(file_name,"rb");
if(fptr==NULL)
{
printf("\n file %s does not exist.\n");
exit(1);
}
switch(ch)
{
case '1':
printf("Enter output file name >");
scanf("%s",file_name);
fptro=fopen(file_name,"wb");
/* Read first line.*/
N=0;

while((buff[N]=getc(fptr))!=EOF)
{
N++;
if(N==256) break;
}
while(N!=0)
{
i=0;
while(i<N)
{
p=buff[i]; /*Read reference value.*/
k=0;
if(i==(N-1)) /*Is reference value last value in line? */
{
if((p&MASK)==MASK)
{
k=k|MASK;
putc(k,fptro);
putc(p,fptro);
}
73
else
putc(p,fptro);
break;
}
i++;
while(p==buff[i])
{
i++;
k++;

if((k==31)||(i==N)) break;
}
if(k==0)
{
if((p&MASK)==MASK)
{
k=MASK;
putc(k,fptro);
putc(p,fptro);
}
else
putc(p,fptro);
}
else
{
k|=MASK;
putc(k,fptro);
putc(p,fptro);
}
}
N=0;
while((buff[N]=getc(fptr))!=EOF)
{
N++;
if(N==256) break;
}
}
fcloseall();
printf("\nDone coding");
break;

case '2': /* Decoding procedure.*/
printf("\nEnter file name for storing decoded image >");
scanf("%s",file_name);
fptro=fopen(file_name,"wb");
while(1)
{
k=getc(fptr);
if(k==EOF) break;
if((k&MASK)==MASK)
{
p=getc(fptr);
if(p==EOF)
{
putc(k,fptro);
break;
}
74
k=k&(NMASK);
for(i=0;i<=k;i++)
putc(p,fptro);
}
else
putc(k,fptro);
}
fcloseall();
break;
}
}
Bài tập 13.4
1. Dùng chơng trình 13.4 trên ảnh IKRAM.IMG.

2. Dùng mã Hufman để nén ảnh và so sánh với các phơng pháp mã hoá khác.
3. áp dụng các bớc giải mã Huffman và RLC để giải nén ảnh.
4. Viết một chơng trình C dùng các khái niệm cơ bản (mã chiều dài thay đổi, mã)
mà không sử dụng các bớc mô tả cho chơng trình 13.4. áp dụng chơng trình
của bạn lên ảnh IKRAM.IMG. Khi đó kích thớc của ảnh sau khi nén so với
ảnh gốc sẽ thế nào ?
5. áp dụng mã Huffman với:
a. File rút ra từ phần 4.
b. Chiều dài thay đổi và giá trị điểm chia bằng cách xem xét chúng nh hai file.
Có thể chuyển mã 1-D RLC sang mã 2-D RLC bằng cách kiểm tra các dòng trớc,
hoặc kiểm tra bốn hớng khác nhau (trên, dới, trái, phải). Các 2-D RLC này có thể
nén ảnh ở mức độ cao hơn.
13.4 Mã chuyển đổi
Nhắc lại là biến đổi Fourier cho một ảnh thì có phần lớn các giá trị lớn nhất nằm
ở miền tần số thấp. Mật độ các giá trị này giảm xuống nhanh chóng khi tần số tăng
lên. Tính chất này, tính chất mà chúng ta áp dụng để lọc ảnh, cũng đợc áp dụng
trong khi nén ảnh. Có một số phép biến đổi thuận tiện hơn phép biến đổi Fourier.
Phép biến đổi tối u nhất là phép biến đổi đợc đề xuất bởi Karhunen-Loeve (KL). Tuy
nhiên, phép biến đổi này tự nó không thể đa ra các bớc tính toán nhanh hoặc hiệu
quả hơn. Một phép biến đổi xem có vẻ giống nh biến đổi KL nhng có thể tính toán
nh biến đổi Fourier rời rạc là phép biến đổi cosin rời rạc. Biến đổi cosin có một sự
thay đổi nhỏ tối u hoá trong miền tập trung năng lợng so với biến đổi KL; nhng do u
điểm của kỹ thuật tính toán nên nó đợc áp dụng nh một tiêu chuẩn trong kỹ thuật
nén ảnh.
Phép biến đổi đợc áp dụng trên toàn bộ ảnh nhng thông thờng ngời ta hay áp
dụng trên các khối nhỏ hơn có kích thớc 8 ì 8 hoặc 16 ì 16. Lý do là:
1. Biến đổi của các khối nhỏ thì dễ tính hơn là biến đổi cho toàn bộ ảnh.
2. Quan hệ giữa các điểm ảnh ít thay đổi giữa các điểm ảnh gần nhau.
Chúng tôi sẽ trình bày dới đây phép biến đổi cosin và các kỹ thuật tính toán hoàn
thiện của nó. Một số phép biến đổi khác nh Hadamard, Walsh, , không đợc nghiên

75
cứu trong cuốn sách này, bởi vì chúng không tối u bằng phép biến đổi Fourier và
chúng có nhiều giới hạn trong lĩnh vực này.
13.4.1 Biến đổi cosin
Biến đổi một chiều cosin rời rạc (DCT-Discrete Cosin Transform) cho bởi


=






+
=
1
0
2
)12(
cos)(
2
)(
N
n
k
N
kn
nx
N

kX


(13.4)
ở đây


k
=
1
2
với k = 0
= 1 với các trờng hợp còn lại.
và k = 0,1,2, , N.
Một số phơng pháp biến đổi nhanh cosin (FCT-Fast Cosine Transform) đã đợc
phát triển. Một trong số đó là biến đổi của Chan và Ho. Thuật toán này tơng tự nh
thuật toán FFT, ngoại trừ một số biến đổi ở đầu vào dữ liệu. Phép biến đổi này coi
rằng N là bội số của 2.
Dữ liệu đầu vào x(n) đợc sắp xếp lại theo thứ tự:

)12()1(
)2(
~
~





+=

=
nxnNx
nxx
n = 0,1,2, ,N/2 - 1 (13.5)
Thay vào biểu thức (13.4) chúng ta đợc

X k x n
n k
N
x n
n k
N
n
N
n
N
( ) ( ) cos
( )
( ) cos
( )
( / ) ( / )
=
+
+ +
+
=

=



2
4 1
2
2 1
4 3
2
0
2 1
0
2 1


Sau khi nhân ta đợc:

N
kn
nxkX
N
n
2
)14(
cos)(
~
)(
1
0
+
=



=

(13.6)
Cũng giống nh phơng pháp hệ thập phân trong miền tần số cùng trong FFT,
chúng ta cũng chia X(k) thành các giá trị chẵn và lẻ theo các bớc sau:
Các mục chỉ số chẵn:


=
+
=
1
0
2
2)14(
cos)(
~
)2(
N
n
N
kn
nxkX


có thể biểu diễn thành:



=







+
++=
1
0
2
2
)14(
cos])
2
(
~
)(
~
[)2(
N
n
N
knN
nxnxkX

(13.7)
76
Các mục chỉ số lẻ:




=
++
=+
1
0
2
)12)(14(
cos)(
~
)12(
N
n
N
kn
nxkX

biểu thức này sau khi nhân một vài phép nhân, có thể biểu diễn thành:






+
+







+=+


=
)12(
2
)14(
cos)
2
(
~
)(
~
)12(
)12/(
0
k
N
nN
nxnxkX
N
n

(13.8)
Nhng cos[(2k + 1)] = 2coscos(2k) - cos[(2k - 1) ]. Vì vậy,



)12(
2
)14(
cos)
2
(
~
)(
~
-
2
2
)14(
cos)
2
(
~
)(
~
2)12(
1)2/(
02
)12/(
02







+
+






+












+






+=+




=

=
k
N
nN
nxnx
k
N
nN
nxnxkX
N
n
N
n


(13.9)
Biểu thức thứ hai trong (13.9) là X(2k - 1). Bởi vậy, nên (13.9) rút gọn thành
}
2
2
)14(
cos
2
)14(
cos)
2

(
~
)(
~
2{)12(
)12/(
02










++






+=+


=
k
N

n
N
nN
nxnxkX
N
n


)12()12(
2
2
)14(
cos











+
+

kXk
N
n


(13.10)
Đặt
)
2
(
~
)(
~
)(
00
N
nxnxnx ++=
(13.11)







+






+=
N

nN
nxnxx
2
)14(
cos2)
2
(
~
)(
~
01

(13.12)
Vì vậy:



=
+
=
12/
0
00
)
2
(2
)14(
cos)()2(
N
n

N
n
nxkX

(13.13)

77

×