Tải bản đầy đủ (.pdf) (44 trang)

CÁC KỸ THUẬT NÉN Ả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 (1.08 MB, 44 trang )

ĐẠI HỌC ĐÀ NẴNG
TRƢỜNG ĐẠI HỌC BÁCH KHOA
oOo

TIỂU LUẬN MÔN HỌC
XỬ LÝ ẢNH NÂNG CAO



Đề tài:
CÁC KỸ THUẬT NÉN ẢNH SỐ

GVHD : TS. NGÔ VĂN SỸ
Nhóm 3 : Mai Thị Kim Liên
Võ Hoàng Nam
Dƣơng Ngọc Pháp
Phạm Hoàng Phƣơng
Lớp : K25.KDT.DN
Niên khoá : 2012-2014


Đà Nẵng, tháng 05/2014

2
MỤC LỤC
MỞ ĐẦU 3
CHƢƠNG 1: GIỚI THIỆU TỔNG QUAN VỀ NÉN ẢNH 4
1.1 Giới thiệu về ảnh số và xử lý ảnh số 4
1.1.1 Ảnh số 4
1.1.2 Xử lý ảnh số 4
1.2 Mục đích và sự cần thiết của nén ảnh 5


1.3 Các khái niệm cơ bản 6
CHƢƠNG 2: CÁC PHƢƠNG PHÁP NÉN ẢNH 8
2.1 Cách phân loại các phƣơng pháp nén ảnh 8
2.1.1 Phân loại dựa vào nguyên lý nén 8
2.1.2 Phân loại dựa vào cách thức thực hiện nén 8
2.1.3 Phân loại dựa vào lý thuyết mã hóa 8
2.1.4 Quá trình nén và giải nén 8
2.2 Phƣơng pháp mã hóa loạt dài RLE 9
2.2.1 Nguyên tắc 9
2.2.2 Thuật toán 11
2.2.3 Một số thủ tục chƣơng trình 11
2.3 Phƣơng pháp mã hóa Huffman 15
2.3.1 Nguyên tắc 15
2.3.2 Thuật toán 16
2.3.3 Một số thủ tục chƣơng trình 20
2.4 Phƣơng pháp mã hóa LZW 22
2.4.1 Nguyên tắc 22
2.4.2 Thuật toán 25
2.4.3 Một số thủ tục chƣơng trình 28
2.5 Phƣơng pháp mã hóa JPEG 30
2.5.1 Nguyên tắc 30
2.5.2 Thuật toán 31
2.5.3 Một số thủ tục chƣơng trình 37
CHƢƠNG 3: CÀI ĐẶT CHƢƠNG TRÌNH VÀ CHẠY THỬ NGHIỆM 43


3
MỞ ĐẦU
Ngày nay, cùng với sự phát triển không ngừng của khoa học và công nghệ
thì máy tính đóng vai trò ngày càng quan trọng và không thể thiếu trong cuộc sống

xã hội loài người. Việc trao đổi thông tin của con người trong tất cả các ngành, các
lĩnh vực của đời sống ngày càng trở nên cần thiết cùng với sự ra đời và phát triển
của mạng Internet.
Xử lý ảnh là một ngành khoa học đang được tập trung nghiên cứu và phát
triển vì những ứng dụng thực tiễn của nó trong nhiều ngành, lĩnh vực khác nhau.
Trong đó “Nén ảnh” là một phần của xử lý ảnh có ứng dụng to lớn trong truyền
thông và trong lưu trữ, đã có rất nhiều phương pháp nén ảnh được ra đời và không
ngừng được cải tiến để ngày càng hoàn thiện đem lại hiệu quả nén cao và cho chất
lượng ảnh tốt nhất. Trong tiểu luận này sẽ tìm hiểu một số phương pháp nén ảnh
như: mã loạt dài RLE, HUFFMAN, LZW, JPEG.

4
CHƢƠNG 1: GIỚI THIỆU TỔNG QUAN VỀ NÉN ẢNH
1.1 Giới thiệu về ảnh số và xử lý ảnh số
1.1.1 Ảnh số
Ảnh có thể biểu diễn dưới dạng tín hiệu tương tự hoặc tín hiệu số. Trong
biểu diễn số của các ảnh đa mức xám, một ảnh được biểu diễn dưới dạng một ma
trận hai chiều. Mỗi phần tử của ma trận biểu diễn cho mức xám hay cường độ của
ảnh tại vị trí đó. Mỗi phần tử trong ma trận được gọi là một phần tử ảnh, thông
thường kí hiệu là PEL (Picture Element) hoặc là điểm ảnh (Pixel).
- Với ảnh đa cấp xám: Nếu dùng 8 bit (1 byte) để biểu diễn mức xám, thì số
các mức xám có thể biểu diễn được là 2
8
hay 256. Mỗi mức xám được biểu diễn
dưới dạng là một số nguyên nằm trong khoảng từ 0 đến 255, với mức 0 biểu diễn
cho mức cường độ đen nhất và 255 biểu diễn cho mức cường độ sáng nhất.
- Với ảnh màu: Cách biểu diễn cũng tương tự như với ảnh đen trắng, chỉ khác
là các số tại mỗi phần tử của ma trận biểu diễn cho ba màu riêng rẽ gồm: đỏ (red),
lục (green) và lam (blue). Để biểu diễn cho một điểm ảnh màu cần 24 bit, 24 bit này
được chia thành ba khoảng 8 bit. Mỗi khoảng này biểu diễn cho cường độ sáng của

