Initial commit

This commit is contained in:
kailai 2021-12-30 19:48:37 +08:00
parent 39c5f79e55
commit d843ca2c7f
135 changed files with 117078 additions and 2 deletions

31
DCT.py Normal file
View File

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
"""
Created on Tue Jun 11 17:10:43 2019
@author: Administrator
"""
import cv2
import numpy as np
img = cv2.imread('a.png',0)
img1 = img.astype('float')
def dct(m):
m = np.float32(m)/255.0
return cv2.dct(m)*255
#print(dct(img1).shape)
new_dct=dct(img1)
after_dct=[]
for i in range(len(new_dct)):
for j in range(len(new_dct[0])):
after_dct.append(int(new_dct[i][j]))
#print(new_dct)
#new_dct=new_dct.reshape(-1,1)
#print(len(after_dct))
#print(after_dct[:600])

60
F3.py Normal file
View File

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
"""
Created on Mon Jun 10 17:30:40 2019
@author: Administrator
"""
from Jsteg import Jsteg
class F3(Jsteg):
def __init__(self):
Jsteg.__init__(self)
def set_sequence_after_dct(self,sequence_after_dct):
self.sequence_after_dct=sequence_after_dct
sum_len=len(self.sequence_after_dct)
zero_len=len([i for i in self.sequence_after_dct if i==0])
one_len=len([i for i in self.sequence_after_dct if i in (-1,1)])
self.available_info_len=sum_len-zero_len-one_len # 不是特别可靠
print ("Load>> 大约可嵌入",sum_len-zero_len-int(one_len/2),'bits')
print ("Load>> 最少可嵌入",self.available_info_len,'bits\n')
def _write(self,index,data):
origin=self.sequence_after_dct[index]
if origin == 0:
return False
elif origin in (-1,1) and data==0:
self.sequence_after_dct[index]=0
return False
lower_bit=origin%2
if lower_bit==data:
pass
elif origin>0:
self.sequence_after_dct[index]=origin-1
elif origin<0:
self.sequence_after_dct[index]=origin+1
return True
def _read(self,index):
if self.sequence_after_dct[index] != 0:
return self.sequence_after_dct[index]%2
else:
return None
if __name__=="__main__":
f3=F3()
# 写
sequence_after_dct=[-1,0,1]*100+[i for i in range(-7,500)]
f3.set_sequence_after_dct(sequence_after_dct)
info1=[0,1,0,1,1,0,1,0]
f3.write(info1)
# 读
sequence_after_dct2=f3.get_sequence_after_dct()
f3.set_sequence_after_dct(sequence_after_dct2)
info2=f3.read()
print (info2)

67
F4.py Normal file
View File

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
"""
Created on Tue Jun 11 16:10:51 2019
@author: Administrator
"""
from Jsteg import Jsteg
class F4(Jsteg):
def __init__(self):
Jsteg.__init__(self)
def set_sequence_after_dct(self,sequence_after_dct):
self.sequence_after_dct=sequence_after_dct
sum_len=len(self.sequence_after_dct)
zero_len=len([i for i in self.sequence_after_dct if i==0])
one_len=len([i for i in self.sequence_after_dct if i in (-1,1)])
self.available_info_len=sum_len-zero_len-one_len # 不是特别可靠
print ("Load>> 大约可嵌入",sum_len-zero_len-int(one_len/2),'bits')
print ("Load>> 最少可嵌入",self.available_info_len,'bits\n')
def _write(self,index,data):
origin=self.sequence_after_dct[index]
if origin == 0:
return False
elif origin == 1 and data==0:
self.sequence_after_dct[index]=0
return False
elif origin == -1 and data==1:
self.sequence_after_dct[index]=0
return False
lower_bit=origin%2
if origin >0:
if lower_bit!=data:
self.sequence_after_dct[index]=origin-1
else:
if lower_bit==data:
self.sequence_after_dct[index]=origin+1
return True
def _read(self,index):
if self.sequence_after_dct[index] >0:
return self.sequence_after_dct[index]%2
elif self.sequence_after_dct[index]<0:
return (self.sequence_after_dct[index]+1)%2
else:
return None
if __name__=="__main__":
f4=F4()
# 写
sequence_after_dct=[-1,0,1]*100+[i for i in range(-7,500)]
f4.set_sequence_after_dct(sequence_after_dct)
info1=[0,1,0,1,1,0,1,0]
f4.write(info1)
# 读
sequence_after_dct2=f4.get_sequence_after_dct()
f4.set_sequence_after_dct(sequence_after_dct2)
info2=f4.read()
print (info2)

128
Jsteg.py Normal file
View File

@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
"""
Created on Mon Jun 10 15:26:43 2019
@author: Administrator
"""
import math
class Jsteg:
def __init__(self):
self.sequence_after_dct=None
def set_sequence_after_dct(self,sequence_after_dct):
self.sequence_after_dct=sequence_after_dct
self.available_info_len=len([i for i in self.sequence_after_dct if i not in (-1,1,0)]) # 不是绝对可靠的
print ("Load>> 可嵌入",self.available_info_len,'bits')
def get_sequence_after_dct(self):
return self.sequence_after_dct
def write(self,info):
"""先嵌入信息的长度,然后嵌入信息"""
info=self._set_info_len(info)
info_len=len(info)
info_index=0
im_index=0
while True:
if info_index>=info_len:
break
data=info[info_index]
if self._write(im_index,data):
info_index+=1
im_index+=1
def read(self):
"""先读出信息的长度,然后读出信息"""
_len,sequence_index=self._get_info_len()
info=[]
info_index=0
while True:
if info_index>=_len:
break
data=self._read(sequence_index)
if data!=None:
info.append(data)
info_index+=1
sequence_index+=1
return info
#===============================================================#
def _set_info_len(self,info):
l=int(math.log(self.available_info_len,2))+1
info_len=[0]*l
_len=len(info)
info_len[-len(bin(_len))+2:]=[int(i) for i in bin(_len)[2:]]
return info_len+info
def _get_info_len(self):
l=int(math.log(self.available_info_len,2))+1
len_list=[]
_l_index=0
_seq_index=0
while True:
if _l_index>=l:
break
_d=self._read(_seq_index)
if _d!=None:
len_list.append(str(_d))
_l_index+=1
_seq_index+=1
_len=''.join(len_list)
_len=int(_len,2)
return _len,_seq_index
# 注意经过DCT会有负值此处最低有效位的嵌入方式与空域LSB略有不同
def _write(self,index,data):
origin=self.sequence_after_dct[index]
if origin in (-1,1,0):
return False
lower_bit=origin%2
if lower_bit==data:
pass
elif origin>0:
if (lower_bit,data) == (0,1):
self.sequence_after_dct[index]=origin+1
elif (lower_bit,data) == (1,0):
self.sequence_after_dct[index]=origin-1
elif origin<0:
if (lower_bit,data) == (0,1):
self.sequence_after_dct[index]=origin-1
elif (lower_bit,data) == (1,0):
self.sequence_after_dct[index]=origin+1
return True
def _read(self,index):
if self.sequence_after_dct[index] not in (-1,1,0):
return self.sequence_after_dct[index]%2
else:
return None
'''
import cv2
import numpy as np
def dct(m):
m = np.float32(m)/255.0
return cv2.dct(m)*255
'''
if __name__=="__main__":
jsteg=Jsteg()
# 写
sequence_after_dct=[-1,0,1]*100+[i for i in range(-7,500)]
#print(sequence_after_dct)
jsteg.set_sequence_after_dct(sequence_after_dct)
info1=[0,1,0,1,1,0,1,0]
jsteg.write(info1)
sequence_after_dct2=jsteg.get_sequence_after_dct()
# 读
jsteg.set_sequence_after_dct(sequence_after_dct2)
info2=jsteg.read()
print (info2)

1
LSB/flag.txt Normal file
View File

@ -0,0 +1 @@
dajkhfahjkf

1
LSB/get_flag.txt Normal file
View File

@ -0,0 +1 @@
dajkhfahjkf

69
LSB/get_info.py Normal file
View File

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
"""
Created on Sun May 19 12:43:26 2019
@author: Administrator
"""
from PIL import Image
def mod(x,y):
return x%y
def toasc(strr):
return int(strr, 2)
#le为所要提取的信息的长度str1为加密载体图片的路径str2为提取文件的保存路径
def func(le,str1,str2):
b=""
im = Image.open(str1)
lenth = le*8
width,height = im.size[0],im.size[1]
count = 0
for h in range(height):
for w in range(width):
#获得(w,h)点像素的值
pixel = im.getpixel((w, h))
#此处余3依次从R、G、B三个颜色通道获得最低位的隐藏信息
if count%3==0:
count+=1
b=b+str((mod(int(pixel[0]),2)))
if count ==lenth:
break
if count%3==1:
count+=1
b=b+str((mod(int(pixel[1]),2)))
if count ==lenth:
break
if count%3==2:
count+=1
b=b+str((mod(int(pixel[2]),2)))
if count ==lenth:
break
if count == lenth:
break
with open(str2,"w",encoding='utf-8') as f:
for i in range(0,len(b),8):
#以每8位为一组二进制转换为十进制
stra = toasc(b[i:i+8])
#将转换后的十进制数视为ascii码再转换为字符串写入到文件中
#print((stra))
f.write(chr(stra))
print("完成信息提取!")
def main():
#文件长度
le = 11
#含有隐藏信息的图片
new = "new.png"
#信息提取出后所存放的文件
get_info = "get_flag.txt"
func(le,new,get_info)
if __name__=='__main__':
main()

85
LSB/hide_info.py Normal file
View File

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
"""
Created on Sun May 19 11:20:05 2019
@author: Administrator
"""
from PIL import Image
def plus(string):
#Python zfill() 方法返回指定长度的字符串原字符串右对齐前面填充0。
return string.zfill(8)
def get_key(strr):
#获取要隐藏的文件内容
with open(strr,"rb") as f:
s = f.read()
string=""
for i in range(len(s)):
#逐个字节将要隐藏的文件内容转换为二进制,并拼接起来
#1.先用ord()函数将s的内容逐个转换为ascii码
#2.使用bin()函数将十进制的ascii码转换为二进制
#3.由于bin()函数转换二进制后,二进制字符串的前面会有"0b"来表示这个字符串是二进制形式所以用replace()替换为空
#4.又由于ascii码转换二进制后是七位而正常情况下每个字符由8位二进制组成所以使用自定义函数plus将其填充为8位
string=string+""+plus(bin(s[i]).replace('0b',''))
#print(string)
return string
def mod(x,y):
return x%y
#str1为载体图片路径str2为隐写文件str3为加密图片保存的路径
def func(str1,str2,str3):
im = Image.open(str1)
#获取图片的宽和高
width,height= im.size[0],im.size[1]
print("width:"+str(width))
print("height:"+str(height))
count = 0
#获取需要隐藏的信息
key = get_key(str2)
keylen = len(key)
for h in range(height):
for w in range(width):
pixel = im.getpixel((w,h))
a=pixel[0]
b=pixel[1]
c=pixel[2]
if count == keylen:
break
#下面的操作是将信息隐藏进去
#分别将每个像素点的RGB值余2这样可以去掉最低位的值
#再从需要隐藏的信息中取出一位,转换为整型
#两值相加,就把信息隐藏起来了
a= a-mod(a,2)+int(key[count])
count+=1
if count == keylen:
im.putpixel((w,h),(a,b,c))
break
b =b-mod(b,2)+int(key[count])
count+=1
if count == keylen:
im.putpixel((w,h),(a,b,c))
break
c= c-mod(c,2)+int(key[count])
count+=1
if count == keylen:
im.putpixel((w,h),(a,b,c))
break
if count % 3 == 0:
im.putpixel((w,h),(a,b,c))
im.save(str3)
def main():
#原图
old = "old.png"
#处理后输出的图片路径
new = "new.png"
#需要隐藏的信息
enc = "flag.txt"
func(old,enc,new)
if __name__=='__main__':
main()

BIN
LSB/new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

BIN
LSB/old.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

282
PDF/PDF格式.md Normal file
View File

@ -0,0 +1,282 @@
# PDF格式学习
## PDF简介
* PDF是Portable Document Format 的缩写可翻译为“便携文件格式”由Adobe System Incorporated 公司在1992年发明。
* PDF文件是一种编程形式的文档格式它所有显示的内容都是通过相应的操作符进行绘制的。
* PDF基本显示单元包括文字图片矢量图图片
* PDF扩展单元包括水印电子署名注释表单多媒体3D
* PDF动作单元书签超链接拥有动作的单元有很多个包括电子署名多媒体等等
## PDF的优点
* 一致性:
在所有可以打开PDF的机器上展示的效果是完全一致不会出现段落错乱、文字乱码这些排版问题。尤其是文档中本身可以嵌入字体避免了客户端没有对应字体而导致文字显示不一致的问题。所以在印刷行业绝大多数用的都是PDF格式。
* 不易修改:
用过PDF文件的人都会知道对已经保存之后的PDF文件想要进行重新排版基本上就不可能的这就保证了从资料源发往外界的资料不容易被篡改。
* 安全性:
PDF文档可以进行加密包括以下几种加密形式文档打开密码文档权限密码文档证书密码加密的方法包括RC4AES通过加密这种形式可以达到资料防扩散等目的。
* 不失真:
PDF文件中使用了矢量图在文件浏览时无论放大多少倍都不会导致使用矢量图绘制的文字图案的失真。
* 支持多种压缩方式:
为了减少PDF文件的sizePDF格式支持各种压缩方式 asciihexascii85lzwrunlengthccittjbig2jpeg(DCT)jpeg2000(jpx)
* 支持多种印刷标准:
支持PDF-APDF-X
## PDF格式
根据PDF官方指南理解PDF格式可以从四个方面下手——**Objects**(对象)、**File structure**(物理文件结构)、**Document structure**(逻辑文件结构)、**Content streams**(内容流)。
### 对象
### 物理文件结构
* 整体上分为文件头Header、对象集合Body、交叉引用表Xref table、文件尾Trailer四个部分结构如图。修改过的PDF结构会有部分变化。
* 未经修改
![未经修改](https://img-blog.csdnimg.cn/20190526170017719.png#pic_center)
* 经修改
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190526170402806.png#pic_center)
#### 文件头
* 文件头是PDF文件的第一行,格式如下:
```
%PDF-1.7
```
* 这是个固定格式表示这个PDF文件遵循的PDF规范版本解析PDF的时候尽量支持高版本的规范以保证支持大多数工具生成的PDF文件。1.7版本支持1.0-1.7之间的所有版本。
#### 对象集合
* 这是一个PDF文件最重要的部分文件中用到的所有对象,包括文本、图象、音乐、视频、字体、超连接、加密信息、文档结构信息等等,都在这里定义。格式如下:
```
2 0 obj
...
end obj
```
* 一个对象的定义包含4个部分前面的2是**对象序号**其用来唯一标记一个对象0是**生成号**按照PDF规范如果一个PDF文件被修改那这个数字是累加的它和对象序号一起标记是原始对象还是修改后的对象但是实际开发中很少有用这种方式修改PDF的都是重新编排对象号obj和endobj是对象的定义范围可以抽象的理解为这就是一个左括号和右括号省略号部分是PDF规定的任意合法对象。
* 可以通过R关键字来引用任何一个对象比如要引用上面的对象可以使用2 0 R需要主意的是R关键字不仅可以引用一个已经定义的对象还可以引用一个并**不存在的对象**,而且效果就和引用了一个空对象一样。
* 对象主要有下面几种
* **booleam**
用关键字true或false表示可以是array对象的一个元素,或dictionary对象的一个条目。也可以用在PostScript计算函数里面做为if或if esle的一个条件。
* **numeric**
包括整形和实型,不支持非十进制数字,不支持指数形式的数字。
例:
1)整数 123 4567 +111 -2
范围:正2的31次方-1到负的2的31次方
2)实数 12.3 0.8 +6.3 -4.01 -3. +.03
范围:±3.403 ×10的38次方 ±1.175 × 10的-38次方
* 注意:如果整数超过表示范围将转化成实数,如果实数超过范围就会出错
* **string**
由一系列0-255之间的字节组成一个string总长度不能超过65535.string有以下两种方式
* 十六进制字串
<>包含起来的一个16进制串两位表示一个字符,不足两位用0补齐。
例: \<Aabb> 表示AA和BB两个字符 \<AAB> 表示AA和B0两个字符
* 直接字串
由()包含起来的一个字串,中间可以使用转义符"/"。
例:
(abc) 表示abc
(a//) 表示a/
转义符的定义如下:
|转义字符| 含义|
|--------|--------|
|/n |换行|
/r |回车
/t |水平制表符
/b |退格
/f |换页Form feed (FF)
/( |左括号
/) |右括号
// |反斜杠
/ddd |八进制形式的字符
* 对象类别(续)
* **name**
由一个前导/和后面一系列字符组成最大长度为127。和string不同的是name是**不可分割**的并且是**唯一**的不可分割就是说一个name对象就是一个原子比如/name不能说n就是这个name的一个元素唯一就是指两个相同的name一定代表同一个对象。从pdf1.2开始除了ascii的0别的都可以用一个#加两个十六进制的数字表示。
例:
/name 表示name
/name#20is 表示name is
/name#200 表示name 0
* **array**
用[]包含的一组对象可以是任何pdf对象(包括array)。虽然pdf只支持一维array但可以通过array的嵌套实现任意维数的array(但是一个array的元素不能超过8191)。
例:[549 3.14 false (Ralph) /SomeName]
* **Dictionary**
用"<<"和">>"包含的若干组条目每组条目都由key和value组成其中key必须是name对象并且一个dictionary内的key是唯一的value可以是任何pdf的合法对象(包括dictionary对象)。
例:
```
<< /IntegerItem 12
/StringItem (a string)
/Subdictionary
<< /Item1 0.4
/Item2 true
/LastItem (not!)
/VeryLastItem (OK)
>>
>>
```
* **stream**
由一个字典和紧跟其后面的一组关键字stream和endstream以及这组关键字中间包含一系列字节组成。内容和string很相似但有区别stream可以分几次读取分开使用不同的部分string必须作为一个整体一次全部读取使用string有长度限制但stream却没有这个限制。一般较大的数据都用stream表示。需要注意的是stream必须是间接对象并且stream的字典必须是直接对象。从1.2规范以后stream可以以外部文件形式存在这种情况下解析PDF的时候stream和endstream之间的内容就被忽略掉。
例:
```
dictionary
stream
…data…
endstream
```
stream字典中常用的字段如下
|字段名 |类型| 值|
|--------|--------|--------|
|Length| 整形|必须关键字stream和endstream之间的数据长度endstream之前可能会有一个多余的EOL标记这个不计算在数据的长度中。
Filter |名字 或 数组 |可选Stream的编码算法名称列表。如果有多个则数组中的编码算法列表顺序就是数据被编码的顺序。
DecodeParms |字典 或 数组 |(可选)一个参数字典或由参数字典组成的一个数组供Filter使用。如果仅有一个Filter并且这个Filter需要参数除非这个Filter的所有参数都已经给了默认值否则的话 DecodeParms必须设置给Filter。如果有多个Filter并且任意一个Filter使用了非默认的参数 DecodeParms 必须是个数组每个元素对应一个Filter的参数列表如果某个Filter无需参数或所有参数都有了默认值就用空对象代替。 如果没有Filter需要参数或者所有Filter的参数都有默认值DecodeParms 就被忽略了。
F |文件标识 |(可选)保存stream数据的文件。如果有这个字段 stream和endstream就被忽略FFilter将会代替Filter, FDecodeParms将代替DecodeParms。Length字段还是表示stream和endstream之间数据的长度但是通常此刻已经没有数据了长度是0.
FFilter |名字 或 字典| (可选)和filter类似针对外部文件。
FDecodeParms |字典 或 数组| (可选)和DecodeParams类似针对外部文件。
Stream的编码算法名称列表。如果有多个则数组中的编码算法列表顺序就是数据被编码的顺序。且需要被编码。编码算法主要如下
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190526185703968.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x5YzQ0ODEzNDE4,size_16,color_FFFFFF,t_70)
编码可视化主要显示为乱码,所以提供了隐藏信息的机会,如下图的steam内容为乱码。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190526185800381.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x5YzQ0ODEzNDE4,size_16,color_FFFFFF,t_70#pic_center)
* **NULL**
用null表示代表空。如果一个key的值为null则这个key可以被忽略如果引用一个不存在的object则等价于引用一个空对象。
#### 交叉引用表
* 交叉引用表是PDf文件内部一种特殊的文件组织方式可以很方便的根据对象号随机访问一个对象。其格式如下:
```
xref
0 1
0000000000 65535 f
4 1
0000000009 00000 n
8 3
0000000074 00000 n
0000000120 00000 n
0000000179 00000 n
```
* 其中,xref是开始标志,表示以下为一个交叉引用表的内容;每个交叉引用表又可以分为若干个子段每个子段的第一行是两个数字第一个是对象起始号后面是连续的对象个数接着每行是这个子段的每个对象的具体信息——每行的前10个数字代表这个这个对象**相对文件头的偏移地址**,后面的5位数字是**生成号**用于标记PDF的更新信息和对象的生成号作用类似最后一位f或n表示对象是否被使用(n表示使用,f表示被删除或没有用)。上面这个交叉引用表一共有3个子段分别有1个1个3个对象第一个子段的对象不可用其余子段对象可用。
#### 文件尾
* 通过trailer可以快速的找到交叉引用表的位置进而可以精确定位每一个对象还可以通过它本身的字典还可以获取文件的一些全局信息作者关键字标题等加密信息等等。具体形式如下:
```
trailer
<<
key1 value1
key2 value2
key3 value3
>>
startxref
553
%%EOF
```
* trailer后面紧跟一个字典包含若干键-值对。具体含义如下:
|键 |值类型| 值说明|
|--------|--------|--------|
|Size| 整形数字| 所有间接对象的个数。一个PDF文件如果被更新过则会有多个对象集合、交叉引用表、trailer最后一个trailer的这个字段记录了之前所有对象的个数。这个值必须是直接对象。|
|Prev |整形数字| 当文件有多个对象集合、交叉引用表和trailer时才会有这个键它表示前一个相对于文件头的偏移位置。这个值必须是直接对象。|
|Root |字典 |Catalog字典文件的逻辑入口点的对象号。必须是间接对象。|
|Encrypt |字典| 文档被保护时,会有这个字段,加密字典的对象号。|
|Info |字典 |存放文档信息的字典,必须是间接对象。|
|ID |数组 |文件的ID|
* 上面代码中的startxref后面的数字表示最后一个交叉引用表相对于文件起始位置的偏移量
* %%EOF文件结束符
### 逻辑文件结构
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190526185950801.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x5YzQ0ODEzNDE4,size_16,color_FFFFFF,t_70#pic_center)
#### catalog根节点
* catalog是整个PDF逻辑结构的根节点这个可以通过trailer的Root字段定位虽然简单但是相当重要因为这里是PDF文件物理结构和逻辑结构的连接点。Catalog字典包含的信息非常多这里仅就最主要的几个字段做个说明。
* Pages字段
这是个必须字段是PDF里面所有页面的描述集合。Pages字段本身是个字典它里面又包含了一下几个主要字段
|字段 |类型 |值|
|--------|--------|--------|
Type |name| (必须)只能为Pages 。
Parent |dictionary |(如果不是catalog里面指定的跟节点则必须有并且必须是间接对象) 当前节点的直接父节点。
Kids |array |(必须)一个间接对象组成的数组节点可能是page或page tree。
Count |integer| (必须) page tree里面所包含叶子节点page 对象)的个数。
从以上字段可以看出Pages最主要的功能就是组织所有的page对象。Page对象描述了一个PDF页面的属性、资源等信息。Page对象是一个字典它主要包含一下几个重要的属性
|字段 |类型 |值|
|--------|--------|--------|
Type |name |(必须)必须是Page。
Parent |dictionary| (必须;并且只能是间接对象)当前page节点的直接父节点page tree 。
LastModified| date| (如果存在PieceInfo字段就必须有否则可选)记录当前页面被最后一次修改的日期和时间。
Resources| dictionary| (必须; 可继承)记录了当前page用到的所有资源。如果当前页不用任何资源则这是个空字典。忽略所有字段则表示继承父节点的资源。
MediaBox |rectangle| (必须; 可继承)定义了要显示或打印页面的物理媒介的区域default user space units
CropBox |rectangle| (可选; 可继承)定义了一个可视区域,当前页被显示或打印的时候,它的内容会被这个区域裁剪。默认值就是 MediaBox。
BleedBox|rectangle |(可选) 定义了一个区域,当输出设备是个生产环境( production environment的时候页面显示的内容会被裁剪。默认值是 CropBox.
Contents |stream or array| (可选) 描述页面内容的流。如果这个字段缺省则页面上什么也不会显示。这个值可以是一个流也可以是由几个流组成的一个数组。如果是数组实际效果相当于所有的流是按顺序连在一起的一个流这就允许PDF生成的时候可以随时插入图片或其他资源。流之间的分割只是词汇上的一个分割并不是逻辑上或者组织形式的切割。
Rotate |integer| (可选; 可继承) 顺时钟旋转的角度数这个必须是90的整数倍默认是0。
Thumb| stream |(可选)定义当前页的缩略图。
Annots| array| (可选) 和当前页面关联的注释。
Metadata |stream| (可选) 当前页包含的元数据。
一个简单例子:
```
3 0 obj
<< /Type /Page
/Parent 4 0 R
/MediaBox [ 0 0 612 792 ]
/Resources <</Font<<
/F3 7 0 R /F5 9 0 R /F7 11 0 R
>>
/ProcSet [ /PDF ]
>>
/Contents 12 0 R
/Thumb 14 0 R
/Annots [ 23 0 R 24 0 R]
>>
endobj
```
* Outlines字段
Outline是PDF里面为了方便用户从PDF的一部分跳转到另外一部分而设计的有时候也叫书签Bookmark它是一个树状结构可以直观的把PDF文件结构展现给用户。用户可以通过鼠标点击来打开或者关闭某个outline项来实现交互当打开一个outline时用户可以看到它的所有子节点关闭一个outline的时候这个outline的所有子节点会自动隐藏。并且在点击的时候阅读器会自动跳转到outline对应的页面位置。Outlines包含以下几个字段
|字段 |类型 |值|
|--------|--------|--------|
Type |name |(可选)如果这个字段有值则必须是Outlines。
First |dictionary |(必须;必须是间接对象) 第一个顶层Outline item。
Last |dictionary |(必须;必须是间接对象)最后一个顶层outline item。
Count |integer |(必须)outline的所有层次的item的总数。
Outline是一个管理outline item的顶层对象我们看到的其实是outline item这个里面才包含了文字、行为、目标区域等等。一个outline item主要有一下几个字段
|字段 |类型 |值|
|--------|--------|--------|
Title |text string| (必须)当前item要显示的标题。
Parent |dictionary |(必须;必须是间接对象) outline层级中当前item的父对象。如果item本身是顶级item则父对象就是它本身。
Prev| dictionary| (除了每层的第一个item外其他item必须有这个字段;必须是间接对象)当前层级中此item的前一个item。
Next |dictionary| (除了每层的最后一个item外其他item必须有这个字段;必须是间接对象)当前层级中此item的后一个item。
First |dictionary| (如果当前item有任何子节点则这个字段是必须的;必须是间接对象) 当前item的第一个直接子节点。
Last |dictionary| (如果当前item有任何子节点则这个字段是必须的;必须是间接对象) 当前item的最后一个直接子节点。
Dest |name,byte string, or array |(可选; 如果A字段存在则这个不能被会略)当前的outline item被激活的时候要显示的区域。
A |dictionary| (可选; 如果Dest 字段存在,则这个不能被忽略)当前的outline item被激活的时候要执行的动作。
* URI字段
URIuniform resource identifier),定义了文档级别的统一资源标识符和相关链接信息。目录和文档中的链接就是通过这个字段来处理的.
* Metadata字段
文档的一些附带信息用xml表示符合adobe的xmp规范。这个可以方便程序不用解析整个文件就能获得文件的大致信息。
* 其他
Catalog字典中常用的字段一般有以下一些
|字段 |类型 |值|
|--------|--------|--------|
Type |name| (必须)必须为Catalog。
Version| name |(可选)PDF文件所遵循的版本号如果比文件头指定的版本号高的话。如果这个字段缺省或者文件头指定的版本比这里的高那就以文件头为准。一个PDF生成程序可以通过更新这个字段的值来修改PDF文件版本号。
Pages |dictionary| (必须并且必须为间接对象)当前文档的页面集合入口。
PageLabels |number tree| (可选) number tree定义了页面和页面label对应关系。
Names| dictionary| (可选)文档的name字典。
Dests |dictionary |(可选;必须是间接对象)name和相应目标对应关系字典。
ViewerPreferences| dictionary| (可选)阅读参数配置字典,定义了文档被打开时候的行为。如果缺省,则使用阅读器自己的配置。
PageLayout |name |(可选) 指定文档被打开的时候页面的布局方式。SinglePageDisplay 单页OneColumnDisplay 单列TwoColumnLeftDisplay 双列奇数页在左TwoColumnRightDisplay 双列奇数页在右TwoPageLeft 双页奇数页在左TwoPageRight 双页,奇数页在右缺省值: SinglePage.
PageMode |name| (可选) 当文档被打开时指定文档怎么显示UseNone 目录和缩略图都不显示UseOutlines 显示目录UseThumbs 显示缩略图FullScreen 全屏模式没有菜单任何其他窗口UseOC 显示Optional content group 面板UseAttachments显示附件面板缺省值: UseNone.
Outlines |dictionary| (可选;必须为间接对象)文档的目录字典
Threads |array |(可选;必须为间接对象)文章线索字典组成的数组。
OpenAction |array or dictionary| (可选) 指定一个区域或一个action在文档打开的时候显示区域或者执行action。如果缺省则会用默认缩放率显示第一页的顶部。
AA |dictionary| (可选)一个附加的动作字典在全局范围内定义了响应各种事件的action。
URI| dictionary |(可选)一个URI字典包含了文档级别的URI action信息。
AcroForm| dictionary |(可选)文档的交互式form (AcroForm)字典。
Metadata |stream |(可选;必须是间接对象)文档包含的元数据流。

