From 6ef1ad77dc74d6ece37744b6454a11adc1296c5f Mon Sep 17 00:00:00 2001 From: darksouls4 <1627517214@qq.com> Date: Fri, 2 Aug 2019 23:38:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=8A=B5=E6=8A=97=E5=BD=A2?= =?UTF-8?q?=E5=8F=98=E6=94=BB=E5=87=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BlindWatermark/BlindWatermark.py | 2 + BlindWatermark/tools.py | 44 ++++++ GUI/BlindWatermark/BlindWatermark.py | 2 + GUI/BlindWatermark/__init__.py | 3 +- .../__pycache__/BlindWatermark.cpython-36.pyc | Bin 9001 -> 9063 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 279 -> 317 bytes .../__pycache__/tools.cpython-36.pyc | Bin 0 -> 1975 bytes GUI/BlindWatermark/tools.py | 57 ++++++++ GUI/Ui_main_win.py | 58 +++++++- GUI/key.json | 1 - GUI/main_win.py | 128 +++++++++++++++--- GUI/main_win.ui | 112 ++++++++++++++- readme.md | 10 +- 13 files changed, 387 insertions(+), 30 deletions(-) create mode 100644 BlindWatermark/tools.py create mode 100644 GUI/BlindWatermark/__pycache__/tools.cpython-36.pyc create mode 100644 GUI/BlindWatermark/tools.py delete mode 100644 GUI/key.json diff --git a/BlindWatermark/BlindWatermark.py b/BlindWatermark/BlindWatermark.py index 9e40cdf..1ee9360 100644 --- a/BlindWatermark/BlindWatermark.py +++ b/BlindWatermark/BlindWatermark.py @@ -313,6 +313,8 @@ def extract(self,filename,out_wm_name): cv2.imwrite(out_wm_name,extract_wm.reshape(64,64)) path,file_name = os.path.split(out_wm_name) + if not os.path.isdir(os.path.join(path,'Y_U_V')): + os.mkdir(os.path.join(path,'Y_U_V')) cv2.imwrite(os.path.join(path,'Y_U_V','Y'+file_name),extract_wm_Y.reshape(64,64)) cv2.imwrite(os.path.join(path,'Y_U_V','U'+file_name),extract_wm_U.reshape(64,64)) cv2.imwrite(os.path.join(path,'Y_U_V','V'+file_name),extract_wm_V.reshape(64,64)) diff --git a/BlindWatermark/tools.py b/BlindWatermark/tools.py new file mode 100644 index 0000000..a3b7581 --- /dev/null +++ b/BlindWatermark/tools.py @@ -0,0 +1,44 @@ +import cv2 +import numpy as np + + + +def recovery(ori_img,attacked_img,outfile_name = './recoveried.png',rate=0.7): + img = cv2.imread(ori_img) + img2 = cv2.imread(attacked_img) + + height = img.shape[0] + width = img.shape[1] + # Initiate SIFT detector + orb = cv2.ORB_create(128) + MIN_MATCH_COUNT=10 + # find the keypoints and descriptors with SIFT + kp1, des1 = orb.detectAndCompute(img,None) + kp2, des2 = orb.detectAndCompute(img2,None) + + FLANN_INDEX_KDTREE = 0 + index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) + search_params = dict(checks = 50) + + flann = cv2.FlannBasedMatcher(index_params, search_params) + + + + des1 = np.float32(des1) + des2 = np.float32(des2) + + matches = flann.knnMatch(des1,des2,k=2) + + # store all the good matches as per Lowe's ratio test. + good = [] + for m,n in matches: + if m.distance < rate*n.distance: + good.append(m) + + if len(good)>MIN_MATCH_COUNT: + src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2) + dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2) + M, mask = cv2.findHomography( dst_pts,src_pts, cv2.RANSAC,5.0) + out = cv2.warpPerspective(img2, M, (width,height)) #先列后行 + cv2.imwrite(outfile_name,out) + \ No newline at end of file diff --git a/GUI/BlindWatermark/BlindWatermark.py b/GUI/BlindWatermark/BlindWatermark.py index 8f32c46..0a62389 100644 --- a/GUI/BlindWatermark/BlindWatermark.py +++ b/GUI/BlindWatermark/BlindWatermark.py @@ -330,6 +330,8 @@ def extract(self,filename,out_wm_name): cv2.imwrite(out_wm_name,extract_wm.reshape(64,64)) path,file_name = os.path.split(out_wm_name) + if not os.path.isdir(os.path.join(path,'Y_U_V')): + os.mkdir(os.path.join(path,'Y_U_V')) cv2.imwrite(os.path.join(path,'Y_U_V','Y'+file_name),extract_wm_Y.reshape(64,64)) cv2.imwrite(os.path.join(path,'Y_U_V','U'+file_name),extract_wm_U.reshape(64,64)) cv2.imwrite(os.path.join(path,'Y_U_V','V'+file_name),extract_wm_V.reshape(64,64)) diff --git a/GUI/BlindWatermark/__init__.py b/GUI/BlindWatermark/__init__.py index d3af6ff..055fca1 100644 --- a/GUI/BlindWatermark/__init__.py +++ b/GUI/BlindWatermark/__init__.py @@ -1,4 +1,5 @@ from .BlindWatermark import watermark from .ncc import NCC from .ncc import average_ncc -from .psnr import average_psnr \ No newline at end of file +from .psnr import average_psnr +from .tools import recovery \ No newline at end of file diff --git a/GUI/BlindWatermark/__pycache__/BlindWatermark.cpython-36.pyc b/GUI/BlindWatermark/__pycache__/BlindWatermark.cpython-36.pyc index 52e26f8dcb39e24f8ba81cabfd2f8fd56eb9ca70..59d6eb2430f8753ef3872d374ec900888e9b7896 100644 GIT binary patch delta 169 zcmZ4K_S}ujn3tEU^1E}atNli<%i@eio9~JDv6|{->1P?1fJmbn#T4FVre?+oti{`a zBF3nqj0`1ASvnB0$)&Q&j7F1Zg2}zIpBYUiZ6GYY>1P>EUMQ=~XgK*On0zVw znbBzSUAbSZELr)Pd7EF!%QG@An#`p*fAU&IUB=4Ew-xgkD<*p>9c8SVETHVi>C3^$ I!^j1M0EN3DyZ`_I diff --git a/GUI/BlindWatermark/__pycache__/__init__.cpython-36.pyc b/GUI/BlindWatermark/__pycache__/__init__.cpython-36.pyc index f9bdcba194238411c709241ef0124c9cd53c1c83..05669989ed2580427e715589922fc0b6592a4753 100644 GIT binary patch delta 121 zcmbQvw3kWQn3tF9+*0S*Nem1OkAWBtn1Cz?ATD;8sB9w0kiwe7p34!%!N`!p7R;c@ zJ~76EUz70`M^S2WepzZ!rJp9}#OeB?QLH8T`8mZ!Y(SkW8HzZ7R1wF-^L9KS77rr? F0szM57!Uve delta 83 zcmdnXG@VJ=n3tF9*aEZIG6n{Q$3P4ROhA?c5Eq+FR5oE{4Q9|}o0wt2?5D{-alJn0 XN`@kKpkxvI#LsqIY(N$dBM1Tjc|i_9 diff --git a/GUI/BlindWatermark/__pycache__/tools.cpython-36.pyc b/GUI/BlindWatermark/__pycache__/tools.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86b1e421c7c7f7d2a5ba4ec7e86eafd3d0025cf4 GIT binary patch literal 1975 zcmbtVUys~05VxIVlg(xS+@BsTAOr{jE1`v6DG#VRdcA9D1-I?>4i%!Sq7Zv`lg+<# zylt;aB&4mNJX9(lgO`2;K8RnDK=&2;0^{snd*T&G9?y)OnV&u5@ugPFe*VYy{ih~E zf1)!@2mUJb;&UJrQ5++8K4O=EOkyq3T|F^e1Ea4I)u{diQJonx&23P;k9x)l{D}rV zBJKS6NbHBB)Q_dPcQEG6r-7P0Z5l9Fp%)(mVaUZ4xrAvLW@#B}>{-+B*WVD~BqrZoB`|GQ~0Gq0*kwD*uUVI2dpa_>}hA}E(^him7 zYNZBLFLj^>Mc@VxdJV1+O3U-YG!&jE&5Va$n2e<33*iS7M(0vC6~i!QUg{@I>f9Hs zXGpzZ@sL|!fj1Sg69I7<)j{IGX$`S4mCrt}{C%@dzb4n>y5 zML$305G15b6v_m`Hg444;E z)e_jTO1oT|k;)Ox$SzH=Y1W(toK-r}QfZbJZBqN0R<%Z`YCrm@s5-0xQSDWrFVEu3r$=N5e=NiIMp{m&#$z^FFKFV>04+hFFUeI*VH5oAfec~RL`jvDCkyRUMNPt*RD zI1Bu^*aYTu@&7<3xF4o~jH`Y02)_UWe^c=_x{cT9_}idWXS0Ac0v6iZRf$gifyUnj zsdrv#!GjIi2oqI$USCCN6k|VU(!PE7nis&FV6sb@V1d|5={9V63L2e;fso5LVn0o< z`30l9zJM@rNzz=J!#MNBr44CK(zKqC7KJ15(}2~NTsHhXXDO9h%u;D`Hh0+`!WF-n zCE1Ak`S@5i?rshCx3*>Hq0jR>j2Ah82p=$MhRH+71Xh;~xs*;5FB^KJETd9qNho^j z+<`s1nkq*+5ZLFKg`=^M#>0?`v8!eLN4K+kYvAo}9cH<@I0R&+OJH*B|be(i@ zm*}9eiK8oBSJQDDui!RmoSyY+w+o)PN7hH z(($~bsUNF~?Ri`!x75N^NUac@UsAuTs>?SOQPuhkQLwqXb81DQyjW0^ZL`+(O#U8d xqyZ6w(yd-L>D)Qq6PNpYVmsrk_EkSD_4+e*Yd7_oSXbsaH&vNfc(8C+{})AZ=bZom literal 0 HcmV?d00001 diff --git a/GUI/BlindWatermark/tools.py b/GUI/BlindWatermark/tools.py new file mode 100644 index 0000000..ec17ff0 --- /dev/null +++ b/GUI/BlindWatermark/tools.py @@ -0,0 +1,57 @@ +import cv2 +import numpy as np +from PyQt5.QtCore import pyqtSignal, QThread + + + +class recovery(QThread): + num_of_good=pyqtSignal(int,str) + def __init__(self,ori_img,attacked_img,outfile_name = './recoveried.png',rate=0.7): + QThread.__init__(self) + self.ori_img = ori_img + self.attacked_img = attacked_img + self.outfile_name = outfile_name + self.rate = rate + + def run(self): + img = cv2.imread(self.ori_img) + img2 = cv2.imread(self.attacked_img) + + height = img.shape[0] + width = img.shape[1] + # Initiate SIFT detector + orb = cv2.ORB_create(128) + MIN_MATCH_COUNT=10 + # find the keypoints and descriptors with SIFT + kp1, des1 = orb.detectAndCompute(img,None) + kp2, des2 = orb.detectAndCompute(img2,None) + + FLANN_INDEX_KDTREE = 0 + index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) + search_params = dict(checks = 50) + + flann = cv2.FlannBasedMatcher(index_params, search_params) + + + + des1 = np.float32(des1) + des2 = np.float32(des2) + + matches = flann.knnMatch(des1,des2,k=2) + + # store all the good matches as per Lowe's ratio test. + good = [] + for m,n in matches: + if m.distance < self.rate*n.distance: + good.append(m) + + if len(good)>MIN_MATCH_COUNT: + src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2) + dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2) + M, mask = cv2.findHomography( dst_pts,src_pts, cv2.RANSAC,5.0) + out = cv2.warpPerspective(img2, M, (width,height)) #先列后行 + cv2.imwrite(self.outfile_name,out) + self.num_of_good.emit(len(good),self.outfile_name) + else : + self.num_of_good.emit(0,'') + \ No newline at end of file diff --git a/GUI/Ui_main_win.py b/GUI/Ui_main_win.py index 781fea3..30488ce 100644 --- a/GUI/Ui_main_win.py +++ b/GUI/Ui_main_win.py @@ -108,6 +108,9 @@ def setupUi(self, MainWindow): self.pushButton_4 = QtWidgets.QPushButton(self.frame_5) self.pushButton_4.setObjectName("pushButton_4") self.gridLayout_5.addWidget(self.pushButton_4, 0, 1, 1, 1) + self.pushButton_11 = QtWidgets.QPushButton(self.frame_5) + self.pushButton_11.setObjectName("pushButton_11") + self.gridLayout_5.addWidget(self.pushButton_11, 1, 1, 1, 1) self.gridLayout_4.addWidget(self.frame_5, 12, 1, 1, 1) self.frame_6 = QtWidgets.QFrame(self.tab) self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel) @@ -218,6 +221,46 @@ def setupUi(self, MainWindow): self.line_5.setObjectName("line_5") self.gridLayout_4.addWidget(self.line_5, 8, 1, 1, 1) self.tabWidget.addTab(self.tab, "") + self.tab_4 = QtWidgets.QWidget() + self.tab_4.setObjectName("tab_4") + self.frame_7 = QtWidgets.QFrame(self.tab_4) + self.frame_7.setGeometry(QtCore.QRect(0, 0, 280, 201)) + self.frame_7.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_7.setFrameShadow(QtWidgets.QFrame.Sunken) + self.frame_7.setObjectName("frame_7") + self.gridLayout_8 = QtWidgets.QGridLayout(self.frame_7) + self.gridLayout_8.setObjectName("gridLayout_8") + self.textBrowser_2 = QtWidgets.QTextBrowser(self.frame_7) + self.textBrowser_2.setObjectName("textBrowser_2") + self.gridLayout_8.addWidget(self.textBrowser_2, 0, 0, 1, 2) + self.pushButton_8 = QtWidgets.QPushButton(self.frame_7) + self.pushButton_8.setObjectName("pushButton_8") + self.gridLayout_8.addWidget(self.pushButton_8, 1, 0, 1, 1) + self.pushButton_10 = QtWidgets.QPushButton(self.frame_7) + self.pushButton_10.setObjectName("pushButton_10") + self.gridLayout_8.addWidget(self.pushButton_10, 2, 0, 1, 1) + self.pushButton_9 = QtWidgets.QPushButton(self.frame_7) + self.pushButton_9.setObjectName("pushButton_9") + self.gridLayout_8.addWidget(self.pushButton_9, 1, 1, 1, 1) + self.frame_8 = QtWidgets.QFrame(self.frame_7) + self.frame_8.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_8.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_8.setObjectName("frame_8") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame_8) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.label_12 = QtWidgets.QLabel(self.frame_8) + self.label_12.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_12.setObjectName("label_12") + self.horizontalLayout.addWidget(self.label_12) + self.doubleSpinBox_3 = QtWidgets.QDoubleSpinBox(self.frame_8) + self.doubleSpinBox_3.setMaximum(1.0) + self.doubleSpinBox_3.setSingleStep(0.1) + self.doubleSpinBox_3.setProperty("value", 0.7) + self.doubleSpinBox_3.setObjectName("doubleSpinBox_3") + self.horizontalLayout.addWidget(self.doubleSpinBox_3) + self.gridLayout_8.addWidget(self.frame_8, 2, 1, 1, 1) + self.tabWidget.addTab(self.tab_4, "") self.tab_2 = QtWidgets.QWidget() self.tab_2.setObjectName("tab_2") self.label_8 = QtWidgets.QLabel(self.tab_2) @@ -309,6 +352,7 @@ def retranslateUi(self, MainWindow): self.pushButton_3.setText(_translate("MainWindow", "嵌入")) self.pushButton_4.setStatusTip(_translate("MainWindow", "在设置工作目录的前提下, 将原图和水印图复制到工作目录下")) self.pushButton_4.setText(_translate("MainWindow", "将图片复制到工作目录")) + self.pushButton_11.setText(_translate("MainWindow", "打开工作目录")) self.pushButton_5.setToolTip(_translate("MainWindow", "设置工作目录后,会在嵌入时在工作目录写入密钥: key.json")) self.pushButton_5.setText(_translate("MainWindow", "设置工作目录")) self.pushButton_6.setText(_translate("MainWindow", "从剪切板导入密钥")) @@ -319,8 +363,19 @@ def retranslateUi(self, MainWindow): self.label_3.setText(_translate("MainWindow", "小波变换级数")) self.checkBox.setText(_translate("MainWindow", "限制相同")) self.label_7.setText(_translate("MainWindow", "水印形状")) - + self.label_6.setText(_translate("MainWindow", "分块形状")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "盲水印")) + self.textBrowser_2.setHtml(_translate("MainWindow", "\n" +"\n" +"