một trong các màu chính.

Hình 1.1 Biểu diễn của một mức xám của ảnh số
1.1.2 Xử lý ảnh số
Xử lý ảnh là một khoa học mặc dù còn tương đối mới so với nhiều
ngành khoa học khác ,nhất là trên quy mô công nghiệp. Xử lý ảnh số có rất nhiều
ứng dụng như làm nổi các ảnh trong y học, khôi phục lại ảnh do tác động của khí
quyển trong thiên văn học, tăng cường độ phân giải của ảnh truyền hình mà không

5
cần thay đổi cấu trúc bên trong của hệ thống chuyển tải, nén ảnh trong khi truyền đi
xa hoặc lưu trữ.
Các giai đoạn chính trong xử lý ảnh có thể được mô tả trong hình sau:

Hình 1.2 Các giai đoạn của quá trình xử lý ảnh
1.2 Mục đích và sự cần thiết của nén ảnh
Nén ảnh là một kỹ thuật mã hoá các ảnh số hoá nhằm giảm số lượng các bit
dữ liệu cần thiết để biểu diễn ảnh. Mục đích là giảm đi những chi phí trong việc lưu
trữ ảnh và chi phí thời gian để truyền ảnh đi xa trong truyền thông nhưng vẫn đảm
bảo được chất lượng của ảnh. Nén ảnh thực hiện được là do một thực tế: thông tin
trong bức ảnh không phải là ngẫu nhiên mà có trật tự , tổ chức.Vì thế nếu bóc tách
được tính trật tự, cấu trúc đó thì sẽ biết phần thông tin nào quan trọng nhất trong
bức ảnh để biểu diễn và truyền đi với số lượng ít bit hơn so với ảnh gốc mà vẫn đảm
bảo tính đầy đủ của thông tin.ở bên nhận quá trình giải mã sẽ tổ chức, sắp xếp lại
được bức ảnh xấp xỉ gần chính xác so với ảnh gốc nhưng vẫn thỏa mãn chất lượng
yêu cầu. Dưới đây là ví dụ về lưu trữ ảnh số và truyền đi xa với đường truyền
9600 baud (9600 bps) để thấy rõ sự cần thiết của việc nén ảnh:
- Ảnh đa cấp xám hay ảnh 256 màu có kích thước 800 x 600, 8 bit/điểm ảnh,
cần 3.840.000 bit lưu trữ và mất 6.67 phút để truyền.
- Ảnh màu RGB (24 bit/điểm ảnh ) cùng độ phân giải như vậy cần hơn 10

triệu bit để lưu trữ và 20 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 x 2000 điểm, 8 bit/pixel, yêu cầu 48 triệu bit
cho lưu giữ ảnh và 83 phút để truyền.
Qua ví dụ trên ta thấy nhiều vấn đề trong việc lưu trữ và truyền tải ảnh số
hoá. Nén ảnh có nhiều ứng dụng trong thực tế như : truyền các văn bản đồ hoạ qua
đường điện thoại (Fax), nén ảnh trong y tế và truyền hình cáp… .Chính sự

6
ứng dụng trong nhiều lĩnh vực của nén ảnh cùng với sự tiến bộ trong lĩnh vực vi
điện tử dẫn đến sự ra đời các chuẩn nén ảnh.
Nén ảnh đạt được bằng cách loại bỏ các phần dư thừa trong ảnh đã
được số hoá. Dư thừa có thể là dư thừa thông tin về không gian, dư thừa về cấp
xám hay dư thừa về thời gian:
- Dư thừa thông tin về không gian: trong một bức ảnh luôn tồn tại sự tương
quan giữa các điểm ảnh cạnh nhau.
- Dư thừa thông tin về cấp xám: là dư thừa dựa vào sự tương quan giữa các
màu sắc cạnh nhau.
- Dư thừa thông tin về thời gian: Trong một chuỗi ảnh video, tồn tại sự tương
quan giữa các điểm ảnh của các frame khác nhau.
1.3 Các khái niệm cơ bản
a. Pixel (picture element) : phần tử ảnh
Ảnh trong thực tế là một ảnh liên tục về không gian và về giá trị độ sáng. Để
có thể xử lý ảnh bằng máy tính cần thiết phải tiến hành số hoá ảnh. Như vậy một
ảnh là một tập hợp các pixel. Mỗi pixel là gồm một cặp toạ độ x, y và màu. Cặp toạ
độ x, y tạo nên độ phân giải (resolution). Màn hình máy tính có nhiều loại với độ
phân giải khác nhau: 320x200, 640x350, 800x600, 1024x768,…
b. Mức xám (Graylevel)
Mức xám là kết quả sự mã hoá tương ứng của mỗi cường độ sáng của mỗi
điểm ảnh với một giá trị số kết quả của quá trình lượng hoá.