Binary file not shown.

BIN
Paper/ALASKA.pdf Normal file

Binary file not shown.

Binary file not shown.

BIN
Paper/Arbitrary_Size.pdf Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
Paper/JRM.pdf Normal file

Binary file not shown.

BIN
Paper/New_YeNet.pdf Normal file

Binary file not shown.

BIN
Paper/Pev10-Hugo.pdf Normal file

Binary file not shown.

Binary file not shown.

BIN
Paper/SRM.pdf Normal file

Binary file not shown.

BIN
Paper/SRnet.pdf Normal file

Binary file not shown.

BIN
Paper/STC.pdf Normal file

Binary file not shown.

BIN
Paper/STC2.pdf Normal file

Binary file not shown.

BIN
Paper/UERD.pdf Normal file

Binary file not shown.

BIN
Paper/UNIWARD-EURASIP.pdf Normal file

Binary file not shown.

BIN
Paper/WISERNet.pdf Normal file

Binary file not shown.

BIN
Paper/YeNet.pdf Normal file

Binary file not shown.

5695
Paper/diouf2014.pdf Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

BIN
Paper/multi_size_image.pdf Normal file

Binary file not shown.

BIN
Paper/nsF5.pdf Normal file

Binary file not shown.

View File

@ -0,0 +1,183 @@
# imports
import json
import time
import pickle
import scipy.misc
import skimage.io
import caffe
import numpy as np
import os.path as osp
from random import shuffle
#from PIL import Image
import matplotlib.image as mpimg
class AugmentDataLayerSync(caffe.Layer):
"""
This is a simple syncronous datalayer for inputting the augmented data layer on the fly
"""
def setup(self, bottom, top):
self.top_names = ['data', 'label']
# === Read input parameters ===
# params is a python dictionary with layer parameters.
params = eval(self.param_str)
# Check the paramameters for validity.
check_params(params)
# store input as class variables
self.batch_size = params['batch_size']
# Create a batch loader to load the images.
self.batch_loader = BatchLoader( params, None )
# === reshape tops ===
# since we use a fixed input image size, we can shape the data layer
# once. Else, we'd have to do it in the reshape call.
top[0].reshape( self.batch_size,
1,
params['im_shape'][0],
params['im_shape'][1] )
# Ground truth
top[1].reshape(self.batch_size)
print_info( "AugmentStegoDataLayerSync", params )
def forward(self, bottom, top):
"""
Load data.
"""
for itt in range(self.batch_size):
# Use the batch loader to load the next image.
im, label = self.batch_loader.load_next_image()
# Add directly to the caffe data layer
top[0].data[itt, 0, :, :] = im
top[1].data[itt] = label
def reshape(self, bottom, top):
"""
There is no need to reshape the data, since the input is of fixed size
(rows and columns)
"""
pass
def backward(self, top, propagate_down, bottom):
"""
These layers does not back propagate
"""
pass
class BatchLoader(object):
"""
This class abstracts away the loading of images.
Images can either be loaded singly, or in a batch. The latter is used for
the asyncronous data layer to preload batches while other processing is
performed.
"""
def __init__(self, params, result):
self.result = result
self.batch_size = params['batch_size']
self.root = params['root']
self.im_shape = params['im_shape']
self.trainMode = ( params['split'] == 'train' ) # determine the mode, if test, no augment
# get list of image indexes.
list_file = params['split'] + '.txt'
TXT_FILE = osp.join( self.root, list_file )
txt_lines = [ line.rstrip('\n') for line in open( TXT_FILE ) ]
total_size = len( txt_lines )
assert total_size%2 == 0, "total_size must be even"
self.images = []
self.labels = np.zeros( ( total_size, ), dtype = np.int64 )
self.indexlist = range( total_size )
for i in np.arange(total_size):
tmp = txt_lines[i].split()
self.images.append(tmp[0])
self.labels[i] = int(tmp[1])
self._cur = 0 # current image
self._epoch = 0 # current epoch count, also used as the randomization seed
self._flp = 1 # Augment flip number,
self._rot = 0 # Augment rotation number
print "BatchLoader initialized with {} images".format(len(self.indexlist))
def load_next_image( self ):
"""
Load the next image in a batch
"""
# Did we finish an epoch
if self._cur == len(self.indexlist):
self._epoch += 1
l = np.random.seed( self._epoch ) #randomize, aslo reproducible
l = np.random.permutation( len(self.indexlist)/2 )
l2 = np.vstack( ( 2*l, 2*l + 1 )).T
self.indexlist = l2.reshape(len(self.indexlist),)
self._cur = 0
# Index list
index = self.indexlist[self._cur]
#load an image
image_file_name = self.images[index]
im = np.asarray( mpimg.imread( image_file_name ))
#Determine the new fliplr and rot90 status, used it in the stego
if ( self.trainMode ):
if ( self._cur % 2 == 0 ):
self._flp = np.random.choice(2)*2 - 1
self._rot = np.random.randint(4)
im = im[:,::self._flp]
im = np.rot90(im, self._rot)
#load the ground truth
label = self.labels[index]
self._cur += 1
return im, label
def check_params(params):
"""
A utility function to check the parameters for the data layers.
"""
assert 'split' in params.keys(
), 'Params must include split (train, val, or test).'
required = ['batch_size', 'root', 'im_shape']
for r in required:
assert r in params.keys(), 'Params must include {}'.format(r)
def print_info(name, params):
"""
Ouput some info regarding the class
"""
print "{} initialized for split: {}, with bs: {}, im_shape: {}.".format(
name,
params['split'],
params['batch_size'],
params['im_shape'])

View File

