Commit 6cbb3879dd61c0533550959f17558fdd998e4a79
1 parent
45a82355
Exists in
master
F4 updated.
Showing
13 changed files
with
178 additions
and
98 deletions
Show diff stats
jpegObj/__init__.py
... | ... | @@ -30,6 +30,8 @@ colorCode = { |
30 | 30 | "YCCK": 5 |
31 | 31 | } |
32 | 32 | |
33 | +colorParam = ['Y', 'Cb', 'Cr'] | |
34 | + | |
33 | 35 | # The JPEG class |
34 | 36 | # ============== |
35 | 37 | |
... | ... | @@ -69,28 +71,35 @@ class Jpeg(Jsteg): |
69 | 71 | # 1D Signal Representations |
70 | 72 | # ------------------------- |
71 | 73 | |
72 | - def rawsignal(self, mask=base.acMaskBlock): | |
74 | + def rawsignal(self, mask=base.acMaskBlock, channel="All"): | |
73 | 75 | """ |
74 | 76 | Return a 1D array of AC coefficients. |
75 | 77 | (Most applications should use getsignal() rather than rawsignal().) |
76 | 78 | """ |
77 | 79 | R = [] |
78 | - for X in self.coef_arrays: | |
80 | + if channel == "All": | |
81 | + for X in self.coef_arrays: | |
82 | + (h, w) = X.shape | |
83 | + A = base.acMask(h, w, mask) | |
84 | + R = np.hstack([R, X[A]]) | |
85 | + else: | |
86 | + cID = self.getCompID(channel) | |
87 | + X = self.coef_arrays[cID] | |
79 | 88 | (h, w) = X.shape |
80 | 89 | A = base.acMask(h, w, mask) |
81 | 90 | R = np.hstack([R, X[A]]) |
82 | 91 | return R |
83 | 92 | |
84 | - def getsignal(self, mask=base.acMaskBlock): | |
93 | + def getsignal(self, mask=base.acMaskBlock, channel="All"): | |
85 | 94 | """Return a 1D array of AC coefficients in random order.""" |
86 | - R = self.rawsignal(mask) | |
95 | + R = self.rawsignal(mask, channel) | |
87 | 96 | if self.key == None: |
88 | 97 | return R |
89 | 98 | else: |
90 | 99 | rnd.seed(self.key) |
91 | 100 | return R[rnd.permutation(len(R))] |
92 | 101 | |
93 | - def setsignal(self, R0, mask=base.acMaskBlock): | |
102 | + def setsignal(self, R0, mask=base.acMaskBlock, channel="All"): | |
94 | 103 | """Reinserts AC coefficients from getitem in the correct positions.""" |
95 | 104 | if self.key != None: |
96 | 105 | rnd.seed(self.key) |
... | ... | @@ -100,13 +109,40 @@ class Jpeg(Jsteg): |
100 | 109 | R[P] = R0 |
101 | 110 | else: |
102 | 111 | R = R0 |
103 | - for X in self.coef_arrays: | |
112 | + if channel == "All": | |
113 | + for cID in range(3): | |
114 | + X = self.coef_arrays[cID] | |
115 | + s = X.size * 63 / 64 | |
116 | + (h, w) = X.shape | |
117 | + X[base.acMask(h, w, mask)] = R[fst:(fst + s)] | |
118 | + fst += s | |
119 | + | |
120 | + # Jset | |
121 | + blocks = self.getCoefBlocks(channel=colorParam[cID]) | |
122 | + xmax, ymax = self.Jgetcompdim(cID) | |
123 | + for y in range(ymax): | |
124 | + for x in range(xmax): | |
125 | + block = blocks[y, x] | |
126 | + self.Jsetblock(x, y, cID, bytearray(block.astype(np.int16))) | |
127 | + | |
128 | + else: | |
129 | + cID = self.getCompID(channel) | |
130 | + X = self.coef_arrays[cID] | |
104 | 131 | s = X.size * 63 / 64 |
105 | 132 | (h, w) = X.shape |
106 | 133 | X[base.acMask(h, w, mask)] = R[fst:(fst + s)] |
107 | 134 | fst += s |
135 | + | |
136 | + # Jset | |
137 | + blocks = self.getCoefBlocks(channel) | |
138 | + xmax, ymax = self.Jgetcompdim(cID) | |
139 | + for y in range(ymax): | |
140 | + for x in range(xmax): | |
141 | + block = blocks[y, x] | |
142 | + self.Jsetblock(x, y, cID, bytearray(block.astype(np.int16))) | |
143 | + | |
108 | 144 | assert len(R) == fst |
109 | - return; | |
145 | + | |
110 | 146 | |
111 | 147 | # Histogram and Image Statistics |
112 | 148 | # ------------------------------ |
... | ... | @@ -247,12 +283,12 @@ class Jpeg(Jsteg): |
247 | 283 | assert blocks.shape[-2:] == (8, 8), "block is expected of size (8,8)" |
248 | 284 | cID = self.getCompID(channel) |
249 | 285 | |
250 | - vmax,hmax = blocks.shape[:2] | |
286 | + vmax, hmax = blocks.shape[:2] | |
251 | 287 | for i in range(vmax): |
252 | 288 | for j in range(hmax): |
253 | 289 | v, h = i * 8, j * 8 |
254 | - self.coef_arrays[cID][v:v + 8, h:h + 8] = blocks[i,j] | |
255 | - self.Jsetblock(j, i, cID, bytearray(blocks[i,j].astype(np.int16))) | |
290 | + self.coef_arrays[cID][v:v + 8, h:h + 8] = blocks[i, j] | |
291 | + self.Jsetblock(j, i, cID, bytearray(blocks[i, j].astype(np.int16))) | |
256 | 292 | |
257 | 293 | # Decompression |
258 | 294 | # ------------- | ... | ... |
jpegObj/__init__.pyc
No preview for this file type
msteg/StegBase.py
1 | -""" | |
2 | -This module implements a common base class of the steganographic | |
3 | -algorithms which embed data into JPEG files. | |
4 | -In order to run plugins inheriting from this class | |
5 | -you must build the rw_dct.so library which interfaces with libjpeg. | |
6 | -To do so, just run | |
7 | -% python setup.py build | |
8 | -in stegotool/util and copy the rw_dct.so | |
9 | -library (which can be found somewhere in the build-directory to | |
10 | -stegotool/util. | |
11 | - | |
12 | -""" | |
1 | +__author__ = 'chunk' | |
2 | + | |
3 | + | |
13 | 4 | import numpy as np |
14 | 5 | import time |
15 | 6 | import os |
... | ... | @@ -21,6 +12,7 @@ import mjsteg |
21 | 12 | import jpegObj |
22 | 13 | from common import * |
23 | 14 | |
15 | +sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, 5908L, 59816L, 56765L] | |
24 | 16 | |
25 | 17 | class StegBase(object): |
26 | 18 | """ |
... | ... | @@ -36,12 +28,14 @@ class StegBase(object): |
36 | 28 | self.cov_jpeg = None |
37 | 29 | self.cov_data = None |
38 | 30 | self.hid_data = None |
31 | + self.key = None | |
39 | 32 | |
40 | 33 | def _get_cov_data(self, img_path): |
41 | 34 | """ |
42 | 35 | Returns DCT coefficients of the cover image. |
43 | 36 | """ |
44 | 37 | self.cov_jpeg = jpegObj.Jpeg(img_path) |
38 | + self.key = self.cov_jpeg.getkey() | |
45 | 39 | self.cov_data = self.cov_jpeg.getCoefBlocks() |
46 | 40 | return self.cov_data |
47 | 41 | |
... | ... | @@ -150,7 +144,7 @@ class StegBase(object): |
150 | 144 | float(emb_size) / cov_size)) |
151 | 145 | |
152 | 146 | # dummy functions to please pylint |
153 | - def _raw_embed(self, cov_data, hid_data, status_begin=0): | |
147 | + def _raw_embed(self, cov_data, hid_data): | |
154 | 148 | pass |
155 | 149 | |
156 | 150 | def _raw_extract(self, steg_data, num_bits): | ... | ... |
msteg/StegBase.pyc
No preview for this file type
msteg/steganography/F3.py
1 | -""" | |
2 | -<p>This module implements the F3 steganography algorithm invented by | |
3 | -Andreas Westfeld.</p> | |
4 | - | |
5 | -It embeds a secret message in JPEG DCT coefficients. | |
6 | -It differs from simpler algorithms such as JSteg by subtracting the | |
7 | -absolute DCT value if the currently embedded bit and the LSB of the DCT | |
8 | -value are not equal. DCT coefficients with value 0 are ignored.<br /> | |
9 | -If a DCT coefficient is equal to 1 or -1 the current bit | |
10 | -is embedded repeatedly in order to make non-ambiguous extraction possible. | |
11 | -""" | |
1 | +__author__ = 'chunk' | |
2 | + | |
3 | + | |
12 | 4 | import time |
13 | 5 | import math |
14 | 6 | import numpy as np |
... | ... | @@ -42,7 +34,7 @@ class F3(StegBase): |
42 | 34 | lossy compression. |
43 | 35 | """ |
44 | 36 | self.t0 = time.time() |
45 | - StegBase._post_embed_actions(self, src_cover, src_hidden, tgt_stego) | |
37 | + self._post_embed_actions(src_cover, src_hidden, tgt_stego) | |
46 | 38 | |
47 | 39 | def extract_raw_data(self, src_steg, tgt_hidden): |
48 | 40 | """ This method extracts secret data from a stego image. It is |
... | ... | @@ -53,9 +45,9 @@ class F3(StegBase): |
53 | 45 | tgt_hidden - A pathname denoting where the extracted data should be saved to. |
54 | 46 | """ |
55 | 47 | self.t0 = time.time() |
56 | - StegBase._post_extract_actions(self, src_steg, tgt_hidden) | |
48 | + self._post_extract_actions(src_steg, tgt_hidden) | |
57 | 49 | |
58 | - def _raw_embed(self, cov_data, hid_data, status_begin=0): | |
50 | + def _raw_embed(self, cov_data, hid_data): | |
59 | 51 | """ |
60 | 52 | cov_data - 4-D numpy.int32 array |
61 | 53 | hid_data - 1-D numpy.uint8 array | ... | ... |
msteg/steganography/F3.pyc
No preview for this file type
msteg/steganography/F4.py
1 | -""" | |
2 | -<p>This module implements a slight variant of the F4 steganography algorithm | |
3 | -invented by Andreas Westfeld. It embeds a secret message in JPEG | |
4 | -DCT coefficients.</p> | |
5 | -It differs from F3 in that even negative and odd positive DCT | |
6 | -coefficients represent a 1 and odd negative and even positive | |
7 | -DCT coefficients represent a 0. It also supports permutative strattling | |
8 | -which is not included in the original description of F4. | |
9 | -""" | |
1 | +__author__ = 'chunk' | |
2 | + | |
10 | 3 | import time |
11 | 4 | import numpy as np |
12 | -from msteg.StegBase import StegBase | |
5 | +import numpy.random as rnd | |
6 | +from msteg.StegBase import * | |
7 | +import mjsteg | |
8 | +import jpegObj | |
13 | 9 | from common import * |
14 | 10 | |
15 | 11 | |
... | ... | @@ -18,51 +14,105 @@ class F4(StegBase): |
18 | 14 | with the F3 algorithm and <i>extract_raw_data</i> to extract data |
19 | 15 | which was embedded previously. """ |
20 | 16 | |
21 | - def __init__(self): | |
17 | + def __init__(self, key=sample_key): | |
22 | 18 | """ |
23 | 19 | Constructor of the F3 class. |
24 | 20 | """ |
25 | 21 | StegBase.__init__(self) |
22 | + self.key = key | |
26 | 23 | |
27 | - def embed_raw_data(self, src_cover, src_hidden, tgt_stego): | |
28 | - """ This method embeds arbitrary data into a cover image. | |
29 | - The cover image must be a JPEG. | |
24 | + def _get_cov_data(self, img_path): | |
25 | + """ | |
26 | + Returns DCT coefficients of the cover image. | |
27 | + """ | |
28 | + self.cov_jpeg = jpegObj.Jpeg(img_path, key=self.key) | |
30 | 29 | |
31 | - src_cover - A valid pathname to an image file which serves as cover image | |
32 | - (the image which the secret image is embedded into). | |
30 | + cov_data = self.cov_jpeg.getsignal(channel='Y') | |
31 | + self.cov_data = np.array(cov_data, dtype=np.int16) | |
32 | + return self.cov_data | |
33 | 33 | |
34 | - src_hidden - A valid pathname to an arbitrary file that is supposed to be | |
35 | - embedded into the cover image. | |
34 | + def embed_raw_data(self, src_cover, src_hidden, tgt_stego): | |
36 | 35 | |
37 | - tgt_stego - Target pathname of the resulting stego image. You should save to a | |
38 | - PNG or another lossless format, because many LSBs don't survive | |
39 | - lossy compression. | |
40 | - """ | |
41 | 36 | self.t0 = time.time() |
42 | - StegBase._post_embed_actions(self, src_cover, src_hidden, tgt_stego) | |
43 | 37 | |
44 | - def extract_raw_data(self, src_steg, tgt_hidden): | |
45 | - """ This method extracts secret data from a stego image. It is | |
46 | - (obviously) the inverse operation of embed_raw_data. | |
38 | + try: | |
39 | + cov_data = self._get_cov_data(src_cover) | |
40 | + hid_data = self._get_hid_data(src_hidden) | |
41 | + # print hid_data.dtype,type(hid_data),hid_data.tolist() | |
47 | 42 | |
48 | - src_stego - A valid pathname to an image file which serves as stego image. | |
43 | + cov_data, bits_cnt = self._raw_embed(cov_data, hid_data) | |
44 | + | |
45 | + if bits_cnt != np.size(hid_data) * 8: | |
46 | + raise Exception("Expected embedded size is %db but actually %db." % ( | |
47 | + np.size(hid_data) * 8, bits_cnt)) | |
48 | + | |
49 | + self.cov_jpeg.setsignal(cov_data, channel='Y') | |
50 | + self.cov_jpeg.Jwrite(tgt_stego) | |
51 | + | |
52 | + # size_cov = os.path.getsize(tgt_stego) | |
53 | + size_cov = np.size(cov_data) / 8 | |
54 | + size_embedded = np.size(hid_data) | |
55 | + | |
56 | + self._display_stats("embedded", size_cov, size_embedded, | |
57 | + time.time() - self.t0) | |
58 | + | |
59 | + except TypeError as e: | |
60 | + raise e | |
61 | + except Exception as expt: | |
62 | + print "Exception when embedding!" | |
63 | + raise | |
64 | + | |
65 | + def extract_raw_data(self, src_steg, tgt_hidden): | |
49 | 66 | |
50 | - tgt_hidden - A pathname denoting where the extracted data should be saved to. | |
51 | - """ | |
52 | 67 | self.t0 = time.time() |
53 | - StegBase._post_extract_actions(self, src_steg, tgt_hidden) | |
54 | 68 | |
55 | - def _raw_embed(self, cov_data, hid_data, status_begin=0): | |
69 | + try: | |
70 | + steg_data = self._get_cov_data(src_steg) | |
71 | + # emb_size = os.path.getsize(src_steg) | |
72 | + emb_size = np.size(steg_data) / 8 | |
73 | + | |
74 | + | |
75 | + # recovering file size | |
76 | + header_size = 4 * 8 | |
77 | + size_data, bits_cnt = self._raw_extract(steg_data, header_size) | |
78 | + size_data = bits2bytes(size_data) | |
79 | + print size_data | |
80 | + | |
81 | + size_hd = 0 | |
82 | + for i in xrange(4): | |
83 | + size_hd += size_data[i] * 256 ** i | |
84 | + | |
85 | + raw_size = size_hd * 8 | |
86 | + | |
87 | + if raw_size > np.size(steg_data): | |
88 | + raise Exception("Supposed secret data too large for stego image.") | |
89 | + | |
90 | + hid_data, bits_cnt = self._raw_extract(steg_data, raw_size) | |
91 | + | |
92 | + if bits_cnt != raw_size: | |
93 | + raise Exception("Expected embedded size is %db but actually %db." % ( | |
94 | + raw_size, bits_cnt)) | |
95 | + | |
96 | + hid_data = bits2bytes(hid_data) | |
97 | + # print hid_data.dtype,type(hid_data),hid_data.tolist() | |
98 | + hid_data[4:].tofile(tgt_hidden) | |
99 | + | |
100 | + self._display_stats("extracted", emb_size, | |
101 | + np.size(hid_data), | |
102 | + time.time() - self.t0) | |
103 | + except Exception as expt: | |
104 | + print "Exception when extracting!" | |
105 | + raise | |
106 | + | |
107 | + def _raw_embed(self, cov_data, hid_data): | |
56 | 108 | """ |
57 | - cov_data - 4-D numpy.int32 array | |
109 | + cov_data - 1-D numpy.int16 array (permunated) | |
58 | 110 | hid_data - 1-D numpy.uint8 array |
59 | 111 | """ |
60 | 112 | hid_data = bytes2bits(hid_data) |
61 | 113 | i = 0 |
62 | - cnt = -1 | |
63 | 114 | for x in np.nditer(cov_data, op_flags=['readwrite']): |
64 | - cnt = cnt + 1 | |
65 | - if x == 0 or cnt % 64 == 0: continue | |
115 | + if x == 0: continue | |
66 | 116 | |
67 | 117 | m = (hid_data[i] & 1) |
68 | 118 | if x > 0 and x & 1 != m: |
... | ... | @@ -72,19 +122,17 @@ class F4(StegBase): |
72 | 122 | if x == 0: continue |
73 | 123 | i += 1 |
74 | 124 | if i == hid_data.size: break |
75 | - | |
76 | - return cov_data | |
125 | + return cov_data, i | |
77 | 126 | |
78 | 127 | def _raw_extract(self, steg_data, num_bits): |
79 | 128 | """ |
80 | 129 | Just a small helper function to extract hidden data. |
130 | + steg_data - 1-D numpy.int16 array (permunated) | |
81 | 131 | """ |
82 | 132 | hid_data = np.zeros(num_bits, np.uint8) |
83 | 133 | j = 0 |
84 | - cnt = -1 | |
85 | - for x in np.nditer(steg_data): | |
86 | - cnt = cnt + 1 | |
87 | - if x == 0 or cnt % 64 == 0: continue | |
134 | + for x in steg_data: | |
135 | + if x == 0: continue | |
88 | 136 | if j >= num_bits: break |
89 | 137 | if x > 0: |
90 | 138 | hid_data[j] = x & 1 |
... | ... | @@ -93,7 +141,7 @@ class F4(StegBase): |
93 | 141 | |
94 | 142 | j = j + 1 |
95 | 143 | |
96 | - return hid_data | |
144 | + return hid_data, j | |
97 | 145 | |
98 | 146 | def __str__(self): |
99 | 147 | return "F4'" | ... | ... |
msteg/steganography/F4.pyc
No preview for this file type
msteg/steganography/LSB.py
1 | -""" | |
2 | -<p>This plugin implements two variants of a well-known steganographic | |
3 | -procedure commonly referred to as LSB algorithm.</p> | |
4 | -The general idea is to | |
5 | -overwrite some portion (usually the k least significant bits) of each byte | |
6 | -in the cover image. The below methods specify the number of overwritten | |
7 | -bits a parameter named word_size. Thus --- in this context --- word means | |
8 | -\"a group of bits of a fixed size\". | |
9 | -""" | |
1 | +__author__ = 'chunk' | |
2 | + | |
3 | + | |
10 | 4 | import time |
11 | 5 | import numpy as np |
12 | 6 | import scipy as sp |
... | ... | @@ -53,7 +47,7 @@ class LSB(StegBase): |
53 | 47 | self.t0 = time.time() |
54 | 48 | StegBase._post_extract_actions(self, src_steg, tgt_hidden) |
55 | 49 | |
56 | - def _raw_embed(self, cov_data, hid_data, status_begin=0): | |
50 | + def _raw_embed(self, cov_data, hid_data): | |
57 | 51 | """ |
58 | 52 | cov_data - 4-D numpy.int32 array |
59 | 53 | hid_data - 1-D numpy.uint8 array | ... | ... |
msteg/steganography/LSB.pyc
No preview for this file type
res/steged.jpg
test_jpeg.py
... | ... | @@ -3,6 +3,7 @@ __author__ = 'chunk' |
3 | 3 | import numpy as np |
4 | 4 | import mjsteg |
5 | 5 | import jpegObj |
6 | +from jpegObj import base | |
6 | 7 | from common import * |
7 | 8 | |
8 | 9 | timer = Timer() |
... | ... | @@ -16,6 +17,7 @@ sample = [[7, 12, 14, -12, 1, 0, -1, 0], |
16 | 17 | [0, 0, 0, 0, 0, 0, 0, 0], |
17 | 18 | [0, 0, 0, 0, 0, 0, 0, 0]] |
18 | 19 | |
20 | +sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, 5908L, 59816L, 56765L] | |
19 | 21 | |
20 | 22 | def diffblock(c1, c2): |
21 | 23 | diff = False |
... | ... | @@ -184,16 +186,28 @@ if __name__ == '__main__': |
184 | 186 | |
185 | 187 | # test_bitbyte() |
186 | 188 | |
187 | - ima = jpegObj.Jpeg("res/test3.jpg") | |
188 | - imb = jpegObj.Jpeg("res/steged.jpg") | |
189 | + ima = jpegObj.Jpeg("res/test3.jpg",key=sample_key) | |
190 | + imb = jpegObj.Jpeg("res/new.jpg",key=sample_key) | |
191 | + imc = jpegObj.Jpeg("res/steged.jpg",key=sample_key) | |
189 | 192 | print ima.Jgetcompdim(0) |
190 | - diffblocks(ima, imb) | |
191 | - | |
192 | - c1 = ima.getCoefBlocks() | |
193 | - c2 = imb.getCoefBlocks() | |
194 | - | |
195 | - print c1[0],c2[0] | |
193 | + print ima.getkey(),imb.getkey() | |
194 | + diffblocks(imb, ima) | |
196 | 195 | |
196 | + # c1 = ima.getCoefBlocks() | |
197 | + # c2 = imb.getCoefBlocks() | |
198 | + # | |
199 | + # # print c1[0],c2[0] | |
200 | + # s1 = imb.getsignal(channel='Y') | |
201 | + # s2 = ima.getsignal(channel='Y') | |
202 | + # imb.setsignal(s2,channel='Y') | |
203 | + # imb.Jwrite('res/new.jpg') | |
204 | + | |
205 | + | |
206 | + # print base.acMask(8,16) | |
207 | + # mmask = base.acMaskBlock | |
208 | + # print mmask | |
209 | + # sample = np.array(sample)[mmask] | |
210 | + # print np.hstack([[],sample]) | |
197 | 211 | pass |
198 | 212 | |
199 | 213 | ... | ... |
test_steg.py
... | ... | @@ -22,10 +22,12 @@ sample = [[7, 12, 14, -12, 1, 0, -1, 0], |
22 | 22 | [0, 0, 0, 0, 0, 0, 0, 0], |
23 | 23 | [0, 0, 0, 0, 0, 0, 0, 0]] |
24 | 24 | |
25 | +sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, 5908L, 59816L, 56765L] | |
26 | + | |
25 | 27 | txtsample = [116, 104, 105, 115, 32, 105, 115, 32, 116, 111, 32, 98, 101, 32, 101, 109, 98, 101, 100, 101, 100, 46, 10] |
26 | 28 | |
27 | 29 | if __name__ == '__main__': |
28 | - f3test = F3.F3() | |
30 | + f3test = F4.F4() | |
29 | 31 | f3test.embed_raw_data("res/test3.jpg", "res/embeded", "res/steged.jpg") |
30 | 32 | f3test.extract_raw_data("res/steged.jpg", "res/extracted") |
31 | 33 | ... | ... |