Python實(shí)現(xiàn)在圖像中隱藏二維碼的方法詳解
一、前言
在某個(gè)App中有一個(gè)加密水印的功能,當(dāng)帖子的主人開(kāi)啟了之后。如果有人截圖,那么這張截圖中就是添加截圖用戶、帖子ID、截圖時(shí)間等信息,而且我們無(wú)法用肉眼看出這些水印。
這可以通過(guò)今天要介紹的隱寫技術(shù)來(lái)實(shí)現(xiàn),我們會(huì)通過(guò)這種技術(shù),借助Python語(yǔ)言和OpenCV模塊來(lái)實(shí)現(xiàn)在圖像中隱藏二維碼的操作。而且這個(gè)二維碼無(wú)法通過(guò)肉眼看出。
二、隱寫
隱寫是一種類似于加密卻又不同于加密的技術(shù)。通常情況下,加密是對(duì)數(shù)據(jù)本身進(jìn)行一個(gè)轉(zhuǎn)換,得到的結(jié)果是一堆人無(wú)法解讀的數(shù)據(jù),比如“你好”進(jìn)行md5加密后的結(jié)果是“7eca689f0d3389d9dea66ae112e5cfd7”,如果光看“7eca689f0d3389d9dea66ae112e5cfd7”我們不知道內(nèi)容,但是我們知道這應(yīng)該是加密后的數(shù)據(jù)。隱寫的目的同樣是讓只有接收方才能獲取數(shù)據(jù),但是隱寫通常更加隱蔽,隱寫更注重于不讓第三方知道我發(fā)送的數(shù)據(jù)中有額外信息。
就像我們?cè)陔娪爸薪?jīng)??吹降囊恍﹦∏?,一場(chǎng)看似普通的對(duì)話卻隱含了許多外人不知道的信息,這實(shí)際上就是一種隱寫。再比如“This is a pig”,看上去像一個(gè)普通的句子,如果通信雙方規(guī)定“T、i、s”這些占三線格上兩個(gè)的字母表示0,而“p、g”這種占三線格下兩格的字母表示1,那么這句話就可以翻譯成“0000000101”。而今天我們要介紹的是“最低有效位”隱寫。
三、位平面分解
在介紹“最低有效位”隱寫之前,需要了解一些圖像相關(guān)的知識(shí)。這里包括數(shù)字圖像、位平面、位平面分解。
3.1 圖像
在計(jì)算機(jī)中,圖像被表示為一個(gè)數(shù)字矩陣,每個(gè)數(shù)字被稱為一個(gè)像素,它們的取值在[0, 255]區(qū)間,可以用8個(gè)二進(jìn)制來(lái)表示。
這個(gè)矩陣大小由圖像分辨率決定,如果是480×480分辨率的圖像,那么這個(gè)矩陣大小就是480×480。如果是彩色圖像,會(huì)用三個(gè)大小相同的矩陣合起來(lái)表示,它們分別表示圖像R(紅色)、G(綠色)、B(藍(lán)色)的程度,也就是俗稱的RGB圖像。
我們可以用OpenCV來(lái)讀取圖像,OpenCV的安裝如下:
pip install opencv-python
安裝完成后就可以讀取圖像:
# 導(dǎo)入模塊
import cv2
# 讀取圖像
img = cv2.imread('test.jpg')
# 輸出圖像
print(img)
其中test.jpg就是我們的圖像名稱或者圖像路徑。上面代碼輸出結(jié)果如下:
[[[ 72 220 234]
[ 72 220 234]
[ 73 221 235]
...
[ 87 147 176]
[ 87 147 176]
[ 87 147 176]]]
因?yàn)檩敵鲞^(guò)長(zhǎng),這里省略了一部分內(nèi)容。
3.2 位平面
在前面我們說(shuō)了一個(gè)圖像是一個(gè)數(shù)字矩陣,比如:
[[2, 2]
[3, 4]]
我們可以理解為一張簡(jiǎn)單的圖像,現(xiàn)在我們把圖像的像素值寫成二進(jìn)制形式:
[[0000 0010, 0000 0010],
[0000 0011, 0000 0100]]
我們把四個(gè)像素的最高位取出,得到新的圖像:
[[0, 0]
[0, 0]]
這個(gè)過(guò)程的圖示如下:

這里取出來(lái)的圖像就叫位平面,因?yàn)槭侨〕龅?位(從左到右依次是7-0)組成的圖像,所以叫第7位平面,也叫最高位平面。而第0位平面也叫“最低有效位”位平面。
如果取出第1位,得到的圖像為:
[[1, 1],
[1, 0]]
這個(gè)圖像叫第1位平面。這里需要注意一點(diǎn),就是每個(gè)位平面的實(shí)際值應(yīng)該乘一個(gè)權(quán)重,這個(gè)權(quán)重位i2,即第7位平面的權(quán)重位72。
3.3 位平面分解
下面我們看看如何分解位平面,分解位平面可以用cv2.bitwise_and函數(shù)來(lái)實(shí)現(xiàn)。我們需要傳入一個(gè)圖像以及一個(gè)分解因子,各個(gè)位平面的分解因子如下:
| 分解因子 | 作用 |
|---|---|
| 0x80 | 分解第7位平面 |
| 0x40 | 分解第6位平面 |
| 0x20 | 分解第5位平面 |
| 0x10 | 分解第4位平面 |
| 0x08 | 分解第3位平面 |
| 0x04 | 分解第2位平面 |
| 0x02 | 分解第1位平面 |
| 0x11 | 分解第0位平面 |
比如分解第7位平面的操作為:
import cv2
# 讀取圖像
img = cv2.imread('test.jpg', 0)
# 分解第7位平面
layer = cv2.bitwise_and(img, 0x80)
其它位平面的分解只需要對(duì)照表進(jìn)行修改即可。
3.4 位平面合成
假如我們以及分解出來(lái)8個(gè)位平面,分別是M0、M1、…、M7。我們只需要將各個(gè)位平面乘上對(duì)應(yīng)的權(quán)重,然后相加就能恢復(fù)原圖,即:

如果我們只對(duì)M1-M7進(jìn)行合成,得到的A`與A的差距最多為1,因此我們可以讓A`≈A。此時(shí)圖像A`的第0個(gè)位平面可以用于隱藏?cái)?shù)據(jù)。
四、圖像隱寫
這里我們使用一種叫“最低有效位”位平面隱寫的技術(shù)來(lái)實(shí)現(xiàn)二維碼的隱藏。其原理就是把圖像“最低有效位”位平面設(shè)置為0,此時(shí)圖像與原圖像像素相差最大為0,人肉眼無(wú)法看出區(qū)別。然后我們可以在圖像的最低有效位任意設(shè)置值,此時(shí)圖像與原圖像素相差最大仍是1。這樣我們就可以用“最低有效位”位平面來(lái)隱寫數(shù)據(jù)。
在前面我們合成原圖時(shí)用M1-M7,而M0位平面則全為0,這時(shí)我們可以用最低有效位存儲(chǔ)數(shù)據(jù)。假如我們的數(shù)據(jù)矩陣為M,該矩陣為一個(gè)0-1矩陣。而二維碼就是一個(gè)黑白矩陣,我們可以把黑當(dāng)作0,白當(dāng)作1,這樣我們讓M為一個(gè)二維碼的矩陣?,F(xiàn)在我們通過(guò)下面的公式來(lái)合成:

這個(gè)A就是帶有隱寫信息的圖像。代碼實(shí)現(xiàn)如下:
import cv2
# ①讀取圖像
img = cv2.imread('test.jpg', 0)
# ②把最低有效位清空
img -= cv2.bitwise_and(img, 0x01)
# ③準(zhǔn)備需要隱寫的信息M
M = cv2.imread('qrcode.jpg', 0)
M = cv2.resize(M, img.shape)
# 把二維碼轉(zhuǎn)換成0-1矩陣
_, M = cv2.threshold(M, 30, 1, cv2.THRESH_BINARY)
# ④將要隱寫的數(shù)據(jù)設(shè)置到圖像最低有效位
img += M
# ⑥以無(wú)損的方式保存隱寫后的
cv2.imwrite('dst.png', img, [int(cv2.IMWRITE_JPEG_QUALITY), 100])
最后保存的dst.png就是我們隱寫后的圖像。
二維碼的生成可以參考詳解Python如何生成優(yōu)雅的二維碼
到此這篇關(guān)于Python實(shí)現(xiàn)在圖像中隱藏二維碼的方法詳解的文章就介紹到這了,更多相關(guān)Python圖像隱藏二維碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Python中回文數(shù)和質(zhì)數(shù)的問(wèn)題
今天小編就為大家分享一篇解決Python中回文數(shù)和質(zhì)數(shù)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
Python中*args和**kwargs的區(qū)別詳解
這篇文章主要介紹了Python中*args和**kwargs的區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
Python項(xiàng)目打包成apk或者其他端的應(yīng)用程序
本文主要介紹了使用Kivy和Buildozer將Python項(xiàng)目打包成Android APK文件的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-11-11
Python實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)與算法之基本搜索詳解
這篇文章主要介紹了Python實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)與算法之基本搜索,詳細(xì)分析了Python順序搜索、二分搜索的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04
pytorch實(shí)現(xiàn)seq2seq時(shí)對(duì)loss進(jìn)行mask的方式
今天小編就為大家分享一篇pytorch實(shí)現(xiàn)seq2seq時(shí)對(duì)loss進(jìn)行mask的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02
Python數(shù)據(jù)分析之?Matplotlib?散點(diǎn)圖繪制
這篇文章主要介紹了Python數(shù)據(jù)分析之?Matplotlib?散點(diǎn)圖繪制,散點(diǎn)圖又稱散點(diǎn)圖,是使用多個(gè)坐標(biāo)點(diǎn)的分布反映數(shù)據(jù)點(diǎn)分布規(guī)律、數(shù)據(jù)關(guān)聯(lián)關(guān)系的圖表,下文對(duì)散點(diǎn)圖的詳細(xì)介紹及繪制,需要的小伙伴可以參考以一下2022-05-05
Python 經(jīng)典貪心算法之Prim算法案例詳解
這篇文章主要介紹了Python 經(jīng)典貪心算法之Prim算法案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
Python學(xué)習(xí)之a(chǎn)syncore模塊用法實(shí)例教程
這篇文章主要介紹了Python學(xué)習(xí)之a(chǎn)syncore模塊用法,主要講述了asyncore模塊的組成、原理及相關(guān)函數(shù)的用法,對(duì)于使用Python進(jìn)行網(wǎng)絡(luò)編程來(lái)說(shuō)非常實(shí)用,需要的朋友可以參考下2014-09-09

