LSB隱寫(最低有效位隱寫)

LSB隱寫(最低有效位隱寫)

我們先思考如下幾個問題,然後再去實現
1、圖片在計算機中存儲的方式
2、什麼原因可以是實現隱寫
3、爲什麼選擇最低有效位?
4、具體實現思路
5、如果用代碼實現LSB隱寫

1、圖片在計算機中存儲的方式
        如果將一幅圖像放大,我們可以看到它是由一個個的小格子組成的,每個小格子就是一個色塊。如果我們用不同的數字來表示不同的顏色,圖像就可以表示爲一個由數字組成的矩陣(matrix),這樣就可以在計算機中存儲。這個小格子就是像素(pixel),矩陣的行數與列數,就是分辨率(resolution)。
        我們常說某張圖像的分辨率是1280*720,指的就是這張圖像是由1280行,720列的像素組成。反過來,如果我們有一個矩陣,將矩陣中的每個數值都轉換爲顏色,並在計算機中顯示出來,就可以復現這張圖像
在這裏插入圖片描述
        我們可以理解爲圖像在計算機中是以點陣的形式存在的,我們可以理解爲一個二位數組或者矩陣,每個點所在的行和列是它的座標,元素的值可以理解爲當前像素的值。
2、什麼原因可以實現隱寫
        這個原因有很多,但是最重要的還是因爲人類的視覺冗餘,其實是對相近的像素的敏感度比較低。所以改變部分像素的值不是很明顯的話,肉眼基本察覺到不到。
3、爲什麼選擇最低有效位?
如果我們使用RGB方式即0—255無符號數字對某個像素點的值進行描述的時候,我們怎麼修改這個數字才能實現我們寫入數據的功能而且對原圖的修改程度最小?我們都知道計算機是基於二進制的,也就是說我們在計算機中能見到的一切數據都是以二進制的形式存在的。例如:
在這裏插入圖片描述
比如說十進制數據253對應的二進制數據是1111 1101‬,即使是字符串也是可以轉換爲二進制類型的數據,我們只需要把要隱藏的數據先轉換爲二進制數據然後再將其按照某種規則差分然後按位寫入圖像的部分像素的二進制數據的最後一位即可。
如果圖片中某點的像素值是253的話,其對應的二進制數據是1111 1101,如果我們把該數據的最後一位替換爲0的話,其對應的十進制數據就是252。
在這裏插入圖片描述
因爲人眼的視覺冗餘,對圖片中某點的像素髮生上述改變時幾乎是察覺不到的。因爲圖像隱寫的目的是不讓別人發現寫入了數據,先不說寫入的問題,如果說圖像本身的變化人眼就能識別的話,何談“隱”字?這種方式對圖像的改變比較小,所以採用該方式。
4、具體實現思路
(1)獲取要隱藏的數據,一般這裏不管是什麼,我們都可以理解爲字符串,本文不涉及圖像寫入圖像。
(2)將獲取到的字符串二值化,即按照一定規則轉換爲二進制數據,一般是8位(不涉及中文隱寫,中文佔2個,因爲我實驗的目的雖然是實現隱寫,我還想提出來呢,並不是寫進去不提出來,所以我得有規則),不夠的前面補0,一定要測試好對應的解碼方法。
(3)準備好宿主圖像,安裝好python環境和PIL,我們不使用opencv實現,因爲opencv可能存在着壓縮,我沒有實現,最後使用PIL實現的。
(4)獲取圖像信息(主要是高度和寬度),這裏作爲入門,我們不使用彩色圖像,以黑白圖像爲例,根據二值化後的字符串的長度,對宿主圖像的像素進行遍歷,然後將數據依次寫入對應像素的最低有效位,寫入完成之後跳出循環,對目標圖像進行持久化即可得到載密圖像。
5、實現

	from PIL import  Image as im
	import re
	
	replace_reg = re.compile(r'[1|0]$')
	
	#替換最後一位的數據,source是被替換數據,target是目標數據,就是batarget放到source最後一位
	def repLstBit(source,target):
	    return replace_reg.sub(target,source)
	#運行結果:'123X'
	print(repLstBit("111110","1"))
	
	#字符串轉換二進制,不夠八位的話補齊8位
	def encode(s):
	    return ''.join(bin(ord(c)).replace('0b','').rjust(8,'0') for c in s)
	
	#切割從圖像中收集到的數據,就是把載密圖像的對應最後一位提取出來之後需要進行切割
	def cut_text(text,lenth):
	    textArr = re.findall('.{'+str(lenth)+'}',text)
	    tempStr = text[(len(textArr) * lenth):]
	    if len(tempStr)!=0:
	        textArr.append(text[(len(textArr)*lenth):])
	    return textArr
	
	#二進制轉換成字符串,看上面切割方法的註釋即可理解該方法存在的意義
	def decode(s):
	    bitArr = cut_text(s,8)
	    return "".join(chr(int(i,2)) for i in bitArr)
	
	
	#讀取宿主圖像和要寫入的信息生成載密圖像。
	if __name__ == '__main__':
	    img = im.open("D:/StegAnograpy/dove.png")
	    width = img.size[0]
	    height = img.size[1]
	    hideInfo = "Hello ImageSteg"
	    hideBitArr = encode(hideInfo)
	    count = 0
	    bitInfoLen = len(hideBitArr)
	
	    print(hideBitArr)
	    for i in range(width):
	        for j in range(height):
	            if count == bitInfoLen:
	                break;
	            pixel = img.getpixel((i,j));
	            print(pixel[0])
	            sourceBit = bin(pixel[0])[2:]
	            print(sourceBit)
	            rspBit = int(repLstBit(sourceBit,hideBitArr[count]),2)
	            count += 1
	            img.putpixel((i,j),(rspBit,rspBit,rspBit))
	    img.save("D:/StegAnograpy/dove1.png")


6、StegSolve檢測結果:
在這裏插入圖片描述
7、總結
(1)這種方式比較容易實現,但是太容易被檢測到。
(2)但是本次實驗的收穫是直到了從0到1的細節處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章