恢复形变攻击

\n" +"

将经过平移,旋转,缩放,透视等形变的图片恢复成接近原图,是从受到形变攻击的图片中提取水印的必要前置步骤

")) + self.pushButton_8.setText(_translate("MainWindow", "读取原图")) + self.pushButton_10.setText(_translate("MainWindow", "恢复")) + self.pushButton_9.setText(_translate("MainWindow", "读取受到攻击的图片")) + self.label_12.setText(_translate("MainWindow", "阈值:")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("MainWindow", "工具")) self.label_8.setText(_translate("MainWindow", "咕咕咕")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "盲水印抗破坏验证")) self.label_9.setText(_translate("MainWindow", "捐赠通道")) @@ -347,7 +402,6 @@ def retranslateUi(self, MainWindow): self.help.setText(_translate("MainWindow", "帮助")) self.support.setText(_translate("MainWindow", "支持")) self.support.setToolTip(_translate("MainWindow", "恰饭")) - self.label_6.setText(_translate("MainWindow", "分块形状")) from img import sources_rc diff --git a/GUI/key.json b/GUI/key.json deleted file mode 100644 index 1af3867..0000000 --- a/GUI/key.json +++ /dev/null @@ -1 +0,0 @@ -{"random_seed_wm": 15643, "random_seed_dct": 4654, "mod": 32.0, "mod2": null, "wm_shape": [64, 64], "block_shape": [4, 4], "dwt_deep": 1} \ No newline at end of file diff --git a/GUI/main_win.py b/GUI/main_win.py index 8b5961f..77d1c85 100644 --- a/GUI/main_win.py +++ b/GUI/main_win.py @@ -8,7 +8,7 @@ from PyQt5.QtWidgets import QApplication, QFileDialog, QMainWindow, QMessageBox, QListView,QDialog from PyQt5.QtGui import QStandardItem, QStandardItemModel, QIcon -from BlindWatermark import watermark , average_ncc, average_psnr +from BlindWatermark import watermark , average_ncc, average_psnr, recovery from Ui_main_win import Ui_MainWindow from Ui_about import Ui_Dialog from os import popen @@ -19,6 +19,7 @@ class bwm_embed_thread(watermark): finish_out = pyqtSignal(str,str,dict) + assert_ERROR = pyqtSignal() def __init__(self,parameter,my_file_path): watermark.__init__(self, random_seed_wm = parameter['random_seed_wm'], @@ -36,19 +37,27 @@ def __init__(self,parameter,my_file_path): def run(self): self.read_ori_img(self.ori_img_path) self.read_wm(self.wm_path) - # return self.bwm.embed2array() - self.embed(self.out_file_path) - - key_dic = { - 'random_seed_wm' : self.random_seed_wm, - 'random_seed_dct' : self.random_seed_dct, - 'mod' : self.mod, - 'mod2' : self.mod2, - 'wm_shape' : self.wm_shape, - 'block_shape' : self.block_shape, - 'dwt_deep' : self.dwt_deep - } - self.finish_out.emit(self.out_file_path,'嵌入完成,保存于',key_dic) + ori_shape = self.ori_img_shape + wm_shape = self.wm_shape + block_shape = self.block_shape + dwt_deep = self.dwt_deep + if self.ori_img_shape[0]*self.ori_img_shape[1]/(4**(self.dwt_deep))>=self.wm_shape[0]*self.wm_shape[1]*self.block_shape[0]*self.block_shape[1]: + # return self.bwm.embed2array() + self.embed(self.out_file_path) + + key_dic = { + 'random_seed_wm' : self.random_seed_wm, + 'random_seed_dct' : self.random_seed_dct, + 'mod' : self.mod, + 'mod2' : self.mod2, + 'wm_shape' : self.wm_shape, + 'block_shape' : self.block_shape, + 'dwt_deep' : self.dwt_deep + } + self.finish_out.emit(self.out_file_path,'嵌入完成,保存于',key_dic) + else: + self.assert_ERROR.emit() + class bwm_extract_thread(watermark): finish_out = pyqtSignal(str,str) def __init__(self,parameter,my_file_path): @@ -75,7 +84,17 @@ def __init__(self,file_path): self.file_path = file_path def run(self): popen('start '+self.file_path) - + +class explorer_thread(QThread): + def __init__(self, dir_path): + QThread.__init__(self) + self.dir_path = dir_path + def run(self): + self.dir_path = self.dir_path.replace('/','\\') + popen('explorer {}'.format(self.dir_path)) + + + class MainWindow(QMainWindow, Ui_MainWindow): """ Class documentation goes here. @@ -90,6 +109,7 @@ def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.my_bwm_parameter = {} + self.my_recovery_parameter = {} self.init_listVIew() def my_setup(self): @@ -122,10 +142,13 @@ def checkItem(self,index): clipboard.setText(file_path) #此处为密钥 else: self.open_pic = open_pic_thread(file_path) - self.open_pic.start() self.open_pic.finished.connect(self.open_pic.deleteLater) + self.open_pic.start() # QMessageBox.information(self,"ListView",'row:%s, text:%s' % (index.row(), index.data())) + def assert_error(self): + QMessageBox.warning(self,'警告','参数错误,请参考关于中的公式修改参数,使其满足公式',QMessageBox.Ok) + def bwm_add_item(self,file_path,action_type=None,key=None): #路径中不允许出现\ / : * ? " < > → if action_type == '嵌入完成,保存于': @@ -164,12 +187,22 @@ def refresh_UI(self,bwm_parameter): self.spinBox.setValue(bwm_parameter['dwt_deep']) self.refresh_parameter() + def show_recovery(self,num,file_path): + if num == 0: + QMessageBox.warning(self,"警告",'检测到的特征点过少,请适当调低阈值',QMessageBox.Ok) + else: + self.open_pic = open_pic_thread(file_path) + self.open_pic.finished.connect(self.open_pic.deleteLater) + self.open_pic.start() + QMessageBox.information(self,"提示",'检测到{}个特征点,恢复图片已写入'.format(num),QMessageBox.Ok) + + @pyqtSlot() def on_pushButton_clicked(self): """ Slot documentation goes here. """ - my_file_path,_ = QFileDialog.getOpenFileName(self, '导入图片', './') + my_file_path,_ = QFileDialog.getOpenFileName(self, '导入图片', self.my_bwm_parameter.get('work_path','./')) if my_file_path: self.my_bwm_parameter['ori_img'] = my_file_path self.label_4.setText(my_file_path.split('/')[-1]) @@ -179,7 +212,7 @@ def on_pushButton_2_clicked(self): """ Slot documentation goes here. """ - my_file_path,_ = QFileDialog.getOpenFileName(self, '导入水印', './') + my_file_path,_ = QFileDialog.getOpenFileName(self, '导入水印', self.my_bwm_parameter.get('work_path','./')) if my_file_path: self.my_bwm_parameter['wm'] = my_file_path self.label_5.setText(my_file_path.split('/')[-1]) @@ -211,11 +244,12 @@ def on_pushButton_3_clicked(self): if self.my_bwm_parameter['mod'] <0.01: QMessageBox.warning(self,"警告",'第一个量化因子:{}不符合要求'.format(self.my_bwm_parameter['mod']),QMessageBox.Ok) else: - my_file_path,_ = QFileDialog.getSaveFileName(self, '保存图片', self.my_bwm_parameter.get('work_path','./')) + my_file_path,_ = QFileDialog.getSaveFileName(self, '保存图片', self.my_bwm_parameter.get('work_path','./'),"PNG (*.png);;JPG (*.jpg);;All Files (*)") if my_file_path: self._thread = bwm_embed_thread(self.my_bwm_parameter,my_file_path) self._thread.finished.connect(self._thread.deleteLater) self._thread.finish_out.connect(self.bwm_add_item) + self._thread.assert_ERROR.connect(self.assert_error) self._thread.valueChanged.connect(self.BlueProgressBar.setValue) self._thread.start() else: @@ -228,7 +262,7 @@ def on_pushButton_3_clicked(self): elif self.my_bwm_parameter['wm_shape'][0] == 0 or self.my_bwm_parameter['wm_shape'][1] == 0: QMessageBox.warning(self,"警告",'提取时需要设定水印形状',QMessageBox.Ok) else: - my_file_path,_ = QFileDialog.getSaveFileName(self, '保存图片', self.my_bwm_parameter.get('work_path','./')) + my_file_path,_ = QFileDialog.getSaveFileName(self, '保存图片', self.my_bwm_parameter.get('work_path','./'),"PNG (*.png);;JPG (*.jpg);;All Files (*)") if my_file_path: self._thread = bwm_extract_thread(self.my_bwm_parameter,my_file_path) self._thread.finished.connect(self._thread.deleteLater) @@ -328,6 +362,58 @@ def on_pushButton_4_clicked(self): string+='水印图片已存在于工作目录\n' QMessageBox.information(self,'信息',string,QMessageBox.Ok) + @pyqtSlot() + def on_pushButton_8_clicked(self): + """ + 读取原图 + """ + file_path,_ = QFileDialog.getOpenFileName(self, '读取原图', self.my_bwm_parameter.get('work_path','./')) + if file_path: + self.my_recovery_parameter['ori_img'] = file_path + + @pyqtSlot() + def on_pushButton_9_clicked(self): + """ + 读取受到攻击的图片 + """ + file_path,_ = QFileDialog.getOpenFileName(self, '读取受到攻击的图片', self.my_bwm_parameter.get('work_path','./')) + if file_path: + self.my_recovery_parameter['attacked_img'] = file_path + + @pyqtSlot() + def on_pushButton_10_clicked(self): + """ + 恢复 + """ + ori_img = self.my_recovery_parameter.get('ori_img',None) + attacked_img = self.my_recovery_parameter.get('attacked_img',None) + if not ori_img: + QMessageBox.warning(self,"警告",'未读取原图',QMessageBox.Ok) + elif not attacked_img: + QMessageBox.warning(self,"警告",'未读取受到攻击的图片',QMessageBox.Ok) + else: + outfile_path,_ = QFileDialog.getSaveFileName(self, '恢复图片', self.my_bwm_parameter.get('work_path','./'),"PNG (*.png);;All Files (*)") + if outfile_path: + rate = self.doubleSpinBox_3.value() + self.recovery_thread = recovery(ori_img,attacked_img,outfile_path,rate) + self.recovery_thread.finished.connect(self.recovery_thread.deleteLater) + self.recovery_thread.num_of_good.connect(self.show_recovery) + self.recovery_thread.start() + + @pyqtSlot() + def on_pushButton_11_clicked(self): + """ + 打开工作目录 + """ + work_path = self.my_bwm_parameter.get('work_path',None) + if work_path: + self.explorer_thread = explorer_thread(work_path) + self.explorer_thread.finished.connect(self.explorer_thread.deleteLater) + self.explorer_thread.start() + else: + QMessageBox.warning(self,"警告",'未设定工作目录',QMessageBox.Ok) + + @pyqtSlot() def on_help_triggered(self): """ @@ -351,4 +437,4 @@ def on_support_triggered(self): ui.show() sys.exit(app.exec_()) - + diff --git a/GUI/main_win.ui b/GUI/main_win.ui index c09e39a..8c34658 100644 --- a/GUI/main_win.ui +++ b/GUI/main_win.ui @@ -30,7 +30,7 @@ - 2 + 3 @@ -226,6 +226,13 @@ + + + + 打开工作目录 + + + @@ -436,6 +443,109 @@ + + + 工具 + + + + + 0 + 0 + 280 + 201 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">恢复形变攻击</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">将经过平移,旋转,缩放,透视等形变的图片恢复成接近原图,是从受到形变攻击的图片中提取水印的必要前置步骤</p></body></html> + + + + + + + 读取原图 + + + + + + + 恢复 + + + + + + + 读取受到攻击的图片 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 阈值: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.700000000000000 + + + + + + + + + 盲水印抗破坏验证 diff --git a/readme.md b/readme.md index 6b76842..63f3ec6 100644 --- a/readme.md +++ b/readme.md @@ -18,14 +18,15 @@ **todo list** - [x] 添加支援恰饭途径 - - [x] 支持多级离散小波变换 - [x] 支持自动补以去除图片长宽需要是偶数的限制 - [ ] 打包并上传pypi , 占时还不考虑,等较完善了再打包上传 - [x] 添加命令行模式 - [ ] 添加更多的嵌入方式(0/n) - 尝试QIM用于嵌入,失败,理论与实际有差距 -- [ ] 用pyqt编写GUI界面 +- [x] 用pyqt编写GUI界面 +- [x] 实现将受到形变攻击,如平移,旋转,缩放,透视等形变的图片恢复成接近原图,是从受到形变攻击的图片中提取水印的必要前置步骤 + - ```from BlindWatermark import recovery``` ### 大更新 @@ -40,6 +41,8 @@ ![条件](./pics_for_show/mic/gongshi.png) +- 实现抵抗形变攻击 + ## 现在的问题 @@ -57,7 +60,7 @@ ## 适用情况 - 适用人群: 希望保护自己创作的图片的创作者 -- 图片要求: 仅要求图片的长和宽均是偶数, 对彩色和黑白没有要求 +- 图片要求: 无 - 水印要求: - 只能嵌入二值化的信息, 会自动取水印的B通道并二值化嵌入,水印最好是黑白的图片 @@ -67,7 +70,6 @@ - python3 - numpy - opencv - - scipy - PyWavelets ## 如何使用