II.4.Phương pháp mã hoá LZW:

Một phần của tài liệu Báo cáo tìm hiểu một số phương pháp nén ảnh (Trang 26 - 37)

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ày 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.

II.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.

Sinh viên thực hiện : Tạ Minh Thắng CT 702 Trang :

27

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=212). Cấu trúc từ điển như sau:

0 0 1 1 … … … … 255 255 256 256 (Clear Code) 257 257 (End Of Information) 258 Chuỗi 259 Chuỗi … … … … 4095 Chuỗi

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

Đầu vào Đầu ra Thực hiện

H(72) H đã có trong từ điển Æ đọc tiếp

E(69) 72 Thêm vào từ điển mã 258 đại diện cho chuỗi HE L(76) 69 Thêm vào từ điển mã 259 đại diện cho chuỗi EL L 76 Thêm vào từ điển mã 260 đại diện cho chuỗi LL O(79) 76 Thêm vào từ điển mã 261 đại diện cho chuỗi LO H 79 Thêm vào từ điển mã 262 đại diện cho chuỗi OH E HE đã có trong từ điển Æ đọc tiếp

L 258 Thêm vào từ điển mã 263 đại diện cho chuỗi HEL L LL đã có trong từ điển Æ đọc tiếp

O 260 Thêm vào từ điển mã 264 đại diện cho chuỗi LLO H OH đã có trong từ điển Æ đọc tiếp

Sinh viên thực hiện : Tạ Minh Thắng CT 702 Trang :

29

L EL đã có trong từ điển Æ đọc tiếp

L 259 Thêm vào từ điển mã 266 đại diện cho chuỗi ELL 76 Input = FALSE

EOI

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 giải nén thực hiện như sau: (adsbygoogle = window.adsbygoogle || []).push({});

Code OutBuff() AddToDictionary() CodeWord String 72 H 69 E 258 HE 76 L 259 EL 76 L 260 LL 79 O 261 LO 258 HE 262 OHE 260 LL 263 HEL 262 OH 264 LLO 259 EL 265 OHE 76 L 266 ELL EOI

Chuỗi thu được sau giải nén :”HELLOHELLOHELL”

II.4.2. Thuật toán:

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 } } Output(Code(OldStr)) Output(EOI)

Sinh viên thực hiện : Tạ Minh Thắng CT 702 Trang :

31

Giá trị cờ Input = TRUE khi vẫn còn dữ liệu đầu vào và ngược lại.

Chức năng của các hàm:

Hàm InitDictionary() : Khởi tạo từ điển. Đặt giá trị cho 256 phần

tử đầu tiên. Gán mã xoá CC cho phần tử thứ 256 và mã kết thúc thông tin EOI cho phần tử thứ 257. Xoá giá trị tất cả các phần tử còn lại.

Hàm InDictionary (NewStr) : Kiểm tra chuỗi NewStr đã có trong

từ điển chưa .

Hàm OutPut() : Gửi chuỗi bit ra file.Tuỳ thuộc vào vị trí của từ

mã trong từ điển mà chuỗi bit có độ dài là 9,10,11 hoặc 12.

Hàm GetNextChar() : Trả về một ký tự từ chuỗi ký tự đầu vào. Hàm này cập nhật giá trị cờ Input xác định xem còn dữ liệu đầu vào nữa hay không.

Hàm AddToDictionary() : Hàm làm chức năng cập nhật mẫu mới

vào phần tử tiếp theo trong từ điển. Nếu từ điển đã đầy nó sẽ gửi ra mã xoá CC và gọi đến hàm InitDictionary() để khởi tạo lại từ điển.

Hàm Code() : Trả về từ mã ứng với 1 chuỗi.

Tư tưởng của đoạn mã trên có thể hiểu như sau: Nếu còn dữ liệu đầu vào thì tiếp tục đọc. Một chuỗi mới sẽ được tạo ra từ chuỗi cũ (chuỗi này ban đầu rỗng, chuỗi này phải là chuỗi đã tồn tại trong từ điển) và ký tự vừa đọc vào. Sau đó kiểm tra xem chuỗi mới đã có trong từ điển hay chưa. Mục đích của công việc này là hi vọng tìm được chuỗi có só ký tự lớn nhất trong từ điển. Nếu tồn tại thì lại tiếp tục đọc một ký tự tiếp theo và lặp lại công việc. Nếu chưa có trong từ điển, thì gửi chuỗi cũ ra ngoài và thêm chuỗi mới vào từ điển.

Giải nén dữ liệu bằng LZW:

Giải thuật giải nén gần như ngược với giải thuật nén. Với giải thuật nén, một từ mã ứng với một chuỗi sẽ được ghi ra tệp khi chuỗi ghép bởi chuỗi trên với ký tự vừa đọc chưa có mặt trong từ điển. Người ta cũng cập nhật ngay vào từ điển từ mã ứng với chuỗi tạo bởi chuỗi cũ với ký tự vừa đọc. Ký tự này đồng thời là ký tự đầu tiên trong chuỗi ứng với từ mã sẽ được ghi ra tiếp theo.