c. Dữ liệu
Trong một bài toán, dữ liệu bao gồm một tập các phần tử cơ sở mà ta gọi là
dữ liệu nguyên tử. Nó có thể là một chữ số, một ký tự, nhưng cũng có thể là một
con số, một từ, điều đó phụ thuộc vào từng bài toán.
d. Nén dữ liệu
Nén dữ liệu là quá trình giảm dung lượng thông tin “dư thừa” trong dữ liệu
gốc và làm cho lượng thông tin thu được sau nén thường nhỏ hơn dữ liệu gốc rất
nhiều. Do vậy, tiết kiệm được bộ nhớ và giảm thời gian trao đổi dữ liệu trên mạng
thông tin mà lại cho phép chúng ta khôi phục lại dữ liệu ban đầu.
e. Tỷ lệ nén
Tỷ lệ nén là một trong các đặc trưng quan trọng của mọi phương pháp nén.
Tỷ lệ nén được định nghĩa như sau:
Tỷ lệ nén = 1/r*%

7
với r là tỷ số nén được định nghĩa:
r = kích thước dữ liệu gốc / kích thước dữ liệu nén.
Như vậy hiệu suất nén = (1ư tỷ lệ nén)*100%.
Đối vơi ảnh tĩnh, kích thước chính là số bit biểu diễn toàn bộ bức ảnh. Đối
với ảnh video, kích thước chính là số bit để biểu diễn một khung hình video (video
frame).

8
CHƢƠNG 2: CÁC PHƢƠNG PHÁP NÉN ẢNH
2.1 Cách phân loại các phƣơng pháp nén ảnh
2.1.1 Phân loại dựa vào nguyên lý nén
Nén bảo toàn thông tin (losses compression): bao gồm các phương pháp nén
mà sau khi giải nén sẽ thu đựơc chính xác dữ liệu gốc.Tuy nhiên nén bảo toàn
thông tin chỉ đạt hiệu quả nhỏ so với phương pháp nén không bảo toàn thông tin.
Nén không bảo toàn thông tin (lossy compression): bao gồm các phương

pháp nén sau khi giải nén sẽ không thu được dữ liệu như bản gốc. Các
phương pháp này được gọi là “tâm lý thị giác” đó là lợi dụng tính chất của mắt
người chấp nhận một số vặn xoắn trong ảnh khi khôi phục lại.Phương pháp này
luôn đem lại hiệu quả cao do loại bỏ đi những thông tin dư thừa không cần thiết.
2.1.2 Phân loại dựa vào cách thức thực hiện nén
Phương pháp không gian (Spatial Data Compression ): các phương pháp
này thực hiện nén bằng cách tác động trực tiếp lên việc lấy mẫu của ảnh
trong miền không gian.
Phương pháp sử dụng biến đổi (Transform Coding): gồm các phương
pháp tác động lên sự biến đổi của ảnh gốc chứ không tác động trực tiếp.
2.1.3 Phân loại dựa vào lý thuyết mã hóa
Các phương pháp nén thế hệ thứ nhất: gồm các phương pháp có mức độ tính
toán đơn giản như lấy mẫu, gán từ mã,… .
Các phương pháp nén thế hệ thứ hai: gồm các phương pháp dựa vào mức độ
bão hoà của tỷ lệ nén bằng cách sử dụng các phép toán tổ hợp đầu ra một cách hợp
lý hoặc sử dụng biểu diễn ảnh như: phương pháp kim tự tháp Laplace, phương pháp
dựa vào vùng gia tăng, phương pháp tách hợp.
2.1.4 Quá trình nén và giải nén
Gồm 2 công đoạn :
Nén : dữ liệu gốc qua bộ mã hoá dữ liệu , bộ mã hoá này thực hiện nén dữ
liệu đến một mức thích hợp cho việc lưu trữ và truyền dẫn thông tin. Quá trình này
sẽ thực hiện việc loại bỏ hay cắt bớt những dư thừa của ảnh để thu được thông tin
cần thiết nhưng vẫn đảm bảo được chất lượng ảnh.
Giải nén : dữ liệu nén đi qua bộ giải mã dữ liệu, bộ giải mã sẽ thực hiện giải
nén để thu được dữ liệu gốc ban đầu.Việc giải nén này thường phải dựa vào các

