如何使用Python實現(xiàn)最低有效位隱寫術(shù)?
本文轉(zhuǎn)載自公眾號“讀芯術(shù)”(ID:AI_Discovery)
隱寫術(shù)是一門關(guān)于在適當?shù)亩嗝襟w載體中傳輸秘密數(shù)據(jù)的科學(xué),例如在圖像、音頻和視頻文件中。它的假設(shè)是,如果特征是可見的,那么攻擊點就是明顯的,因此這里的目標總是隱藏嵌入數(shù)據(jù)的存在。
LSB圖像隱寫術(shù)
LSB隱寫術(shù)是一種圖像隱寫術(shù)技術(shù),通過用要隱藏的信息位替換每個像素的最低有效位,將信息隱藏在圖像中。為了更好地理解,將數(shù)字圖像視為2D像素陣列,每個像素包含的值取決于其類型和深度。
我們將考慮最廣泛使用的模式——RGB(3×8位像素,真彩)和RGBA(4x8位像素,帶透明遮罩的真彩),這些值的范圍從0到255(8位值)。

將圖像表示為2D陣列的RGB像素
通過使用ASCII表,可以將消息轉(zhuǎn)換為十進制值,然后轉(zhuǎn)換為二進制值,接著逐個迭代像素值,在將像素值轉(zhuǎn)換為二進制后,將每個最低有效位替換為序列中的該消息位。
要解碼一個已編碼的圖像,只需顛倒這個過程:收集并存儲每個像素的最后一位,將它們分成8個一組,并將其轉(zhuǎn)換回ASCII字符,以得到隱藏的信息。
PYTHON操作
試著使用Python庫PIL和NumPY來逐步實現(xiàn)上述概念。
- 步驟1:導(dǎo)入所有必需的python庫
 
- import numpy as np
 - from PIL import Image
 
- 步驟2:啟用編碼器功能
 
首先,編寫代碼,將源圖像轉(zhuǎn)換成一個NumPy像素陣列,并存儲圖像的大小。檢查圖像的模式是RGB還是RGBA,然后設(shè)置n的值。還需計算像素的總數(shù)。
- def Encode(src, message, dest):
 - img = Image.open(src, 'r')
 - width, height = img.size array = np.array(list(img.getdata()))
 - if img.mode == 'RGB':
 - n = 3
 - m = 0
 - elif img.mode == 'RGBA':
 - n = 4
 - m = 1
 - total_pixels = array.size//n
 
其次,在秘密消息的末尾添加一個分隔符(“$T3G0”),這樣程序在解碼時就知道什么時候該停止,將這個更新后的消息轉(zhuǎn)換成二進制形式,并計算出所需的像素。
- message += "$t3g0"
 - b_message = ''.join([format(ord(i), "08b") for i in message])
 - req_pixels = len(b_message)
 
接著,檢查可用的總像素是否足夠用于秘密消息。如果繼續(xù)逐個迭代像素,并將它們的最低有效位修改為秘密消息的位,直到包括分隔符的完整消息已經(jīng)被隱藏。
- if req_pixels > total_pixels:
 - print("ERROR: Need larger filesize")else: index=0 for p in range(total_pixels):
 - for q in range(m, n):
 - if index < req_pixels:
 - array[p][q] =int(bin(array[p][q])[2:9] + b_message[index], 2)
 - index += 1
 
最后,有了更新后的像素數(shù)組,可以使用它來創(chuàng)建并保存為目標輸出圖像。
- array=array.reshape(height, width, n)
 - enc_img = Image.fromarray(array.astype('uint8'), img.mode)
 - enc_img.save(dest)print("Image Encoded Successfully")
 
這樣,編碼器功能就完成了,是這樣的:
- def Encode(src, message, dest):
 - img = Image.open(src, 'r') width, height = img.size array = np.array(list(img.getdata())) if img.mode == 'RGB': n = 3 m = 0 elif img.mode == 'RGBA': n = 4 m = 1 total_pixels = array.size//n
 - message += "$t3g0"
 - b_message = ''.join([format(ord(i),"08b") for i in message])
 - req_pixels = len(b_message)
 - if req_pixels > total_pixels:
 - print("ERROR: Need largerfile size") else: index=0 for p in range(total_pixels):
 - for q in range(m, n):
 - if index < req_pixels:
 - array[p][q] =int(bin(array[p][q])[2:9] + b_message[index], 2)
 - index += 1
 - array=array.reshape(height,width, n)
 - enc_img =Image.fromarray(array.astype('uint8'), img.mode)
 - enc_img.save(dest) print("Image EncodedSuccessfully")
 
