Skip to content

Commit c8c8f44

Browse files
committed
Camera Ready
0 parents  commit c8c8f44

22 files changed

+1793
-0
lines changed

.gitignore

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
share/python-wheels/
24+
*.egg-info/
25+
.installed.cfg
26+
*.egg
27+
MANIFEST
28+
29+
# PyInstaller
30+
# Usually these files are written by a python script from a template
31+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
32+
*.manifest
33+
*.spec
34+
35+
# Installer logs
36+
pip-log.txt
37+
pip-delete-this-directory.txt
38+
39+
# Unit test / coverage reports
40+
htmlcov/
41+
.tox/
42+
.nox/
43+
.coverage
44+
.coverage.*
45+
.cache
46+
nosetests.xml
47+
coverage.xml
48+
*.cover
49+
*.py,cover
50+
.hypothesis/
51+
.pytest_cache/
52+
cover/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
.pybuilder/
76+
target/
77+
78+
# Jupyter Notebook
79+
.ipynb_checkpoints
80+
81+
# IPython
82+
profile_default/
83+
ipython_config.py
84+
85+
# pyenv
86+
# For a library or package, you might want to ignore these files since the code is
87+
# intended to run in multiple environments; otherwise, check them in:
88+
# .python-version
89+
90+
# pipenv
91+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
93+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
94+
# install all needed dependencies.
95+
#Pipfile.lock
96+
97+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
98+
__pypackages__/
99+
100+
# Celery stuff
101+
celerybeat-schedule
102+
celerybeat.pid
103+
104+
# SageMath parsed files
105+
*.sage.py
106+
107+
# Environments
108+
.env
109+
.venv
110+
env/
111+
venv/
112+
ENV/
113+
env.bak/
114+
venv.bak/
115+
116+
# Spyder project settings
117+
.spyderproject
118+
.spyproject
119+
120+
# Rope project settings
121+
.ropeproject
122+
123+
# mkdocs documentation
124+
/site
125+
126+
# mypy
127+
.mypy_cache/
128+
.dmypy.json
129+
dmypy.json
130+
131+
# Pyre type checker
132+
.pyre/
133+
134+
# pytype static type analyzer
135+
.pytype/
136+
137+
# Cython debug symbols
138+
cython_debug/
139+
140+
.idea/
141+
.DS_Store

LICENSE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright © <Year 2021 to Year 2023> by Cornell University.
2+
3+
All Rights Reserved.
4+
5+
Permission to use the software entitled “Learning Iterative Neural Optimizers for Image Steganography (LISO)”, developed through research conducted at Cornell University and officially docketed at the Center for Technology Licensing at Cornell University as D-9827 (the “WORK”), and its associated copyrights solely for educational, research and non-profit purposes, without fee, and without a written agreement is hereby granted, provided that the above copyright notice, this paragraph and the following three paragraphs shall be displayed in all copies or substantial portions of the Work.
6+
7+
Those desiring to incorporate WORK into commercial products or use WORK and its associated copyrights for commercial purposes should contact the Center for Technology Licensing at Cornell University at 395 Pine Tree Road, Suite 310, Ithaca, NY 14850; email: ctl- [email protected]; Tel: 607-254-4698; FAX: 607-254-5454 for a commercial license.
8+
9+
IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF WORK AND ITS ASSOCIATED COPYRIGHTS, EVEN IF CORNELL UNIVERSITY MAY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
10+
11+
THE WORK PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. CORNELL UNIVERSITY MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR THAT THE USE OF WORK AND ITS ASSOCIATED COPYRIGHTS WILL NOT INFRINGE ANY PATENT, TRADEMARK OR OTHER RIGHTS.
12+
13+
The WORK was developed with funding in part by DOD/DARPA under award number HR00112090091. The concepts used in WORK are patent pending.