9
thông tin đi kém theo dữ liệu nén ,tuỳ thuộc vào kiểu nén hay phương pháp nén mà
dữ liệu giải nén được có hoàn toàn giống với dữ liệu gốc ban đầu hay không.
Tóm lại quá trình nén và giải nén dữ liệu có thể mô tả một cách tóm tắt theo

sơ đồ dưới đây:

Hình 2.1 Quá trình nén và giải nén
2.2 Phƣơng pháp mã hóa loạt dài RLE
Mã hoá theo độ dài loạt RLE (Run Length Encoding) là một phương
pháp nén ảnh dựa trên sự cắt bớt các dư thừa về không gian (một vài hình ảnh có
vùng màu lớn không đổi đặc biệt đối với ảnh nhị phân). Loạt được định nghĩa là
dãy các phần tử điểm ảnh (pixel) liên tiếp có cùng chung một giá trị.
2.2.1 Nguyên tắc
Nguyên tắc của phương pháp này là phát hiện một loạt các điểm ảnh lặp lại
liên tiếp, ví dụ :110000000000000011 .Ta thấy điểm ảnh có giá trị 0 xuất hiện nhiều
lần liên tiếp thay vì phải lưu trữ toàn bộ các điểm ảnh có giá trị 0 ta chỉ cần lưu trữ
chúng bằng cách sử dụng các cặp (độ dài loạt, giá trị).
Ví dụ:
Cho một chuỗi nguồn d :
d =5 5 5 5 5 5 5 5 5 5 19 19 19 19 19 0 0 0 0 0 0 0 23 23 23 23 23 23 23 23
Ta sẽ có chuỗi mới :
(10 5) (5 19) (7 0) (8 23)
Tỷ số nén = 20/ 8 = 2.5
Đối với ảnh đen trắng chỉ sử dụng 1 bit để biểu diễn 1 điểm ảnh thì
phương pháp này tỏ ra rất hiệu quả, ta thấy điều đó qua ví dụ sau :
Cho một chuỗi nguồn d:
000000000000000111111111100000000001111111111000000000000000
Ta có chuỗi mới :
(15, 10, 10, 10, 15)

10
Tỷ số nén = 60 bit / (5*4 bit) = 3 ( chỉ sử dụng 4 bit để thể hiện độ dài loạt và
không thể hiện giá trị loạt vì ảnh đen trắng chỉ có 2 giá trị bit là 0 hoặc là 1)
Chú ý:

- Đối với ảnh chiều dài của một dãy lặp có thể lớn hơn 255, nếu ta dùng 1
byte để lưu trữ chiều dài thì sẽ không đủ. Giải pháp được dùng là tách
chuỗi đó thành 2 chuỗi: một chuỗi có chiều dài là 255, chuỗi kia có chiều dài còn
lại.
- Phương pháp nén RLE chỉ đạt hiệu quả khi chuỗi lặp lớn hơn 1 ngưỡng
nhất định nào đó hay nói các khác trong ảnh cần nén phải có nhiều điểm ảnh kề
nhau có cùng giá trị màu. Do đó phương pháp này không đem lại cho ta kết quả một
cách ổn định vì nó phụ thuộc hoàn toàn vào ảnh nén chỉ thích hợp cho những ảnh
đen trắng hay ảnh đa cấp xám.
Ví dụ:
Ta có một chuỗi nguồn: d=5 7 9 11 13 18 28 38 48 58 30 35 40 45
Chuỗi kết quả sau khi mã hoá :
1 5 1 7 1 9 1 11 1 3 1 18 1 28 1 38 1 48 1 58 1 30 1 35 1 40 1 45
Tỷ số nén = 14 / 28 = 0.2
Như vậy chuỗi sau khi mã hoá đã lớn hơn nhiều chuỗi nguồn ban đầu. Do đó
cần phương pháp cải tiến để xử lý những trường hợp như trên tránh làm mở rộng
chuỗi dữ liệu nguồn nghĩa là chỉ mã hoá độ dài loạt dữ liệu lặp lại. Người ta đã đưa
ra cách đó là thêm kí tự tiền tố vào trước độ dài loạt, việc giải mã được thực hiện
nếu gặp kí tự tiền tố với độ dài loạt và giá trị điểm ảnh theo sau.
Ví dụ:
Ta có chuỗi nguồn :
d = 5 8 4 8 8 8 8 8 8 8 8 10 10 10 10 10 10 10 10 10
Giả sử kí tự tiền tố là “+” ta có : 5 8 4 +7 8 + 9 10
Tỷ số nén = 19 / 9 = 2.1
Tuy nhiên trong một số trường hợp các điểm ảnh có độ tương quan với nhau
vể giá trị mức xám như trong ví dụ dưới đây ta có thể tiến hành xử lý như sau.
Ví dụ:
Ta có một chuỗi nguồn:
d = 5 7 9 11 13 18 28 38 48 58 55 60 65 70 75 80 85 90 95 100
Ta có dựa vào độ tương quan này để có được hiệu quả nén cao , bằng việc áp

