Python實踐項目——LSB隱寫術
- 2022 年 9 月 9 日
- 筆記
此為北京理工大學某專業某學期某課程的某次作業
一、項目背景
1、隱寫術
隱寫術是一門關於資訊隱藏的技巧與科學,所謂資訊隱藏指的是不讓除預期的接收者之外的任何人知曉資訊的傳遞事件或者資訊的內容。
2.LSB 隱寫術
LSB 隱寫術是一種影像隱寫術技術,其中通過將每個像素的最低有效位替換為要隱藏的消息位來將消息隱藏在影像中。
3.實現原理
為了更好地理解,讓我們將數字影像視為像素的二維陣列,每個像素包含取決於其類型和深度的值,使用最廣泛的顏色模式RGB,這些值的範圍為0–255之間。

可以使用 ASCII Table 將消息轉換為十進位值,然後再轉換為二進位。然後,我們逐個迭代像素值,將它們轉換為二進位後,我們將每個最低有效位替換為序列中的消息位。
要解碼編碼影像,我們只需反轉該過程,收集並存儲每個像素的最後一位,然後將它們分成 8 組,並將其轉換回 ASCII 字元以獲取隱藏消息。
二、項目目標
1.主要目標
編寫LSB影像隱寫程式,包括:加密程式和解密程式。
2.目標分解
a)實現文本資訊加密到影像
b)實現影像文件解密到文本
三、技術選型
1.問題:如何以二進位方式讀寫影像文件?
首先安裝pillow庫,win+R輸入cmd快速打開控制台,直接輸入以下程式碼即可自動安裝
pip install pillow
然後讀取圖片
from PIL import Image #從pillow庫(即PIL)中導入Image類
img = Image.open('../xx.jpg') #讀取圖片存入變數img中
print(img.format) #輸出圖片格式(str)
print(img.size) #輸出圖片大小資訊 (寬度w,高度h)tuple = (int,int)
獲取像素資訊
#像素載入
pix = img.load()
width = img.size[0] #.size 方法返回的是一個元組 tuple =(int,int)
height = img.size[1]
#獲取像素點的RGB值
rgb_list = [] #創建一個數組存儲RGB值
for y in range(height):#遍歷每一個像素點,將影像看作是一個二維數組,
for x in range(width): #如果x循環在外層輸出的影像會發生一個九十度的翻轉
r,g,b =pix[x,y] #此處的r,g,b是像素點pix[x,y]的RGB值
rgb_list.append(r)
rgb_list.append(g)
rgb_list.append(b)
輸出影像
#輸出影像
j = 0
pixels = [] #以[(r1,g1,b1),(r2,g2,b2)]形式存放每個像素點的RGB值,於繪製影像
img_out = Image.new(img.mode,img.size) #生成新影像,以原圖的格式和大小
#img_out此時還是一張白紙,下面的程式碼旨在更新img_out的像素資訊
while j<len(rgb_list): #循環次數高達786432次
pixels.append((rgb_list[j],rgb_list[j+1],rgb_list[j+2])) #以元組的形式
j += 3
img_out.putdata(pixels)#放置像素資訊
img_out.save("img_out2.png")#將影像保存為,程式運行後會出現在根目錄
2.問題:資訊如何轉換
二進位轉文本(解密)
def bina_to_txt(bina):
#只要傳入一個二進位數組成的序列即可翻譯成文本
tex = []
for i in bina:
tex.append(chr(int(i,2)))
return tex #返回一個單字元序列
#要求bina的格式為['01010101','11111111']
文本轉二進位(加密)
def txt_to_bina(txt):
c=[]
for a in txt:
c.append("{:0>8}".format(bin(ord(a)).lstrip('0b')))
#格式化將二進位碼保存起來
#注意要在右側補齊八位,否則資訊會錯位
resultlist = []
for i in c:
for j in range(8):
resultlist.append(i[j])
return resultlist
#txt 為字元串類型,如 「hello world!」
#print(txt_to_bina("h")) 輸出測試
#test_output:['0', '1', '1', '0', '1', '0', '0', '0']
替換資訊位(加密)
#替換資訊位的資訊
i = 0
while i < len(txt_to_bina(txt)):
temp =list(bin(rgb_list[i])) #用 bin()強制轉換,bin()返回一個字元串類型
temp[-1]=txt_to_bina(txt)[i] #將二進位型的RGB資訊的最後一位轉換成文本二進位碼
rgb_list[i] = int(''.join(temp),2)
i += 1
#txt_to_bina()是自定義的一個函數,旨在將文本轉化成二進位碼,返回一個單字元的序列
#這裡是直接用第一個像素的RGB值作為隱寫的開頭,所以rgb_list和txt_to_bina()[]的index是一樣的
#此處可以做一個加密
#特別注意在python中字元串不能直接修改,replace方法不會改變原來的string
#修改字元串要將字元串轉換成一個序列,修改序列後在將序列轉成字元串,實現程式碼如下
s = 'abcde'
temp = list(s)
temp[-1] = 'f' #假設要將s的最後一位「e」修改為「f」
s = ''.join(temp)
提取資訊位資訊
#這裡直接用的是「hello world!」的長度,後期優化可以加個旗幟識別
c = ''
for i in range(96):
c += bin(rgb_list[i])[-1] #影像處理後得到rgb_list,取二進位碼的最後一位
out_list_bin =[]
for i in range(12):
out_list_bin.append(c[i*8:(i+1)*8])#每八位為一組轉換出文本
print(''.join(bina_to_txt(out_list_bin)))