- 步驟3:啟用解碼器功能
 
首先,重復(fù)類似的步驟,將源圖像的像素保存為一個數(shù)組,計算模式,并計算總像素。
- def Decode(src):
 - img = Image.open(src, 'r')
 - array = np.array(list(img.getdata()))
 - if img.mode == 'RGB':
 - n = 3
 - m = 0
 - elif img.mode == 'RGBA':
 - n = 4
 - m = 1
 - total_pixels = array.size//n
 
其次,需要從圖像左上角開始的每個像素中提取最低有效位,并以8個為一組存儲。接下來,將這些組轉(zhuǎn)換成ASCII字符來查找隱藏的消息,直到完全讀取之前插入的分隔符。
- hidden_bits = ""
 - for p in range(total_pixels):
 - for q in range(m, n):
 - hidden_bits +=(bin(array[p][q])[2:][-1])
 - hidden_bits = [hidden_bits[i:i+8] for i in range(0, len(hidden_bits), 8)]
 - message = ""
 - for i in range(len(hidden_bits)):
 - if message[-5:] == "$t3g0":
 - break
 - else:
 - message +=chr(int(hidden_bits[i], 2))
 
最后,檢查是否找到了分隔符。如果沒有,意味著圖像中沒有隱藏的信息。
- if "$t3g0" in message:
 - print("Hidden Message:",message[:-5])
 - else:
 - print("No Hidden MessageFound")
 
這樣,我們的解碼器功能就完成了,看起來應(yīng)該是這樣的:
- def Decode(src):
 - img = Image.open(src, 'r') array = np.array(list(img.getdata())) if img.mode == 'RGB': n = 3 m = 0 elif img.mode == 'RGBA': n = 4 m = 1 total_pixels = array.size//n
 - hidden_bits = ""
 - for p in range(total_pixels):
 - for q in range(m, n):
 - hidden_bits +=(bin(array[p][q])[2:][-1])
 - hidden_bits = [hidden_bits[i:i+8] fori in range(0, len(hidden_bits), 8)]
 - message = ""
 - for i in range(len(hidden_bits)):
 - if message[-5:] =="$t3g0": break else: message +=chr(int(hidden_bits[i], 2)) if "$t3g0" in message: print("Hidden Message:",message[:-5]) else: print("No Hidden MessageFound")
 
- 步驟4:制作主要功能
 
對于主要功能,我們需詢問用戶想要執(zhí)行哪個功能——編碼還是解碼。
若是編碼,則要求用戶輸入以下內(nèi)容——帶擴展名的源圖像名稱、秘密消息和帶擴展名的目標圖像名稱。若是解碼,則要求用戶提供隱藏了消息的源圖像。
- def Stego():
 - print("--Welcome to$t3g0--")
 - print("1: Encode")
 - print("2: Decode")
 - func = input()
 - if func == '1':
 - print("Enter Source ImagePath")
 - src = input()
 - print("Enter Message toHide")
 - message = input()
 - print("Enter Destination ImagePath")
 - dest = input()
 - print("Encoding...")
 - Encode(src, message, dest) elif func == '2':
 - print("Enter Source ImagePath")
 - src = input()
 - print("Decoding...")
 - Decode(src) else:
 - print("ERROR: Invalid optionchosen")
 
- 步驟5:把以上所有功能放在一起,我們的LSB圖像隱寫程序就準備好了。
 
請注意,在一項研究中,觀察到傳統(tǒng)的LSB在JPEG的情況下是無效的,因為數(shù)據(jù)會因為其有損性質(zhì)而在壓縮時被操縱。而對于PNG圖像,簡單的LSB是適用的,在壓縮時不會有任何數(shù)據(jù)損失。因此,最好只在PNG圖像上運行你的程序。
舉例

1.編碼信息

2.解碼信息

















 
 
 







 
 
 
 