dụng e(i) = d(i) - d(i-1) sẽ thu được :

11
5 2 2 2 2 5 10 10 10 10 -3 5 5 5 5 5 5 5 5 5
Áp dụng phương pháp nén loạt dài ta dễ dàng thu được :
(5 1)( 2 4)(5 1)(10 5)(-3 1)(5 9)
2.2.2 Thuật toán
Thuật toán như sau :
- Tiến hành duyệt trên từng hàng cho đến khi kết thúc vùng dữ liệu ảnh, trong
quá trình duyệt tiến hành kiểm tra để tìm ra những loạt có cùng giá trị đồng thời chú
ý những kí hiệu xuống dòng (hay kết thúc dòng) ,kết thúc ảnh Bitmap, …
- Khi gặp loạt có độ dài > 3 thì nhảy đến chế độ nén ngược lại nhảy đến chế
độ không nén tuy nhiên nếu loạt > 255 thì sẽ tách ra chỉ mã < 255 sau đó mã tiếp
phần còn lại. Ngoài ra còn các chế độ khác như : bắt đầu , kết thúc 1 dòng.
- Kết thúc khi gặp kí hiệu kết thúc bitmap ( end - of - bitmap)
2.2.3 Một số thủ tục chƣơng trình
a. Chương trình nén theo phương pháp RLE
void CRLE::CompressInRLE8(BYTE*pSrcBits,CByteArray& pRLEBits,
int&
RLE_size)
{
int line;
int src_index = 0, dst_index = 0, counter, i;
for ( line = 0; line < m_dib.dsBmih.biHeight; line++)
{
state_start:
if ( EndOfLine(src_index))
{
pRLEBits[dst_index++] = 1;
pRLEBits[dst_index++] = pSrcBits[src_index];

src_index++;
goto end_of_line;
}
if(pSrcBits[src_index]==pSrcBits[src_index+1])
goto tate_compress;
if ( EndOfLine(src_index+1))
{

12
pRLEBits[dst_index++] = 1;
pRLEBits[dst_index++] = pSrcBits[src_index++];
pRLEBits[dst_index++] = 1;
pRLEBits[dst_index++] = pSrcBits[src_index++];
goto end_of_line;
}
if (pSrcBits[src_index+1] == pSrcBits[src_index+2])
{
pRLEBits[dst_index++] = 1;
pRLEBits[dst_index++] = pSrcBits[src_index++];
goto state_compress;
}
else
goto state_no_compress;
state_compress:
for ( counter = 1; counter <= 254; counter++)
{
if ( pSrcBits[src_index+counter] !=
pSrcBits[src_index] )
break;
if ( EndOfLine(src_index+counter) )

{
pRLEBits[dst_index++] = counter+1;
pRLEBits[dst_index++] = pSrcBits[src_index];
src_index += counter +1;
goto end_of_line;
}
}
pRLEBits[dst_index++] = counter;
pRLEBits[dst_index++] = pSrcBits[src_index];
src_index += counter;
goto state_start;
state_no_compress:
for (counter = 2; counter <= 254; counter++)

13
{
if ( EndOfLine(src_index+counter) ) {
pRLEBits[dst_index++] = 0;
pRLEBits[dst_index++] = counter + 1;
for (i = counter + 1; i > 0; i )
pRLEBits[dst_index++]=pSrcBits[src_index++];
if ( 0 != ((counter+1) % 2) ) pRLEBits[dst_index++];
goto end_of_line;
}
if(EndOfLine(src_index+counter+1)||pSrcBits[src_index+cou
nter] != pSrcBits[src_index+counter+1] )
continue;
if(EndOfLine(src_index+counter+2)
||SrcBits[src_index+counter+1] != pSrcBits[src_index+counter+2] )
continue;

else
{
if ( counter > 2) counter ;
pRLEBits[dst_index++] = 0;
pRLEBits[dst_index++] = counter+1;
for (i = counter+1; i > 0; i )
pRLEBits[dst_index++] = pSrcBits[src_index++];
if ( 0 != ((counter+1) % 2) ) pRLEBits[dst_index++];
goto state_compress;
}
}
pRLEBits[dst_index++] = 0;
pRLEBits[dst_index++] = counter;
for (i = counter; i > 0; i )
pRLEBits[dst_index++] = pSrcBits[src_index++];
if ( 0 != ((counter) % 2) )
pRLEBits[dst_index++];
goto state_start;
end_of_line:

14
if ( 0 != (src_index % 4 ))
{
int pad = 4 - (src_index%4);
src_index += pad;
}
pRLEBits[dst_index++] = 0;
pRLEBits[dst_index++] = 0;
}
pRLEBits[dst_index++] = 0;

pRLEBits[dst_index++] = 1;
RLE_size = dst_index;
}
b. Chương trình giải nén phương pháp RLE
BOOL CRLE::DecRLE8(ifstream &fil, BYTE *pDest)
{
DWORD x, y, paddedwidth;
paddedwidth=BMPWIDTHBYTES(pInfo->biWidth*pInfo->
biBitCount);
BYTE FirstByte, SecondByte;
WORD data;
x = y = 0;
while (!fil.eof())
{
if (!fil.read((char*)&data, 2)) return FALSE;
FirstByte = LOBYTE(data);
SecondByte = HIBYTE(data);
if (FirstByte == 0)
{
switch (SecondByte)
{
case 0:
x = 0;
y++;
break;

15
case 1:
return TRUE;
case 2:

if (!fil.read((char*)&data, 2)) return FALSE;
x += LOBYTE(data);
y += HIBYTE(data);
break;
default:
char ch;
for (BYTE i = 0; i < SecondByte; i++)
{
if (!fil.get(ch)) return FALSE;
pDest[paddedwidth * y + x++] = (BYTE)ch;
}
if ((SecondByte % 2) == 1)
if (!fil.get(ch)) return FALSE;
}
}
else
{
for (BYTE i = 0; i < FirstByte; i++)
pDest[paddedwidth * y + x++] = SecondByte;
}
}
return FALSE;
}
2.3 Phƣơng pháp mã hóa Huffman
2.3.1 Nguyên tắc
Phương pháp mã hoá Huffman là phương pháp dựa vào mô hình thống kê.
Người ta tính tần suất xuất hiện của các ký tự bằng cách duyệt tuần tự từ đầu tệp
gốc đến cuối tệp. Việc xử lý ở đây tính theo bit. Trong phương pháp này các ký tự
có tần suất cao một từ mã ngắn, các ký tự có tần suất thấp một từ mã dài. Như vậy
với cách thức này ta đã làm giảm chiều dài trung bình của từ mã hoá bằng cách