@ -0,0 +1,122 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[HG:] Train Set = 30000, Test = 10000\n"
]
}
],
"source": [
"import numpy as np\n",
"import os\n",
"\n",
"import sys\n",
"\n",
"\n",
"N_TRN = 5000\n",
"N_TST = 5000\n",
"\n",
"DataBaseFolder = '/home/mchen/tmp/caffe/data/JStego/'\n",
"\n",
"BOSS2CoverPath = os.path.join( DataBaseFolder, '75') # BOSS2 cover subfolder\n",
"BOSS2StegoPath = os.path.join( DataBaseFolder, 'JUNI_0.4') # BOSS2 stego subfolder\n",
"\n",
"BOW2CoverPath = os.path.join( DataBaseFolder, 'BOWS2_75') # BOWS cover subfolder\n",
"BOW2StegoPath = os.path.join( DataBaseFolder, 'BOWS2_JUNI_0.4') # BOWS stego subfolder\n",
"\n",
"TxtListFolder = '/home/mchen/tmp/caffe/data/JStego/MiracleList/'\n",
"\n",
"np.random.seed(0) # reset the random seed\n",
"\n",
"RandomImages = np.random.permutation(10000) + 1\n",
"\n",
"print (\"[HG:] Train Set = %d, Test = %d\"%( 20000 + N_TRN * 2, N_TST * 2 ) )"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"with open('{}/train.txt'.format(TxtListFolder), 'w') as f:\n",
" for idx in range(N_TRN):\n",
" imageName = str( RandomImages[idx] ) + '.jpg'\n",
" f.write('{} 0\\n'.format(BOSS2CoverPath + '/' + imageName ) )\n",
" f.write('{} 1\\n'.format(BOSS2StegoPath + '/' + imageName ) )\n",
" \n",
" for idx in range(10000):\n",
" imageName = str( RandomImages[idx] ) + '.jpg'\n",
" f.write('{} 0\\n'.format(BOW2CoverPath + '/' + imageName ) )\n",
" f.write('{} 1\\n'.format(BOW2StegoPath + '/' + imageName ) ) \n",
" \n",
" f.close()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"with open('{}/test.txt'.format(TxtListFolder), 'w') as f:\n",
" for idx in range(N_TST):\n",
" imageName = str( RandomImages[N_TRN + idx] ) + '.jpg'\n",
" f.write('{} 0\\n'.format(BOSS2CoverPath + '/' + imageName ) )\n",
" f.write('{} 1\\n'.format(BOSS2StegoPath + '/' + imageName ) )\n",
" f.close()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
train_net: "/tmp/tmpNMRrso"
test_net: "/tmp/tmpwTrC5X"
test_iter: 1
test_interval: 1000000
base_lr: 0.0010000000475
display: 10
max_iter: 1000000
lr_policy: "step"
gamma: 0.75
momentum: 0.899999976158
weight_decay: 0.00999999977648
stepsize: 6000
snapshot: 6000
snapshot_prefix: "/home/mchen/tmp/caffe/examples/PhaseAwareNet"
solver_mode: GPU
iter_size: 1
type: "SGD"

View File

@ -0,0 +1,463 @@
layer {
name: "data"
type: "Python"
top: "data"
top: "label"
python_param {
module: "AugStegoDataLayer"
layer: "AugmentDataLayerSync"
param_str: "{\'im_shape\': [512, 512], \'root\': \'/home/mchen/tmp/caffe/data/JStego/JUNI_0.4/\', \'split\': \'train\', \'batch_size\': 40}"
}
}
layer {
name: "conv0"
type: "Convolution"
bottom: "data"
top: "conv0"
param {
lr_mult: 0.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
convolution_param {
num_output: 4
pad: 2
kernel_size: 5
stride: 1
weight_filler {
type: "constant"
value: 0.0
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "conv1"
type: "Convolution"
bottom: "conv0"
top: "conv1"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
convolution_param {
num_output: 8
pad: 2
kernel_size: 5
stride: 1
weight_filler {
type: "gaussian"
std: 0.00999999977648
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "abs1"
type: "AbsVal"
bottom: "conv1"
top: "abs1"
}
layer {
name: "bn1"
type: "BatchNorm"
bottom: "abs1"
top: "bn1"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
batch_norm_param {
moving_average_fraction: 0.980000019073
eps: 9.99999974738e-05
scale_filler {
type: "constant"
value: 1.0
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "tanh1"
type: "TanH"
bottom: "bn1"
top: "bn1"
}
layer {
name: "conv2"
type: "Convolution"
bottom: "bn1"
top: "conv2"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
convolution_param {
num_output: 16
pad: 2
kernel_size: 5
stride: 1
weight_filler {
type: "gaussian"
std: 0.00999999977648
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "bn2"
type: "BatchNorm"
bottom: "conv2"
top: "bn2"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
batch_norm_param {
moving_average_fraction: 0.980000019073
eps: 9.99999974738e-05
scale_filler {
type: "constant"
value: 1.0
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "tanh2"
type: "TanH"
bottom: "bn2"
top: "bn2"
}
layer {
name: "sbp"
type: "SplitByPhase"
bottom: "bn2"
top: "sbp"
}
layer {
name: "conv3"
type: "Convolution"
bottom: "sbp"
top: "conv3"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
convolution_param {
num_output: 128
pad: 0
kernel_size: 1
stride: 1
weight_filler {
type: "gaussian"
std: 0.00999999977648
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "bn3"
type: "BatchNorm"
bottom: "conv3"
top: "bn3"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
batch_norm_param {
moving_average_fraction: 0.980000019073
eps: 9.99999974738e-05
scale_filler {
type: "constant"
value: 1.0
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "bn3"
top: "bn3"
}
layer {
name: "pool3"
type: "Pooling"
bottom: "bn3"
top: "pool3"
pooling_param {
pool: AVE
kernel_size: 5
stride: 2
pad: 1
}
}
layer {
name: "conv4"
type: "Convolution"
bottom: "pool3"
top: "conv4"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
convolution_param {
num_output: 256
pad: 0
kernel_size: 1
stride: 1
weight_filler {
type: "gaussian"
std: 0.00999999977648
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "bn4"
type: "BatchNorm"
bottom: "conv4"
top: "bn4"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
batch_norm_param {
moving_average_fraction: 0.980000019073
eps: 9.99999974738e-05
scale_filler {
type: "constant"
value: 1.0
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "bn4"
top: "bn4"
}
layer {
name: "pool4"
type: "Pooling"
bottom: "bn4"
top: "pool4"
pooling_param {
pool: AVE
kernel_size: 5
stride: 2
pad: 1
}
}
layer {
name: "conv5"
type: "Convolution"
bottom: "pool4"
top: "conv5"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
convolution_param {
num_output: 512
pad: 0
kernel_size: 1
stride: 1
weight_filler {
type: "gaussian"
std: 0.00999999977648
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "bn5"
type: "BatchNorm"
bottom: "conv5"
top: "bn5"
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 1.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
param {
lr_mult: 0.0
decay_mult: 0.0
}
batch_norm_param {
moving_average_fraction: 0.980000019073
eps: 9.99999974738e-05
scale_filler {
type: "constant"
value: 1.0
}
bias_filler {
type: "constant"
value: 0.0
}
}
}
layer {
name: "relu5"
type: "ReLU"
bottom: "bn5"
top: "bn5"
}
layer {
name: "pool5"
type: "Pooling"
bottom: "bn5"
top: "pool5"
pooling_param {
pool: AVE
global_pooling: true
}
}
layer {
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
param {
lr_mult: 1.0
decay_mult: 1.0
}
param {
lr_mult: 2.0
decay_mult: 0.0
}
inner_product_param {
num_output: 2
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value: 0.00999999977648
}
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "fc6"
bottom: "label"
top: "loss"
}
layer {
name: "acc"
type: "Accuracy"
bottom: "fc6"
bottom: "label"
top: "acc"
}

Binary file not shown.

View File

@ -0,0 +1,76 @@
#include <algorithm>
#include <vector>
#include "caffe/filler.hpp"
#include "caffe/layer_factory.hpp"
#include "caffe/layers/split_by_phase_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template <typename Dtype>
void SplitByPhaseLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
num_images_ = bottom[0]->num();
num_filters_ = bottom[0]->channels();
height_ = bottom[0]->height();
width_ = bottom[0]->width();
CHECK_EQ(height_, 512);
CHECK_EQ(width_, 512);
}
template <typename Dtype>
void SplitByPhaseLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
top[0]->Reshape(num_images_, num_filters_*64, 64, 64);
}
template <typename Dtype>
void SplitByPhaseLayer<Dtype>::Forward_cpu(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
const Dtype* bottom_data = bottom[0]->cpu_data();
Dtype* top_data = top[0]->mutable_cpu_data();
int n, c, p, h, w, source_index;
for (int index = 0; index < bottom[0]->count(); ++index) {
w = index % 64;
h = (index / 64) % 64;
p = (index / 64 / 64) % 64;
c = (index / 64 / 64 / 64) % num_filters_;
n = index / 64 / 64 / 64 / num_filters_;
source_index = ((w*8)+(h*8*512)+(p%8)+(p/8)*512)+((n*num_filters_+c)*512*512);
top_data[index] = bottom_data[source_index];
}
}
template <typename Dtype>
void SplitByPhaseLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
if (propagate_down[0]) {
const Dtype* top_diff = top[0]->cpu_diff();
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
int n, c, p, h, w, source_index;
for (int index = 0; index < bottom[0]->count(); ++index) {
w = index % 64;
h = (index / 64) % 64;
p = (index / 64 / 64) % 64;
c = (index / 64 / 64 / 64) % num_filters_;
n = index / 64 / 64 / 64 / num_filters_;
source_index = ((w*8)+(h*8*512)+(p%8)+(p/8)*512)+((n*num_filters_+c)*512*512);
bottom_diff[source_index] = top_diff[index];
}
}
}
#ifdef CPU_ONLY
STUB_GPU(SplitByPhaseLayer);
#endif
INSTANTIATE_CLASS(SplitByPhaseLayer);
REGISTER_LAYER_CLASS(SplitByPhase);
} // namespace caffe

View File

@ -0,0 +1,92 @@
#include <cfloat>
#include <vector>
#include "caffe/layers/split_by_phase_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template <typename Dtype>
__global__ void SplitByPhaseForward(const int nthreads,
const Dtype* const bottom_data, const int num_filters, Dtype* const top_data) {
CUDA_KERNEL_LOOP(index, nthreads) {
const int w = index % 64;
const int h = (index / 64) % 64;
const int p = (index / 64 / 64) % 64;
const int c = (index / 64 / 64 / 64) % num_filters;
const int n = index / 64 / 64 / 64 / num_filters;
const int source_index = ((w*8)+(h*8*512)+(p%8)+(p/8)*512)+((n*num_filters+c)*512*512);
top_data[index] = bottom_data[source_index];;
}
}
template <typename Dtype>
__global__ void SplitByPhaseForwardSlow(const int nthreads,
const Dtype* const bottom_data, const int num_filters, Dtype* const top_data) {
CUDA_KERNEL_LOOP(index, nthreads) {
int h, p, c, n, source_index;
for (int w = 0; w < 64; ++w) {
h = index % 64;
p = (index / 64) % 64;
c = (index / 64 / 64) % num_filters;
n = index / 64 / 64 / num_filters;
source_index = ((w*8)+(h*8*512)+(p%8)+(p/8)*512)+((n*num_filters+c)*512*512);
top_data[index*64+w] = bottom_data[source_index];
}
}
}
template <typename Dtype>
void SplitByPhaseLayer<Dtype>::Forward_gpu(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
const Dtype* bottom_data = bottom[0]->gpu_data();
Dtype* top_data = top[0]->mutable_gpu_data();
const int count = top[0]->count();
// NOLINT_NEXT_LINE(whitespace/operators)
SplitByPhaseForward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
count, bottom_data, num_filters_, top_data);
}
template <typename Dtype>
__global__ void SplitByPhaseBackwardSlow(const int nthreads,
Dtype* const bottom_diff, const int num_filters, const Dtype* const top_diff) {
CUDA_KERNEL_LOOP(index, nthreads) {
const int w = index % 64;
const int h = (index / 64) % 64;
const int p = (index / 64 / 64) % 64;
const int c = (index / 64 / 64 / 64) % num_filters;
const int n = index / 64 / 64 / 64 / num_filters;
const int source_index = ((w*8)+(h*8*512)+(p%8)+(p/8)*512)+((n*num_filters+c)*512*512);
bottom_diff[source_index] = top_diff[index];
}
}
template <typename Dtype>
__global__ void SplitByPhaseBackward(const int nthreads,
Dtype* const bottom_diff, const int num_filters, const Dtype* const top_diff) {
CUDA_KERNEL_LOOP(index, nthreads) {
const int w = index % 512;
const int h = (index / 512) % 512;
const int c = (index / 512 / 512) % num_filters;
const int n = index / 512 / 512 / num_filters;
const int target_index = ((w/8)+64*(h/8))+(64*64*(((w%8)+8*(h%8))))+(512*512*(n*num_filters+c));
bottom_diff[index] = top_diff[target_index];
}
}
template <typename Dtype>
void SplitByPhaseLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
if (propagate_down[0]) {
const Dtype* top_diff = top[0]->gpu_diff();
Dtype* bottom_diff = bottom[0]->mutable_gpu_diff();
const int count = bottom[0]->count();
// NOLINT_NEXT_LINE(whitespace/operators)
SplitByPhaseBackward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
count, bottom_diff, num_filters_, top_diff);
}
}
INSTANTIATE_LAYER_GPU_FUNCS(SplitByPhaseLayer);
} // namespace caffe

View File

@ -0,0 +1,57 @@
#ifndef CAFFE_SPLIT_BY_PHASE_LAYER_HPP_
#define CAFFE_SPLIT_BY_PHASE_LAYER_HPP_
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
namespace caffe {
template <typename Dtype>
class SplitByPhaseLayer: public Layer<Dtype> {
public:
explicit SplitByPhaseLayer(const LayerParameter& param)
: Layer<Dtype>(param) {}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual inline const char* type() const { return "SplitByPhase"; }
virtual inline int ExactNumBottomBlobs() const { return 1; }
virtual inline int ExactNumTopBlobs() const { return 1; }
protected:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
shared_ptr<Layer<Dtype> > bias_layer_;
vector<Blob<Dtype>*> bias_bottom_vec_;
vector<bool> bias_propagate_down_;
int bias_param_id_;
Blob<Dtype> sum_multiplier_;
Blob<Dtype> sum_result_;
Blob<Dtype> temp_;
int axis_;
int outer_dim_, scale_dim_, inner_dim_;
int num_images_;
int num_filters_;
int height_;
int width_;
};
} // namespace caffe
#endif // CAFFE_SPLIT_BY_PHASE_LAYER_HPP_

View File

@ -0,0 +1,123 @@
#include <vector>
#include "gtest/gtest.h"
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/filler.hpp"
#include "caffe/layers/split_by_phase_layer.hpp"
#include "caffe/test/test_caffe_main.hpp"
#include "caffe/test/test_gradient_check_util.hpp"
namespace caffe {
#ifndef CPU_ONLY
extern cudaDeviceProp CAFFE_TEST_CUDA_PROP;
#endif
template <typename TypeParam>
class SplitByPhaseLayerTest : public MultiDeviceTest<TypeParam> {
typedef typename TypeParam::Dtype Dtype;
protected:
SplitByPhaseLayerTest()
: blob_bottom_(new Blob<Dtype>(1, 1, 512, 512)),
blob_bottom_nobatch_(new Blob<Dtype>(1, 2, 3, 4)),
blob_top_(new Blob<Dtype>()) {
// fill the values
FillerParameter filler_param;
// filler_param.s
filler_param.set_min(-9);
filler_param.set_max(9);
UniformFiller<Dtype> filler(filler_param);
filler.Fill(this->blob_bottom_);
blob_top_vec_.push_back(blob_top_);
}
virtual ~SplitByPhaseLayerTest() {
delete blob_bottom_;
delete blob_bottom_nobatch_;
delete blob_top_;
}
Blob<Dtype>* const blob_bottom_;
Blob<Dtype>* const blob_bottom_nobatch_;
Blob<Dtype>* const blob_top_;
vector<Blob<Dtype>*> blob_bottom_vec_;
vector<Blob<Dtype>*> blob_top_vec_;
};
TYPED_TEST_CASE(SplitByPhaseLayerTest, TestDtypesAndDevices);
TYPED_TEST(SplitByPhaseLayerTest, TestSetUp) {
typedef typename TypeParam::Dtype Dtype;
this->blob_bottom_vec_.push_back(this->blob_bottom_);
LayerParameter layer_param;
shared_ptr<SplitByPhaseLayer<Dtype> > layer(
new SplitByPhaseLayer<Dtype>(layer_param));
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
EXPECT_EQ(this->blob_top_vec_[0]->num(), 1);
EXPECT_EQ(this->blob_top_vec_[0]->channels(), 1*64);
EXPECT_EQ(this->blob_top_vec_[0]->height(), 64);
EXPECT_EQ(this->blob_top_vec_[0]->width(), 64);
}
TYPED_TEST(SplitByPhaseLayerTest, TestForward) {
typedef typename TypeParam::Dtype Dtype;
Dtype* bottom_data = this->blob_bottom_->mutable_cpu_data();
const int num_filters = this->blob_bottom_->channels();
int n, c, p, h, w, bottom_fill_idx;
for (int index = 0; index < this->blob_bottom_->count(); ++index) {
w = index % 64;
h = (index / 64) % 64;
p = (index / 64 / 64) % 64;
c = (index / 64 / 64 / 64) % num_filters;
n = index / 64 / 64 / 64 / num_filters;
bottom_fill_idx = ((w*8)+(h*8*512)+(p%8)+(p/8)*512)+((n*num_filters+c)*512*512);
bottom_data[bottom_fill_idx] = p;
}
this->blob_bottom_vec_.push_back(this->blob_bottom_);
bool IS_VALID_CUDA = false;
#ifndef CPU_ONLY
IS_VALID_CUDA = CAFFE_TEST_CUDA_PROP.major >= 2;
#endif
if (Caffe::mode() == Caffe::CPU ||
sizeof(Dtype) == 4 || IS_VALID_CUDA) {
LayerParameter layer_param;
shared_ptr<SplitByPhaseLayer<Dtype> > layer(
new SplitByPhaseLayer<Dtype>(layer_param));
layer->SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer->Forward(this->blob_bottom_vec_, this->blob_top_vec_);
const Dtype* data = this->blob_top_vec_[0]->cpu_data();
const int num_phase_blocks = this->blob_top_vec_[0]->num()*this->blob_top_vec_[0]->channels();
for (int nc = 0; nc < num_phase_blocks; ++nc) {
for (int h = 0; h < 64 ; ++h ) {
for (int w = 0; w < 64 ; ++w ) {
CHECK_EQ(data[nc*(64*64)+h*64+w], nc%64);
}
}
}
} else {
LOG(ERROR) << "Skipping test due to old architecture.";
}
}
TYPED_TEST(SplitByPhaseLayerTest, TestGradient) {
typedef typename TypeParam::Dtype Dtype;
this->blob_bottom_vec_.push_back(this->blob_bottom_);
bool IS_VALID_CUDA = false;
#ifndef CPU_ONLY
IS_VALID_CUDA = CAFFE_TEST_CUDA_PROP.major >= 2;
#endif
// if (Caffe::mode() == Caffe::CPU ||
// sizeof(Dtype) == 4 || IS_VALID_CUDA) {
if (Caffe::mode() == Caffe::GPU) {
LayerParameter layer_param;
SplitByPhaseLayer<Dtype> layer(layer_param);
GradientChecker<Dtype> checker(1e-2, 1e-3);
checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_,
this->blob_top_vec_);
} else {
LOG(ERROR) << "Skipping test due to old architecture.";
}
}
} // namespace caffe

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

View File

@ -0,0 +1,70 @@
function [BN_Moments, stats] = bn_refine_phaseaware(varargin)
% Test the phasesplit net
opts.batchSize = 48;
opts.expDir = fullfile('data', 'JUNI-7504-PNet-dagnn-40-Seed-0-log_short') ;
opts.testEpoch = 40;
opts.saveResult = true;
opts.bnEpochCollectSize = 2000;
opts.gpuIdx = 1;
opts = vl_argparse( opts, varargin );
opts.imdbPath = fullfile(opts.expDir, 'imdb.mat');
opts.train = struct('gpus', opts.gpuIdx, 'cudnn', true, 'stegoShuffle', true ) ;
if ~isfield(opts.train, 'gpus'), opts.train.gpus = []; end;
% put it to drawing
if ( ~exist( opts.expDir, 'dir' ) )
error('expDir is empty' );
end
% -------------------------------------------------------------------------
% Find the data base
% -------------------------------------------------------------------------
if exist(opts.imdbPath, 'file')
imdb = load(opts.imdbPath) ;
else
error(' cannot find imdb' );
end
meta.inputSize = [512, 512, 1, opts.batchSize];
[BN_Moments, stats] = cnn_bnrefine_dag(imdb, getBatchFn( opts, meta ), ...
'expDir', opts.expDir, ...
'batchSize', opts.batchSize, ...
'testEpoch', opts.testEpoch, ...
'bnEpochCollectSize', opts.bnEpochCollectSize, ...
'saveResult', opts.saveResult, ...
opts.train ) ;
% -------------------------------------------------------------------------
function fn = getBatchFn(opts, meta)
% -------------------------------------------------------------------------
bopts.useGpu = numel(opts.train.gpus) > 0 ;
bopts.imageSize = meta.inputSize;
fn = @(x,y) getDagNNBatch(bopts,x,y) ;
% -------------------------------------------------------------------------
function inputs = getDagNNBatch(opts, imdb, batch)
% -------------------------------------------------------------------------
% label
labels = imdb.images.label(1,batch) ;
% images
images = zeros(opts.imageSize(1), opts.imageSize(2), ...
opts.imageSize(3), numel(batch), 'single') ;
for i = 1:numel(batch)
imt = load(imdb.images.name{batch(i)}, 'im');
images(:,:,:,i) = single(imt.im);
% imt = imread(imdb.images.name{batch(i)});
% images(:,:,:,i) = single(imt);
end
if opts.useGpu > 0
images = gpuArray(images) ;
end
inputs = {'input', images, 'label', labels} ;

View File

@ -0,0 +1,149 @@
function [net, info] = cnn_phaseaware(varargin)
%CNN_PHASEAWARE Demonstrates training a PhaseAwareNet on JUNI and UED
run(fullfile(fileparts(mfilename('fullpath')), ...
'..', '..', 'matlab', 'vl_setupnn.m')) ;
opts.modelType = 'PNet';
opts.seed = 0;
opts.networkType = 'dagnn' ;
opts.batchSize = 40;
opts.lrSequence = 'log_short';
opts.printDotFile = true;
opts.coverPath = 'C:\DeepLearning\matconvnet-1.0-beta20\data\JStego\75_mat';
opts.stegoPath = 'C:\DeepLearning\matconvnet-1.0-beta20\data\JStego\JUNI_0.4_mat';
sfx = [opts.modelType, '-', opts.networkType, '-', num2str(opts.batchSize), ...
'-Seed-', num2str(opts.seed), '-', opts.lrSequence] ;
opts.expDir = fullfile('data', ['JUNI-7504-' sfx]) ; % TODO
opts.imdbPath = fullfile(opts.expDir, 'imdb.mat');
opts.train = struct('gpus', [1,2], 'cudnn', true, 'stegoShuffle', true, 'computeBNMoment', true) ;
if ~isfield(opts.train, 'gpus'), opts.train.gpus = []; end;
% -------------------------------------------------------------------------
% Prepare model
% -------------------------------------------------------------------------
if (strcmpi( opts.modelType, 'PNet' ))
net = cnn_phaseaware_PNet_init( 'networkType', opts.networkType, ...
'batchSize', opts.batchSize, ...
'seed', opts.seed, ...
'lrSequence', opts.lrSequence );
elseif (strcmpi( opts.modelType, 'VNet' ))
net = cnn_phaseaware_VNet_init( 'networkType', opts.networkType, ...
'batchSize', opts.batchSize, ...
'seed', opts.seed, ...
'lrSequence', opts.lrSequence );
else
error('Unknown model type');
end
% put it to drawing
if ( ~exist( opts.expDir, 'dir' ) )
mkdir( opts.expDir );
end
if opts.printDotFile
net2dot(net, fullfile( opts.expDir, 'NetConfig.dot' ), ...
'BatchSize', net.meta.trainOpts.batchSize, ...
'Inputs', {'input', [net.meta.inputSize, net.meta.trainOpts.batchSize]});
end
% -------------------------------------------------------------------------
% Prepare data
% -------------------------------------------------------------------------
if exist(opts.imdbPath, 'file')
imdb = load(opts.imdbPath) ;
else
imdb = cnn_phaseaware_imdb_setup('coverPath', opts.coverPath, 'stegoPath', opts.stegoPath) ;
save(opts.imdbPath, '-struct', 'imdb') ;
end
% Set the class names in the network
net.meta.classes.name = imdb.classes.name ;
net.meta.classes.description = imdb.classes.description ;
% -------------------------------------------------------------------------
% Learn
% -------------------------------------------------------------------------
switch opts.networkType
case 'dagnn', trainFn = @cnn_train_dag ;
otherwise, error('wrong network type');
end
[net, info] = trainFn(net, imdb, getBatchFn(opts, net.meta), ...
'expDir', opts.expDir, ...
net.meta.trainOpts, ...
opts.train) ;
modelPath = fullfile(opts.expDir, 'net-deployed.mat');
switch opts.networkType
case 'dagnn'
net_ = net.saveobj() ;
save(modelPath, '-struct', 'net_') ;
clear net_ ;
end
% -------------------------------------------------------------------------
function fn = getBatchFn(opts, meta)
% -------------------------------------------------------------------------
bopts.useGpu = numel(opts.train.gpus) > 0 ;
bopts.imageSize = meta.inputSize;
switch lower(opts.networkType)
case 'dagnn'
fn = @(x,y) getDagNNBatch(bopts,x,y) ;
end
% -------------------------------------------------------------------------
function inputs = getDagNNBatch(opts, imdb, batch)
% -------------------------------------------------------------------------
% label
labels = imdb.images.label(1,batch) ;
% images
images = zeros(opts.imageSize(1), opts.imageSize(2), ...
opts.imageSize(3), numel(batch), 'single') ;
for i = 1:numel(batch)/2
% cover = imread(imdb.images.name{batch(2*i-1)});
% stego = imread(imdb.images.name{batch(2*i)});
imt = load(imdb.images.name{batch(2*i-1)}, 'im');
cover = single(imt.im);
imt = load(imdb.images.name{batch(2*i)}, 'im');
stego = single(imt.im);
% random rotate, 0, 90, 180, 270
r = randi(4) - 1;
cover = rot90( cover, r );
stego = rot90( stego, r );
% random mirror flip
if ( rand > 0.5 )
cover = fliplr( cover );
stego = fliplr( stego );
end
images(:,:,:,2*i-1) = single(cover);
images(:,:,:,2*i) = single(stego);
end
if opts.useGpu > 0
images = gpuArray(images) ;
end
inputs = {'input', images, 'label', labels} ;

View File

@ -0,0 +1,265 @@
function net = cnn_phaseaware_PNet_init(varargin)
% Define and initialize PhaseAwareNet net
opts.networkType = 'dagnn' ;
opts.batchSize = 40;
opts.seed = 0;
opts.lrSequence = 'step_long2';
opts = vl_argparse(opts, varargin) ;
rng( opts.seed );
net.layers = {} ;
convOpts = {'CudnnWorkspaceLimit', 1024*1024*1204} ;
HPF = zeros(5, 5, 1, 4, 'single');
HPF(:,:,1,1) = [ -1, 2, -2, 2, -1; ...
2, -6, 8, -6, 2; ...
-2, 8, -12, 8, -2; ...
2, -6, 8, -6, 2; ...
-1, 2, -2, 2, -1]/12;
HPF(:,:,1,2) = [ 0, 0, 5.2, 0, 0; ...
0, 23.4, 36.4, 23.4, 0; ...
5.2, 36.4, -261.0, 36.4, 5.2; ...
0, 23.4, 36.4, 23.4, 0; ...
0, 0, 5.2, 0, 0]/261;
HPF(:,:,1,3) = [ 0.0562, -0.1354, 0.0000, 0.1354, -0.0562; ...
0.0818, -0.1970, 0.0000, 0.1970, -0.0818; ...
0.0926, -0.2233, 0.0000, 0.2233, -0.0926; ...
0.0818, -0.1970, 0.0000, 0.1970, -0.0818; ...
0.0562, -0.1354, 0.0000, 0.1354, -0.0562 ];
HPF(:,:,1,4) = [-0.0562, -0.0818, -0.0926, -0.0818, -0.0562; ...
0.1354, 0.1970, 0.2233, 0.1970, 0.1354; ...
0.0000, 0.0000, 0.0000, -0.0000, -0.0000; ...
-0.1354, -0.1970, -0.2233, -0.1970, -0.1354; ...
0.0562, 0.0818, 0.0926, 0.0818, 0.0562 ];
net.layers{end+1} = struct('type', 'conv', ...
'name', 'HPFs', ...
'weights', {{HPF, []}}, ...
'learningRate', [0, 0], ...
'stride', 1, ...
'pad', 2, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
% Group 1
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_1', ...
'weights', {{init_weight('gaussian', 5, 5, 4, 8, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 2, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'abs', 'name', 'ABS_1') ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_1', ...
'weights', {{ones(8, 1, 'single'), ...
zeros(8, 1, 'single'), ...
zeros(8, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'tanh', 'name', 'TanH_1') ;
% Group 2
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_2', ...
'weights', {{init_weight('gaussian', 5, 5, 8, 16, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 2, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_2', ...
'weights', {{ones(16, 1, 'single'), ...
zeros(16, 1, 'single'), ...
zeros(16, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'tanh', 'name', 'TanH_2') ;
% Phase split here
net.layers{end+1} = struct('type', 'phasesplit', ...
'name', 'DCTPhaseSplit', ...
'pool', [1, 1], ...
'stride', 8, ...
'pad', 0 );
DCTMode = 64;
% Group 3
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_3', ...
'weights', {{init_weight('gaussian', 1, 1, 16, 32*DCTMode, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 0, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_3', ...
'weights', {{ones(32*DCTMode, 1, 'single'), ...
zeros(32*DCTMode, 1, 'single'), ...
zeros(32*DCTMode, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'relu', 'name', 'ReLU_3') ;
net.layers{end+1} = struct('type', 'pool', ...
'name', 'Pool_3', ...
'method', 'avg', ...
'pool', [5 5], ...
'stride', 2, ...
'pad', 2) ;
% Group 4
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_4', ...
'weights', {{init_weight('gaussian', 1, 1, 32, 64*DCTMode, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 0, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_4', ...
'weights', {{ones(64*DCTMode, 1, 'single'), ...
zeros(64*DCTMode, 1, 'single'), ...
zeros(64*DCTMode, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'relu', 'name', 'ReLU_4') ;
net.layers{end+1} = struct('type', 'pool', ...
'name', 'Pool_4', ...
'method', 'avg', ...
'pool', [5 5], ...
'stride', 2, ...
'pad', 2) ;
% Group 5
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_5', ...
'weights', {{init_weight('gaussian', 1, 1, 64, 128*DCTMode, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 0, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_5', ...
'weights', {{ones(128*DCTMode, 1, 'single'), ...
zeros(128*DCTMode, 1, 'single'), ...
zeros(128*DCTMode, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'relu', 'name', 'ReLU_5') ;
net.layers{end+1} = struct('type', 'pool', ...
'name', 'Pool_5', ...
'method', 'avg', ...
'pool', [16 16], ...
'stride', 1, ...
'pad', 0) ;
% Full connect layer
net.layers{end+1} = struct('type', 'conv', ...
'name', 'FC',...
'weights', {{init_weight('xavier', 1,1,128*DCTMode,2, 'single'), ...
0.01*ones(2, 1, 'single')}}, ...
'learningRate', [1 2], ...
'weightDecay', [1 0], ...
'stride', 1, ...
'pad', 0) ;
% Softmax layer
net.layers{end+1} = struct('type', 'softmaxloss', 'name', 'loss') ;
% Meta parameters
net.meta.inputSize = [512 512 1] ;
lr = get_lr_sequence(opts.lrSequence);
net.meta.trainOpts.learningRate = lr;
net.meta.trainOpts.numEpochs = numel(lr) ;
net.meta.trainOpts.batchSize = opts.batchSize ;
net.meta.trainOpts.weightDecay = 0.01;
% Fill in default values
net = vl_simplenn_tidy(net) ;
% Switch to DagNN if requested
switch lower(opts.networkType)
case 'simplenn'
% done
case 'dagnn'
net = dagnn.DagNN.fromSimpleNN(net, 'canonicalNames', true) ;
net.addLayer('error', dagnn.Loss('loss', 'classerror'), ...
{'prediction','label'}, 'error') ;
otherwise
assert(false) ;
end
% -------------------------------------------------------------------------
function weights = init_weight(weightInitMethod, h, w, in, out, type)
% -------------------------------------------------------------------------
% See K. He, X. Zhang, S. Ren, and J. Sun. Delving deep into
% rectifiers: Surpassing human-level performance on imagenet
% classification. CoRR, (arXiv:1502.01852v1), 2015.
switch lower(weightInitMethod)
case 'gaussian'
sc = 0.01 ;
weights = randn(h, w, in, out, type)*sc;
case 'xavier'
sc = sqrt(3/(h*w*in)) ;
weights = (rand(h, w, in, out, type)*2 - 1)*sc ;
case 'xavierimproved'
sc = sqrt(2/(h*w*out)) ;
weights = randn(h, w, in, out, type)*sc ;
otherwise
error('Unknown weight initialization method''%s''', weightInitMethod);
end
function lr = get_lr_sequence( lrGenerationMethod )
switch lower(lrGenerationMethod)
case 'step_short'
lr = 0.001 * ones(1, 2);
for i = 1:39
lr =[lr, lr(end-1:end)*0.9];
end
case 'log_short'
%lr = logspace(-3, -5, 80);
lr = logspace(-3, -5, 40 );
case 'step_long'
numInterationPerEpoch = 8000/64;
lrStepSize = 5000/numInterationPerEpoch; %
totalStep = 220000/5000; % CNN is trained for 120,000 iterations
lr = 0.001*ones(1,lrStepSize);
for i = 1:totalStep - 1
lr = [lr, lr(end-lrStepSize+1:end) *0.9];
end
case 'step_long2'
numInterationPerEpoch = 8000/64;
lrStepSize = 2500/numInterationPerEpoch; %
totalStep = 12;
lr = 0.001*ones(1,lrStepSize);
for i = 1:totalStep - 1
lr = [lr, lr(end-lrStepSize+1:end) *0.75];
end
case 'step_long3'
numInterationPerEpoch = 8000/64;
lrStepSize = 2500/numInterationPerEpoch/2; %
totalStep = 10;
lr = 0.001*ones(1,lrStepSize);
for i = 1:totalStep - 1
lr = [lr, lr(end-lrStepSize+1:end) *0.5];
end
otherwise
error('unkown type of lr sequence generation method''%s''', lrGenerationMethod);
end

View File

@ -0,0 +1,264 @@
function net = cnn_phaseaware_VNet_init(varargin)
% Define and initialize PhaseAwareNet net
opts.networkType = 'dagnn' ;
opts.batchSize = 40;
opts.seed = 0;
opts.lrSequence = 'step_long2';
opts = vl_argparse(opts, varargin) ;
rng( opts.seed );
net.layers = {} ;
convOpts = {'CudnnWorkspaceLimit', 1024*1024*1204} ;
HPF = zeros(5, 5, 1, 4, 'single');
HPF(:,:,1,1) = [ -1, 2, -2, 2, -1; ...
2, -6, 8, -6, 2; ...
-2, 8, -12, 8, -2; ...
2, -6, 8, -6, 2; ...
-1, 2, -2, 2, -1]/12;
HPF(:,:,1,2) = [ 0, 0, 5.2, 0, 0; ...
0, 23.4, 36.4, 23.4, 0; ...
5.2, 36.4, -261.0, 36.4, 5.2; ...
0, 23.4, 36.4, 23.4, 0; ...
0, 0, 5.2, 0, 0]/261;
HPF(:,:,1,3) = [ 0.0562, -0.1354, 0.0000, 0.1354, -0.0562; ...
0.0818, -0.1970, 0.0000, 0.1970, -0.0818; ...
0.0926, -0.2233, 0.0000, 0.2233, -0.0926; ...
0.0818, -0.1970, 0.0000, 0.1970, -0.0818; ...
0.0562, -0.1354, 0.0000, 0.1354, -0.0562 ];
HPF(:,:,1,4) = [-0.0562, -0.0818, -0.0926, -0.0818, -0.0562; ...
0.1354, 0.1970, 0.2233, 0.1970, 0.1354; ...
0.0000, 0.0000, 0.0000, -0.0000, -0.0000; ...
-0.1354, -0.1970, -0.2233, -0.1970, -0.1354; ...
0.0562, 0.0818, 0.0926, 0.0818, 0.0562 ];
net.layers{end+1} = struct('type', 'conv', ...
'name', 'HPFs', ...
'weights', {{HPF, []}}, ...
'learningRate', [0, 0], ...
'stride', 1, ...
'pad', 2, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
% Group 1
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_1', ...
'weights', {{init_weight('gaussian', 5, 5, 4, 8, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 2, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'abs', 'name', 'ABS_1') ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_1', ...
'weights', {{ones(8, 1, 'single'), ...
zeros(8, 1, 'single'), ...
zeros(8, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'tanh', 'name', 'TanH_1') ;
% Group 2
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_2', ...
'weights', {{init_weight('gaussian', 5, 5, 8, 16, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 2, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_2', ...
'weights', {{ones(16, 1, 'single'), ...
zeros(16, 1, 'single'), ...
zeros(16, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'tanh', 'name', 'TanH_2') ;
% Phase split here
net.layers{end+1} = struct('type', 'phasesplit', ...
'name', 'DCTPhaseSplit', ...
'pool', [1, 1], ...
'stride', 8, ...
'pad', 0 );
DCTMode = 64;
% Group 3
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_3', ...
'weights', {{init_weight('gaussian', 1, 1, 16*DCTMode, 128, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 0, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_3', ...
'weights', {{ones(128, 1, 'single'), ...
zeros(128, 1, 'single'), ...
zeros(128, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'relu', 'name', 'ReLU_3') ;
net.layers{end+1} = struct('type', 'pool', ...
'name', 'Pool_3', ...
'method', 'avg', ...
'pool', [5 5], ...
'stride', 2, ...
'pad', 2) ;
% Group 4
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_4', ...
'weights', {{init_weight('gaussian', 1, 1, 128, 256, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 0, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_4', ...
'weights', {{ones(256, 1, 'single'), ...
zeros(256, 1, 'single'), ...
zeros(256, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'relu', 'name', 'ReLU_4') ;
net.layers{end+1} = struct('type', 'pool', ...
'name', 'Pool_4', ...
'method', 'avg', ...
'pool', [5 5], ...
'stride', 2, ...
'pad', 2) ;
% Group 5
net.layers{end+1} = struct('type', 'conv', ...
'name', 'CONV_5', ...
'weights', {{init_weight('gaussian', 1, 1, 256, 512, 'single'), ...
[]}}, ...
'learningRate', [1, 0], ...
'stride', 1, ...
'pad', 0, ...
'weightDecay', [0, 0], ...
'opts', {convOpts}) ;
net.layers{end+1} = struct('type', 'bnorm', 'name', 'BN_5', ...
'weights', {{ones(512, 1, 'single'), ...
zeros(512, 1, 'single'), ...
zeros(512, 2, 'single')}}, ...
'learningRate', [1 1 0.01], ...
'weightDecay', [0 0]) ;
net.layers{end+1} = struct('type', 'relu', 'name', 'ReLU_5') ;
net.layers{end+1} = struct('type', 'pool', ...
'name', 'Pool_5', ...
'method', 'avg', ...
'pool', [16 16], ...
'stride', 1, ...
'pad', 0) ;
% Full connect layer
net.layers{end+1} = struct('type', 'conv', ...
'name', 'FC',...
'weights', {{init_weight('xavier', 1,1,512,2, 'single'), ...
0.01*ones(2, 1, 'single')}}, ...
'learningRate', [1 2], ...
'weightDecay', [1 0], ...
'stride', 1, ...
'pad', 0) ;
% Softmax layer
net.layers{end+1} = struct('type', 'softmaxloss', 'name', 'loss') ;
% Meta parameters
net.meta.inputSize = [512 512 1] ;
lr = get_lr_sequence(opts.lrSequence);
net.meta.trainOpts.learningRate = lr;
net.meta.trainOpts.numEpochs = numel(lr) ;
net.meta.trainOpts.batchSize = opts.batchSize ;
net.meta.trainOpts.weightDecay = 0.01; % In the paper it is 0.01,
%but it is only applied to Batch Normalization
% Fill in default values
net = vl_simplenn_tidy(net) ;
% Switch to DagNN if requested
switch lower(opts.networkType)
case 'simplenn'
% done
case 'dagnn'
net = dagnn.DagNN.fromSimpleNN(net, 'canonicalNames', true) ;
net.addLayer('error', dagnn.Loss('loss', 'classerror'), ...
{'prediction','label'}, 'error') ;
otherwise
assert(false) ;
end
% -------------------------------------------------------------------------
function weights = init_weight(weightInitMethod, h, w, in, out, type)
% -------------------------------------------------------------------------
% See K. He, X. Zhang, S. Ren, and J. Sun. Delving deep into
% rectifiers: Surpassing human-level performance on imagenet
% classification. CoRR, (arXiv:1502.01852v1), 2015.
switch lower(weightInitMethod)
case 'gaussian'
sc = 0.01 ;
weights = randn(h, w, in, out, type)*sc;
case 'xavier'
sc = sqrt(3/(h*w*in)) ;
weights = (rand(h, w, in, out, type)*2 - 1)*sc ;
case 'xavierimproved'
sc = sqrt(2/(h*w*out)) ;
weights = randn(h, w, in, out, type)*sc ;
otherwise
error('Unknown weight initialization method''%s''', weightInitMethod);
end
function lr = get_lr_sequence( lrGenerationMethod )
switch lower(lrGenerationMethod)
case 'step_short'
lr = 0.001 * ones(1, 2);
for i = 1:39
lr =[lr, lr(end-1:end)*0.9];
end
case 'log_short'
%lr = logspace(-3, -5, 80);
lr = logspace(-3, -5, 40 );
case 'step_long'
numInterationPerEpoch = 8000/64;
lrStepSize = 5000/numInterationPerEpoch; %
totalStep = 220000/5000; % CNN is trained for 120,000 iterations
lr = 0.001*ones(1,lrStepSize);
for i = 1:totalStep - 1
lr = [lr, lr(end-lrStepSize+1:end) *0.9];
end
case 'step_long2'
numInterationPerEpoch = 8000/64;
lrStepSize = 2500/numInterationPerEpoch; %
totalStep = 12; % 8: 160 epoch 12: 240 epoch
lr = 0.001*ones(1,lrStepSize);
for i = 1:totalStep - 1
lr = [lr, lr(end-lrStepSize+1:end) *0.75];
end
case 'step_long3'
numInterationPerEpoch = 8000/64;
lrStepSize = 2500/numInterationPerEpoch/2; %
totalStep = 12;
lr = 0.001*ones(1,lrStepSize);
for i = 1:totalStep - 1
lr = [lr, lr(end-lrStepSize+1:end) *0.5];
end
otherwise
error('unkown type of lr sequence generation method''%s''', lrGenerationMethod);
end

View File

@ -0,0 +1,149 @@
function imdb = cnn_phaseaware_imdb_setup( varargin )
opts.seed = 0;
opts.coverPath = 'C:\DeepLearning\matconvnet-1.0-beta20\data\JStego\75_mat';
opts.stegoPath = 'C:\DeepLearning\matconvnet-1.0-beta20\data\JStego\JUNI_0.4_mat';
opts.ratio = [0.6, 0.15, 0.25]; % train, validation, and test
opts.libSize = inf;
opts = vl_argparse( opts, varargin );
rng( opts.seed );
opts.ratio = opts.ratio/sum(opts.ratio);
% -------------------------------------------------------------------------
% Sanity Check
% -------------------------------------------------------------------------
fprintf('sanity check the library images ...') ;
targetSize = 10000;
expArray = linspace(1, targetSize, targetSize);
% first, sanilty the two data base
list = dir(fullfile(opts.coverPath, '*.mat'));
tokens = regexp({list.name}, '([\d]+).mat', 'tokens') ;
nameArray = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
if( ~isequal( sort(nameArray), expArray ) )
error('coverPath = %s is corrupted', opts.coverPath);
end
list = dir(fullfile(opts.stegoPath, '*.mat'));
tokens = regexp({list.name}, '([\d]+).mat', 'tokens') ;
nameArray = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
if( ~isequal( sort(nameArray), expArray ) )
error('stegoPath = %s is corrupted', opts.stegoPath);
end
fprintf('[checked]\n') ;
% meta
randomImages = randperm( targetSize );
totalSize = min( opts.libSize, targetSize );
numTrn = fix( totalSize * opts.ratio(1));
numVal = fix( totalSize * opts.ratio(2));
numTst = fix( totalSize * opts.ratio(3));
imdb.classes.name = {'Cover', 'Stego'} ;
n = strfind(opts.coverPath, filesep);
if( isempty( n ) )
coverDes = 'Cover Images';
else
coverDes = opts.coverPath(n(end)+1:end);
end
n = strfind(opts.stegoPath, filesep);
if( isempty( n ) )
stegoDes = 'Stego Images';
else
stegoDes = opts.stegoPath(n(end)+1:end);
end
imdb.classes.description = {coverDes, stegoDes} ;
imdb.classes.coverPath = opts.coverPath;
imdb.classes.stegoPath = opts.stegoPath;
fprintf('%d Trn Image, %d Val Images, and %d Test Images \n ', ...
numTrn, numVal, numTst) ;
% -------------------------------------------------------------------------
% Training images
% -------------------------------------------------------------------------
fprintf('searching training images ...') ;
names = cell(1, numTrn * 2 );
labels = ones(1, numTrn * 2 );
for i = 1:numTrn
idx = randomImages(i);
names{2*i-1} = fullfile(opts.coverPath, strcat(num2str(idx),'.mat'));
labels(2*i - 1) = 1;
names{2*i} = fullfile(opts.stegoPath, strcat(num2str(idx),'.mat'));
labels(2*i) = 2;
end
imdb.images.id = 1:numel(names) ;
imdb.images.name = names ;
imdb.images.set = ones(1, numel(names)) ;
imdb.images.label = labels ;
fprintf('done\n') ;
% -------------------------------------------------------------------------
% Validation images
% -------------------------------------------------------------------------
fprintf('searching validation images ...') ;
names = cell(1, numVal * 2 );
labels = ones(1, numVal * 2 );
for i = 1:numVal
idx = randomImages( numTrn + i);
names{2*i-1} = fullfile(opts.coverPath, strcat(num2str(idx),'.mat'));
labels(2*i - 1) = 1;
names{2*i} = fullfile(opts.stegoPath, strcat(num2str(idx),'.mat'));
labels(2*i) = 2;
end
imdb.images.id = horzcat( imdb.images.id, (1:numel(names)) + 1e7 - 1 );
imdb.images.name = horzcat(imdb.images.name, names );
imdb.images.set = horzcat( imdb.images.set, 2 * ones(1, numel(names)));
imdb.images.label = horzcat( imdb.images.label, labels ) ;
fprintf('done\n') ;
% -------------------------------------------------------------------------
% Test images
% -------------------------------------------------------------------------
fprintf('searching test images ...') ;
names = cell(1, numTst * 2 );
labels = ones(1, numTst * 2 );
for i = 1:numTst
idx = randomImages( numTrn + numVal + i);
names{2*i-1} = fullfile(opts.coverPath, strcat(num2str(idx),'.mat'));
labels(2*i - 1) = 1;
names{2*i} = fullfile(opts.stegoPath, strcat(num2str(idx),'.mat'));
labels(2*i) = 2;
end
imdb.images.id = horzcat( imdb.images.id, (1:numel(names)) + 2e7 - 1 );
imdb.images.name = horzcat(imdb.images.name, names );
imdb.images.set = horzcat( imdb.images.set, 3 * ones(1, numel(names)));
imdb.images.label = horzcat( imdb.images.label, labels ) ;
fprintf('done\n') ;

View File

@ -0,0 +1,72 @@
function [stats, state] = test_phaseaware(varargin)
% Test the phasesplit net
opts.batchSize = 40;
opts.expDir = fullfile('data', 'JUNI-7504-PNet-dagnn-40-Seed-0-log_short') ;
opts.testEpoch = 40;
opts.testSelect = [0, 1, 1]; % (1) training; (2)validation; (3), testing
opts.saveResult = true;
opts.bnRefine = true;
opts = vl_argparse( opts, varargin );
opts.imdbPath = fullfile(opts.expDir, 'imdb.mat');
opts.train = struct('gpus', [1, 2], 'cudnn', true, 'stegoShuffle', false ) ;
%opts.train = struct('gpus', [], 'stegoShuffle', true) ; // CPU debugging
if ~isfield(opts.train, 'gpus'), opts.train.gpus = []; end;
% put it to drawing
if ( ~exist( opts.expDir, 'dir' ) )
error('expDir is empty' );
end
% -------------------------------------------------------------------------
% Find the data base
% -------------------------------------------------------------------------
if exist(opts.imdbPath, 'file')
imdb = load(opts.imdbPath) ;
else
error(' cannot find imdb' );
end
meta.inputSize = [512, 512, 1, opts.batchSize];
[state, stats] = cnn_test_dag(imdb, getBatchFn( opts, meta ), ...
'expDir', opts.expDir, ...
'batchSize', opts.batchSize, ...
'testEpoch', opts.testEpoch, ...
'testSelect', opts.testSelect, ...
'saveResult', opts.saveResult, ...
'bnRefine', opts.bnRefine, ...
opts.train ) ;
% -------------------------------------------------------------------------
function fn = getBatchFn(opts, meta)
% -------------------------------------------------------------------------
bopts.useGpu = numel(opts.train.gpus) > 0 ;
bopts.imageSize = meta.inputSize;
fn = @(x,y) getDagNNBatch(bopts,x,y) ;
% -------------------------------------------------------------------------
function inputs = getDagNNBatch(opts, imdb, batch)
% -------------------------------------------------------------------------
% label
labels = imdb.images.label(1,batch) ;
% images
images = zeros(opts.imageSize(1), opts.imageSize(2), ...
opts.imageSize(3), numel(batch), 'single') ;
for i = 1:numel(batch)
% imt = imread(imdb.images.name{batch(i)});
% images(:,:,:,i) = single(imt);
imt = load(imdb.images.name{batch(i)}, 'im');
images(:,:,:,i) = single(imt.im);
end
if opts.useGpu > 0
images = gpuArray(images) ;
end
inputs = {'input', images, 'label', labels} ;

View File

@ -0,0 +1,284 @@
function [BN_Moments,stats] = cnn_bnrefine_dag( imdb, getBatch, varargin)
%CNN_TEST_DAG Demonstrates test a CNN using the DagNN wrapper
% CNN_TEST_DAG() is a slim version to CNN_TRAIN_DAG(), just do the
% testing of the final net in the export
opts.expDir = fullfile('data','exp') ;
opts.batchSize = 256 ;
opts.train = [] ;
opts.val = [] ;
opts.test = [];
opts.gpus = [] ;
opts.prefetch = false ;
opts.testEpoch = inf;
opts.bnEpochCollectSize = 2000;
opts.saveResult = true;
opts.randomSeed = 0 ;
opts.stegoShuffle = false;
opts.cudnn = true ;
opts.extractStatsFn = @extractStats ;
opts = vl_argparse(opts, varargin) ;
if ~exist(opts.expDir, 'dir'), mkdir(opts.expDir) ; end
if isempty(opts.train), opts.train = find(imdb.images.set==1) ; end
if isnan(opts.train), opts.train = [] ; end
% we must restrict the BN moment pooling from train set only
% -------------------------------------------------------------------------
% Initialization
% -------------------------------------------------------------------------
state.getBatch = getBatch ;
% -------------------------------------------------------------------------
% Train and validate
% -------------------------------------------------------------------------
modelPath = @(ep) fullfile(opts.expDir, sprintf('net-epoch-%d.mat', ep));
resultPath = @(ep) fullfile(opts.expDir, sprintf('bn-epoch-%d.mat', ep));
start = findLastCheckpoint(opts.expDir) ;
if( start < 1 )
error( 'Found no net' );
end
if start >= 1
start = min(start, opts.testEpoch);
fprintf('%s: testing by loading epoch %d\n', mfilename, start) ;
net = loadState(modelPath(start)) ;
end
% First, create the structure to pool the BN moments
numLayers = numel(net.layers);
BN_Moments = struct('layer', {}, ...
'name', {}, ...
'inputs', {}, ...
'outputs', {}, ...
'shape', {}, ...
'dataType', {}, ...
'oldValue', {}, ...
'hist', {} ) ;
for i = 1:numLayers
if ( isa( net.layers(i).block, 'dagnn.BatchNorm') )
% Neet to save the BN moments for pooling
net.layers(i).block.computeMoment = true;
name = net.layers(i).params{3};
dataType = class(net.getParam(name).value);
shape = size(net.getParam(name).value);
BN_Moments(end+1).layer = net.layers(i).name;
BN_Moments(end).name = name ;
BN_Moments(end).inputs = net.layers(i).inputs;
BN_Moments(end).outputs = net.layers(i).outputs;
BN_Moments(end).shape = shape ;
BN_Moments(end).dataType = dataType ;
BN_Moments(end).oldValue = net.getParam(name).value;
end
end
if( numel(opts.gpus) > 1 )
error( 'cannot support multiple GPU now ')
end
numEpoch = ceil(opts.bnEpochCollectSize/(numel(opts.train)/opts.batchSize));
rng(start + opts.randomSeed) ;
for epoch = start:start + numEpoch - 1
% Set the random seed based on the epoch and opts.randomSeed.
% This is important for reproducibility, including when training
% is restarted from a checkpoint.
prepareGPUs( opts, true ) ;
% Train for one epoch.
state.epoch = epoch ;
% shuffle
if( opts.stegoShuffle )
N = numel(opts.train); % TRN
Lab = max( 1, numel(opts.gpus));
% M and N must be even, and multiple Lab
assert( rem( N, 2*Lab ) == 0 );
seq = opts.train( 2*randperm(N/2) - 1 );
seq = reshape( seq, Lab, N/(2*Lab) );
state.train = reshape( [seq; seq+1], 1, N );
else
state.train = opts.train(randperm(numel(opts.train))) ;
end
state.imdb = imdb ;
% keep pooling the result
[stats.train(epoch - start + 1), BN_Moments] = process_epoch(net, state, opts, BN_Moments ) ;
end
% Reset the parameters
for i = 1:numel(BN_Moments)
bn_moment_name = BN_Moments(i).name;
statsVal = median(BN_Moments(i).hist, 3);
% set the new value
paramIdx = net.getParamIndex(bn_moment_name);
% double check the shape, see if it matches
assert( isequal(size(statsVal), size(net.params(paramIdx).value ) ) );
% reset the BN moment parameters
net.params(paramIdx).value = statsVal;
end
% Revert it back
for i = 1:numel(net.layers)
if ( isa( net.layers(i).block, 'dagnn.BatchNorm') )
net.layers(i).block.computeMoment = false;
end
end
saveState(resultPath(start), net, stats, BN_Moments ) ;
% -------------------------------------------------------------------------
function [stats, BN_Moments] = process_epoch(net, state, opts, BN_Moments )
% -------------------------------------------------------------------------
% move CNN to GPU as needed
numGpus = numel(opts.gpus) ;
if numGpus >= 1
net.move('gpu') ;
end
subset = state.train;
num = 0 ;
stats.num = 0 ; % return something even if subset = []
stats.time = 0 ;
adjustTime = 0 ;
start = tic ;
for t=1:opts.batchSize:numel(subset)
fprintf('%s: epoch %02d: %3d/%3d:', 'test', state.epoch, ...
fix((t-1)/opts.batchSize)+1, ceil(numel(subset)/opts.batchSize)) ;
batchSize = min(opts.batchSize, numel(subset) - t + 1) ;
% get this image batch and prefetch the next
s = 1;
batchStart = t + (labindex-1) + (s-1) * numlabs ;
batchEnd = min(t+opts.batchSize-1, numel(subset)) ;
batch = subset(batchStart : numlabs : batchEnd) ;
num = num + numel(batch) ;
if numel(batch) == 0, continue ; end
inputs = state.getBatch(state.imdb, batch) ;
net.mode = 'test' ;
net.eval(inputs) ;
% update here
for i = 1:numel(BN_Moments)
layer_name = BN_Moments(i).layer;
newVal = gather( net.getLayer(layer_name).block.moments );
assert( ~isempty( newVal ) ); % in case the BatchNorm is not set up
BN_Moments(i).hist = cat( 3, BN_Moments(i).hist, newVal );
end
% get statistics
time = toc(start) + adjustTime ;
batchTime = time - stats.time ;
stats = opts.extractStatsFn(net) ;
stats.num = num ;
stats.time = time ;
currentSpeed = batchSize / batchTime ;
averageSpeed = (t + batchSize - 1) / time ;
if t == opts.batchSize + 1
% compensate for the first iteration, which is an outlier
adjustTime = 2*batchTime - time ;
stats.time = time + adjustTime ;
end
fprintf(' %.1f (%.1f) Hz', averageSpeed, currentSpeed) ;
for f = setdiff(fieldnames(stats)', {'num', 'time'})
f = char(f) ;
fprintf(' %s:', f) ;
fprintf(' %.3f', stats.(f)) ;
end
fprintf('\n') ;
end
net.reset() ;
net.move('cpu') ;
% -------------------------------------------------------------------------
function stats = extractStats(net)
% -------------------------------------------------------------------------
sel = find(cellfun(@(x) isa(x,'dagnn.Loss'), {net.layers.block})) ;
stats = struct() ;
for i = 1:numel(sel)
stats.(net.layers(sel(i)).outputs{1}) = net.layers(sel(i)).block.average ;
end
% -------------------------------------------------------------------------
function saveState(fileName, net, stats, BN_Moments )
% -------------------------------------------------------------------------
net_ = net ;
net = net_.saveobj() ;
save(fileName, 'net', 'stats', 'BN_Moments') ;
% -------------------------------------------------------------------------
function net = loadState(fileName)
% -------------------------------------------------------------------------
load(fileName, 'net' ) ;
net = dagnn.DagNN.loadobj(net) ;
% -------------------------------------------------------------------------
function epoch = findLastCheckpoint(modelDir)
% -------------------------------------------------------------------------
list = dir(fullfile(modelDir, 'net-epoch-*.mat')) ;
tokens = regexp({list.name}, 'net-epoch-([\d]+).mat', 'tokens') ;
epoch = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
epoch = max([epoch 0]) ;
% -------------------------------------------------------------------------
function prepareGPUs(opts, cold)
% -------------------------------------------------------------------------
numGpus = numel(opts.gpus) ;
if numGpus > 1
% check parallel pool integrity as it could have timed out
pool = gcp('nocreate') ;
if ~isempty(pool) && pool.NumWorkers ~= numGpus
delete(pool) ;
end
pool = gcp('nocreate') ;
if isempty(pool)
parpool('local', numGpus) ;
cold = true ;
end
end
if numGpus >= 1 && cold
fprintf('%s: resetting GPU\n', mfilename)
if numGpus == 1
gpuDevice(opts.gpus)
else
spmd, gpuDevice(opts.gpus(labindex)), end
end
end
%end

View File

@ -0,0 +1,341 @@
function [state,stats] = cnn_test_dag( imdb, getBatch, varargin)
%CNN_TEST_DAG Demonstrates test a CNN using the DagNN wrapper
% CNN_TEST_DAG() is a slim version to CNN_TRAIN_DAG(), just do the
% testing of the final net in the export
opts.expDir = fullfile('data','exp') ;
opts.batchSize = 256 ;
opts.numSubBatches = 1 ;
opts.train = [] ;
opts.val = [] ;
opts.test = [];
opts.gpus = [] ;
opts.prefetch = false ;
opts.testEpoch = inf;
opts.testSelect = [1, 1, 1]; % (1) training; (2)validation; (3), testing
opts.saveResult = true;
opts.bnRefine = false;
opts.randomSeed = 0 ;
opts.stegoShuffle = false;
opts.cudnn = true ;
opts.extractStatsFn = @extractStats ;
opts = vl_argparse(opts, varargin) ;
if ~exist(opts.expDir, 'dir'), mkdir(opts.expDir) ; end
if isempty(opts.train), opts.train = find(imdb.images.set==1) ; end
if isempty(opts.val), opts.val = find(imdb.images.set==2) ; end
if isempty(opts.test), opts.test = find(imdb.images.set==3); end
if isnan(opts.train), opts.train = [] ; end
% -------------------------------------------------------------------------
% Initialization
% -------------------------------------------------------------------------
state.getBatch = getBatch ;
% -------------------------------------------------------------------------
% Train and validate
% -------------------------------------------------------------------------
if ( opts.bnRefine )
modelPath = @(ep) fullfile(opts.expDir, sprintf('bn-epoch-%d.mat', ep));
resultPath = @(ep) fullfile(opts.expDir, sprintf('test-bn-epoch-%d.mat', ep));
else
modelPath = @(ep) fullfile(opts.expDir, sprintf('net-epoch-%d.mat', ep));
resultPath = @(ep) fullfile(opts.expDir, sprintf('test-net-epoch-%d.mat', ep));
end
start = findLastCheckpoint(opts.expDir) ;
if( start < 1 )
error( 'Found no net' );
end
if start >= 1
start = min(start, opts.testEpoch);
fprintf('%s: testing by loading epoch name %s\n', mfilename, modelPath(start) );
net = loadState(modelPath(start)) ;
end
% Make sure that we use the estimated BN moments
for i = 1:numel(net.layers)
if ( isa( net.layers(i).block, 'dagnn.BatchNorm') )
net.layers(i).block.computeMoment = false;
end
end
for epoch = start
% Set the random seed based on the epoch and opts.randomSeed.
% This is important for reproducibility, including when training
% is restarted from a checkpoint.
rng(epoch + opts.randomSeed) ;
prepareGPUs(opts, true ) ;
% Train for one epoch.
state.epoch = epoch ;
% shuffle
if( opts.stegoShuffle )
N = numel(opts.train); % TRN
M = numel(opts.val); % VAL
K = numel(opts.test); % TST
Lab = max( 1, numel(opts.gpus));
% M and N must be even, and multiple Lab
assert( ( rem( N, 2*Lab ) == 0 ) & ...
( rem( M, 2*Lab ) == 0 ) & ...
( rem( K, 2*Lab ) == 0 ) );
seq = opts.train( 2*randperm(N/2) - 1 );
seq = reshape( seq, Lab, N/(2*Lab) );
state.train = reshape( [seq; seq+1], 1, N );
seq = opts.val( 2*randperm(M/2) - 1 );
seq = reshape( seq, Lab, M/(2*Lab) );
state.val = reshape( [seq; seq+1], 1, M );
seq = opts.test( 2*randperm(K/2) - 1 );
seq = reshape( seq, Lab, K/(2*Lab) );
state.test = reshape( [seq; seq+1], 1, K );
else
state.train = opts.train(randperm(numel(opts.train))) ;
state.val = opts.val(randperm(numel(opts.val))) ;
state.test = opts.test(randperm(numel(opts.test))) ;
% N = numel(opts.train); % TRN
% M = numel(opts.val); % VAL
% K = numel(opts.test); % TST
%
%
% state.train = opts.train([1:2:N, 2:2:N]);
% state.val = opts.val([1:2:M, 2:2:M]);
% state.test = opts.test([1:2:K, 2:2:K]);
end
state.imdb = imdb ;
if numel(opts.gpus) <= 1
if( opts.testSelect(1) )
stats.train = process_epoch(net, state, opts, 'train') ;
end
if( opts.testSelect(2) )
stats.val = process_epoch(net, state, opts, 'val') ;
end
if( opts.testSelect(3) )
stats.test = process_epoch(net, state, opts, 'test');
end
else
savedNet = net.saveobj() ;
spmd
net_ = dagnn.DagNN.loadobj(savedNet) ;
if( opts.testSelect(1) )
stats_.train = process_epoch(net_, state, opts, 'train') ;
end
if( opts.testSelect(2) )
stats_.val = process_epoch(net_, state, opts, 'val') ;
end
if( opts.testSelect(3) )
stats_.test = process_epoch(net_, state, opts, 'test');
end
if labindex == 1, savedNet_ = net_.saveobj() ; end
end
net = dagnn.DagNN.loadobj(savedNet_{1}) ;
stats__ = accumulateStats(stats_) ;
if( opts.testSelect(1) )
stats.train = stats__.train ;
end
if( opts.testSelect(2) )
stats.val = stats__.val ;
end
if( opts.testSelect(3) )
stats.test = stats__.test;
end
clear net_ stats_ stats__ savedNet savedNet_ ;
end
% save
if( opts.saveResult == true )
saveState(resultPath(epoch), net, stats, state) ;
end
end
% -------------------------------------------------------------------------
function stats = process_epoch(net, state, opts, mode)
% -------------------------------------------------------------------------
% move CNN to GPU as needed
numGpus = numel(opts.gpus) ;
if numGpus >= 1
net.move('gpu') ;
end
subset = state.(mode) ;
num = 0 ;
stats.num = 0 ; % return something even if subset = []
stats.time = 0 ;
adjustTime = 0 ;
start = tic ;
for t=1:opts.batchSize:numel(subset)
fprintf('%s: epoch %02d: %3d/%3d:', mode, state.epoch, ...
fix((t-1)/opts.batchSize)+1, ceil(numel(subset)/opts.batchSize)) ;
batchSize = min(opts.batchSize, numel(subset) - t + 1) ;
for s=1:opts.numSubBatches
% get this image batch and prefetch the next
batchStart = t + (labindex-1) + (s-1) * numlabs ;
batchEnd = min(t+opts.batchSize-1, numel(subset)) ;
batch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
num = num + numel(batch) ;
if numel(batch) == 0, continue ; end
inputs = state.getBatch(state.imdb, batch) ;
if opts.prefetch
if s == opts.numSubBatches
batchStart = t + (labindex-1) + opts.batchSize ;
batchEnd = min(t+2*opts.batchSize-1, numel(subset)) ;
else
batchStart = batchStart + numlabs ;
end
nextBatch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
state.getBatch(state.imdb, nextBatch) ;
end
net.mode = 'test' ;
net.eval(inputs) ;
end
% get statistics
time = toc(start) + adjustTime ;
batchTime = time - stats.time ;
stats = opts.extractStatsFn(net) ;
stats.num = num ;
stats.time = time ;
currentSpeed = batchSize / batchTime ;
averageSpeed = (t + batchSize - 1) / time ;
if t == opts.batchSize + 1
% compensate for the first iteration, which is an outlier
adjustTime = 2*batchTime - time ;
stats.time = time + adjustTime ;
end
fprintf(' %.1f (%.1f) Hz', averageSpeed, currentSpeed) ;
for f = setdiff(fieldnames(stats)', {'num', 'time'})
f = char(f) ;
fprintf(' %s:', f) ;
fprintf(' %.3f', stats.(f)) ;
end
fprintf('\n') ;
end
net.reset() ;
net.move('cpu') ;
% -------------------------------------------------------------------------
function stats = accumulateStats(stats_)
% -------------------------------------------------------------------------
for s = {'train', 'val', 'test'}
s = char(s) ;
total = 0 ;
% initialize stats stucture with same fields and same order as
% stats_{1}
stats__ = stats_{1} ;
if ( ~isfield(stats__, s) )
continue;
end
names = fieldnames(stats__.(s))' ;
values = zeros(1, numel(names)) ;
fields = cat(1, names, num2cell(values)) ;
stats.(s) = struct(fields{:}) ;
for g = 1:numel(stats_)
stats__ = stats_{g} ;
num__ = stats__.(s).num ;
total = total + num__ ;
for f = setdiff(fieldnames(stats__.(s))', 'num')
f = char(f) ;
stats.(s).(f) = stats.(s).(f) + stats__.(s).(f) * num__ ;
if g == numel(stats_)
stats.(s).(f) = stats.(s).(f) / total ;
end
end
end
stats.(s).num = total ;
end
% -------------------------------------------------------------------------
function stats = extractStats(net)
% -------------------------------------------------------------------------
sel = find(cellfun(@(x) isa(x,'dagnn.Loss'), {net.layers.block})) ;
stats = struct() ;
for i = 1:numel(sel)
stats.(net.layers(sel(i)).outputs{1}) = net.layers(sel(i)).block.average ;
end
% -------------------------------------------------------------------------
function saveState(fileName, net, stats, state )
% -------------------------------------------------------------------------
net_ = net ;
net = net_.saveobj() ;
save(fileName, 'net', 'stats', 'state') ;
% -------------------------------------------------------------------------
function [net, stats] = loadState(fileName)
% -------------------------------------------------------------------------
load(fileName, 'net', 'stats') ;
net = dagnn.DagNN.loadobj(net) ;
% -------------------------------------------------------------------------
function epoch = findLastCheckpoint(modelDir)
% -------------------------------------------------------------------------
list = dir(fullfile(modelDir, 'net-epoch-*.mat')) ;
tokens = regexp({list.name}, 'net-epoch-([\d]+).mat', 'tokens') ;
epoch = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
epoch = max([epoch 0]) ;
% -------------------------------------------------------------------------
function prepareGPUs(opts, cold)
% -------------------------------------------------------------------------
numGpus = numel(opts.gpus) ;
if numGpus > 1
% check parallel pool integrity as it could have timed out
pool = gcp('nocreate') ;
if ~isempty(pool) && pool.NumWorkers ~= numGpus
delete(pool) ;
end
pool = gcp('nocreate') ;
if isempty(pool)
parpool('local', numGpus) ;
cold = true ;
end
end
if numGpus >= 1 && cold
fprintf('%s: resetting GPU\n', mfilename)
if numGpus == 1
gpuDevice(opts.gpus)
else
spmd, gpuDevice(opts.gpus(labindex)), end
end
end
%end

View File

@ -0,0 +1,516 @@
function [net,stats] = cnn_train_dag(net, imdb, getBatch, varargin)
%CNN_TRAIN_DAG Demonstrates training a CNN using the DagNN wrapper
% CNN_TRAIN_DAG() is similar to CNN_TRAIN(), but works with
% the DagNN wrapper instead of the SimpleNN wrapper.
% Copyright (C) 2014-16 Andrea Vedaldi.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).
opts.expDir = fullfile('data','exp') ;
opts.continue = true ;
opts.batchSize = 256 ;
opts.numSubBatches = 1 ;
opts.train = [] ;
opts.val = [] ;
opts.gpus = [] ;
opts.prefetch = false ;
opts.numEpochs = 300 ;
opts.learningRate = 0.001 ;
opts.weightDecay = 0.0005 ;
opts.momentum = 0.9 ;
opts.randomSeed = 0 ;
opts.stegoShuffle = false;
opts.computeBNMoment = false;
opts.memoryMapFile = fullfile(tempdir, 'matconvnet.bin') ;
opts.profile = false ;
opts.cudnn = true ;
opts.derOutputs = {'objective', 1} ;
opts.extractStatsFn = @extractStats ;
opts.plotStatistics = true;
opts = vl_argparse(opts, varargin) ;
if ~exist(opts.expDir, 'dir'), mkdir(opts.expDir) ; end
if isempty(opts.train), opts.train = find(imdb.images.set==1) ; end
if isempty(opts.val), opts.val = find(imdb.images.set==2) ; end
if isnan(opts.train), opts.train = [] ; end
% -------------------------------------------------------------------------
% Initialization
% -------------------------------------------------------------------------
evaluateMode = isempty(opts.train) ;
if ~evaluateMode
if isempty(opts.derOutputs)
error('DEROUTPUTS must be specified when training.\n') ;
end
end
state.getBatch = getBatch ;
stats = [] ;
% -------------------------------------------------------------------------
% Train and validate
% -------------------------------------------------------------------------
modelPath = @(ep) fullfile(opts.expDir, sprintf('net-epoch-%d.mat', ep));
modelFigPath = fullfile(opts.expDir, 'net-train.pdf') ;
start = opts.continue * findLastCheckpoint(opts.expDir) ;
if start >= 1
fprintf('%s: resuming by loading epoch %d\n', mfilename, start) ;
[net, stats] = loadState(modelPath(start)) ;
end
if ( opts.computeBNMoment == true )
% Validation without using the moving average of BN momemnts
for i = 1:numel(net.layers)
if ( isa( net.layers(i).block, 'dagnn.BatchNorm') )
net.layers(i).block.computeMoment = true;
end
end
end
for epoch=start+1:opts.numEpochs
% Set the random seed based on the epoch and opts.randomSeed.
% This is important for reproducibility, including when training
% is restarted from a checkpoint.
rng(epoch + opts.randomSeed) ;
prepareGPUs(opts, epoch == start+1) ;
% Train for one epoch.
state.epoch = epoch ;
state.learningRate = opts.learningRate(min(epoch, numel(opts.learningRate))) ;
%state.train = opts.train(randperm(numel(opts.train))) ; % shuffle
%state.val = opts.val(randperm(numel(opts.val))) ;
% shuffle
if( opts.stegoShuffle == 1 )
N = numel(opts.train);
M = numel(opts.val);
Lab = max(1, numel(opts.gpus));
% M and N must be even, and multiple Lab
assert( ( rem(N, 2*Lab) == 0 ) & ( rem(M, 2*Lab) == 0 ) );
% state.train(1:2:N) = opts.train(2*randperm(N/2) - 1);
% state.train(2:2:N) = state.train(1:2:N) + 1;
%
% state.val(1:2:M) = opts.val(2*randperm(M/2) - 1);
% state.val(2:2:M) = state.val(1:2:M) + 1;
seq = opts.train(2*randperm(N/2) - 1);
seq = reshape(seq, Lab, N/(2*Lab));
state.train = reshape([seq; seq+1], 1, N);
seq = opts.val(2*randperm(M/2) - 1);
seq = reshape(seq, Lab, M/(2*Lab));
state.val = reshape([seq; seq+1], 1, M);
elseif ( opts.stegoShuffle < 0 )
% for regression task
K = abs( opts.stegoShuffle );
M = numel(opts.train)/K;
seq = K * ( randperm(M) - 1 );
seq = [seq + 1; seq + 2; seq + 3; seq + 4; seq + 5; seq + 6];
seq = reshape(seq, numel(seq), 1);
state.train = opts.train(seq);
N = numel(opts.val)/K;
seq = K * ( randperm(N) - 1 ) ;
seq = [seq + 1; seq + 2; seq + 3; seq + 4; seq + 5; seq + 6];
seq = reshape(seq, numel(seq), 1);
state.val = opts.val(seq);
else
state.train = opts.train(randperm(numel(opts.train))) ;
state.val = opts.val(randperm(numel(opts.val))) ;
end
state.imdb = imdb ;
if numel(opts.gpus) <= 1
[stats.train(epoch),prof] = process_epoch(net, state, opts, 'train') ;
stats.val(epoch) = process_epoch(net, state, opts, 'val') ;
if opts.profile
profview(0,prof) ;
keyboard ;
end
else
savedNet = net.saveobj() ;
spmd
net_ = dagnn.DagNN.loadobj(savedNet) ;
[stats_.train, prof_] = process_epoch(net_, state, opts, 'train') ;
stats_.val = process_epoch(net_, state, opts, 'val') ;
if labindex == 1, savedNet_ = net_.saveobj() ; end
end
net = dagnn.DagNN.loadobj(savedNet_{1}) ;
stats__ = accumulateStats(stats_) ;
stats.train(epoch) = stats__.train ;
stats.val(epoch) = stats__.val ;
if opts.profile
mpiprofile('viewer', [prof_{:,1}]) ;
keyboard ;
end
clear net_ stats_ stats__ savedNet savedNet_ ;
end
% save
if ~evaluateMode
saveState(modelPath(epoch), net, stats) ;
end
if opts.plotStatistics
switchFigure(1) ; clf ;
plots = setdiff(...
cat(2,...
fieldnames(stats.train)', ...
fieldnames(stats.val)'), {'num', 'time'}) ;
for p = plots
p = char(p) ;
values = zeros(0, epoch) ;
leg = {} ;
for f = {'train', 'val'}
f = char(f) ;
if isfield(stats.(f), p)
tmp = [stats.(f).(p)] ;
values(end+1,:) = tmp(1,:)' ;
leg{end+1} = f ;
end
end
subplot(1,numel(plots),find(strcmp(p,plots))) ;
plot(1:epoch, values','o-') ;
xlabel('epoch') ;
title(p) ;
legend(leg{:}) ;
grid on ;
end
drawnow ;
print(1, modelFigPath, '-dpdf') ;
end
end
if ( opts.computeBNMoment == true )
% Revert it back
for i = 1:numel(net.layers)
if ( isa( net.layers(i).block, 'dagnn.BatchNorm') )
net.layers(i).block.computeMoment = false;
end
end
end
% -------------------------------------------------------------------------
function [stats, prof] = process_epoch(net, state, opts, mode)
% -------------------------------------------------------------------------
% initialize empty momentum
if strcmp(mode,'train')
state.momentum = num2cell(zeros(1, numel(net.params))) ;
end
% move CNN to GPU as needed
numGpus = numel(opts.gpus) ;
if numGpus >= 1
net.move('gpu') ;
if strcmp(mode,'train')
state.momentum = cellfun(@gpuArray,state.momentum,'UniformOutput',false) ;
end
end
if numGpus > 1
mmap = map_gradients(opts.memoryMapFile, net, numGpus) ;
else
mmap = [] ;
end
% profile
if opts.profile
if numGpus <= 1
profile clear ;
profile on ;
else
mpiprofile reset ;
mpiprofile on ;
end
end
subset = state.(mode) ;
num = 0 ;
stats.num = 0 ; % return something even if subset = []
stats.time = 0 ;
adjustTime = 0 ;
start = tic ;
for t=1:opts.batchSize:numel(subset)
fprintf('%s: epoch %02d: %3d/%3d:', mode, state.epoch, ...
fix((t-1)/opts.batchSize)+1, ceil(numel(subset)/opts.batchSize)) ;
batchSize = min(opts.batchSize, numel(subset) - t + 1) ;
for s=1:opts.numSubBatches
% get this image batch and prefetch the next
batchStart = t + (labindex-1) + (s-1) * numlabs ;
batchEnd = min(t+opts.batchSize-1, numel(subset)) ;
batch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
num = num + numel(batch) ;
if numel(batch) == 0, continue ; end
inputs = state.getBatch(state.imdb, batch) ;
if opts.prefetch
if s == opts.numSubBatches
batchStart = t + (labindex-1) + opts.batchSize ;
batchEnd = min(t+2*opts.batchSize-1, numel(subset)) ;
else
batchStart = batchStart + numlabs ;
end
nextBatch = subset(batchStart : opts.numSubBatches * numlabs : batchEnd) ;
state.getBatch(state.imdb, nextBatch) ;
end
if strcmp(mode, 'train')
net.mode = 'normal' ;
net.accumulateParamDers = (s ~= 1) ;
net.eval(inputs, opts.derOutputs) ;
else
net.mode = 'test' ;
net.eval(inputs) ;
end
end
% accumulate gradient
if strcmp(mode, 'train')
if ~isempty(mmap)
write_gradients(mmap, net) ;
labBarrier() ;
end
state = accumulate_gradients(state, net, opts, batchSize, mmap) ;
end
% get statistics
time = toc(start) + adjustTime ;
batchTime = time - stats.time ;
stats = opts.extractStatsFn(net) ;
stats.num = num ;
stats.time = time ;
currentSpeed = batchSize / batchTime ;
averageSpeed = (t + batchSize - 1) / time ;
if t == opts.batchSize + 1
% compensate for the first iteration, which is an outlier
adjustTime = 2*batchTime - time ;
stats.time = time + adjustTime ;
end
fprintf(' %.1f (%.1f) Hz', averageSpeed, currentSpeed) ;
for f = setdiff(fieldnames(stats)', {'num', 'time'})
f = char(f) ;
fprintf(' %s:', f) ;
fprintf(' %.3f', stats.(f)) ;
end
fprintf('\n') ;
end
if ~isempty(mmap)
unmap_gradients(mmap) ;
end
if opts.profile
if numGpus <= 1
prof = profile('info') ;
profile off ;
else
prof = mpiprofile('info');
mpiprofile off ;
end
else
prof = [] ;
end
net.reset() ;
net.move('cpu') ;
% -------------------------------------------------------------------------
function state = accumulate_gradients(state, net, opts, batchSize, mmap)
% -------------------------------------------------------------------------
numGpus = numel(opts.gpus) ;
otherGpus = setdiff(1:numGpus, labindex) ;
for p=1:numel(net.params)
% accumualte gradients from multiple labs (GPUs) if needed
if numGpus > 1
tag = net.params(p).name ;
for g = otherGpus
tmp = gpuArray(mmap.Data(g).(tag)) ;
net.params(p).der = net.params(p).der + tmp ;
end
end
switch net.params(p).trainMethod
case 'average' % mainly for batch normalization
thisLR = net.params(p).learningRate ;
net.params(p).value = ...
(1 - thisLR) * net.params(p).value + ...
(thisLR/batchSize/net.params(p).fanout) * net.params(p).der ;
case 'gradient'
thisDecay = opts.weightDecay * net.params(p).weightDecay ;
thisLR = state.learningRate * net.params(p).learningRate ;
state.momentum{p} = opts.momentum * state.momentum{p} ...
- thisDecay * net.params(p).value ...
- (1 / batchSize) * net.params(p).der ;
net.params(p).value = net.params(p).value + thisLR * state.momentum{p} ;
case 'otherwise'
error('Unknown training method ''%s'' for parameter ''%s''.', ...
net.params(p).trainMethod, ...
net.params(p).name) ;
end
end
% -------------------------------------------------------------------------
function mmap = map_gradients(fname, net, numGpus)
% -------------------------------------------------------------------------
format = {} ;
for i=1:numel(net.params)
format(end+1,1:3) = {'single', size(net.params(i).value), net.params(i).name} ;
end
format(end+1,1:3) = {'double', [3 1], 'errors'} ;
if ~exist(fname) && (labindex == 1)
f = fopen(fname,'wb') ;
for g=1:numGpus
for i=1:size(format,1)
fwrite(f,zeros(format{i,2},format{i,1}),format{i,1}) ;
end
end
fclose(f) ;
end
labBarrier() ;
mmap = memmapfile(fname, ...
'Format', format, ...
'Repeat', numGpus, ...
'Writable', true) ;
% -------------------------------------------------------------------------
function write_gradients(mmap, net)
% -------------------------------------------------------------------------
for i=1:numel(net.params)
mmap.Data(labindex).(net.params(i).name) = gather(net.params(i).der) ;
end
% -------------------------------------------------------------------------
function unmap_gradients(mmap)
% -------------------------------------------------------------------------
% -------------------------------------------------------------------------
function stats = accumulateStats(stats_)
% -------------------------------------------------------------------------
for s = {'train', 'val'}
s = char(s) ;
total = 0 ;
% initialize stats stucture with same fields and same order as
% stats_{1}
stats__ = stats_{1} ;
names = fieldnames(stats__.(s))' ;
values = zeros(1, numel(names)) ;
fields = cat(1, names, num2cell(values)) ;
stats.(s) = struct(fields{:}) ;
for g = 1:numel(stats_)
stats__ = stats_{g} ;
num__ = stats__.(s).num ;
total = total + num__ ;
for f = setdiff(fieldnames(stats__.(s))', 'num')
f = char(f) ;
stats.(s).(f) = stats.(s).(f) + stats__.(s).(f) * num__ ;
if g == numel(stats_)
stats.(s).(f) = stats.(s).(f) / total ;
end
end
end
stats.(s).num = total ;
end
% -------------------------------------------------------------------------
function stats = extractStats(net)
% -------------------------------------------------------------------------
sel = find(cellfun(@(x) isa(x,'dagnn.Loss'), {net.layers.block})) ;
stats = struct() ;
for i = 1:numel(sel)
stats.(net.layers(sel(i)).outputs{1}) = net.layers(sel(i)).block.average ;
end
% -------------------------------------------------------------------------
function saveState(fileName, net, stats)
% -------------------------------------------------------------------------
net_ = net ;
net = net_.saveobj() ;
save(fileName, 'net', 'stats') ;
% -------------------------------------------------------------------------
function [net, stats] = loadState(fileName)
% -------------------------------------------------------------------------
load(fileName, 'net', 'stats') ;
net = dagnn.DagNN.loadobj(net) ;
% -------------------------------------------------------------------------
function epoch = findLastCheckpoint(modelDir)
% -------------------------------------------------------------------------
list = dir(fullfile(modelDir, 'net-epoch-*.mat')) ;
tokens = regexp({list.name}, 'net-epoch-([\d]+).mat', 'tokens') ;
epoch = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
epoch = max([epoch 0]) ;
% -------------------------------------------------------------------------
function switchFigure(n)
% -------------------------------------------------------------------------
if get(0,'CurrentFigure') ~= n
try
set(0,'CurrentFigure',n) ;
catch
figure(n) ;
end
end
% -------------------------------------------------------------------------
function prepareGPUs(opts, cold)
% -------------------------------------------------------------------------
numGpus = numel(opts.gpus) ;
if numGpus > 1
% check parallel pool integrity as it could have timed out
pool = gcp('nocreate') ;
if ~isempty(pool) && pool.NumWorkers ~= numGpus
delete(pool) ;
end
pool = gcp('nocreate') ;
if isempty(pool)
parpool('local', numGpus) ;
cold = true ;
end
if exist(opts.memoryMapFile)
delete(opts.memoryMapFile) ;
end
end
if numGpus >= 1 && cold
fprintf('%s: resetting GPU\n', mfilename)
if numGpus == 1
gpuDevice(opts.gpus)
else
spmd, gpuDevice(opts.gpus(labindex)), end
end
end
%end

View File

@ -0,0 +1,12 @@
classdef Abs < dagnn.ElementWise
methods
function outputs = forward(obj, inputs, params)
outputs{1} = vl_nnabs(inputs{1}) ;
end
function [derInputs, derParams] = backward(obj, inputs, params, derOutputs)
derInputs{1} = vl_nnabs(inputs{1}, derOutputs{1}) ;
derParams = {} ;
end
end
end

View File

@ -0,0 +1,60 @@
classdef BatchNorm < dagnn.ElementWise
properties
numChannels
epsilon = 1e-4
computeMoment = false;
end
properties (Transient)
moments
end
methods
function outputs = forward(obj, inputs, params)
if strcmp(obj.net.mode, 'test')
if( obj.computeMoment )
[outputs{1}, obj.moments] = vl_nnbnorm(inputs{1}, params{1}, ...
params{2}, 'epsilon', obj.epsilon) ;
else
outputs{1} = vl_nnbnorm(inputs{1}, params{1}, params{2}, ...
'moments', params{3}, ...
'epsilon', obj.epsilon) ;
obj.moments = [];
end
else
[outputs{1}, obj.moments] = vl_nnbnorm(inputs{1}, params{1}, ...
params{2}, 'epsilon', obj.epsilon) ;
end
end
function [derInputs, derParams] = backward(obj, inputs, params, derOutputs)
[derInputs{1}, derParams{1}, derParams{2}, derParams{3}] = ...
vl_nnbnorm(inputs{1}, params{1}, params{2}, derOutputs{1}, ...
'epsilon', obj.epsilon) ;
obj.moments = [];
% multiply the moments update by the number of images in the batch
% this is required to make the update additive for subbatches
% and will eventually be normalized away
derParams{3} = derParams{3} * size(inputs{1},4) ;
end
% ---------------------------------------------------------------------
function obj = BatchNorm(varargin)
obj.load(varargin{:}) ;
end
function params = initParams(obj)
params{1} = ones(obj.numChannels,1,'single') ;
params{2} = zeros(obj.numChannels,1,'single') ;
params{3} = zeros(obj.numChannels,2,'single') ;
end
function attach(obj, net, index)
attach@dagnn.ElementWise(obj, net, index) ;
p = net.getParamIndex(net.layers(index).params{3}) ;
net.params(p).trainMethod = 'average' ;
net.params(p).learningRate = 0.01 ;
end
end
end

View File

@ -0,0 +1,36 @@
classdef PhaseSplit < dagnn.Filter
% Construct PhaseSplit in a way similar to Pooling, mightbe we could
% do it in a better and clean way.
properties
poolSize = [1 1]
end
methods
function outputs = forward(self, inputs, params)
outputs{1} = vl_nnphasesplit( inputs{1} ) ;
end
function [derInputs, derParams] = backward(self, inputs, params, derOutputs)
derInputs{1} = vl_nnphasesplit( inputs{1}, derOutputs{1} ) ;
derParams = {} ;
end
function kernelSize = getKernelSize(obj)
kernelSize = obj.poolSize ;
end
function outputSizes = getOutputSizes(obj, inputSizes)
%outputSizes = getOutputSizes@dagnn.Filter(obj, inputSizes) ;
outputSizes{1}(1) = inputSizes{1}(1)/8 ;
outputSizes{1}(2) = inputSizes{1}(2)/8;
outputSizes{1}(3) = inputSizes{1}(3)*64;
outputSizes{1}(4) = inputSizes{1}(4);
end
function obj = PhaseSplit(varargin)
%obj.load(varargin) ;
obj.pad = [0 0 0 0];
obj.stride = [8 8];
end
end
end

View File

@ -0,0 +1,12 @@
classdef TanH < dagnn.ElementWise
methods
function outputs = forward(obj, inputs, params)
outputs{1} = vl_nntanh(inputs{1}) ;
end
function [derInputs, derParams] = backward(obj, inputs, params, derOutputs)
derInputs{1} = vl_nntanh(inputs{1}, derOutputs{1}) ;
derParams = {} ;
end
end
end

View File

@ -0,0 +1,236 @@
function obj = fromSimpleNN(net, varargin)
% FROMSIMPLENN Initialize a DagNN object from a SimpleNN network
% FROMSIMPLENN(NET) initializes the DagNN object from the
% specified CNN using the SimpleNN format.
%
% SimpleNN objects are linear chains of computational layers. These
% layers exchange information through variables and parameters that
% are not explicitly named. Hence, FROMSIMPLENN() uses a number of
% rules to assign such names automatically:
%
% * From the input to the output of the CNN, variables are called
% `x0` (input of the first layer), `x1`, `x2`, .... In this
% manner `xi` is the output of the i-th layer.
%
% * Any loss layer requires two inputs, the second being a label.
% These are called `label` (for the first such layers), and then
% `label2`, `label3`,... for any other similar layer.
%
% Additionally, given the option `CanonicalNames` the function can
% change the names of some variables to make them more convenient to
% use. With this option turned on:
%
% * The network input is called `input` instead of `x0`.
%
% * The output of each SoftMax layer is called `prob` (or `prob2`,
% ...).
%
% * The output of each Loss layer is called `objective` (or `
% objective2`, ...).
%
% * The input of each SoftMax or Loss layer of type *softmax log
% loss* is called `prediction` (or `prediction2`, ...). If a Loss
% layer immediately follows a SoftMax layer, then the rule above
% takes precendence and the input name is not changed.
%
% FROMSIMPLENN(___, 'OPT', VAL, ...) accepts the following options:
%
% `CanonicalNames`:: false
% If `true` use the rules above to assign more meaningful
% names to some of the variables.
% Copyright (C) 2015 Karel Lenc and Andrea Vedaldi.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).
opts.canonicalNames = false ;
opts = vl_argparse(opts, varargin) ;
import dagnn.*
obj = DagNN() ;
net = vl_simplenn_move(net, 'cpu') ;
net = vl_simplenn_tidy(net) ;
% copy meta-information as is
obj.meta = net.meta ;
for l = 1:numel(net.layers)
inputs = {sprintf('x%d',l-1)} ;
outputs = {sprintf('x%d',l)} ;
params = struct(...
'name', {}, ...
'value', {}, ...
'learningRate', [], ...
'weightDecay', []) ;
if isfield(net.layers{l}, 'name')
name = net.layers{l}.name ;
else
name = sprintf('layer%d',l) ;
end
switch net.layers{l}.type
case {'conv', 'convt'}
sz = size(net.layers{l}.weights{1}) ;
hasBias = ~isempty(net.layers{l}.weights{2}) ;
params(1).name = sprintf('%sf',name) ;
params(1).value = net.layers{l}.weights{1} ;
if hasBias
params(2).name = sprintf('%sb',name) ;
params(2).value = net.layers{l}.weights{2} ;
end
if isfield(net.layers{l},'learningRate')
params(1).learningRate = net.layers{l}.learningRate(1) ;
if hasBias
params(2).learningRate = net.layers{l}.learningRate(2) ;
end
end
if isfield(net.layers{l},'weightDecay')
params(1).weightDecay = net.layers{l}.weightDecay(1) ;
if hasBias
params(2).weightDecay = net.layers{l}.weightDecay(2) ;
end
end
switch net.layers{l}.type
case 'conv'
block = Conv() ;
block.size = sz ;
block.pad = net.layers{l}.pad ;
block.stride = net.layers{l}.stride ;
case 'convt'
block = ConvTranspose() ;
block.size = sz ;
block.upsample = net.layers{l}.upsample ;
block.crop = net.layers{l}.crop ;
block.numGroups = net.layers{l}.numGroups ;
end
block.hasBias = hasBias ;
block.opts = net.layers{l}.opts ;
case 'pool'
block = Pooling() ;
block.method = net.layers{l}.method ;
block.poolSize = net.layers{l}.pool ;
block.pad = net.layers{l}.pad ;
block.stride = net.layers{l}.stride ;
block.opts = net.layers{l}.opts ;
case 'phasesplit'
block = PhaseSplit();
case {'normalize', 'lrn'}
block = LRN() ;
block.param = net.layers{l}.param ;
case {'dropout'}
block = DropOut() ;
block.rate = net.layers{l}.rate ;
case {'relu'}
block = ReLU() ;
block.leak = net.layers{l}.leak ;
case {'sigmoid'}
block = Sigmoid() ;
case {'abs'}
block = Abs();
case {'tanh'}
block = TanH();
case {'tlu'}
block = TLU();
case {'softmax'}
block = SoftMax() ;
case {'softmaxloss'}
block = Loss('loss', 'softmaxlog') ;
% The loss has two inputs
inputs{2} = getNewVarName(obj, 'label') ;
case {'l2'}
block = Loss('loss', 'l2') ;
% The loss has two inputs
inputs{2} = getNewVarName(obj, 'label') ;
case {'bnorm'}
block = BatchNorm() ;
params(1).name = sprintf('%sm',name) ;
params(1).value = net.layers{l}.weights{1} ;
params(2).name = sprintf('%sb',name) ;
params(2).value = net.layers{l}.weights{2} ;
params(3).name = sprintf('%sx',name) ;
params(3).value = net.layers{l}.weights{3} ;
if isfield(net.layers{l},'learningRate')
params(1).learningRate = net.layers{l}.learningRate(1) ;
params(2).learningRate = net.layers{l}.learningRate(2) ;
params(3).learningRate = net.layers{l}.learningRate(3) ;
end
if isfield(net.layers{l},'weightDecay')
params(1).weightDecay = net.layers{l}.weightDecay(1) ;
params(2).weightDecay = net.layers{l}.weightDecay(2) ;
params(3).weightDecay = 0 ;
end
otherwise
error([net.layers{l}.type ' is unsupported']) ;
end
obj.addLayer(...
name, ...
block, ...
inputs, ...
outputs, ...
{params.name}) ;
for p = 1:numel(params)
pindex = obj.getParamIndex(params(p).name) ;
if ~isempty(params(p).value)
obj.params(pindex).value = params(p).value ;
end
if ~isempty(params(p).learningRate)
obj.params(pindex).learningRate = params(p).learningRate ;
end
if ~isempty(params(p).weightDecay)
obj.params(pindex).weightDecay = params(p).weightDecay ;
end
end
end
% --------------------------------------------------------------------
% Rename variables to canonical names
% --------------------------------------------------------------------
if opts.canonicalNames
for l = 1:numel(obj.layers)
if l == 1
obj.renameVar(obj.layers(l).inputs{1}, 'input') ;
end
if isa(obj.layers(l).block, 'dagnn.SoftMax')
obj.renameVar(obj.layers(l).outputs{1}, getNewVarName(obj, 'prob')) ;
obj.renameVar(obj.layers(l).inputs{1}, getNewVarName(obj, 'prediction')) ;
end
if isa(obj.layers(l).block, 'dagnn.Loss') %|| isa(obj.layers(l).block, 'dagnn.L2Dist')
obj.renameVar(obj.layers(l).outputs{1}, 'objective') ;
if isempty(regexp(obj.layers(l).inputs{1}, '^prob.*'))
obj.renameVar(obj.layers(l).inputs{1}, ...
getNewVarName(obj, 'prediction')) ;
end
end
end
end
% --------------------------------------------------------------------
function name = getNewVarName(obj, prefix)
% --------------------------------------------------------------------
t = 0 ;
name = prefix ;
while any(strcmp(name, {obj.vars.name}))
t = t + 1 ;
name = sprintf('%s%d', prefix, t) ;
end

View File

@ -0,0 +1,19 @@
function out = vl_nnabs(x,dzdy)
%VL_NNABS CNN ABS unit.
% Y = VL_NNABS(X) computes the absolute value of the data X. X can
% have an arbitrary size. The abs is defined as follows:
%
% ABS(X) = |X|.
%
% DZDX = VL_NNABS(X, DZDY) computes the derivative of the
% block projected onto DZDY. DZDX and DZDY have the same
% dimensions as X and Y respectively.
% Note, MATLAB built-in function ABS() and SIGN() are used because their
% support for gpuArray
if nargin <= 1 || isempty(dzdy)
out = abs( x ) ;
else
out = dzdy .* sign( x );
end

View File

@ -0,0 +1,58 @@
function y = vl_nnphasesplit( x, dzdy )
%VL_NNPHASESPLIT CNN phase split the feature plane into 8x8 = 64 DCT mode.
% Y = VL_NNCROP(X, STRIDE) phase split the input X into 64 DCT phase mode.
%
% DZDX = VL_NNCROP(X, DZDY) computes the derivative DZDX of the
% function projected on the output derivative DZDY. DZDX has the same
% dimension as X and DZDY the same dimension as Y.
%
% dimension must be divided by 8
assert( rem(size(x,1), 8 ) == 0 & rem( size(x,2), 8) == 0 );
% Initialize some parameters
inputSize = [size(x,1) size(x,2) size(x,3) size(x,4)] ;
outputSize = [size(x,1)/8, size(x,2)/8, 64*size(x,3), size(x,4)];
% zig zag order
zzag = zeros(64, 4);
idx = 1;
startCh = 1;
for i = 0:7
for j = 0:7
stopCh = startCh + inputSize(3);
zzag(idx, :) = [ i, j, startCh, stopCh - 1 ];
idx = idx + 1;
startCh = stopCh;
end
end
% sampling array
sy = 1:8:inputSize(1);
sx = 1:8:inputSize(2);
if nargin <= 1 || isempty(dzdy)
% forward function
if isa( x, 'gpuArray' )
y = gpuArray.zeros(outputSize, classUnderlying(x)) ;
else
y = zeros(outputSize, 'like', x ) ;
end
for i = 1:64
y(:,:,zzag(i,3):zzag(i,4),:) = x(sy + zzag(i,1), sx + zzag(i,2), :, : );
end
else
% backward function
if isa(dzdy, 'gpuArray')
y = gpuArray.zeros(inputSize, classUnderlying(dzdy)) ;
else
y = zeros(inputSize, 'like', x) ;
end
for i = 1:64
y(sy + zzag(i,1), sx + zzag(i,2), :, : ) = dzdy(:,:,zzag(i,3):zzag(i,4),:);
end
end

View File

@ -0,0 +1,21 @@
function out = vl_nntanh(x,dzdy)
%VL_NNTANH CNN TanH hyperbolic non-linearity
% Y = VL_NNTANH(X) computes the hyperbolic tangent non-linearity of the
% data X. X can have an arbitrary size. The tanh is defined as follows:
%
% TANH(X) = (EXP(2X) - 1 )/( EXP(2x) + 1 ).
%
% DZDX = VL_NNTANH(X, DZDY) computes the derivative of the
% block projected onto DZDY. DZDX and DZDY have the same
% dimensions as X and Y respectively.
%
% NOTE: Matlab build-in function TANH() is used since it has extended
% support for gpuArray
y = tanh( x );
if nargin <= 1 || isempty(dzdy)
out = y;
else
out = dzdy .* ( 1 - y.*y );
end

View File

@ -0,0 +1,36 @@
classdef nnabs < nntest
properties
x
delta
end
methods (TestClassSetup)
function data(test,device)
% make sure that all elements in x are differentiable. in this way,
% we can compute numerical derivatives reliably by adding a delta < .5.
delta = 0.01;
test.range = 10 ;
x = test.randn(15,14,3,2) ;
ind = find(( x < 0 )&( x > -2*delta));
if (~isempty(ind))
x(ind) = -2 + rand([1, length(ind)], 'like', x);
end
test.x = x ;
test.delta = delta;
if strcmp(device,'gpu'), test.x = gpuArray(test.x) ; end
end
end
methods (Test)
function basic(test)
x = test.x ;
y = vl_nnabs(x) ;
dzdy = test.randn(size(y)) ;
dzdx = vl_nnabs(x,dzdy) ;
test.der(@(x) vl_nnabs(x), x, dzdy, dzdx, 1e-2) ;
end
end
end

View File

@ -0,0 +1,23 @@
classdef nnphasesplit < nntest
properties
x
end
methods (TestClassSetup)
function data(test,device)
test.range = 10 ;
test.x = test.randn(32,32,4,2) ;
if strcmp(device,'gpu'), test.x = gpuArray(test.x) ; end
end
end
methods (Test)
function basic(test)
x = test.x ;
y = vl_nnphasesplit(x) ;
dzdy = test.randn(size(y)) ;
dzdx = vl_nnphasesplit(x,dzdy) ;
test.der(@(x) vl_nnphasesplit(x), x, dzdy, dzdx, 1e-3) ;
end
end
end

View File

@ -0,0 +1,24 @@
classdef nntanh < nntest
properties
x
delta
end
methods (TestClassSetup)
function data(test,device)
test.range = 10 ;
test.x = test.randn(15,14,3,2) ;
if strcmp(device,'gpu'), test.x = gpuArray(test.x) ; end
end
end
methods (Test)
function basic(test)
x = test.x ;
y = vl_nntanh(x) ;
dzdy = test.randn(size(y)) ;
dzdx = vl_nntanh(x,dzdy) ;
test.der(@(x) vl_nntanh(x), x, dzdy, dzdx, 1e-2) ;
end
end
end

Binary file not shown.

135
README.md
View File

@ -1,3 +1,134 @@
# Steganalysis # Steganography
Python 实现LSB算法进行信息隐藏 包含空域与变换域 JPEG信息隐藏算法 对PDF文件进行信息隐藏 基于卷积神经网络的隐写分析 Matlab SRM、SCA隐写分析 # 空域编码图像
* 空域编码是指在图像空间域进行编码,也就是直接针对图像像素进行编码
* 对像素进行编码,如 LSB 算法,主要有下面两种方式
* 光栅格式
* 调色板格式 GIF(graphics interchange format)
* 一个图像编码标准往往包括多类编码方法,一个图像仅仅是其一类方法的实例。例如,常见的 BMPBitmap、 TIFF
Tagged Image File Format、 PNGPortable Network
Graphics均支持光栅格式与调色板格式编码对这两种格式
编码分别又支持多种具体编码方法
## LSB 隐写算法
* LSB 隐写是最基础、最简单的隐写方法,具有容量大、嵌入速度快、对载体图像质量影响小的特点
* LSB 的大意就是最低比特位隐写。我们将深度为 8 的 BMP 图像,分为 8 个二值平面位平面我们将待嵌入的信息info直接写到最低的位平面上。换句话说如果秘密信息与最低比特位相同则不改动如果秘密信息与最低比特位不同则使用秘密信息值代替最低比特位
[代码实现](/LSB)
![](https://www.writebug.com/myres/static/uploads/2021/12/30/d2a334c6057be058e3bdaa79efb202fb.writebug)
嵌入信息前的载体图片
![](https://www.writebug.com/myres/static/uploads/2021/12/30/5ef450e6b4aa705dfab9eaeffb47091f.writebug)
嵌入信息后的载体图片
# 变换域编码图像
## JPEG
* Joint Photographic Experts Group联合图像专家小组的缩写
* JPEG 编码
![](https://www.writebug.com/myres/static/uploads/2021/12/30/e251af7a91f2675226c55b1f2dc29186.writebug)
### JSteg 隐写
* JSteg 的算法的主要思想是将秘密消息嵌入在量化后的 DCT 系数的最低比特位上,但对原始值为 0、+1、-1 的 DCT 系数不进行嵌入,提取秘密消息时,只需将载密图像中不等于 0、l 的量化 DCT 系数的 LSB 取出即可
* JSteg 算法步骤
1. 选择载体图像,并且将载体图像划分为连续的 8×8 的子块。
2. 对每个子块使用离散余弦变换之后,用相应的质量因数的量化表量化,得到对应的 8×8 量化 DCT 子块。
3. 将需要隐藏的信息编码为二进制数据流,对 DCT 子块系数进行 Z 字形扫描,并且使用秘密信息的二进制流替换非 0 和非 1 的 DCT 系数的最低比特位。
4. 进行熵编码等,产生 JPEG 隐密图像。
* JSteg 的具体嵌入过程
1. 部分解码 JPEG 图像,得到二进制存储的 AC 系数,判断该 AC 系数是否等于正负 1 或 0若等于则跳过该 AC 系数,否则,执行下一步
2. 判断二进制存储的 AC 系数的 LSB 是否与要嵌入的秘密信息比特相同,若相同,则不对其进行修改,否则执行下一步
3. 用秘密信息比特替换二进制存储的 AC 系数的 LSB将修改后的 AC 系数重新编码得到隐秘 JPEG 图像
* JSteg 不使用 0、1 的原因
1. DCT 系数中“0”的比例最大一般可达到 60% 以上,取决于图像质量和压缩因子),压缩编码是利用大量出现连零实现的,如果改变 DCT 系数中“0”的话不能很好的实现压缩
2. DCT 系数中的“1”若变成“0”由于接受端无法区分未使用的“0”和嵌入消息后得到的“0”从而无法实现秘密信息的提取
[代码实现](Jsteg.py)
### F3 隐写
* 为了改善大量 DCT 系数不隐藏信息这一状况,人们提出了 F3 隐写
* F3 对原始值为 +1 和-1 的 DCT 系数进行了利用。F3 隐写的规则如下
1. 每个非 0 的 DCT 数据用于隐藏 1 比特秘密信息,为 0 的 DCT 系数不负载秘密信息
2. 如果秘密信息与 DCT 的 LSB 相同,便不作改动;如果不同,将 DCT 系数的绝对值减小 1符号不变
3. 当原始值为 +1 或-1 且预嵌入秘密信息为 0 时,将这个位置归 0 并视为无效,在下一个 DCT 系数上重新嵌入
* 编写代码实现嵌入,并观察 DCT 系数变化
[代码实现](F3.py)
```
JPEG的DCT系数
{0: 32939, 1: 15730, 2: 13427, 3: 11523, 4: 9540, 5: 7957, 6: 6607, 7: 5697, 8: 4834, -1: 15294, -2: 13637, -3: 11479, -4: 9683, -5: 7979, -6: 6878, -7: 5631, -8: 4871}
Jsteg begin writing!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
经过信息隐藏后JPEG的DCT系数变化
{0: 32939, 1: 15730, 2: 12552, 3: 12398, 4: 8739, 5: 8758, 6: 6165, 7: 6139, 8: 4487, -1: 15294, -2: 12721, -3: 12395, -4: 8891, -5: 8771, -6: 6319, -7: 6190, -8: 4463}
F3steg begin writing!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
经过信息隐藏后JPEG的DCT系数变化
{0: 47068, 1: 13416, 2: 13519, 3: 10075, 4: 9545, 5: 7077, 6: 6650, 7: 5016, 8: 4754, -1: 13308, -2: 13668, -3: 10124, -4: 9571, -5: 7249, -6: 6591, -7: 5098, -8: 4733}
F4steg begin writing!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
经过信息隐藏后JPEG的DCT系数变化
{0: 59320, 1: 13618, 2: 11987, 3: 9875, 4: 8328, 5: 6860, 6: 5883, 7: 4910, 8: 4239, -1: 13692, -2: 11976, -3: 9976, -4: 8428, -5: 7007, -6: 5834, -7: 4964, -8: 4190}
```
* 条形图绘制
![](https://www.writebug.com/myres/static/uploads/2021/12/30/7a40002ebbd1e4d6e160bcce7b50f636.writebug)
* 未经过信息隐藏的 DCT 系数,系数近似符合拉普拉斯分布,具有几个典型特点
* 对称性 以 0 为中心达到最大值,两侧分布近似对称
* 单侧单调性 以 0 值为中心达到最大值,两侧单调下降
* 梯度下降性 小值样点较多,大值样点较少,分布曲线在两侧下降梯度逐渐减小
![](https://www.writebug.com/myres/static/uploads/2021/12/30/dd1e5a48b8e006d7456a3ba1f2c4ef35.writebug)
* JSteg 隐写的 DCT 系数
* JSteg 隐写可嵌入信息的 DCT 系数较少,隐写量较小,且相邻数值样点的个数接近,如 2 和 3-2 和-3 形成了值对,卡方特征变化明显,因而提出了 F3 隐写
![](https://www.writebug.com/myres/static/uploads/2021/12/30/5231d6a6b7690459b0c318d62221b60e.writebug)
* F3 隐写的 DCT 系数
* F3 的设计虽然防止了相邻值出现数量接近的现象,也维持了分布函数的对称性,但使得偶数的分布增加,没有满足单调性
* 这是因为载体绝对值为 1 的数值较多,当其被修改为 0 时,嵌入算法继续嵌入直到找到一个偶数值,或者将一个奇数值改为偶数值,这样绝对值为 1 的系数可以支持嵌入 1但是不支持嵌入 0需要使用或制造一个偶数
* 另外0 系数的数量有相应的增加,产生分布曲线向 0 收缩的现象
### F4 隐写
* 为了克服 F3 的缺陷F4 对不同正负号的奇偶系数采用了不同的嵌入与消息表示方法
* **F4 用负偶数、正奇数代表嵌入了消息比特 1用负奇数、正偶数代表嵌入了 0**
* 但仍然通过减小绝对值的方法进行修改,如果减小绝对值后系数为 0 则继续往下嵌入当前比特
* [代码实现](F4.py)
![](https://www.writebug.com/myres/static/uploads/2021/12/30/cf7f25ee63efd8a6f3f3b0688ca49fe1.writebug)
* F4 隐写的 DCT 系数
* F4 显然保持了载体分布函数的对称性,也保持了载体分布函数的单调性与梯度下降性
* 但 F4 依然存在使含密载体分布函数形状向 0 收缩的现象
### F5 隐写
F5 隐写实现了基于汉明码的矩阵编码隐写,在一个分组上最多修改 R=1 次以嵌入 $2^r-1-r$ 比特,采用的基本嵌入方法是基于 F4 隐写的
F5 的嵌入步骤
- 获得嵌入域。若输入的是位图,则进行 JPEG 编码得到 JPEG 系数;若输入的是 JPEG 图像,则进行熵编码的解码得到 JPEG 系数
- 位置置乱。根据口令生成的密钥位一个伪随机数发生器,基于伪随机数发生器置乱 JPEG 系数的位置
- 编码参数确定。为了提高嵌入效率,一般希望 n 尽可能大,因此,根据载体中可用系数的数量与消息的长度确定参数 r并计算$n=2^r-1$
- 基于($n=2^r-1,r$)的汉明分组码得到编码校验矩阵,开始嵌入消息:
- ① 按置乱后的顺序取下面 n 个非零系数,在其中的 LSB 序列中按照以上编码嵌入 n-r 比特的消息;
- ② 如果未发生修改,并且还有需要嵌入的消息,则返回 ① 继续嵌入下一分组;
- ③ 如果进行了修改,则判断是不是有系数值收缩到 0如果没有并且还有需要嵌入的消息则返回 ① 继续嵌入下一分组,如果有,取出一个新的非零系数组成新的一组 n 个非零系数,在其中的 LSB 序列中按照以上编码重新嵌入以上 n-r 比特的消息,直到没有修改或收缩,最后,如果还有需要嵌入的消息,则返回 ① 继续嵌入下一分组
- 位置逆置乱。恢复 DCT 系数原来的位置顺序
- 熵编码。按照 JPEG 标准无损压缩 DCT 量化系数,得到 JPEG 文件
# 参考资料
* 隐写学原理与技术 By 赵险峰
* 数字媒体中的隐写术 By J.Fridrich

118
SRNet/SCA_SRNet.py Normal file
View File

@ -0,0 +1,118 @@
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 18 19:59:14 2019
@author: Lee
"""
import os
import sys
os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'
os.environ['CUDA_VISIBLE_DEVICES'] = '1' # set a GPU (with GPU Number)
home = os.path.expanduser("~")
sys.path.append(home + '/tflib/') # path for 'tflib' folder
import matplotlib.pyplot as plt
from scipy.io import loadmat
from SCA_SRNet_Spatial import * # use 'SCA_SRNet_JPEG' for JPEG domain
def trnGen(cover_path, stego_path, cover_beta_path, stego_beta_path, thread_idx=0, n_threads=1):
IL=os.listdir(cover_path)
img_shape = plt.imread(cover_path +IL[0]).shape
batch = np.empty((2, img_shape[0], img_shape[1], 2), dtype='float32')
while True:
indx = np.random.permutation(len(IL))
for i in indx:
batch[0,:,:,0] = plt.imread(cover_path + IL[i]) # use loadmat for loading JPEG decompressed images
batch[0,:,:,1] = loadmat(cover_beta_path + IL[i].replace('pgm','mat'))['Beta'] # adjust for JPEG images
batch[1,:,:,0] = plt.imread(stego_path + IL[i]) # use loadmat for loading JPEG decompressed images
batch[1,:,:,1] = loadmat(stego_beta_path + IL[i].replace('pgm','mat'))['Beta'] # adjust for JPEG images
rot = random.randint(0,3)
if rand() < 0.5:
yield [np.rot90(batch, rot, axes=[1,2]), np.array([0,1], dtype='uint8')]
else:
yield [np.flip(np.rot90(batch, rot, axes=[1,2]), axis=2), np.array([0,1], dtype='uint8')]
def valGen(cover_path, stego_path, cover_beta_path, stego_beta_path, thread_idx=0, n_threads=1):
IL=os.listdir(cover_path)
img_shape = plt.imread(cover_path +IL[0]).shape
batch = np.empty((2, img_shape[0], img_shape[1], 2), dtype='float32')
while True:
for i in range(len(IL)):
batch[0,:,:,0] = plt.imread(cover_path + IL[i]) # use loadmat for loading JPEG decompressed images
batch[0,:,:,1] = loadmat(cover_beta_path + IL[i].replace('pgm','mat'))['Beta'] # adjust for JPEG images
batch[1,:,:,0] = plt.imread(stego_path + IL[i]) # use loadmat for loading JPEG decompressed images
batch[1,:,:,1] = loadmat(stego_beta_path + IL[i].replace('pgm','mat'))['Beta'] # adjust for JPEG images
yield [batch, np.array([0,1], dtype='uint8') ]
train_batch_size = 32
valid_batch_size = 40
max_iter = 500000
train_interval=100
valid_interval=5000
save_interval=5000
num_runner_threads=10
# save Betas as '.mat' files with variable name "Beta" and put them in thier corresponding directoroies. Make sure
# all mat files in the directories can be loaded in Python without any errors.
TRAIN_COVER_DIR = '/media/Cover_TRN/'
TRAIN_STEGO_DIR = '/media/Stego_WOW_0.5_TRN/'
TRAIN_COVER_BETA_DIR = '/media/Beta_Cover_WOW_0.5_TRN/'
TRAIN_STEGO_BETA_DIR = '/media/Beta_Stego_WOW_0.5_TRN/'
VALID_COVER_DIR = '/media/Cover_VAL/'
VALID_STEGO_DIR = '/media/Stego_WOW_0.5_VAL/'
VALID_COVER_BETA_DIR = '/media/Beta_Cover_WOW_0.5_VAL/'
VALID_STEGO_BETA_DIR = '/media/Beta_Stego_WOW_0.5_VAL/'
train_gen = partial(trnGen, \
TRAIN_COVER_DIR, TRAIN_STEGO_DIR, TRAIN_COVER_BETA_DIR, TRAIN_STEGO_BETA_DIR)
valid_gen = partial(valGen, \
VALID_COVER_DIR, VALID_STEGO_DIR, VALID_COVER_BETA_DIR, VALID_STEGO_BETA_DIR)
LOG_DIR= '/media/LogFiles/SCA_WOW_0.5' # path for a log direcotry
# load_path= LOG_DIR + 'Model_460000.ckpt' # continue training from a specific checkpoint
load_path=None # training from scratch
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
train_ds_size = len(glob(TRAIN_COVER_DIR + '/*')) * 2
valid_ds_size = len(glob(VALID_COVER_DIR +'/*')) * 2
print 'train_ds_size: %i'%train_ds_size
print 'valid_ds_size: %i'%valid_ds_size
if valid_ds_size % valid_batch_size != 0:
raise ValueError("change batch size for validation")
optimizer = AdamaxOptimizer
boundaries = [400000] # learning rate adjustment at iteration 400K
values = [0.001, 0.0001] # learning rates
train(SCA_SRNet, train_gen, valid_gen , train_batch_size, valid_batch_size, valid_ds_size, \
optimizer, boundaries, values, train_interval, valid_interval, max_iter,\
save_interval, LOG_DIR,num_runner_threads, load_path)
# Testing
TEST_COVER_DIR = '/media/Cover_TST/'
TEST_STEGO_DIR = '/media/Stego_WOW_0.5_TST/'
TEST_COVER_BETA_DIR = '/media/Beta_Cover_WOW_0.5_TST/'
TEST_STEGO_BETA_DIR = '/media/Beta_Stego_WOW_0.5_TST/'
test_batch_size=40
LOG_DIR = '/media/LogFiles/SCA_WOW_0.5/'
LOAD_DIR = LOG_DIR + 'Model_435000.ckpt' # loading from a specific checkpoint
test_gen = partial(gen_valid, \
TEST_COVER_DIR, TEST_STEGO_DIR)
test_ds_size = len(glob(TEST_COVER_DIR + '/*')) * 2
print 'test_ds_size: %i'%test_ds_size
if test_ds_size % test_batch_size != 0:
raise ValueError("change batch size for testing!")
test_dataset(SCA_SRNet, test_gen, test_batch_size, test_ds_size, LOAD_DIR)

View File

@ -0,0 +1,178 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import os\n",
"import sys\n",
"os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'\n",
"os.environ['CUDA_VISIBLE_DEVICES'] = '1' # set a GPU (with GPU Number)\n",
"home = os.path.expanduser(\"~\")\n",
"sys.path.append(home + '/tflib/') # path for 'tflib' folder\n",
"import matplotlib.pyplot as plt\n",
"from scipy.io import loadmat\n",
"from SCA_SRNet_Spatial import * # use 'SCA_SRNet_JPEG' for JPEG domain"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def trnGen(cover_path, stego_path, cover_beta_path, stego_beta_path, thread_idx=0, n_threads=1):\n",
" IL=os.listdir(cover_path)\n",
" img_shape = plt.imread(cover_path +IL[0]).shape\n",
" batch = np.empty((2, img_shape[0], img_shape[1], 2), dtype='float32')\n",
" while True:\n",
" indx = np.random.permutation(len(IL))\n",
" for i in indx:\n",
" batch[0,:,:,0] = plt.imread(cover_path + IL[i]) # use loadmat for loading JPEG decompressed images \n",
" batch[0,:,:,1] = loadmat(cover_beta_path + IL[i].replace('pgm','mat'))['Beta'] # adjust for JPEG images\n",
" batch[1,:,:,0] = plt.imread(stego_path + IL[i]) # use loadmat for loading JPEG decompressed images \n",
" batch[1,:,:,1] = loadmat(stego_beta_path + IL[i].replace('pgm','mat'))['Beta'] # adjust for JPEG images\n",
" rot = random.randint(0,3)\n",
" if rand() < 0.5:\n",
" yield [np.rot90(batch, rot, axes=[1,2]), np.array([0,1], dtype='uint8')]\n",
" else:\n",
" yield [np.flip(np.rot90(batch, rot, axes=[1,2]), axis=2), np.array([0,1], dtype='uint8')] "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def valGen(cover_path, stego_path, cover_beta_path, stego_beta_path, thread_idx=0, n_threads=1):\n",
" IL=os.listdir(cover_path)\n",
" img_shape = plt.imread(cover_path +IL[0]).shape\n",
" batch = np.empty((2, img_shape[0], img_shape[1], 2), dtype='float32')\n",
" while True:\n",
" for i in range(len(IL)):\n",
" batch[0,:,:,0] = plt.imread(cover_path + IL[i]) # use loadmat for loading JPEG decompressed images \n",
" batch[0,:,:,1] = loadmat(cover_beta_path + IL[i].replace('pgm','mat'))['Beta'] # adjust for JPEG images\n",
" batch[1,:,:,0] = plt.imread(stego_path + IL[i]) # use loadmat for loading JPEG decompressed images \n",
" batch[1,:,:,1] = loadmat(stego_beta_path + IL[i].replace('pgm','mat'))['Beta'] # adjust for JPEG images\n",
" yield [batch, np.array([0,1], dtype='uint8') ]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"train_batch_size = 32\n",
"valid_batch_size = 40\n",
"max_iter = 500000\n",
"train_interval=100\n",
"valid_interval=5000\n",
"save_interval=5000\n",
"num_runner_threads=10\n",
"\n",
"# save Betas as '.mat' files with variable name \"Beta\" and put them in thier corresponding directoroies. Make sure \n",
"# all mat files in the directories can be loaded in Python without any errors.\n",
"\n",
"TRAIN_COVER_DIR = '/media/Cover_TRN/'\n",
"TRAIN_STEGO_DIR = '/media/Stego_WOW_0.5_TRN/'\n",
"TRAIN_COVER_BETA_DIR = '/media/Beta_Cover_WOW_0.5_TRN/'\n",
"TRAIN_STEGO_BETA_DIR = '/media/Beta_Stego_WOW_0.5_TRN/'\n",
"\n",
"VALID_COVER_DIR = '/media/Cover_VAL/'\n",
"VALID_STEGO_DIR = '/media/Stego_WOW_0.5_VAL/'\n",
"VALID_COVER_BETA_DIR = '/media/Beta_Cover_WOW_0.5_VAL/'\n",
"VALID_STEGO_BETA_DIR = '/media/Beta_Stego_WOW_0.5_VAL/'\n",
"\n",
"train_gen = partial(trnGen, \\\n",
" TRAIN_COVER_DIR, TRAIN_STEGO_DIR, TRAIN_COVER_BETA_DIR, TRAIN_STEGO_BETA_DIR) \n",
"valid_gen = partial(valGen, \\\n",
" VALID_COVER_DIR, VALID_STEGO_DIR, VALID_COVER_BETA_DIR, VALID_STEGO_BETA_DIR)\n",
"\n",
"LOG_DIR= '/media/LogFiles/SCA_WOW_0.5' # path for a log direcotry\n",
"# load_path= LOG_DIR + 'Model_460000.ckpt' # continue training from a specific checkpoint\n",
"load_path=None # training from scratch\n",
"\n",
"if not os.path.exists(LOG_DIR):\n",
" os.makedirs(LOG_DIR)\n",
" \n",
"train_ds_size = len(glob(TRAIN_COVER_DIR + '/*')) * 2\n",
"valid_ds_size = len(glob(VALID_COVER_DIR +'/*')) * 2\n",
"print 'train_ds_size: %i'%train_ds_size\n",
"print 'valid_ds_size: %i'%valid_ds_size\n",
"\n",
"if valid_ds_size % valid_batch_size != 0:\n",
" raise ValueError(\"change batch size for validation\")\n",
"\n",
"optimizer = AdamaxOptimizer\n",
"boundaries = [400000] # learning rate adjustment at iteration 400K\n",
"values = [0.001, 0.0001] # learning rates\n",
"train(SCA_SRNet, train_gen, valid_gen , train_batch_size, valid_batch_size, valid_ds_size, \\\n",
" optimizer, boundaries, values, train_interval, valid_interval, max_iter,\\\n",
" save_interval, LOG_DIR,num_runner_threads, load_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Testing \n",
"TEST_COVER_DIR = '/media/Cover_TST/'\n",
"TEST_STEGO_DIR = '/media/Stego_WOW_0.5_TST/'\n",
"TEST_COVER_BETA_DIR = '/media/Beta_Cover_WOW_0.5_TST/'\n",
"TEST_STEGO_BETA_DIR = '/media/Beta_Stego_WOW_0.5_TST/'\n",
"\n",
"test_batch_size=40\n",
"LOG_DIR = '/media/LogFiles/SCA_WOW_0.5/' \n",
"LOAD_DIR = LOG_DIR + 'Model_435000.ckpt' # loading from a specific checkpoint\n",
"\n",
"test_gen = partial(gen_valid, \\\n",
" TEST_COVER_DIR, TEST_STEGO_DIR)\n",
"\n",
"test_ds_size = len(glob(TEST_COVER_DIR + '/*')) * 2\n",
"print 'test_ds_size: %i'%test_ds_size\n",
"\n",
"if test_ds_size % test_batch_size != 0:\n",
" raise ValueError(\"change batch size for testing!\")\n",
"\n",
"test_dataset(SCA_SRNet, test_gen, test_batch_size, test_ds_size, LOAD_DIR)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.15rc1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

127
SRNet/SRNet_Example.ipynb Normal file
View File

@ -0,0 +1,127 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import os\n",
"import sys\n",
"os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'\n",
"os.environ['CUDA_VISIBLE_DEVICES'] = '1' # set a GPU (with GPU Number)\n",
"home = os.path.expanduser(\"~\")\n",
"sys.path.append(home + '/tflib/') # path for 'tflib' folder\n",
"from SRNet import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"train_batch_size = 32\n",
"valid_batch_size = 40\n",
"max_iter = 500000\n",
"train_interval=100\n",
"valid_interval=5000\n",
"save_interval=5000\n",
"num_runner_threads=10\n",
"\n",
"# Cover and Stego directories for training and validation. For the spatial domain put cover and stego images in their \n",
"# corresponding direcotries. For the JPEG domain, decompress images to the spatial domain without rounding to integers and \n",
"# save them as '.mat' files with variable name \"im\". Put the '.mat' files in thier corresponding directoroies. Make sure \n",
"# all mat files in the directories can be loaded in Python without any errors.\n",
"\n",
"TRAIN_COVER_DIR = '/media/TRN/Cover/'\n",
"TRAIN_STEGO_DIR = '/media/TRN/JUNI_75_04/'\n",
"\n",
"VALID_COVER_DIR = '/media/VAL/Cover/'\n",
"VALID_STEGO_DIR = '/media/VAL/JUNI_75_04/'\n",
" \n",
"train_gen = partial(gen_flip_and_rot, \\\n",
" TRAIN_COVER_DIR, TRAIN_STEGO_DIR ) \n",
"valid_gen = partial(gen_valid, \\\n",
" VALID_COVER_DIR, VALID_STEGO_DIR)\n",
"\n",
"LOG_DIR = '/media/LogFiles/JUNI_75_04' # path for a log direcotry \n",
"# load_path = LOG_DIR + 'Model_460000.ckpt' # continue training from a specific checkpoint\n",
"load_path=None # training from scratch\n",
"\n",
"if not os.path.exists(LOG_DIR):\n",
" os.makedirs(LOG_DIR)\n",
"\n",
"train_ds_size = len(glob(TRAIN_COVER_DIR + '/*')) * 2\n",
"valid_ds_size = len(glob(VALID_COVER_DIR +'/*')) * 2\n",
"print 'train_ds_size: %i'%train_ds_size\n",
"print 'valid_ds_size: %i'%valid_ds_size\n",
"\n",
"if valid_ds_size % valid_batch_size != 0:\n",
" raise ValueError(\"change batch size for validation\")\n",
" \n",
"optimizer = AdamaxOptimizer\n",
"boundaries = [400000] # learning rate adjustment at iteration 400K\n",
"values = [0.001, 0.0001] # learning rates\n",
"\n",
"train(SRNet, train_gen, valid_gen , train_batch_size, valid_batch_size, valid_ds_size, \\\n",
" optimizer, boundaries, values, train_interval, valid_interval, max_iter,\\\n",
" save_interval, LOG_DIR,num_runner_threads, load_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Testing \n",
"# Cover and Stego directories for testing\n",
"TEST_COVER_DIR = '/media/TST/Cover/'\n",
"TEST_STEGO_DIR = '/media/TST/JUNI_75_04/'\n",
"\n",
"test_batch_size=40\n",
"LOG_DIR = '/media/LogFiles/JUNI_75_04/' \n",
"LOAD_CKPT = LOG_DIR + 'Model_435000.ckpt' # loading from a specific checkpoint\n",
"\n",
"test_gen = partial(gen_valid, \\\n",
" TEST_COVER_DIR, TEST_STEGO_DIR)\n",
"\n",
"test_ds_size = len(glob(TEST_COVER_DIR + '/*')) * 2\n",
"print 'test_ds_size: %i'%test_ds_size\n",
"\n",
"if test_ds_size % test_batch_size != 0:\n",
" raise ValueError(\"change batch size for testing!\")\n",
"\n",
"test_dataset(SRNet, test_gen, test_batch_size, test_ds_size, LOAD_CKPT)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.15rc1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,133 @@
import tensorflow as tf
from functools import partial
from tensorflow.contrib import layers
from tensorflow.contrib.framework import arg_scope
import functools
from queues import *
from generator import *
from utils_multistep_lr import *
class SCA_SRNet(Model):
def _build_model(self, input_batch):
inputs_image, inputs_Beta = tf.split(input_batch, num_or_size_splits=2, axis=3)
if self.data_format == 'NCHW':
reduction_axis = [2,3]
_inputs_image = tf.cast(tf.transpose(inputs_image, [0, 3, 1, 2]), tf.float32)
_inputs_Beta = tf.cast(tf.transpose(inputs_Beta, [0, 3, 1, 2]), tf.float32)
else:
reduction_axis = [1,2]
_inputs_image = tf.cast(inputs_image, tf.float32)
_inputs_Beta = tf.cast(inputs_Beta, tf.float32)
with arg_scope([layers.conv2d], num_outputs=16,
kernel_size=3, stride=1, padding='SAME',
data_format=self.data_format,
activation_fn=None,
weights_initializer=layers.variance_scaling_initializer(),
weights_regularizer=layers.l2_regularizer(2e-4),
biases_initializer=tf.constant_initializer(0.2),
biases_regularizer=None),\
arg_scope([layers.batch_norm],
decay=0.9, center=True, scale=True,
updates_collections=None, is_training=self.is_training,
fused=True, data_format=self.data_format),\
arg_scope([layers.avg_pool2d],
kernel_size=[3,3], stride=[2,2], padding='SAME',
data_format=self.data_format):
with tf.variable_scope('Layer1'): # 256*256
W = tf.get_variable('W', shape=[3,3,1,64],\
initializer=layers.variance_scaling_initializer(), \
dtype=tf.float32, \
regularizer=layers.l2_regularizer(5e-4))
b = tf.get_variable('b', shape=[64], dtype=tf.float32, \
initializer=tf.constant_initializer(0.2))
conv = tf.nn.bias_add( \
tf.nn.conv2d(tf.cast(_inputs_image, tf.float32), \
W, [1,1,1,1], 'SAME', \
data_format=self.data_format), b, \
data_format=self.data_format, name='Layer1')
actv=tf.nn.relu(conv)
prob_map = tf.sqrt(tf.nn.conv2d(tf.cast(_inputs_Beta, tf.float32), \
tf.abs(W), [1,1,1,1], 'SAME', \
data_format=self.data_format))
out_L1=tf.add_n([actv,prob_map])
with tf.variable_scope('Layer2'): # 256*256
conv=layers.conv2d(out_L1)
actv=tf.nn.relu(layers.batch_norm(conv))
with tf.variable_scope('Layer3'): # 256*256
conv1=layers.conv2d(actv)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn2=layers.batch_norm(conv2)
res= tf.add(actv, bn2)
with tf.variable_scope('Layer4'): # 256*256
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn2=layers.batch_norm(conv2)
res= tf.add(res, bn2)
with tf.variable_scope('Layer5'): # 256*256
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer6'): # 256*256
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer7'): # 256*256
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer8'): # 256*256
convs = layers.conv2d(res, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer9'): # 128*128
convs = layers.conv2d(res, num_outputs=64, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=64)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=64)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer10'): # 64*64
convs = layers.conv2d(res, num_outputs=128, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=128)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=128)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer11'): # 32*32
convs = layers.conv2d(res, num_outputs=256, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=256)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=256)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer12'): # 16*16
conv1=layers.conv2d(res, num_outputs=512)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=512)
bn=layers.batch_norm(conv2)
avgp = tf.reduce_mean(bn, reduction_axis, keep_dims=True )
ip=layers.fully_connected(layers.flatten(avgp), num_outputs=2,
activation_fn=None, normalizer_fn=None,
weights_initializer=tf.random_normal_initializer(mean=0., stddev=0.01),
biases_initializer=tf.constant_initializer(0.), scope='ip')
self.outputs = ip
return self.outputs

View File

@ -0,0 +1,133 @@
import tensorflow as tf
from functools import partial
from tensorflow.contrib import layers
from tensorflow.contrib.framework import arg_scope
import functools
from tflib.queues import *
from tflib.generator import *
from tflib.utils_multistep_lr import *
class SCA_SRNet(Model):
def _build_model(self, input_batch):
inputs_image, inputs_Beta = tf.split(input_batch, num_or_size_splits=2, axis=3)
if self.data_format == 'NCHW':
reduction_axis = [2,3]
_inputs_image = tf.cast(tf.transpose(inputs_image, [0, 3, 1, 2]), tf.float32)
_inputs_Beta = tf.cast(tf.transpose(inputs_Beta, [0, 3, 1, 2]), tf.float32)
else:
reduction_axis = [1,2]
_inputs_image = tf.cast(inputs_image, tf.float32)
_inputs_Beta = tf.cast(inputs_Beta, tf.float32)
with arg_scope([layers.conv2d], num_outputs=16,
kernel_size=3, stride=1, padding='SAME',
data_format=self.data_format,
activation_fn=None,
weights_initializer=layers.variance_scaling_initializer(),
weights_regularizer=layers.l2_regularizer(2e-4),
biases_initializer=tf.constant_initializer(0.2),
biases_regularizer=None),\
arg_scope([layers.batch_norm],
decay=0.9, center=True, scale=True,
updates_collections=None, is_training=self.is_training,
fused=True, data_format=self.data_format),\
arg_scope([layers.avg_pool2d],
kernel_size=[3,3], stride=[2,2], padding='SAME',
data_format=self.data_format):
with tf.variable_scope('Layer1'): # 256*256
W = tf.get_variable('W', shape=[3,3,1,64],\
initializer=layers.variance_scaling_initializer(), \
dtype=tf.float32, \
regularizer=layers.l2_regularizer(5e-4))
b = tf.get_variable('b', shape=[64], dtype=tf.float32, \
initializer=tf.constant_initializer(0.2))
conv = tf.nn.bias_add( \
tf.nn.conv2d(tf.cast(_inputs_image, tf.float32), \
W, [1,1,1,1], 'SAME', \
data_format=self.data_format), b, \
data_format=self.data_format, name='Layer1')
actv=tf.nn.relu(conv)
prob_map = tf.nn.conv2d(tf.cast(_inputs_Beta, tf.float32), \
tf.abs(W), [1,1,1,1], 'SAME', \
data_format=self.data_format)
out_L1=tf.add_n([actv,prob_map])
with tf.variable_scope('Layer2'): # 256*256
conv=layers.conv2d(out_L1)
actv=tf.nn.relu(layers.batch_norm(conv))
with tf.variable_scope('Layer3'): # 256*256
conv1=layers.conv2d(actv)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn2=layers.batch_norm(conv2)
res= tf.add(actv, bn2)
with tf.variable_scope('Layer4'): # 256*256
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn2=layers.batch_norm(conv2)
res= tf.add(res, bn2)
with tf.variable_scope('Layer5'): # 256*256
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer6'): # 256*256
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer7'): # 256*256
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer8'): # 256*256
convs = layers.conv2d(res, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer9'): # 128*128
convs = layers.conv2d(res, num_outputs=64, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=64)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=64)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer10'): # 64*64
convs = layers.conv2d(res, num_outputs=128, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=128)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=128)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer11'): # 32*32
convs = layers.conv2d(res, num_outputs=256, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=256)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=256)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer12'): # 16*16
conv1=layers.conv2d(res, num_outputs=512)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=512)
bn=layers.batch_norm(conv2)
avgp = tf.reduce_mean(bn, reduction_axis, keep_dims=True )
ip=layers.fully_connected(layers.flatten(avgp), num_outputs=2,
activation_fn=None, normalizer_fn=None,
weights_initializer=tf.random_normal_initializer(mean=0., stddev=0.01),
biases_initializer=tf.constant_initializer(0.), scope='ip')
self.outputs = ip
return self.outputs

117
SRNet/tflib/SRNet.py Normal file
View File

@ -0,0 +1,117 @@
import tensorflow as tf
from functools import partial
from tensorflow.contrib import layers
from tensorflow.contrib.framework import arg_scope
import functools
from tflib.queues import *
from tflib.generator import *
from tflib.utils_multistep_lr import *
class SRNet(Model):
def _build_model(self, inputs):
self.inputs = inputs
if self.data_format == 'NCHW':
reduction_axis = [2,3]
_inputs = tf.cast(tf.transpose(inputs, [0, 3, 1, 2]), tf.float32)
else:
reduction_axis = [1,2]
_inputs = tf.cast(inputs, tf.float32)
with arg_scope([layers.conv2d], num_outputs=16,
kernel_size=3, stride=1, padding='SAME',
data_format=self.data_format,
activation_fn=None,
weights_initializer=layers.variance_scaling_initializer(),
weights_regularizer=layers.l2_regularizer(2e-4),
biases_initializer=tf.constant_initializer(0.2),
biases_regularizer=None),\
arg_scope([layers.batch_norm],
decay=0.9, center=True, scale=True,
updates_collections=None, is_training=self.is_training,
fused=True, data_format=self.data_format),\
arg_scope([layers.avg_pool2d],
kernel_size=[3,3], stride=[2,2], padding='SAME',
data_format=self.data_format):
with tf.variable_scope('Layer1'):
conv=layers.conv2d(_inputs, num_outputs=64, kernel_size=3)
actv=tf.nn.relu(layers.batch_norm(conv))
with tf.variable_scope('Layer2'):
conv=layers.conv2d(actv)
actv=tf.nn.relu(layers.batch_norm(conv))
with tf.variable_scope('Layer3'):
conv1=layers.conv2d(actv)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn2=layers.batch_norm(conv2)
res= tf.add(actv, bn2)
with tf.variable_scope('Layer4'):
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn2=layers.batch_norm(conv2)
res= tf.add(res, bn2)
with tf.variable_scope('Layer5'):
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer6'):
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer7'):
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
res= tf.add(res, bn)
with tf.variable_scope('Layer8'):
convs = layers.conv2d(res, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer9'):
convs = layers.conv2d(res, num_outputs=64, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=64)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=64)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer10'):
convs = layers.conv2d(res, num_outputs=128, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=128)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=128)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer11'):
convs = layers.conv2d(res, num_outputs=256, kernel_size=1, stride=2)
convs = layers.batch_norm(convs)
conv1=layers.conv2d(res, num_outputs=256)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=256)
bn=layers.batch_norm(conv2)
pool = layers.avg_pool2d(bn)
res= tf.add(convs, pool)
with tf.variable_scope('Layer12'):
conv1=layers.conv2d(res, num_outputs=512)
actv1=tf.nn.relu(layers.batch_norm(conv1))
conv2=layers.conv2d(actv1, num_outputs=512)
bn=layers.batch_norm(conv2)
avgp = tf.reduce_mean(bn, reduction_axis, keep_dims=True )
ip=layers.fully_connected(layers.flatten(avgp), num_outputs=2,
activation_fn=None, normalizer_fn=None,
weights_initializer=tf.random_normal_initializer(mean=0., stddev=0.01),
biases_initializer=tf.constant_initializer(0.), scope='ip')
self.outputs = ip
return self.outputs

81
SRNet/tflib/generator.py Normal file
View File

@ -0,0 +1,81 @@
import numpy as np
from scipy import misc, io
from glob import glob
import random
# from itertools import izip
from random import random as rand
from random import shuffle
def gen_flip_and_rot(cover_dir, stego_dir, thread_idx, n_threads):
cover_list = sorted(glob(cover_dir + '/*'))
# print(cover_list)
stego_list = sorted(glob(stego_dir + '/*'))
nb_data = len(cover_list)
assert len(stego_list) != 0, "the stego directory '%s' is empty" % stego_dir
assert nb_data != 0, "the cover directory '%s' is empty" % cover_dir
assert len(stego_list) == nb_data, "the cover directory and " + \
"the stego directory don't " + \
"have the same number of files " + \
"respectively %d and %d" % (nb_data, + \
len(stego_list))
load_mat=cover_list[0].endswith('.mat')
if load_mat:
img = io.loadmat(cover_list[0])['im']
img_shape = img.shape
batch = np.empty((2,img_shape[0],img_shape[1],1), dtype='float32')
else:
img = misc.imread(cover_list[0])
img_shape = img.shape
batch = np.empty((2,img_shape[0],img_shape[1],1), dtype='uint8')
iterable = list(zip(cover_list, stego_list))
while True:
shuffle(iterable)
print("shuffling……")
for cover_path, stego_path in iterable:
if load_mat:
batch[0,:,:,0] = io.loadmat(cover_path)['im']
batch[1,:,:,0] = io.loadmat(stego_path)['im']
else:
batch[0,:,:,0] = misc.imread(cover_path)
batch[1,:,:,0] = misc.imread(stego_path)
rot = random.randint(0,3)
if rand() < 0.5:
yield [np.rot90(batch, rot, axes=[1,2]), np.array([0,1], dtype='uint8')]
else:
yield [np.flip(np.rot90(batch, rot, axes=[1,2]), axis=2), np.array([0,1], dtype='uint8')]
def gen_valid(cover_dir, stego_dir, thread_idx, n_threads):
cover_list = sorted(glob(cover_dir + '/*'))
stego_list = sorted(glob(stego_dir + '/*'))
nb_data = len(cover_list)
assert len(stego_list) != 0, "the stego directory '%s' is empty" % stego_dir
assert nb_data != 0, "the cover directory '%s' is empty" % cover_dir
assert len(stego_list) == nb_data, "the cover directory and " + \
"the stego directory don't " + \
"have the same number of files " + \
"respectively %d and %d" % (nb_data, \
len(stego_list))
load_mat=cover_list[0].endswith('.mat')
if load_mat:
img = io.loadmat(cover_list[0])['im']
img_shape = img.shape
batch = np.empty((2,img_shape[0],img_shape[1],1), dtype='float32')
else:
img = misc.imread(cover_list[0])
img_shape = img.shape
batch = np.empty((2,img_shape[0],img_shape[1],1), dtype='uint8')
img_shape = img.shape
labels = np.array([0, 1], dtype='uint8')
while True:
for cover_path, stego_path in zip(cover_list, stego_list):
if load_mat:
batch[0,:,:,0] = io.loadmat(cover_path)['im']
batch[1,:,:,0] = io.loadmat(stego_path)['im']
else:
batch[0,:,:,0] = misc.imread(cover_path)
batch[1,:,:,0] = misc.imread(stego_path)
# print([batch,labels])
yield [batch, labels]

68
SRNet/tflib/queues.py Normal file
View File

@ -0,0 +1,68 @@
import tensorflow as tf
import threading
class GeneratorRunner():
"""
This class manage a multithreaded queue filled with a generator
"""
def __init__(self, generator, capacity):
"""
inputs: generator feeding the data, must have thread_idx
as parameter (but the parameter may be not used)
"""
self.generator = generator
_input = generator(0,1).__next__()
if type(_input) is not list:
raise ValueError("generator doesn't return" \
"a list: %r" % type(_input))
input_batch_size = _input[0].shape[0]
if not all(_input[i].shape[0] == input_batch_size for i in range(len(_input))):
raise ValueError("all the inputs doesn't have the same batch size,"\
"the batch sizes are: %s" % [_input[i].shape[0] for i in range(len(_input))])
self.data = []
self.dtypes = []
self.shapes = []
for i in range(len(_input)):
self.shapes.append(_input[i].shape[1:])
self.dtypes.append(_input[i].dtype)
self.data.append(tf.placeholder(dtype=self.dtypes[i], \
shape=(input_batch_size,) + self.shapes[i]))
self.queue = tf.FIFOQueue(capacity, shapes=self.shapes, \
dtypes=self.dtypes)
self.enqueue_op = self.queue.enqueue_many(self.data)
self.close_queue_op = self.queue.close(cancel_pending_enqueues=True)
def get_batched_inputs(self, batch_size):
"""
Return tensors containing a batch of generated data
"""
batch = self.queue.dequeue_many(batch_size)
return batch
def thread_main(self, sess, thread_idx=0, n_threads=1):
try:
for data in self.generator(thread_idx, n_threads):
sess.run(self.enqueue_op, feed_dict={i: d \
for i, d in zip(self.data, data)})
if self.stop_threads:
return
except RuntimeError:
pass
def start_threads(self, sess, n_threads=1):
self.stop_threads = False
self.threads = []
for n in range(n_threads):
t = threading.Thread(target=self.thread_main, args=(sess, n, n_threads))
t.daemon = True
t.start()
self.threads.append(t)
return self.threads
def stop_runner(self, sess):
self.stop_threads = True
sess.run(self.close_queue_op)
def queueSelection(runners, sel, batch_size):
selection_queue = tf.FIFOQueue.from_list(sel, [r.queue for r in runners])
return selection_queue.dequeue_many(batch_size)

View File

@ -0,0 +1,231 @@
import tensorflow as tf
import time
from tflib.queues import *
from tflib.generator import *
class average_summary:
def __init__(self, variable, name, num_iterations):
self.sum_variable = tf.get_variable(name, shape=[], \
initializer=tf.constant_initializer(0), \
dtype=variable.dtype.base_dtype, \
trainable=False, \
collections=[tf.GraphKeys.LOCAL_VARIABLES])
with tf.control_dependencies([variable]):
self.increment_op = tf.assign_add(self.sum_variable, variable)
self.mean_variable = self.sum_variable / float(num_iterations)
self.summary = tf.summary.scalar(name, self.mean_variable)
with tf.control_dependencies([self.summary]):
self.reset_variable_op = tf.assign(self.sum_variable, 0)
def add_summary(self, sess, writer, step):
s, _ = sess.run([self.summary, self.reset_variable_op])
writer.add_summary(s, step)
class Model:
def __init__(self, is_training=None, data_format='NCHW'):
self.data_format = data_format
if is_training is None:
self.is_training = tf.get_variable('is_training', dtype=tf.bool, \
initializer=tf.constant_initializer(True), \
trainable=False)
else:
self.is_training = is_training
def _build_model(self, inputs):
raise NotImplementedError('Here is your model definition')
def _build_losses(self, labels):
self.labels = tf.cast(labels, tf.int64)
with tf.variable_scope('loss'):
oh = tf.one_hot(self.labels, 2)
xen_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits( \
labels=oh,logits=self.outputs))
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
self.loss = tf.add_n([xen_loss] + reg_losses)
with tf.variable_scope('accuracy'):
am = tf.argmax(self.outputs, 1)
equal = tf.equal(am, self.labels)
self.accuracy = tf.reduce_mean(tf.cast(equal, tf.float32))
return self.loss, self.accuracy
def train(model_class, train_gen, valid_gen, train_batch_size, \
valid_batch_size, valid_ds_size, optimizer, boundaries, values, \
train_interval, valid_interval, max_iter, \
save_interval, log_path, num_runner_threads=1, \
load_path=None):
tf.reset_default_graph()
train_runner = GeneratorRunner(train_gen, train_batch_size * 10)
valid_runner = GeneratorRunner(valid_gen, valid_batch_size * 10)
is_training = tf.get_variable('is_training', dtype=tf.bool, \
initializer=True, trainable=False)
if train_batch_size == valid_batch_size:
batch_size = train_batch_size
disable_training_op = tf.assign(is_training, False)
enable_training_op = tf.assign(is_training, True)
else:
batch_size = tf.get_variable('batch_size', dtype=tf.int32, \
initializer=train_batch_size, \
trainable=False, \
collections=[tf.GraphKeys.LOCAL_VARIABLES])
disable_training_op = tf.group(tf.assign(is_training, False), \
tf.assign(batch_size, valid_batch_size))
enable_training_op = tf.group(tf.assign(is_training, True), \
tf.assign(batch_size, train_batch_size))
img_batch, label_batch = queueSelection([valid_runner, train_runner], \
tf.cast(is_training, tf.int32), \
batch_size)
model = model_class(is_training, 'NCHW')
model._build_model(img_batch)
loss, accuracy = model._build_losses(label_batch)
train_loss_s = average_summary(loss, 'train_loss', train_interval)
train_accuracy_s = average_summary(accuracy, 'train_accuracy', \
train_interval)
valid_loss_s = average_summary(loss, 'valid_loss', \
float(valid_ds_size) / float(valid_batch_size))
valid_accuracy_s = average_summary(accuracy, 'valid_accuracy', \
float(valid_ds_size) / float(valid_batch_size))
global_step = tf.get_variable('global_step', dtype=tf.int32, shape=[], \
initializer=tf.constant_initializer(0), \
trainable=False)
learning_rate = tf.train.piecewise_constant(global_step, boundaries, values)
lr_summary = tf.summary.scalar('learning_rate', learning_rate)
optimizer = optimizer(learning_rate)
minimize_op = optimizer.minimize(loss, global_step)
train_op = tf.group(minimize_op, train_loss_s.increment_op, \
train_accuracy_s.increment_op)
increment_valid = tf.group(valid_loss_s.increment_op, \
valid_accuracy_s.increment_op)
init_op = tf.group(tf.global_variables_initializer(), \
tf.local_variables_initializer())
saver = tf.train.Saver(max_to_keep=10000)
with tf.Session() as sess:
sess.run(init_op)
if load_path is not None:
saver.restore(sess, load_path)
train_runner.start_threads(sess, num_runner_threads)
valid_runner.start_threads(sess, 1)
writer = tf.summary.FileWriter(log_path + '/LogFile/', \
sess.graph)
start = sess.run(global_step)
sess.run(disable_training_op)
sess.run([valid_loss_s.reset_variable_op, \
valid_accuracy_s.reset_variable_op, \
train_loss_s.reset_variable_op, \
train_accuracy_s.reset_variable_op])
_time = time.time()
for j in range(0, valid_ds_size, valid_batch_size):
sess.run([increment_valid])
_acc_val = sess.run(valid_accuracy_s.mean_variable)
print ("initial accuracy on validation set:", _acc_val)
print ("evaluation time on validation set:", time.time() - _time, "seconds")
valid_accuracy_s.add_summary(sess, writer, start)
valid_loss_s.add_summary(sess, writer, start)
sess.run(enable_training_op)
print ("network will be evaluatd every %i iterations on validation set" %valid_interval)
for i in range(start+1, max_iter+1):
sess.run(train_op)
if i % train_interval == 0:
train_loss_s.add_summary(sess, writer, i)
train_accuracy_s.add_summary(sess, writer, i)
s = sess.run(lr_summary)
writer.add_summary(s, i)
if i % valid_interval == 0:
sess.run(disable_training_op)
for j in range(0, valid_ds_size, valid_batch_size):
sess.run([increment_valid])
valid_loss_s.add_summary(sess, writer, i)
valid_accuracy_s.add_summary(sess, writer, i)
sess.run(enable_training_op)
if i % save_interval == 0:
saver.save(sess, log_path + '/Model_' + str(i) + '.ckpt')
def test_dataset(model_class, gen, batch_size, ds_size, load_path):
tf.reset_default_graph()
runner = GeneratorRunner(gen, batch_size * 10)
img_batch, label_batch = runner.get_batched_inputs(batch_size)
model = model_class(False, 'NCHW')
model._build_model(img_batch)
loss, accuracy = model._build_losses(label_batch)
loss_summary = average_summary(loss, 'loss', \
float(ds_size) / float(batch_size))
accuracy_summary = average_summary(accuracy, 'accuracy', \
float(ds_size) / float(batch_size))
increment_op = tf.group(loss_summary.increment_op, \
accuracy_summary.increment_op)
global_step = tf.get_variable('global_step', dtype=tf.int32, shape=[], \
initializer=tf.constant_initializer(0), \
trainable=False)
init_op = tf.group(tf.global_variables_initializer(), \
tf.local_variables_initializer())
saver = tf.train.Saver(max_to_keep=10000)
with tf.Session() as sess:
sess.run(init_op)
saver.restore(sess, load_path)
runner.start_threads(sess, 1)
for j in range(0, ds_size, batch_size):
sess.run(increment_op)
mean_loss, mean_accuracy = sess.run([loss_summary.mean_variable ,\
accuracy_summary.mean_variable])
print ("Accuracy:", mean_accuracy, " | Loss:", mean_loss)
### Implementation of Adamax optimizer, taken from : https://github.com/openai/iaf/blob/master/tf_utils/adamax.py
from tensorflow.python.ops import control_flow_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import state_ops
from tensorflow.python.framework import ops
from tensorflow.python.training import optimizer
import tensorflow as tf
class AdamaxOptimizer(optimizer.Optimizer):
"""
Optimizer that implements the Adamax algorithm.
See [Kingma et. al., 2014](http://arxiv.org/abs/1412.6980)
([pdf](http://arxiv.org/pdf/1412.6980.pdf)).
@@__init__
"""
def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999, use_locking=False, name="Adamax"):
super(AdamaxOptimizer, self).__init__(use_locking, name)
self._lr = learning_rate
self._beta1 = beta1
self._beta2 = beta2
# Tensor versions of the constructor arguments, created in _prepare().
self._lr_t = None
self._beta1_t = None
self._beta2_t = None
def _prepare(self):
self._lr_t = ops.convert_to_tensor(self._lr, name="learning_rate")
self._beta1_t = ops.convert_to_tensor(self._beta1, name="beta1")
self._beta2_t = ops.convert_to_tensor(self._beta2, name="beta2")
def _create_slots(self, var_list):
# Create slots for the first and second moments.
for v in var_list:
self._zeros_slot(v, "m", self._name)
self._zeros_slot(v, "v", self._name)
def _apply_dense(self, grad, var):
lr_t = math_ops.cast(self._lr_t, var.dtype.base_dtype)
beta1_t = math_ops.cast(self._beta1_t, var.dtype.base_dtype)
beta2_t = math_ops.cast(self._beta2_t, var.dtype.base_dtype)
if var.dtype.base_dtype == tf.float16:
eps = 1e-7 # Can't use 1e-8 due to underflow -- not sure if it makes a big difference.
else:
eps = 1e-8
v = self.get_slot(var, "v")
v_t = v.assign(beta1_t * v + (1. - beta1_t) * grad)
m = self.get_slot(var, "m")
m_t = m.assign(tf.maximum(beta2_t * m + eps, tf.abs(grad)))
g_t = v_t / m_t
var_update = state_ops.assign_sub(var, lr_t * g_t)
return control_flow_ops.group(*[var_update, m_t, v_t])
def _apply_sparse(self, grad, var):
raise NotImplementedError("Sparse gradient updates are not supported.")

View File

@ -0,0 +1,8 @@
# YeNet-Tensorflow
Tensorflow implementation of "Deep Learning Hierarchical Representation for Image Steganalysis" by Jian Ye, Jiangqun Ni and Yang Yi
## Dataset
Training and validation contains there own cover and stego / beta maps images directory, stego images must have the same name than there corresponding cover
## Publication
[The publication can be found here](http://ieeexplore.ieee.org/document/7937836/)

Binary file not shown.

119
YeNet-Tensorflow/YeNet.py Normal file
View File

@ -0,0 +1,119 @@
import tensorflow as tf
from tensorflow.contrib import layers
from tensorflow.contrib.framework import add_arg_scope, arg_scope, arg_scoped_arguments
import layers as my_layers
from utils import *
SRM_Kernels = np.load('SRM_Kernels.npy')
class YeNet(Model):
def __init__(self, is_training=None, data_format='NCHW', \
with_bn=False, tlu_threshold=3):
super(YeNet, self).__init__(is_training=is_training, \
data_format=data_format)
self.with_bn = with_bn
self.tlu_threshold = tlu_threshold
def _build_model(self, inputs):
self.inputs = inputs
if self.data_format == 'NCHW':
channel_axis = 1
_inputs = tf.cast(tf.transpose(inputs, [0, 3, 1, 2]), tf.float32)
else:
channel_axis = 3
_inputs = tf.cast(inputs, tf.float32)
self.L = []
with arg_scope([layers.avg_pool2d], \
padding='VALID', data_format=self.data_format):
with tf.variable_scope('SRM_preprocess'):
W_SRM = tf.get_variable('W', initializer=SRM_Kernels, \
dtype=tf.float32, \
regularizer=None)
b = tf.get_variable('b', shape=[30], dtype=tf.float32, \
initializer=tf.constant_initializer(0.))
self.L.append(tf.nn.bias_add( \
tf.nn.conv2d(_inputs, \
W_SRM, [1,1,1,1], 'VALID', \
data_format=self.data_format), b, \
data_format=self.data_format, name='Layer1'))
self.L.append(tf.clip_by_value(self.L[-1], \
-self.tlu_threshold, self.tlu_threshold, \
name='TLU'))
with tf.variable_scope('ConvNetwork'):
with arg_scope([my_layers.conv2d], num_outputs=30, \
kernel_size=3, stride=1, padding='VALID', \
data_format=self.data_format, \
activation_fn=tf.nn.relu, \
weights_initializer=layers.xavier_initializer_conv2d(), \
weights_regularizer=layers.l2_regularizer(5e-4), \
biases_initializer=tf.constant_initializer(0.2), \
biases_regularizer=None), arg_scope([layers.batch_norm], \
decay=0.9, center=True, scale=True, \
updates_collections=None, is_training=self.is_training, \
fused=True, data_format=self.data_format):
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm1'))
self.L.append(my_layers.conv2d(self.L[-1], \
scope='Layer2'))
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm2'))
self.L.append(my_layers.conv2d(self.L[-1], \
scope='Layer3'))
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm3'))
self.L.append(my_layers.conv2d(self.L[-1], \
scope='Layer4'))
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm4'))
self.L.append(layers.avg_pool2d(self.L[-1], \
kernel_size=[2,2], scope='Stride1'))
with arg_scope([my_layers.conv2d], kernel_size=5, \
num_outputs=32):
self.L.append(my_layers.conv2d(self.L[-1], \
scope='Layer5'))
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm5'))
self.L.append(layers.avg_pool2d(self.L[-1], \
kernel_size=[3,3], \
scope='Stride2'))
self.L.append(my_layers.conv2d(self.L[-1], \
scope='Layer6'))
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm6'))
self.L.append(layers.avg_pool2d(self.L[-1], \
kernel_size=[3,3], \
scope='Stride3'))
self.L.append(my_layers.conv2d(self.L[-1], \
scope='Layer7'))
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm7'))
self.L.append(layers.avg_pool2d(self.L[-1], \
kernel_size=[3,3], \
scope='Stride4'))
self.L.append(my_layers.conv2d(self.L[-1], \
num_outputs=16, \
scope='Layer8'))
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm8'))
self.L.append(my_layers.conv2d(self.L[-1], \
num_outputs=16, stride=3, \
scope='Layer9'))
if self.with_bn:
self.L.append(layers.batch_norm(self.L[-1], \
scope='Norm9'))
self.L.append(layers.flatten(self.L[-1]))
self.L.append(layers.fully_connected(self.L[-1], num_outputs=2, \
activation_fn=None, normalizer_fn=None, \
weights_initializer=tf.random_normal_initializer(mean=0., stddev=0.01), \
biases_initializer=tf.constant_initializer(0.), scope='ip'))
self.outputs = self.L[-1]
return self.outputs

Binary file not shown.

View File

@ -0,0 +1,178 @@
import numpy as np
from scipy import misc, io
from glob import glob
import random
from itertools import izip
from random import random as rand
from random import shuffle
import h5py
def gen_embedding_otf(cover_dir, beta_dir, shuf_pair, \
thread_idx=0, n_threads=1):
cover_list = sorted(glob(cover_dir + '/*'))
beta_list = sorted(glob(beta_dir + '/*'))
nb_data = len(cover_list)
assert len(beta_list) != 0, "the beta directory '%s' is empty" % beta_dir
assert nb_data != 0, "the cover directory '%s' is empty" % cover_dir
assert len(beta_list) == nb_data, "the cover directory and " + \
"the beta directory don't " + \
"have the same number of files " + \
"respectively %d and %d" % (nb_data, \
len(beta_list))
img = misc.imread(cover_list[0])
img_shape = img.shape
batch = np.empty((2, img_shape[0], img_shape[1], 1), dtype='uint8')
beta_map = np.empty(img_shape, dtype='<f8')
inf_map = np.empty(img_shape, dtype='bool')
rand_arr = np.empty(img_shape, dtype='float64')
shuf_cov = np.empty(img_shape, dtype='uint8')
while True:
if shuf_pair:
list_i = np.random.permutation(nb_data)
list_j = np.random.permutation(nb_data)
for i, j in izip(list_i, list_j):
batch[0,:,:,0] = misc.imread(cover_list[i])
beta_map[:,:] = io.loadmat(beta_list[j])['pChange']
shuf_cov[:,:] = misc.imread(cover_list[j])
rand_arr[:,:] = np.random.rand(img_shape[0], img_shape[1])
inf_map[:,:] = rand_arr < (beta_map / 2.)
batch[1,:,:,0] = np.copy(shuf_cov)
batch[1,np.logical_and(shuf_cov != 255, inf_map),0] += 1
batch[1,np.logical_and(shuf_cov != 0, \
np.logical_and(np.logical_not(inf_map), \
rand_arr < beta_map)), 0] -= 1
rot = random.randint(0,3)
if rand() < 0.5:
yield [np.rot90(batch, rot, axes=[1,2]), np.array([0,1], dtype='uint8')]
else:
yield [np.flip(np.rot90(batch, rot, axes=[1,2]), axis=2), np.array([0,1], dtype='uint8')]
else:
list_i = np.random.permutation(nb_data)
for i in list_i:
batch[0,:,:,0] = misc.imread(cover_list[i])
beta_map[:,:] = io.loadmat(beta_list[i])['pChange']
rand_arr[:,:] = np.random.rand(img_shape[0], img_shape[1])
inf_map[:,:] = rand_arr < (beta_map / 2.)
batch[1,:,:,0] = np.copy(batch[0,:,:,0])
batch[1,np.logical_and(batch[0,:,:,0] != 255, inf_map),0] += 1
batch[1,np.logical_and(batch[0,:,:,0] != 0, \
np.logical_and(np.logical_not(inf_map), \
rand_arr < beta_map)), 0] -= 1
rot = random.randint(0,3)
if rand() < 0.5:
yield [np.rot90(batch, rot, axes=[1,2]), np.array([0,1], dtype='uint8')]
else:
yield [np.flip(np.rot90(batch, rot, axes=[1,2]), axis=2), np.array([0,1], dtype='uint8')]
def gen_all_flip_and_rot(cover_dir, stego_dir, thread_idx, n_threads):
cover_list = sorted(glob(cover_dir + '/*'))
stego_list = sorted(glob(stego_dir + '/*'))
nb_data = len(cover_list)
assert len(stego_list) != 0, "the beta directory '%s' is empty" % stego_dir
assert nb_data != 0, "the cover directory '%s' is empty" % cover_dir
assert len(stego_list) == nb_data, "the cover directory and " + \
"the beta directory don't " + \
"have the same number of files " + \
"respectively %d and %d" % (nb_data, + \
len(stego_list))
img = misc.imread(cover_list[0])
img_shape = img.shape
batch = np.empty((2,img_shape[0],img_shape[1],1), dtype='uint8')
iterable = zip(cover_list, stego_list)
for cover_path, stego_path in iterable:
batch[0,:,:,0] = misc.imread(cover_path)
batch[1,:,:,0] = misc.imread(stego_path)
for rot in range(4):
yield [np.rot90(batch, rot, axes=[1,2]), np.array([0,1], dtype='uint8')]
for rot in range(4):
yield [np.flip(np.rot90(batch, rot, axes=[1,2]), axis=2), np.array([0,1], dtype='uint8')]
def gen_flip_and_rot(cover_dir, stego_dir, shuf_pair=False, thread_idx=0, n_threads=1):
cover_list = sorted(glob(cover_dir + '/*'))
stego_list = sorted(glob(stego_dir + '/*'))
nb_data = len(cover_list)
assert len(stego_list) != 0, "the beta directory '%s' is empty" % stego_dir
assert nb_data != 0, "the cover directory '%s' is empty" % cover_dir
assert len(stego_list) == nb_data, "the cover directory and " + \
"the beta directory don't " + \
"have the same number of files " + \
"respectively %d and %d" % (nb_data, + \
len(stego_list))
img = misc.imread(cover_list[0])
img_shape = img.shape
batch = np.empty((2,img_shape[0],img_shape[1],1), dtype='uint8')
if not shuf_pair:
iterable = zip(cover_list, stego_list)
while True:
if shuf_pair:
shuffle(cover_list)
shuffle(stego_list)
iterable = izip(cover_list, stego_list)
else:
shuffle(iterable)
for cover_path, stego_path in iterable:
batch[0,:,:,0] = misc.imread(cover_path)
batch[1,:,:,0] = misc.imread(stego_path)
rot = random.randint(0,3)
if rand() < 0.5:
yield [np.rot90(batch, rot, axes=[1,2]), np.array([0,1], dtype='uint8')]
else:
yield [np.flip(np.rot90(batch, rot, axes=[1,2]), axis=2), np.array([0,1], dtype='uint8')]
def gen_valid(cover_dir, stego_dir, thread_idx, n_threads):
cover_list = sorted(glob(cover_dir + '/*'))
stego_list = sorted(glob(stego_dir + '/*'))
nb_data = len(cover_list)
assert len(stego_list) != 0, "the beta directory '%s' is empty" % stego_dir
assert nb_data != 0, "the cover directory '%s' is empty" % cover_dir
assert len(stego_list) == nb_data, "the cover directory and " + \
"the beta directory don't " + \
"have the same number of files " + \
"respectively %d and %d" % (nb_data, \
len(stego_list))
img = misc.imread(cover_list[0])
img_shape = img.shape
batch = np.empty((2,img_shape[0],img_shape[1],1), dtype='uint8')
labels = np.array([0, 1], dtype='uint8')
while True:
for cover_path, stego_path in zip(cover_list, stego_list):
batch[0,:,:,0] = misc.imread(cover_path)
batch[1,:,:,0] = misc.imread(stego_path)
yield [batch, labels]
# def trainGen(thread_idx, n_threads):
# batch = np.empty((2,256,256,1), dtype='uint8')
# beta_map = np.empty((256, 256), dtype='<f8')
# inf_map = np.empty((256, 256), dtype='bool')
# rand_arr = np.empty((256, 256), dtype='float64')
# cover_ds_path = LOG_DIR + '/cover_train' + str(thread_idx) +'.txt'
# shuf_cover_ds_path = LOG_DIR + '/shuf_cover_train' + str(thread_idx) +'.txt'
# shuf_beta_ds_path = LOG_DIR + '/shuf_beta_train' + str(thread_idx) +'.txt'
# createSubFile(DS_PATH + 'train_cover.txt', \
# cover_ds_path, thread_idx, n_threads)
# createSubFile(DS_PATH + 'train_beta.txt', \
# shuf_beta_ds_path, thread_idx, n_threads)
# createSubFile(DS_PATH + 'train_cover.txt', \
# shuf_cover_ds_path, thread_idx, n_threads)
# while True:
# with open(cover_ds_path, 'r') as f_cover, \
# open(shuf_cover_ds_path, 'r') as f_shuf_cover, \
# open(shuf_beta_ds_path, 'r') as f_shuf_beta:
# for cov_path, shuf_cov_path, shuf_beta_path in \
# izip(f_cover, f_shuf_cover, f_shuf_beta):
# batch[0,:,:,0] = misc.imread(str.strip(cov_path))
# shuf_cov = misc.imread(str.strip(shuf_cov_path))
# beta_map[:,:] = io.loadmat(str.strip(shuf_beta_path))['pChange']
# rand_arr[:,:] = np.random.rand(256, 256)
# inf_map[:,:] = rand_arr < (beta_map / 2.)
# batch[1,:,:,0] = np.copy(shuf_cov)
# batch[1,np.logical_and(shuf_cov != 255, inf_map),0] += 1
# batch[1,np.logical_and(shuf_cov != 0, \
# np.logical_and(np.logical_not(inf_map), rand_arr < beta_map)), 0] -= 1
# rot = random.randint(0,3)
# if rand() < 0.5:
# yield [np.rot90(batch, rot, axes=[1,2]), np.array([0,1], dtype='uint8')]
# else:
# yield [np.flip(np.rot90(batch, rot, axes=[1,2]), axis=2), np.array([0,1], dtype='uint8')]

368
YeNet-Tensorflow/layers.py Normal file
View File

@ -0,0 +1,368 @@
import tensorflow as tf
from tensorflow.contrib import layers
from tensorflow.contrib.framework import add_arg_scope
@add_arg_scope
def double_conv2d(ref_half, real_half,
num_outputs,
kernel_size,
stride=1,
padding='SAME',
data_format=None,
rate=1,
activation_fn=tf.nn.relu,
normalizer_fn=None,
normalize_after_activation=True,
normalizer_params=None,
weights_initializer=layers.xavier_initializer(),
weights_regularizer=None,
biases_initializer=tf.zeros_initializer(),
biases_regularizer=None,
reuse=None,
variables_collections=None,
outputs_collections=None,
trainable=True,
scope=None):
with tf.variable_scope(scope, 'Conv', reuse=reuse):
if data_format == 'NHWC':
num_inputs = real_half.get_shape().as_list()[3]
height = real_half.get_shape().as_list()[1]
width = real_half.get_shape().as_list()[2]
if isinstance(stride, int):
strides = [1, stride, stride, 1]
elif isinstance(stride, list) or isinstance(stride, tuple):
if len(stride) == 1:
strides = [1] + stride * 2 + [1]
else:
strides = [1, stride[0], stride[1], 1]
else:
raise TypeError('stride is not an int, list or' \
+ 'a tuple, is %s' % type(stride))
else:
num_inputs = real_half.get_shape().as_list()[1]
height = real_half.get_shape().as_list()[2]
width = real_half.get_shape().as_list()[3]
if isinstance(stride, int):
strides = [1, 1, stride, stride]
elif isinstance(stride, list) or isinstance(stride, tuple):
if len(stride) == 1:
strides = [1, 1] + stride * 2
else:
strides = [1, 1, stride[0], stride[1]]
else:
raise TypeError('stride is not an int, list or' \
+ 'a tuple, is %s' % type(stride))
if isinstance(kernel_size, int):
kernel_height = kernel_size
kernel_width = kernel_size
elif isinstance(kernel_size, list) \
or isinstance(kernel_size, tuple):
kernel_height = kernel_size[0]
kernel_width = kernel_size[1]
else:
raise ValueError('kernel_size is not an int, list or' \
+ 'a tuple, is %s' % type(kernel_size))
weights = tf.get_variable('weights', [kernel_height, \
kernel_width, num_inputs, num_outputs], \
'float32', weights_initializer, \
weights_regularizer, trainable, \
variables_collections)
ref_outputs = tf.nn.conv2d(ref_half, weights, strides, padding, \
data_format=data_format)
real_outputs = tf.nn.conv2d(real_half, weights, strides, padding, \
data_format=data_format)
if biases_initializer is not None:
biases = tf.get_variable('biases', [num_outputs], 'float32', \
biases_initializer, \
biases_regularizer, \
trainable, variables_collections)
ref_outputs = tf.nn.bias_add(ref_outputs, biases, data_format)
real_outputs = tf.nn.bias_add(real_outputs, biases, data_format)
if normalizer_fn is not None \
and not normalize_after_activation:
normalizer_params = normalizer_params or {}
ref_outputs, real_outputs = normalizer_fn(ref_outputs, \
real_outputs, \
**normalizer_params)
if activation_fn is not None:
ref_outputs = activation_fn(ref_outputs)
real_outputs = activation_fn(real_outputs)
if normalizer_fn is not None and normalize_after_activation:
normalizer_params = normalizer_params or {}
ref_outputs, real_outputs = normalizer_fn(ref_outputs, \
real_outputs,\
**normalizer_params)
return ref_outputs, real_outputs
@add_arg_scope
def conv2d(inputs,
num_outputs,
kernel_size,
stride=1,
padding='SAME',
data_format=None,
rate=1,
activation_fn=tf.nn.relu,
normalizer_fn=None,
normalize_after_activation=True,
normalizer_params=None,
weights_initializer=layers.xavier_initializer(),
weights_regularizer=None,
biases_initializer=tf.zeros_initializer(),
biases_regularizer=None,
reuse=None,
variables_collections=None,
outputs_collections=None,
trainable=True,
scope=None):
with tf.variable_scope(scope, 'Conv', reuse=reuse):
if data_format == 'NHWC':
num_inputs = inputs.get_shape().as_list()[3]
height = inputs.get_shape().as_list()[1]
width = inputs.get_shape().as_list()[2]
if isinstance(stride, int):
strides = [1, stride, stride, 1]
elif isinstance(stride, list) or isinstance(stride, tuple):
if len(stride) == 1:
strides = [1] + stride * 2 + [1]
else:
strides = [1, stride[0], stride[1], 1]
else:
raise TypeError('stride is not an int, list or' \
+ 'a tuple, is %s' % type(stride))
else:
num_inputs = inputs.get_shape().as_list()[1]
height = inputs.get_shape().as_list()[2]
width = inputs.get_shape().as_list()[3]
if isinstance(stride, int):
strides = [1, 1, stride, stride]
elif isinstance(stride, list) or isinstance(stride, tuple):
if len(stride) == 1:
strides = [1, 1] + stride * 2
else:
strides = [1, 1, stride[0], stride[1]]
else:
raise TypeError('stride is not an int, list or' \
+ 'a tuple, is %s' % type(stride))
if isinstance(kernel_size, int):
kernel_height = kernel_size
kernel_width = kernel_size
elif isinstance(kernel_size, list) \
or isinstance(kernel_size, tuple):
kernel_height = kernel_size[0]
kernel_width = kernel_size[1]
else:
raise ValueError('kernel_size is not an int, list or' \
+ 'a tuple, is %s' % type(kernel_size))
weights = tf.get_variable('weights', [kernel_height, \
kernel_width, num_inputs, num_outputs], \
'float32', weights_initializer, \
weights_regularizer, trainable, \
variables_collections)
outputs = tf.nn.conv2d(inputs, weights, strides, padding, \
data_format=data_format)
if biases_initializer is not None:
biases = tf.get_variable('biases', [num_outputs], 'float32', \
biases_initializer, \
biases_regularizer, \
trainable, variables_collections)
outputs = tf.nn.bias_add(outputs, biases, data_format)
if normalizer_fn is not None \
and not normalize_after_activation:
normalizer_params = normalizer_params or {}
outputs = normalizer_fn(outputs, **normalizer_params)
if activation_fn is not None:
outputs = activation_fn(outputs)
if normalizer_fn is not None and normalize_after_activation:
normalizer_params = normalizer_params or {}
outputs = normalizer_fn(outputs, **normalizer_params)
return outputs
class Vbn_double(object):
def __init__(self, x, epsilon=1e-5, scope=None):
shape = x.get_shape().as_list()
needs_reshape = len(shape) != 4
if needs_reshape:
orig_shape = shape
if len(shape) == 2:
if data_format == 'NCHW':
x = tf.reshape(x, [shape[0], shape[1], 0, 0])
else:
x = tf.reshape(x, [shape[0], 1, 1, shape[1]])
elif len(shape) == 1:
x = tf.reshape(x, [shape[0], 1, 1, 1])
else:
assert False, shape
shape = x.get_shape().as_list()
with tf.variable_scope(scope):
self.epsilon = epsilon
self.scope = scope
self.mean, self.var = tf.nn.moments(x, [0,2,3], \
keep_dims=True)
self.inv_std = tf.rsqrt(self.var + epsilon)
self.batch_size = int(x.get_shape()[0])
out = self._normalize(x, self.mean, self.inv_std)
if needs_reshape:
out = tf.reshape(out, orig_shape)
self.reference_output = out
def __call__(self, x):
shape = x.get_shape().as_list()
needs_reshape = len(shape) != 4
if needs_reshape:
orig_shape = shape
if len(shape) == 2:
if self.data_format == 'NCHW':
x = tf.reshape(x, [shape[0], shape[1], 0, 0])
else:
x = tf.reshape(x, [shape[0], 1, 1, shape[1]])
elif len(shape) == 1:
x = tf.reshape(x, [shape[0], 1, 1, 1])
else:
assert False, shape
with tf.variable_scope(self.scope, reuse=True):
out = self._normalize(x, self.mean, self.inv_std)
if needs_reshape:
out = tf.reshape(out, orig_shape)
return out
def _normalize(self, x, mean, inv_std):
shape = x.get_shape().as_list()
assert len(shape) == 4
gamma = tf.get_variable("gamma", [1,shape[1],1,1],
initializer=tf.constant_initializer(1.))
beta = tf.get_variable("beta", [1,shape[1],1,1],
initializer=tf.constant_initializer(0.))
coeff = gamma * inv_std
return (x * coeff) + (beta - mean * coeff)
@add_arg_scope
def vbn_double(ref_half, real_half, center=True, scale=True, epsilon=1e-5, \
data_format='NCHW', instance_norm=True, scope=None, \
reuse=None):
assert isinstance(epsilon, float)
shape = real_half.get_shape().as_list()
batch_size = int(real_half.get_shape()[0])
with tf.variable_scope(scope, 'VBN', reuse=reuse):
if data_format == 'NCHW':
if scale:
gamma = tf.get_variable("gamma", [1,shape[1],1,1],
initializer=tf.constant_initializer(1.))
if center:
beta = tf.get_variable("beta", [1,shape[1],1,1],
initializer=tf.constant_initializer(0.))
ref_mean, ref_var = tf.nn.moments(ref_half, [0,2,3], \
keep_dims=True)
else:
if scale:
gamma = tf.get_variable("gamma", [1,1,1,shape[-1]],
initializer=tf.constant_initializer(1.))
if center:
beta = tf.get_variable("beta", [1,1,1,shape[-1]],
initializer=tf.constant_initializer(0.))
ref_mean, ref_var = tf.nn.moments(ref_half, [0,1,2], \
keep_dims=True)
def _normalize(x, mean, var):
inv_std = tf.rsqrt(var + epsilon)
if scale:
coeff = inv_std * gamma
else:
coeff = inv_std
if center:
return (x * coeff) + (beta - mean * coeff)
else:
return (x - mean) * coeff
if instance_norm:
if data_format == 'NCHW':
real_mean, real_var = tf.nn.moments(real_half, [2,3], \
keep_dims=True)
else:
real_mean, real_var = tf.nn.moments(real_half, [1,2], \
keep_dims=True)
real_coeff = 1. / (batch_size + 1.)
ref_coeff = 1. - real_coeff
new_mean = real_coeff * real_mean + ref_coeff * ref_mean
new_var = real_coeff * real_var + ref_coeff * ref_var
ref_output = _normalize(ref_half, ref_mean, ref_var)
real_output = _normalize(real_half, new_mean, new_var)
else:
ref_output = _normalize(ref_half, ref_mean, ref_var)
real_output = _normalize(real_half, ref_mean, ref_var)
return ref_output, real_output
@add_arg_scope
def vbn_single(x, center=True, scale=True, \
epsilon=1e-5, data_format='NCHW', \
instance_norm=True, scope=None, \
reuse=None):
assert isinstance(epsilon, float)
shape = x.get_shape().as_list()
if shape[0] is None:
half_size = x.shape[0] // 2
else:
half_size = shape[0] // 2
needs_reshape = len(shape) != 4
if needs_reshape:
orig_shape = shape
if len(shape) == 2:
if data_format == 'NCHW':
x = tf.reshape(x, [shape[0], shape[1], 0, 0])
else:
x = tf.reshape(x, [shape[0], 1, 1, shape[1]])
elif len(shape) == 1:
x = tf.reshape(x, [shape[0], 1, 1, 1])
else:
assert False, shape
shape = x.get_shape().as_list()
batch_size = int(x.get_shape()[0])
with tf.variable_scope(scope, 'VBN', reuse=reuse):
ref_half = tf.slice(x, [0,0,0,0], [half_size, shape[1], \
shape[2], shape[3]])
if data_format == 'NCHW':
if scale:
gamma = tf.get_variable("gamma", [1,shape[1],1,1],
initializer=tf.constant_initializer(1.))
if center:
beta = tf.get_variable("beta", [1,shape[1],1,1],
initializer=tf.constant_initializer(0.))
ref_mean, ref_var = tf.nn.moments(ref_half, [0,2,3], \
keep_dims=True)
else:
if scale:
gamma = tf.get_variable("gamma", [1,1,1,shape[-1]],
initializer=tf.constant_initializer(1.))
if center:
beta = tf.get_variable("beta", [1,1,1,shape[-1]],
initializer=tf.constant_initializer(0.))
ref_mean, ref_var = tf.nn.moments(ref_half, [0,1,2], \
keep_dims=True)
def _normalize(x, mean, var):
inv_std = tf.rsqrt(var + epsilon)
if scale:
coeff = inv_std * gamma
else:
coeff = inv_std
if center:
return (x * coeff) + (beta - mean * coeff)
else:
return (x - mean) * coeff
if instance_norm:
real_half = tf.slice(x, [half_size,0,0,0], \
[half_size, shape[1], shape[2], shape[3]])
if data_format == 'NCHW':
real_mean, real_var = tf.nn.moments(real_half, [2,3], \
keep_dims=True)
else:
real_mean, real_var = tf.nn.moments(real_half, [1,2], \
keep_dims=True)
real_coeff = 1. / (batch_size + 1.)
ref_coeff = 1. - real_coeff
new_mean = real_coeff * real_mean + ref_coeff * ref_mean
new_var = real_coeff * real_var + ref_coeff * ref_var
ref_output = _normalize(ref_half, ref_mean, ref_var)
real_output = _normalize(real_half, new_mean, new_var)
return tf.concat([ref_output, real_output], axis=0)
else:
return _normalize(x, ref_mean, ref_var)

77
YeNet-Tensorflow/main.py Normal file
View File

@ -0,0 +1,77 @@
import argparse
import numpy as np
import tensorflow as tf
from functools import partial
from utils import *
from generator import *
from queues import *
from YeNet import YeNet
parser = argparse.ArgumentParser(description='PyTorch implementation of YeNet')
parser.add_argument('train_cover_dir', type=str, metavar='PATH',
help='path of directory containing all ' +
'training cover images')
parser.add_argument('train_stego_dir', type=str, metavar='PATH',
help='path of directory containing all ' +
'training stego images or beta maps')
parser.add_argument('valid_cover_dir', type=str, metavar='PATH',
help='path of directory containing all ' +
'validation cover images')
parser.add_argument('valid_stego_dir', type=str, metavar='PATH',
help='path of directory containing all ' +
'validation stego images or beta maps')
parser.add_argument('--batch-size', type=int, default=32, metavar='N',
help='input batch size for training (default: 32)')
parser.add_argument('--test-batch-size', type=int, default=32, metavar='N',
help='input batch size for testing (default: 32)')
parser.add_argument('--epochs', type=int, default=1000, metavar='N',
help='number of epochs to train (default: 1000)')
parser.add_argument('--lr', type=float, default=4e-1, metavar='LR',
help='learning rate (default: 4e-1)')
parser.add_argument('--use-batch-norm', action='store_true', default=False,
help='use batch normalization after each activation,' +
' also disable pair constraint (default: False)')
parser.add_argument('--embed-otf', action='store_true', default=False,
help='use beta maps and embed on the fly instead' +
' of use stego images (default: False)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--gpu', type=int, default=0,
help='index of gpu used (default: 0)')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=200, metavar='N',
help='how many batches to wait ' +
'before logging training status')
parser.add_argument('--log-path', type=str, default='logs/',
metavar='PATH', help='path to generated log file')
args = parser.parse_args()
import os
os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'
os.environ['CUDA_VISIBLE_DEVICES'] = '' if args.no_cuda else str(args.gpu)
tf.set_random_seed(args.seed)
train_ds_size = len(glob(args.train_cover_dir + '/*')) * 2
if args.embed_otf:
train_gen = partial(gen_embedding_otf, args.train_cover_dir, \
args.train_stego_dir, args.use_batch_norm)
else:
train_gen = partial(gen_flip_and_rot, args.train_cover_dir, \
args.train_stego_dir, args.use_batch_norm)
valid_ds_size = len(glob(args.valid_cover_dir + '/*')) * 2
valid_gen = partial(gen_valid, args.valid_cover_dir, \
args.valid_stego_dir)
if valid_ds_size % 32 != 0:
raise ValueError("change batch size for validation")
optimizer = tf.train.AdadeltaOptimizer(args.lr)
train(YeNet, train_gen, valid_gen, args.batch_size, \
args.test_batch_size, valid_ds_size, \
optimizer, args.log_interval, train_ds_size, \
args.epochs * train_ds_size, train_ds_size, args.log_path, 8)

120
YeNet-Tensorflow/queues.py Normal file
View File

@ -0,0 +1,120 @@
import numpy as np
import tensorflow as tf
import threading
import h5py
import functools
def hdf5baseGen(filepath, thread_idx, n_threads):
with h5py.File(filepath, 'r') as f:
keys = f.keys()
nb_data = f[keys[0]].shape[0]
idx = thread_idx
while True:
yield [np.expand_dims(f[key][idx], 0) for key in keys]
idx = (idx + n_threads) % nb_data
class GeneratorRunner():
"""
This class manage a multithreaded queue filled with a generator
"""
def __init__(self, generator, capacity):
"""
inputs: generator feeding the data, must have thread_idx
as parameter (but the parameter may be not used)
"""
self.generator = generator
_input = generator(0,1).next()
if type(_input) is not list:
raise ValueError("generator doesn't return" \
"a list: %r" % type(_input))
input_batch_size = _input[0].shape[0]
if not all(_input[i].shape[0] == input_batch_size for i in range(len(_input))):
raise ValueError("all the inputs doesn't have " + \
"the same batch size," \
"the batch sizes are: %s" % [_input[i].shape[0] \
for i in range(len(_input))])
self.data = []
self.dtypes = []
self.shapes = []
for i in range(len(_input)):
self.shapes.append(_input[i].shape[1:])
self.dtypes.append(_input[i].dtype)
self.data.append(tf.placeholder(dtype=self.dtypes[i], \
shape=(input_batch_size,) + self.shapes[i]))
self.queue = tf.FIFOQueue(capacity, shapes=self.shapes, \
dtypes=self.dtypes)
self.enqueue_op = self.queue.enqueue_many(self.data)
self.close_queue_op = self.queue.close(cancel_pending_enqueues=True)
def get_batched_inputs(self, batch_size):
"""
Return tensors containing a batch of generated data
"""
batch = self.queue.dequeue_many(batch_size)
return batch
def thread_main(self, sess, thread_idx=0, n_threads=1):
try:
for data in self.generator(thread_idx, n_threads):
sess.run(self.enqueue_op, feed_dict={i: d \
for i, d in zip(self.data, data)})
if self.stop_threads:
return
except RuntimeError:
pass
except tf.errors.CancelledError:
pass
def start_threads(self, sess, n_threads=1):
self.stop_threads = False
self.threads = []
for n in range(n_threads):
t = threading.Thread(target=self.thread_main, args=(sess, n, n_threads))
t.daemon = True
t.start()
self.threads.append(t)
return self.threads
def stop_runner(self, sess):
self.stop_threads = True
# j = 0
# while np.any([t.is_alive() for t in self.threads]):
# j += 1
# if j % 100 = 0:
# print [t.is_alive() for t in self.threads]
sess.run(self.close_queue_op)
def queueSelection(runners, sel, batch_size):
selection_queue = tf.FIFOQueue.from_list(sel, [r.queue for r in runners])
return selection_queue.dequeue_many(batch_size)
def doubleQueue(runner1, runner2, is_runner1, batch_size1, batch_size2):
return tf.cond(is_runner1, lambda: runner1.queue.dequeue_many(batch_size1), \
lambda: runner2.queue.dequeue_many(batch_size2))
if __name__ == '__main__':
def randomGen(img_size, enqueue_batch_size, thread_idx, n_threads):
while True:
batch_of_1_channel_imgs = np.random.rand(enqueue_batch_size, \
img_size, img_size, 1)
batch_of_labels = np.random.randint(0,11,enqueue_batch_size)
return [batch_of_1_channel_imgs, batch_of_labels]
TRAIN_BATCH_SIZE = 64
VALID_BATCH_SIZE = 10
train_runner = GeneratorRunner(functool.partial(randomGen, \
(128, 10)), TRAIN_BATCH_SIZE * 10)
valid_runner = GeneratorRunner(functool.partial(randomGen, \
(128, 10)), VALID_BATCH_SIZE * 10)
is_training = tf.Variable(True)
batch_size = tf.Variable(TRAIN_BATCH_SIZE)
enable_training_op = tf.group(tf.assign(is_training, True), \
tf.assign(batch_size, TRAIN_BATCH_SIZE))
disable_training_op = tf.group(tf.assign(is_training, False), \
tf.assign(batch_size, VALID_BATCH_SIZE))
img_batch, label_batch = queueSelection([valid_runner, train_runner], \
tf.cast(is_training, tf.int32), \
batch_size)
# img_batch, label_batch = doubleQueue(train_runner, valid_runner, \
# is_training, TRAIN_BATCH_SIZE, \
# VALID_BATCH_SIZE)

View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
"""
Created on Thu Aug 8 14:33:16 2019
@author: Lee
"""
import numpy as np
SRM_Kernels = np.load('SRM_Kernels.npy')
print(SRM_Kernels[:1])

View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 4 15:24:05 2019
@author: Lee
"""
import torch
print(torch.rand(5))

298
YeNet-Tensorflow/utils.py Normal file
View File

@ -0,0 +1,298 @@
import tensorflow as tf
import numpy as np
import sys
import time
from glob import glob
from functools import partial
import os
from os.path import expanduser
home = expanduser("~")
user = home.split('/')[-1]
sys.path.append(home + '/tflib/')
from queues import *
from generator import *
def optimistic_restore(session, save_file, \
graph=tf.get_default_graph()):
reader = tf.train.NewCheckpointReader(save_file)
saved_shapes = reader.get_variable_to_shape_map()
var_names = sorted([(var.name, var.name.split(':')[0]) for var in tf.global_variables()
if var.name.split(':')[0] in saved_shapes])
restore_vars = []
for var_name, saved_var_name in var_names:
curr_var = graph.get_tensor_by_name(var_name)
var_shape = curr_var.get_shape().as_list()
if var_shape == saved_shapes[saved_var_name]:
restore_vars.append(curr_var)
opt_saver = tf.train.Saver(restore_vars)
opt_saver.restore(session, save_file)
class average_summary(object):
def __init__(self, variable, name, num_iterations):
self.sum_variable = tf.get_variable(name, shape=[], \
initializer=tf.constant_initializer(0.), \
dtype='float32', \
trainable=False, \
collections=[tf.GraphKeys.LOCAL_VARIABLES])
with tf.control_dependencies([variable]):
self.increment_op = tf.assign_add(self.sum_variable, variable)
self.mean_variable = self.sum_variable / float(num_iterations)
self.summary = tf.summary.scalar(name, self.mean_variable)
with tf.control_dependencies([self.summary]):
self.reset_variable_op = tf.assign(self.sum_variable, 0)
def add_summary(self, sess, writer, step):
s, _ = sess.run([self.summary, self.reset_variable_op])
writer.add_summary(s, step)
class Model(object):
def __init__(self, is_training=None, data_format='NCHW'):
self.data_format = data_format
if is_training is None:
self.is_training = tf.get_variable('is_training', dtype=tf.bool, \
initializer=tf.constant_initializer(True), \
trainable=False)
else:
self.is_training = is_training
def _build_model(self, inputs):
raise NotImplementedError('Here is your model definition')
def _build_losses(self, labels):
self.labels = tf.cast(labels, tf.int64)
with tf.variable_scope('loss'):
oh = tf.one_hot(self.labels, 2)
self.loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits( \
labels=oh, logits=self.outputs))
with tf.variable_scope('accuracy'):
am = tf.argmax(self.outputs, 1)
equal = tf.equal(am, self.labels)
self.accuracy = tf.reduce_mean(tf.cast(equal, tf.float32))
return self.loss, self.accuracy
def train(model_class, train_gen, valid_gen, train_batch_size, \
valid_batch_size, valid_ds_size, optimizer, \
train_interval, valid_interval, max_iter, \
save_interval, log_path, num_runner_threads=1, \
load_path=None):
tf.reset_default_graph()
train_runner = GeneratorRunner(train_gen, train_batch_size * 10)
valid_runner = GeneratorRunner(valid_gen, valid_batch_size * 10)
is_training = tf.get_variable('is_training', dtype=tf.bool, \
initializer=True, trainable=False)
if train_batch_size == valid_batch_size:
batch_size = train_batch_size
disable_training_op = tf.assign(is_training, False)
enable_training_op = tf.assign(is_training, True)
else:
batch_size = tf.get_variable('batch_size', dtype=tf.int32, \
initializer=train_batch_size, \
trainable=False, \
collections=[tf.GraphKeys.LOCAL_VARIABLES])
disable_training_op = tf.group(tf.assign(is_training, False), \
tf.assign(batch_size, valid_batch_size))
enable_training_op = tf.group(tf.assign(is_training, True), \
tf.assign(batch_size, train_batch_size))
img_batch, label_batch = queueSelection([valid_runner, train_runner], \
tf.cast(is_training, tf.int32), \
batch_size)
model = model_class(is_training, 'NCHW')
model._build_model(img_batch)
loss, accuracy = model._build_losses(label_batch)
regularization_losses = tf.get_collection(
tf.GraphKeys.REGULARIZATION_LOSSES)
regularized_loss = tf.add_n([loss] + regularization_losses)
train_loss_s = average_summary(loss, 'train_loss', train_interval)
train_accuracy_s = average_summary(accuracy, 'train_accuracy', \
train_interval)
valid_loss_s = average_summary(loss, 'valid_loss', \
float(valid_ds_size) / float(valid_batch_size))
valid_accuracy_s = average_summary(accuracy, 'valid_accuracy', \
float(valid_ds_size) / float(valid_batch_size))
global_step = tf.get_variable('global_step', dtype=tf.int32, shape=[], \
initializer=tf.constant_initializer(0), \
trainable=False)
minimize_op = optimizer.minimize(regularized_loss, global_step)
train_op = tf.group(minimize_op, train_loss_s.increment_op, \
train_accuracy_s.increment_op)
increment_valid = tf.group(valid_loss_s.increment_op, \
valid_accuracy_s.increment_op)
init_op = tf.group(tf.global_variables_initializer(), \
tf.local_variables_initializer())
saver = tf.train.Saver(max_to_keep=10000)
with tf.Session() as sess:
sess.run(init_op)
if load_path is not None:
loader = tf.train.Saver(reshape=True)
loader.restore(sess, load_path)
train_runner.start_threads(sess, num_runner_threads)
valid_runner.start_threads(sess, 1)
writer = tf.summary.FileWriter(log_path + '/LogFile/', \
sess.graph)
start = sess.run(global_step)
sess.run(disable_training_op)
sess.run([valid_loss_s.reset_variable_op, \
valid_accuracy_s.reset_variable_op, \
train_loss_s.reset_variable_op, \
train_accuracy_s.reset_variable_op])
_time = time.time()
for j in range(0, valid_ds_size, valid_batch_size):
sess.run([increment_valid])
_acc_val = sess.run(valid_accuracy_s.mean_variable)
print "validation:", _acc_val, " | ", \
"duration:", time.time() - _time, \
"seconds long"
valid_accuracy_s.add_summary(sess, writer, start)
valid_loss_s.add_summary(sess, writer, start)
sess.run(enable_training_op)
print valid_interval
for i in xrange(start+1, max_iter+1):
sess.run(train_op)
if i % train_interval == 0:
train_loss_s.add_summary(sess, writer, i)
train_accuracy_s.add_summary(sess, writer, i)
if i % valid_interval == 0:
sess.run(disable_training_op)
for j in range(0, valid_ds_size, valid_batch_size):
sess.run([increment_valid])
valid_loss_s.add_summary(sess, writer, i)
valid_accuracy_s.add_summary(sess, writer, i)
sess.run(enable_training_op)
if i % save_interval == 0:
saver.save(sess, log_path + '/Model_' + str(i) + '.ckpt')
def test_dataset(model_class, gen, batch_size, ds_size, load_path):
tf.reset_default_graph()
runner = GeneratorRunner(gen, batch_size * 10)
img_batch, label_batch = runner.get_batched_inputs(batch_size)
model = model_class(False, 'NCHW')
model._build_model(img_batch)
loss, accuracy = model._build_losses(label_batch)
loss_summary = average_summary(loss, 'loss', \
float(ds_size) / float(batch_size))
accuracy_summary = average_summary(accuracy, 'accuracy', \
float(ds_size) / float(batch_size))
increment_op = tf.group(loss_summary.increment_op, \
accuracy_summary.increment_op)
global_step = tf.get_variable('global_step', dtype=tf.int32, shape=[], \
initializer=tf.constant_initializer(0), \
trainable=False)
init_op = tf.group(tf.global_variables_initializer(), \
tf.local_variables_initializer())
saver = tf.train.Saver(max_to_keep=10000)
with tf.Session() as sess:
sess.run(init_op)
saver.restore(sess, load_path)
runner.start_threads(sess, 1)
for j in range(0, ds_size, batch_size):
sess.run(increment_op)
mean_loss, mean_accuracy = sess.run([loss_summary.mean_variable ,\
accuracy_summary.mean_variable])
print "Accuracy:", mean_accuracy, " | Loss:", mean_loss
def find_best(model_class, valid_gen, test_gen, valid_batch_size, \
test_batch_size, valid_ds_size, test_ds_size, load_paths):
tf.reset_default_graph()
valid_runner = GeneratorRunner(valid_gen, valid_batch_size * 30)
img_batch, label_batch = valid_runner.get_batched_inputs(valid_batch_size)
model = model_class(False, 'NCHW')
model._build_model(img_batch)
loss, accuracy = model._build_losses(label_batch)
loss_summary = average_summary(loss, 'loss', \
float(valid_ds_size) \
/ float(valid_batch_size))
accuracy_summary = average_summary(accuracy, 'accuracy', \
float(valid_ds_size) \
/ float(valid_batch_size))
increment_op = tf.group(loss_summary.increment_op, \
accuracy_summary.increment_op)
global_step = tf.get_variable('global_step', dtype=tf.int32, shape=[], \
initializer=tf.constant_initializer(0), \
trainable=False)
init_op = tf.group(tf.global_variables_initializer(), \
tf.local_variables_initializer())
saver = tf.train.Saver(max_to_keep=10000)
accuracy_arr = []
loss_arr = []
print "validation"
for load_path in load_paths:
with tf.Session() as sess:
sess.run(init_op)
saver.restore(sess, load_path)
valid_runner.start_threads(sess, 1)
_time = time.time()
for j in range(0, valid_ds_size, valid_batch_size):
sess.run(increment_op)
mean_loss, mean_accuracy = sess.run([loss_summary.mean_variable ,\
accuracy_summary.mean_variable])
accuracy_arr.append(mean_accuracy)
loss_arr.append(mean_loss)
print load_path
print "Accuracy:", accuracy_arr[-1], "| Loss:", loss_arr[-1], \
"in", time.time() - _time, "seconds."
argmax = np.argmax(accuracy_arr)
print "best savestate:", load_paths[argmax], "with", \
accuracy_arr[argmax], "accuracy and", loss_arr[argmax], \
"loss on validation"
print "test:"
test_dataset(model_class, test_gen, test_batch_size, test_ds_size, \
load_paths[argmax])
return argmax, accuracy_arr, loss_arr
def extract_stats_outputs(model_class, gen, batch_size, ds_size, load_path):
tf.reset_default_graph()
runner = GeneratorRunner(gen, batch_size * 10)
img_batch, label_batch = runner.get_batched_inputs(batch_size)
model = model_class(False, 'NCHW')
model._build_model(img_batch)
global_step = tf.get_variable('global_step', dtype=tf.int32, shape=[], \
initializer=tf.constant_initializer(0), \
trainable=False)
init_op = tf.group(tf.global_variables_initializer(), \
tf.local_variables_initializer())
saver = tf.train.Saver(max_to_keep=10000)
stats_outputs_arr = np.empty([ds_size, \
model.stats_outputs.get_shape().as_list()[1]])
with tf.Session() as sess:
sess.run(init_op)
saver.restore(sess, load_path)
runner.start_threads(sess, 1)
for j in range(0, ds_size, batch_size):
stats_outputs_arr[j:j+batch_size] = sess.run(model.stats_outputs)
return stats_outputs_arr
def stats_outputs_all_datasets(model_class, ds_head_dir, payload, \
algorithm, load_path, save_dir):
if not os.path.exists(save_dir):
os.makedirs(save_dir + '/')
payload_str = ''.join(str(payload).strip('.'))
train_ds_size = len(glob(ds_head_dir + '/train/cover/*'))
valid_ds_size = len(glob(ds_head_dir + '/valid/cover/*'))
test_ds_size = len(glob(ds_head_dir + '/test/cover/*'))
train_gen = partial(gen_all_flip_and_rot, ds_head_dir + \
'/train/cover/', ds_head_dir + '/train/' + \
algorithm + '/payload' + payload_str + '/stego/')
valid_gen = partial(gen_valid, ds_head_dir + '/valid/cover/', \
ds_head_dir + '/valid/' + algorithm + \
'/payload' + payload_str + '/stego/')
test_gen = partial(gen_valid, ds_head_dir + '/test/cover/', \
ds_head_dir + '/test/' + algorithm + \
'/payload' + payload_str + '/stego/')
print "train..."
stats_outputs = extract_stats_outputs(model_class, train_gen, 16, \
train_ds_size * 2 * 4 * 2, \
load_path)
stats_shape = stats_outputs.shape
stats_outputs = stats_outputs.reshape(train_ds_size, 2, 4, \
2, stats_shape[-1])
stats_outputs = np.transpose(stats_outputs, axes=[0,3,2,1,4])
np.save(save_dir + '/train.npy', stats_outputs)
print "validation..."
stats_outputs = extract_stats_outputs(model_class, valid_gen, 16, \
valid_ds_size * 2, load_path)
np.save(save_dir + '/valid.npy', stats_outputs)
print "test..."
stats_outputs = extract_stats_outputs(model_class, test_gen, 16, \
test_ds_size * 2, load_path)
np.save(save_dir + '/test.npy', stats_outputs)

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More