README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Learning Iterative Neural Optimizers for Image Steganography (LISO)
2+
3+
This repo contains official PyTorch implementation of [Learning Iterative Neural Optimizers for Image Steganography](https://openreview.net/pdf?id=gLPkzWjdhBN) (ICLR 2023).
4+
5+
```
6+
@inproceedings{chenlearning,
7+
title={Learning Iterative Neural Optimizers for Image Steganography},
8+
author={Chen, Xiangyu and Kishore, Varsha and Weinberger, Kilian Q},
9+
booktitle={The Eleventh International Conference on Learning Representations}
10+
}
11+
```
12+
13+
## Introduction
14+
LISO is a learned steganographic optimizer that operates on the manifold of natural images.
15+
Compared to SOTA approaches, LISO is faster and more reliable, reducing recovery error rate by multiple orders of magnitude, and achieving zero error up to 3 bits per pixel.
16+
LISO is also highly extensible, e.g., to JPEG compression, or avoiding steganalysis detection.
17+
18+
![Figure](liso.jpg)
19+
20+
21+
## Prerequisites
22+
- Python >= 3.6
23+
- pyTorch >= 1.10.2
24+
- CUDA >= 10.2
25+
- cuDNN >= 7.6
26+
27+
## Getting Started
28+
Download and extract [subsampled image datasets](https://drive.google.com/file/d/1ai9D3Z0lcdEnRX24pUL_XfuFSjWtbh5K) and [pretrained weights](https://drive.google.com/file/d/1128829Sq5nJJmckqYcdTUs4EmBeg3NGH) into `LISO/`.
29+
We provide our custom subset of [Div2k](https://data.vision.ee.ethz.ch/cvl/DIV2K/), [CelebA](https://mmlab.ie.cuhk.edu.hk/projects/CelebA.html), and [MS COCO](https://cocodataset.org), and corresponding trained LISO model weights under payload of `1-4 bits per pixel` / `JPEG compression` (on Div2k).
30+
Trained [SiaStegNet](https://ieeexplore.ieee.org/document/9153041) and [XuNet](https://dl.acm.org/doi/10.1145/3082031.3083236) weights are also included.
31+
32+
To use a custom dataset, organize images in the format below:
33+
34+
<pre>
35+
LISO/
36+
| datasets/
37+
| DATASET_NAME/
38+
| train/
39+
| _
40+
| img0000.xxx
41+
| img0001.xxx
42+
| val/
43+
| _
44+
| img0000.xxx
45+
| img0001.xxx
46+
</pre>
47+
48+
### Evaluation
49+
LISO
50+
```bash
51+
python train_bits.py --eval --bits 1 --dataset div2k --load checkpoints/div2k/1_bits.steg
52+
```
53+
54+
LISO + L-BFGS
55+
```bash
56+
python train_bits.py --eval --lbfgs --bits 1 --dataset div2k --load checkpoints/div2k/1_bits.steg
57+
```
58+
59+
LISO + JPEG
60+
```bash
61+
python train_bits.py --eval --eval-jpeg --bits 1 --dataset div2k --load checkpoints/div2k_jpeg/1_bits.steg
62+
```
63+
64+
LISO + Avoiding XuNet Detection
65+
```bash
66+
python train_bits.py --eval --test-xunet-weight 100 --bits 1 --dataset div2k --load checkpoints/div2k/1_bits.steg
67+
```
68+
69+
### Training
70+
LISO
71+
```bash
72+
python train_bits.py --bits 1 --dataset div2k
73+
```
74+
75+
LISO + JPEG
76+
```bash
77+
python train_bits.py --bits 1 --dataset div2k --jpeg
78+
```
79+
80+
## Acknowledgements
81+
- [SteganoGAN](https://github.com/DAI-Lab/SteganoGAN)
82+
- [RAFT](https://github.com/princeton-vl/RAFT)
83+
- [SiaStegNet](https://github.com/SiaStg/SiaStegNet)
84+
- [XuNet](https://github.com/brijeshiitg/XuNet-Structural-Design-of-Convolutional-Neural-Networksfor-Steganalysis)

SiaStegNet/src/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
from . import data
6+
from . import utils
7+
from . import models
8+
#from . import matlab

SiaStegNet/src/models/KeNet.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
from .modules import *
6+
7+
class KeNet(nn.Module):
8+
9+
def __init__(self, norm_layer=None, zero_init_residual=True, p=0.5):
10+
super(KeNet, self).__init__()
11+
12+
self.zero_init_residual = zero_init_residual
13+
14+
if norm_layer is None:
15+
norm_layer = nn.BatchNorm2d
16+
17+
self.srm = SRMConv2d(1, 0)
18+
self.bn1 = norm_layer(30)
19+
20+
self.A1 = BlockA(30, 30, norm_layer=norm_layer)
21+
self.A2 = BlockA(30, 30, norm_layer=norm_layer)
22+
self.AA = BlockA(30, 30, norm_layer=norm_layer)
23+
24+
# self.B1 = BlockB(30, 30, norm_layer=norm_layer)
25+
# self.B2 = BlockB(30, 64, norm_layer=norm_layer)
26+
27+
self.B3 = BlockB(30, 64, norm_layer=norm_layer)
28+
self.A3 = BlockA(64, 64, norm_layer=norm_layer)
29+
30+
self.B4 = BlockB(64, 128, norm_layer=norm_layer)
31+
self.A4 = BlockA(128, 128, norm_layer=norm_layer)
32+
33+
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
34+
# self.bnfc = nn.BatchNorm1d(128)
35+
self.relu = nn.ReLU(inplace=True)
36+
# self.fcfusion = nn.Linear(128, 128) #4
37+
self.fc = nn.Linear(128 * 4 + 1, 2)
38+
self.dropout = nn.Dropout(p=p)
39+
40+
self.reset_parameters()
41+
42+
def reset_parameters(self):
43+
for m in self.modules():
44+
if isinstance(m, nn.Conv2d):
45+
# nn.init.xavier_uniform_(m.weight)
46+
# nn.init.constant_(m.bias, 0.2)
47+
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
48+
elif isinstance(m, nn.BatchNorm2d):
49+
nn.init.constant_(m.weight, 1)
50+
nn.init.constant_(m.bias, 0)
51+
elif isinstance(m, nn.Linear):
52+
nn.init.normal_(m.weight, std=0.01)
53+
54+
if self.zero_init_residual:
55+
for m in self.modules():
56+
if isinstance(m, (BlockA, BlockB)):
57+
nn.init.constant_(m.bn2.weight, 0)
58+
59+
def extract_feat(self, x):
60+
x = x.float()
61+
out = self.srm(x)
62+
out = self.bn1(out)
63+
out = self.relu(out)
64+
65+
out = self.A1(out)
66+
out = self.A2(out)
67+
out = self.AA(out)
68+
69+
# out = self.B1(out)
70+
# out = self.B2(out)
71+
72+
out = self.B3(out)
73+
out = self.A3(out)
74+
75+
out = self.B4(out)
76+
out = self.A4(out)
77+
78+
out = self.avgpool(out)
79+
out = out.view(out.size(0), out.size(1))
80+
81+
# out = self.relu(out)
82+
# out = self.bnfc(out)
83+
84+
return out
85+
86+
def forward(self, *args):
87+
############# statistics fusion start #############
88+
feats = torch.stack(
89+
[self.extract_feat(subarea) for subarea in args], dim=0
90+
)
91+
euclidean_distance = F.pairwise_distance(feats[0], feats[1], eps=1e-6,
92+
keepdim=True)
93+
94+
if feats.shape[0] == 1:
95+
final_feat = feats.squeeze(dim=0)
96+
else:
97+
# feats_sum = feats.sum(dim=0)
98+
# feats_sub = feats[0] - feats[1]
99+
feats_mean = feats.mean(dim=0)
100+
feats_var = feats.var(dim=0)
101+
feats_min, _ = feats.min(dim=0)
102+
feats_max, _ = feats.max(dim=0)
103+
104+
'''feats_sum = feats.sum(dim=0)
105+
feats_sub = abs(feats[0] - feats[1])
106+
feats_prod = feats.prod(dim=0)
107+
feats_max, _ = feats.max(dim=0)'''
108+
109+
#final_feat = torch.cat(
110+
# [feats[0], feats[1], feats[0], feats[1]], dim=-1
111+
# #[euclidean_distance, feats_sum, feats_sub, feats_prod, feats_max], dim=-1
112+
#)
113+
114+
final_feat = torch.cat(
115+
[euclidean_distance, feats_mean, feats_var, feats_min, feats_max], dim=-1
116+
#[euclidean_distance, feats_sum, feats_sub, feats_prod, feats_max], dim=-1
117+
)
118+
119+
out = self.dropout(final_feat)
120+
# out = self.fcfusion(out)
121+
# out = self.relu(out)
122+
out = self.fc(out)
123+
124+
return out, feats[0], feats[1]

SiaStegNet/src/models/SRM_Kernels.npy

3.01 KB
Binary file not shown.

SiaStegNet/src/models/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
from .KeNet import *
6+
from .modules import *
7+
from .xunet import XuNet

0 commit comments

Comments
 (0)