16
dùng chiều dài biến đổi tuy nhiên cũng có trường hợp bị thiệt 1 ít bit khi tần suất là
rất thấp.
2.3.2 Thuật toán
Thuật toán mã hóa Huffman gồm 2 bước chính:
- Bước 1: Tính tần suất của các ký tự trong dữ liệu gốc bằng cách duyệt tệp
gốc một cách tuần tự từ đầu đến cuối để xây dựng bảng mã và tính toán tần suất.
Tiếp theo sau là sắp xếp lại bảng mã theo thứ tự tần suất giảm dần.
- Bước 2: Duyệt bảng tấn suất từ cuối lên đầu để thực hiện ghép hai phần tử
có tần suất thấp thành một phần tử duy nhất có tần suất bằng tổng hai tần suất
thành phần. Cập nhật phần tử này vào vị trí phù hợp trong bảng và loại bỏ hai phần
tử đã xét. Thực hiện cho đến khi bảng chỉ có một phần tử. Đây là quá trình tạo cây
nhị phân Huffman ,p hần tử có tần suất thấp ở bên phải, phần tử kia ở bên trái. Sau
khi cây đã tạo xong người ta tiến hành gán mã cho các nút lá. Việc mã hoá thực
hiện theo quy định : mỗi lần xuống bên phải ta thêm một bit „1‟ vào từ mã, mỗi lần
xuống bên trái ta thêm một bit „0‟ vào từ mã.
Quá trình giải nén cũng khá đơn giản được tiến hành theo chiều ngược lại.
Người ta cũng phải dựa vào bảng mã tạo ra trong giai đoạn nén (bảng này được lưu
trữ trong cấu trúc đầu của tệp nén cùng với dữ liệu nén).
Ví dụ: Một tệp bất kỳ có tần suất xuất hiện của các kí tự số như bảng sau.

Sau khi sắp xếp giảm dần

17



Mô tả : Ta tiến hành hợp nhất hay cộng 2 tần suất nhỏ nhất ở cuối bảng để
thu được giá trị tần suất mới sau đó đưa giá trị này trở lại bảng tần suất ban đầu đã

bỏ đi 2 tần suất thành phần tạo thành nó. Sau khi đưa giá trị mới vào bảng ta phải
tiến hành sắp xếp lại toàn bộ bảng , lúc này số lượng tần suất chỉ còn là nư1 nếu ban
đầu số lượng tần suất là n. Tiếp tục thực hiện lần lượt theo thứ tự như trên cho đến
khi nào số lượng tần suất chỉ còn lại duy nhất 1 giá trị.
Việc tạo cây nhị phân có thể được thực hiện theo một thuật toán sau:
1. Tất cả những ký tự ban đầu được xem như là những ký tự giao điểm tự do.
2. Hai nút tự do với tần số xuất hiện thấp nhất được phân công tới một nút gốc
với giá trị bằng với tổng của hai nút con tự do.
3. Hai nút con được chuyển khỏi danh sách nút tự do. Chuyển nút gốc mới tạo
thành công vào danh sách.
4. Bước hai sang bước ba được lặp cho đến khi chỉ có 1 nút tự do về phía trái.
Nút tự do này là gốc của cây.

