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

Một phần của tài liệu Tài liệu Đồ án tốt nghiệp Tìm hiểu một số phương pháp nén ảnh docx (Trang 27 - 38)

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ựngnghĩ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

Sinh viên thực hiện : Tạ Minh Thắng CT 702 Trang : 27 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 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:

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

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 Tài liệu Đồ án tốt nghiệp Tìm hiểu một số phương pháp nén ảnh docx (Trang 27 - 38)