一、數字水印定義
數字水印是將一些標識信息直接嵌入數字載體當中(包括多媒體、文檔、軟件等),通過這些隱藏在載體中的信息,既不影響載體的使用價值,也不易檢測或修改。可以達到確認內容創建者、購買者、傳送隱祕信息或者判斷載體是否被篡改等目的。
數字水印是指在載體中嵌入一些信息, 載體數據可以是文檔、圖片、音頻或者視頻, 嵌入的水印可以是與載體數據有關的, 如作者身份、時間戳、產品屬性等, 水印的存在形式可以是文字、圖形、數列等, 當遇到疑似侵權等問題時可以通過算法把水印信息提取出來, 從而證明數字產品是否被篡改或者僞造。數字水印具有以下特點:
嵌入成功率。又稱嵌入有效性, 主要指的是當有需要將水印信息提取出來時, 水印被檢測到的機率, 一般來說這種機率小於100%。
保真度。也稱相似度, 是指將水印嵌入原始載體作品後, 載體作品在人的視覺效果中沒有發生變化。
魯棒性。又稱抗攻擊性, 載體數據在網絡傳輸過程中經常會受到一些常規的信息處理, 如濾波噪聲、圖像壓縮、幾何失真或者惡意攻擊等, 之後要仍然能在載體中檢測到水印的存在。
安全性。當載體受到非法攻擊時, 水印沒有發生變化或者變化很小, 說明水印的安全性較高。
二、數字水印應用
數字水印技術主要的應用領域是版權保護, 因爲水印和載體是密不可分的, 因此, 它更適用於檢測鑑別。一般用於版權保護的水印都是由有意義的信息組成, 如所有者的信息、產品的屬性等, 這樣可以防止版權被侵犯。
數字水印技術通過在數字作品中的水印嵌入證明作品的版權歸屬信息,從而防止在未經原創作者許可的情況下,其他人擅自使用網絡作品的侵權問題出現,其主要的方式如下:第一,數字媒體將水印信息添加到作品中;第二,將網絡作品通過數字媒體發行;第三,通過檢測網絡作品數字媒體的數字水印信息來識別媒體版權信息的真假。
三、圖像數字水印
在這裏我通過使用PIL這個庫文件中的圖像處理模塊,PIL中,使用Image模塊的open()函數打開後,返回的圖像對象的模式都是“RGB”。而對於灰度圖像,不管其圖像格式是PNG,還是BMP,或者JPG,打開後,其模式爲“L”。
我們利用該圖像處理模塊設計了數字水印信息的嵌入和提取,我們用過將水印信息嵌入到圖像中的像素點中的方式來嵌入水印,具體的圖像數字水印嵌入代碼如下:
嵌入:
def watermark_embed(filePath, watermarkInfo, outPath):
img = Image.open(filePath)
imgSize = img.size
pixel = img.load() # 獲取圖片所有的像素點
len1 = floor((imgSize[0] * imgSize[1]) / (2 * (imgSize[0] + imgSize[1]))) # 數值的下舍整數
flag = 0
if imgSize[0] > imgSize[1]:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[0] - imgSize[1])))
flag = 1
elif imgSize[0] < imgSize[1]:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[1] - imgSize[0])))
flag = 2
else:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[0] + imgSize[1])))
if flag == 0 or flag == 1:
row = len1
col = len2
elif flag == 2:
row = len2
col = len1
new_array1 = []
new_array2 = []
x2 = ""
for i in range(0, len(watermarkInfo)):
new_array1.append(255 - ord(watermarkInfo[i])) # ord()函數以字符(長度爲1的字符串)作爲參數,返回對應的ASCII或Unicode
for i in range(0, len(x2)):
new_array2.append(255 - ord(x2[i]))
new_pixel1 = pixel[row, col]
new_pixel2 = pixel[(new_pixel1[0] + new_pixel1[1]), (new_pixel1[2] + new_pixel1[0])]
pix2 = int(new_pixel1[2])
pix1 = int(new_pixel1[1])
pix0 = int(new_pixel1[0])
col = imgSize[1]
row = imgSize[0]
for i in range(0, len(watermarkInfo)):
watermark_pix = new_array1.pop(0)
pixel[(pix0 + pix1), (pix2 + pix1)] = (new_pixel2[0], watermark_pix, new_pixel2[2])
pix2 = col - (new_pixel2[2] / 16) //分母"16"數值越大,則可嵌入的水印信息越多
col = pix2
pix1 = i
pix0 = row - (new_pixel2[0] / 16)
row = pix0
if (new_pixel1[2] + i) < 0:
print('Encryption intercepted')
break
if (new_pixel1[1] + i) < 0:
print('Encryption intercepted')
break
new_pixel2 = pixel[(new_pixel1[0] + i + 1), (new_pixel1[2] + 1 + i)]
pixel[imgSize[0] - 3, imgSize[1] - 3] = (0, len(watermarkInfo), len(x2))
for i in range(0, len(x2)):
watermark_pix = new_array2.pop(0)
pixel[(new_pixel1[0] + new_pixel1[1]), (new_pixel1[2] + new_pixel1[1])] = (
new_pixel2[0], watermark_pix, new_pixel2[2])
pix2 = (col) - new_pixel2[2]
col = pix2
pix1 = i
pix0 = (row) - new_pix1[0]
row = pix0
if (new_pixel1[2] + i) < 0:
print('Encryption intercepted')
if (new_pixel1[1] + i) < 0:
print('Encryption intercepted')
new_pix1 = pixel[(new_pixel1[0] + i), new_pixel1[2] + i]
img.save(outPath)
嵌入的數字水印圖像通過img.save存儲到新的圖像中。當需要提取圖像中的數字水印時,可通過提取函數來提取。如下
提取:
def watermark_extract(filePath):
img = Image.open(filePath)
imgSize = img.size
pix = img.load()
len1 = floor((imgSize[0] * imgSize[1]) / (2 * (imgSize[0] + imgSize[1])))
flag = 0
if imgSize[0] > imgSize[1]:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[0] - imgSize[1])))
flag = 1
elif imgSize[0] < imgSize[1]:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[1] - imgSize[0])))
flag = 2
else:
len2 = floor((imgSize[0] * imgSize[1]) / (len1 * (imgSize[0] + imgSize[1])))
if flag == 0 or flag == 1:
row = len1
col = len2
elif flag == 2:
row = len2
col = len1
new_pix1 = pix[row, col]
new_pix2 = pix[(new_pix1[0] + new_pix1[1]), (new_pix1[2] + new_pix1[0])]
pix2 = int(new_pix1[2])
pix1 = int(new_pix1[1])
pix0 = int(new_pix1[0])
col = imgSize[1]
row = imgSize[0]
var = pix[imgSize[0] - 3, imgSize[1] - 3]
watermarkInfo_1 = ""
watermarkInfo_2 = ""
for i in range(0, int(var[1])):
new_var = pix[(pix0 + pix1), (pix2 + pix1)]
watermarkInfo_1 += (chr(255 - (new_var[1]))) # chr() ASCII -> 對應的字符
pix2 = col - (new_pix2[2] / 16)
col = pix2
pix1 = i
pix0 = row - (new_pix2[0] / 16)
row = pix0
new_pix2 = pix[(new_pix1[0] + i + 1), (new_pix1[2] + 1 + i)]
for i in range(0, var[2]):
new_var = pix[(pix0 + pix1), (pix2 + pix1)]
watermarkInfo_2 += chr(255 - (new_var[2]))
pix2 = col - new_pix2[2]
col = pix2
pix1 = i
pix0 = row - new_pix2[0]
row = pix0
new_pix2 = pix[(new_pix1[0] + i + 1), (new_pix1[2] + 1 + i)]
if var[2] != 0:
watermarkInfo = watermarkInfo_2
print(watermarkInfo)
else:
watermarkInfo = watermarkInfo_1
print(watermarkInfo)
測試效果:
嵌入水印
python embed.py input.png python-digital-watermark out.png
提取水印
python extract.py out.png
result: python-digital-watermark
四、PDF數字水印
除了圖像以外,當前PDF資源也越來越多,對與PDF資源的保護的需要也迫在眉睫,接下來我們呈現了一種PDF的數字水印的嵌入和提取,這裏我們用到了一些tricks。
首先,我們需要安裝PyPDF2這個PDF文檔處理的庫文件以及reportlab庫文件中的canvas工具,我們通過canvas工具設置了一個畫布,然後將水印信息隱藏在畫布中;通過page.mergePage將含有水印信息的pdf頁融合到需要嵌入水印的pdf文檔中。具體的通過canvas工具創造數字水印新的函數如下:
def create_watermark(file_name, content):
c = canvas.Canvas(file_name, pagesize=(30 * cm, 30 * cm))
# 移動座標原點(座標系左下爲(0,0))
c.translate(50, 600)
# 設置字體
c.setFont("Helvetica", 5)
# 指定填充顏色
c.setFillColorRGB(255, 255, 255)
# 設置透明度,1爲不透明
c.setFillAlpha(0)
# 畫幾個文本,注意座標系旋轉的影響
c.drawString(2 * cm, 0 * cm, content)
# 關閉並保存pdf文件
c.save()
return file_name
接着,我們將嵌入的水印融合到需要嵌入水印的文檔中:
嵌入:
def embed_watermark(pdf_file_in, tmp_file, watermarkInfo, pdf_file_out):
pdf_file_mark = create_watermark(tmp_file, watermarkInfo)
# 輸入文件
pdf_input = PyPDF2.PdfFileReader(open(pdf_file_in, 'rb'))
# 讀入水印pdf文件
pdf_watermark = PyPDF2.PdfFileReader(open(pdf_file_mark, 'rb'))
# 輸出文件
pdf_output = PyPDF2.PdfFileWriter()
# 獲取輸入pdf文件的頁數
pageNum = pdf_input.getNumPages()
for i in range(pageNum):
page = pdf_input.getPage(i)
# 將水印信息嵌入在最後一頁上
if i == pageNum - 1:
page.mergePage(pdf_watermark.getPage(0))
page.compressContentStreams() # 壓縮內容
pdf_output.addPage(page)
pdf_output.write(open(pdf_file_out, 'wb'))
嵌入的數字水印圖像通過img.save存儲到新的圖像中。當需要提取圖像中的數字水印時,可通過提取函數來提取。如下
提取:
def extract_watermark(file_watermarked):
# 輸入文件
pdf_input = PyPDF2.PdfFileReader(open(file_watermarked, 'rb'))
pageNum = pdf_input.getNumPages()
extractedText = pdf_input.getPage(pageNum - 1).extractText()
watermarkInfo = extractedText.split()[-1]
print(watermarkInfo)
總結
本文中,我們介紹了將數字水印信息嵌入到圖像和pdf文檔中,基本可以滿足需求,但是仍然未做到真正的數字水印的隱藏效果。在圖像水印中,我們嵌入的是明文信息,若是被破解,攻擊者很容易得到嵌入的水印信息並進行篡改;在pdf水印中,我們是利用了一些tricks來模擬達到了數字水印的效果;所以上述的兩種方法有可取之處,但仍有弊端。
在接下來的文章中,我們將會介紹基於Arnold置換的圖像數字水印嵌入和驗證方法,該方法做到了更高的安全性,更完美的數字水印。