18
Quá trình xây dựng cây nhị phân Huffman được thể hiện chi tiết như trong
hình sau :



Hình 2.2 Quá trình tạo cây nhị phân
Ta có cây mã Huffman tương ứng như sau:

19

Hình 2.3 Cây mã nhị phân Huffman
Bảng từ mã gán cho các ký tự số như sau:

Như vậy với ví dụ sau đây ta có thể tiến hành mã hoá như sau:
Chuỗi nguồn : 00000000006666693333 ? kích thước = 20*8=160 bit
Sử dụng mã Huffman theo bảng trên

 kích thước =10*1+5*3+1*6+4*3= 43 bit

20
Vì gía trị trị 0 xuất hiện 10 lần nhưng chỉ dùng 1 bit để thể hiện, giá trị 6 xuất
hiện 5 lần dùng 3 bit để thể hiện ,giá trị 9 dùng 6 bit và giá trị 3 xuất hiện 4 lần dùng
3 bit để thể hiện.
Tỷ số nén = 160 / 43 = 3.7
Trong phương pháp mã Huffman mã của ký tự là duy nhất và không mã nào
là phần bắt đầu của mã trước.Vì vậy khi đọc theo từng bit từ đầu đến cuối tệp nén ta
có thể duyệt cây mã cho đến một lá, tức là ký tự đã được giải mã. Việc giải mã chắc
chắn phải sử dụng cây nhị phân giống như trong mã hoá. Để đọc, giải mã được yêu
cầu phải sử dụng theo đúng tiêu chuẩn nhất định .
2.3.3 Một số thủ tục chƣơng trình
a. Chương trình nén theo phương pháp Huffman
bool CompressHuffman(BYTE*pSrc, int nSrcLen, BYTE *&pDes, int
&nDesLen)
{
CHuffmanNode nodes[511];
for(int nCount = 0; nCount < 256; nCount++)
nodes[nCount].byAscii = nCount;
for(nCount = 0; nCount < nSrcLen; nCount++)
nodes[pSrc[nCount]].nFrequency++;
qsort(nodes, 256, sizeof(CHuffmanNode), frequencyCompare);
int nNodeCount = GetHuffmanTree(nodes);
int nNodeSize = sizeof(DWORD)+sizeof(BYTE);
nDesLen = nSrcLen+nNodeCount*nNodeSize;
pDes = (BYTE*)malloc(nDesLen);
BYTE *pDesPtr = pDes;
memset(pDesPtr, 0, nDesLen);
*(DWORD*)pDesPtr = nSrcLen;

pDesPtr += sizeof(DWORD);
*pDesPtr = nNodeCount-1;
pDesPtr += sizeof(BYTE);
for(nCount = 0; nCount < nNodeCount; nCount++)
{
memcpy(pDesPtr, &nodes[nCount], nNodeSize);
pDesPtr += nNodeSize;

21
}
qsort(nodes, 256, sizeof(CHuffmanNode), asciiCompare);
int nDesIndex = 0;
for(nCount = 0; nCount < nSrcLen; nCount++)
{
*(DWORD*)(pDesPtr+(nDesIndex>>3))|=nodes[pSrc[nCount]].dwCode<<
(nDesIndex&7);
nDesIndex += nodes[pSrc[nCount]].nCodeLength;
}
nDesLen = (pDesPtr-pDes)+(nDesIndex+7)/8;
pDes = (BYTE*)realloc(pDes, nDesLen);
return true;
}
b. Chương trình giải nén phương pháp Huffman
bool DecompressHuffman(BYTE*pSrc,int nSrcLen,BYTE*&pDes, int
&nDesLen)
{
nDesLen = *(DWORD*)pSrc;
pDes = (BYTE*)malloc(nDesLen+1);
int nNodeCount = *(pSrc+sizeof(DWORD))+1;
CHuffmanNode nodes[511], *pNode;

int nNodeSize = sizeof(DWORD)+sizeof(BYTE), nSrcIndex = nNodeSize;
for(int nCount = 0; nCount < nNodeCount; nCount++)
{ memcpy(&nodes[nCount], pSrc+nSrcIndex, nNodeSize);
nSrcIndex += nNodeSize;
}
GetHuffmanTree(nodes, false);
CHuffmanNode *pRoot = &nodes[0];
while(pRoot->pParent)
pRoot = pRoot->pParent;
int nDesIndex = 0;
DWORD nCode;
nSrcIndex <<= 3;
while(nDesIndex < nDesLen)

22
{ nCode = (*(DWORD*)(pSrc+(nSrcIndex>>3)))>>(nSrcIndex&7);
pNode = pRoot;
while(pNode->pLeft)
{ pNode = (nCode&1) ? pNode->pRight : pNode->pLeft;
nCode >>= 1;
nSrcIndex++;
}
pDes[nDesIndex++] = pNode->byAscii;
}
return true;
}
2.4 Phƣơng pháp mã hóa LZW
Khái niệm nén từ điển được Jocob Lempe và Abraham Ziv đưa ra lần đầu
tiên năm 1977 sau đó phát triển thành một họ giải thuật nén từ điển LZ. Năm 1984
Welch đã cải tiến giải thuật LZ thành giải thuật mới hiệu quả hơn và được đặt tên