Thuật toán được mô tả như sau: WHILE (GetNextCode() != EOI) {

IF (First_Code) /*Mã đầu tiên của mỗi mảnh ảnh*/ { OutBuff(DeCode(code)) OldStr = DeCode(code) } ELSE { IF (code == CC) /*Mã xoá*/ { InitDictionary() First_Code = TRUE } NewStr = DeCode(code) OutBuff(NewStr)

Sinh viên thực hiện : Tạ Minh Thắng CT 702 Trang : 33 33 AddToDictionary(OldStr) OldStr = NewStr } }

Giá trị cờ First_Code = TRUE chỉ mã vừa đọc là mã đầu tiên của mỗi mảnh ảnh.

Chức năng của các hàm:

Mã CC báo hiệu hết một mảnh ảnh. Mã EOI báo hiệu hêt toàn bộ thông tin ảnh.

Hàm GetNextCode() : Hàm này đọc thông tin đầu vào (dữ liệu nén)

trả về mã tương ứng. (adsbygoogle = window.adsbygoogle || []).push({});

Hàm OutBuff() : Hàm này gửi chuỗi đã giải mã ra vùng nhớ đệm.

Hàm InitDictionary(): Khởi tạo từ điển. Đặt giá trị cho 256 phần tử

đầu tiên. Gán mã xoá CC cho phần tử thứ 256 và mã kết thúc thông tin EOI cho phần tử thứ 257. Xoá giá trị tất cả các phần tử còn lại.

Hàm DeCode() : Hàm này tra cứu từ điển và trả về chuỗi ký tự tương

ứng với mã.

Hàm FirstChar() : Lấy ký tự đầu tiên của một chuỗi. Ký tự vừa xác định nối tiếp vào chuỗi ký tự cũ (đã giải mã ở bước trước) ta được chuỗi ký tự có mặt trong từ điển khi nén. Chuỗi này sẽ được thêm vào từ điển giải nén.

Hàm AddToDictionary() : Hàm làm chức năng cập nhật mẫu mới vào

phần tử tiếp theo trong từ điển. Nếu từ điển đã đầy nó sẽ gửi ra mã xoá CC và gọi đến hàm InitDictionary() để khởi tạo lại từ điển..

Chương trình nén phương pháp LZW :

BOOL CLZW::Compress(CFile &source, CFile &destination) {

long prefix = 0; long result = 0; BYTE readByte = 0; unsigned long filetotal = 0; CString logString; DWORD resAdd = 256; Init(); filetotal = source.GetLength(); if (m_tudien == NULL) { CreateDictionary(); } source.Read(&prefix, 1);

while (source.GetPosition() < filetotal) {

source.Read(&readByte, 1);

result = m_tudien->GetEntry(prefix, readByte); if (result == -1)

{

resAdd = m_tudien->AddEntry(prefix, readByte); CalculateBitSize(resAdd);

logString.Format("Adding combination of %d and %d to dictionary to entry %d.",prefix, readByte, resAdd);

Log(logString);

CompressData(destination, prefix);

prefix = readByte;

Sinh viên thực hiện : Tạ Minh Thắng CT 702 Trang : 35 35 } else { prefix = result; readByte = 0; } } CompressData(destination, prefix); CloseCompressedFile(destination); ClearDictionary(); return TRUE; }

Chương trình giải nén phương pháp LZW :

BOOL CLZW::Decompress(CFile &source, CFile &destination) {

DWORD prefix = 0, data = 0; CString logString;

CByteArray decodeString; BYTE writeData = 0, character = 0; int counter = 0; Init(); if (m_tudien == NULL) { CreateDictionary(); } prefix = DecompressData(source); character = (BYTE)prefix; destination.Write(&prefix, 1);

while ((data = DecompressData(source)) != m_MaxCode[m_MaxBits]) { if (!m_tudien->IsCodeExist(data)) { decodeString.Add((BYTE)character); m_tudien->GetBytesFromCode(&decodeString, prefix); } else { m_tudien->GetBytesFromCode(&decodeString, data); character = decodeString.GetAt(decodeString.GetSize() - 1); } counter = decodeString.GetSize(); while (counter > 0) { writeData = (BYTE)decodeString.GetAt(--counter); destination.Write(&writeData, 1);

logString.Format("Adding character code %d with know visualisation of: %s", writeData, convertASCIIToText(writeData)); Log(logString); } decodeString.RemoveAll(); m_tudien->AddEntry(prefix, (BYTE)character); CalculateBitSize(m_tudien->GetMaxCode()+1); prefix = data;

Sinh viên thực hiện : Tạ Minh Thắng CT 702 Trang : 37 37 } return TRUE; }

Một phần của tài liệu Báo cáo tìm hiểu một số phương pháp nén ảnh (Trang 26 - 37)