là LZW (Lempe - Ziv - Welch). Phương pháp nà xây dựng từ điển lưu các chuỗi ký
tự có tần suất lặp lại cao và thay thế bằng từ mã tương ứng mỗi khi gặp lại chúng,
nó hay hơn các phương pháp trước đó ở kỹ thuật tổ chức từ điển cho phép nâng cao
tỷ lệ nén.
Giải thuật LZW được dùng cho tất cả các loại file nhị phân, thường
được dùng để nén các loại dữ liệu như : văn bản, ảnh đen trắng, ảnh màu, ảnh đa
cấp sám… và là chuẩn nén cho các dạng ảnh GIF và TIFF. Số bit / pixel không ảnh
hưởng đến hiệu quả của LZW.
2.4.1 Nguyên tắc
Giải thuật nén LZW xây dựng một từ điển lưu các mẫu có tần suất xuất hiện
cao trong ảnh. Từ điển là tập hợp những cặp (từ vựng và nghĩa của từ vựng).
Trong đó từ vựng sẽ là các từ mã được sắp xếp theo thứ tự nhất định. Nghĩa là một
chuỗi con trong dữ liệu ảnh. Từ điển được xây dựng song song với quá trình đọc dữ
liệu. Sự xuất hiện của chuỗi con trong từ điển khẳng định rằng chuỗi đó đã từng
xuất hiện trong phần dữ liệu đã đựoc đọc qua. Thuật toán liên tục tra cứu và sau
mỗi lần đọc một ký tự ở dữ liệu đầu vào thì tiến hành cập nhật lại từ điển.

23
Do giới hạn của bộ nhớ và để đảm bảo tốc độ tìm kiếm nhanh, từ điển chỉ
giới hạn 4096 phần tử dùng để lưu trữ giá trị của các từ mã. Như vậy độ dài lớn
nhất của từ mã là 12 bit (4096=2
12
). Cấu trúc từ điển như sau:

- 256 từ mã đầu tiên theo thứ tự từ 0 … 255 chứa các số nguyên từ 0 …255.
Đây là mã của 256 kí tự cơ bản trong bảng mã ASCII.
- Từ mã thứ 256 chứa một mã đặc biệt là mã xoá (CC ư Clear Code). Khi
số mẫu lặp lớn hơn 4096 thì người ta sẽ coi ảnh gồm nhiều mảnh ảnh và từ điển sẽ
gồm nhiều từ điển con. Khi hết một mảnh ảnh sẽ gửi 1 mã xoá ( CC ) để báo hiệu
kết thúc mảnh ảnh cũ và bắt đầu mảnh ảnh mới đồng thời sẽ khởi tạo lại từ điển.

- Từ mã thứ 257 chứa mã kết thúc thông tin (EOI - End Of Information).
Thông thường một file ảnh GIF có thể chứa nhiều mảnh ảnh,mỗi mảnh ảnh này sẽ
được mã hoá riêng. Chương trình giải mã sẽ lặp đi lặp lại thao tác giải mã từng ảnh
cho đến khi gặp mã kết thúc thông tin thì dừng lại.
- Các từ mã còn lại (từ 258 đến 4095) chứa các mẫu thường lặp lại
trong ảnh. 512 phần tử đầu tiên của từ điển biểu diễn bằng 9 bit. Các từ mã từ 512
đến 1023 biểu diễn bởi 10 bit, từ 1024 đến 2047 biểu diễn bởi 11 bit, và từ 2048
đến 4095 biểu diễn bởi 12 bit.
Ví dụ:
Cho chuỗi đầu vào “HELLOHELLOHELL”
Từ điền ban đầu đã gồm 256 kí tự cơ bản.
Kích thước đầu vào : 14 x 8 = 112 bit

24

Chuỗi đầu ra là : 72 69 76 76 79 258 260 262 259 76
Kích thước đầu ra : 6 x 8 + 4 x 9 = 84 bit
Tỷ số nén : 112 / 84 = 1.3
Quá trình thực hiện giải nén như sau:

25

Chuỗi thu được sau giải nén: “HELLOHELLOHELL”
2.4.2 Thuật toán
a. Thuật toán nén bằng LZW:
InitDictionary()
Output(Clear_Code)
OldStr = NULL
WHILE ( Input )
{

NewChar = GetNextChar()
NewStr = OldStr + NewChar
IF ( InDictionary(NewStr) ) OldsSr = NewStr
ELSE
{
Output(Code(OldStr))
AddToDictionary(NewStr)
OldStr = NewChar
}

×