Commit 8cfc1a2373c0fb2aa9b6fcbd6a4e795f3ac76320
1 parent
033d3b0d
Exists in
master
F5 half-finished.
Showing
13 changed files
with
278 additions
and
203 deletions
Show diff stats
jpegObj/__init__.pyc
No preview for this file type
msteg/StegBase.py
| ... | ... | @@ -102,6 +102,10 @@ class StegBase(object): |
| 102 | 102 | # recovering file size |
| 103 | 103 | header_size = 4 * 8 |
| 104 | 104 | size_data, bits_cnt = self._raw_extract(steg_data, header_size) |
| 105 | + if bits_cnt < header_size: | |
| 106 | + raise Exception("Expected embedded size is %db but actually %db." % ( | |
| 107 | + header_size, bits_cnt)) | |
| 108 | + | |
| 105 | 109 | size_data = bits2bytes(size_data) |
| 106 | 110 | size_hd = 0 |
| 107 | 111 | for i in xrange(4): | ... | ... |
msteg/StegBase.pyc
No preview for this file type
msteg/steganography/F3.pyc
No preview for this file type
| ... | ... | @@ -0,0 +1,101 @@ |
| 1 | +__author__ = 'chunk' | |
| 2 | + | |
| 3 | +""" | |
| 4 | +<p>This module implements a slight variant of the F4 steganography algorithm | |
| 5 | +invented by Andreas Westfeld. It embeds a secret message in JPEG | |
| 6 | +DCT coefficients.</p> | |
| 7 | +It differs from F3 in that even negative and odd positive DCT | |
| 8 | +coefficients represent a 1 and odd negative and even positive | |
| 9 | +DCT coefficients represent a 0. It also supports permutative strattling | |
| 10 | +which is not included in the original description of F4. | |
| 11 | +""" | |
| 12 | +import time | |
| 13 | +import numpy as np | |
| 14 | +from msteg.StegBase import StegBase | |
| 15 | +from common import * | |
| 16 | + | |
| 17 | + | |
| 18 | +class F4(StegBase): | |
| 19 | + """ This module has two methods: <i>embed_raw_data</i> to embed data | |
| 20 | + with the F3 algorithm and <i>extract_raw_data</i> to extract data | |
| 21 | + which was embedded previously. """ | |
| 22 | + | |
| 23 | + def __init__(self): | |
| 24 | + """ | |
| 25 | + Constructor of the F3 class. | |
| 26 | + """ | |
| 27 | + StegBase.__init__(self) | |
| 28 | + | |
| 29 | + def embed_raw_data(self, src_cover, src_hidden, tgt_stego): | |
| 30 | + """ This method embeds arbitrary data into a cover image. | |
| 31 | + The cover image must be a JPEG. | |
| 32 | + | |
| 33 | + src_cover - A valid pathname to an image file which serves as cover image | |
| 34 | + (the image which the secret image is embedded into). | |
| 35 | + | |
| 36 | + src_hidden - A valid pathname to an arbitrary file that is supposed to be | |
| 37 | + embedded into the cover image. | |
| 38 | + | |
| 39 | + tgt_stego - Target pathname of the resulting stego image. You should save to a | |
| 40 | + PNG or another lossless format, because many LSBs don't survive | |
| 41 | + lossy compression. | |
| 42 | + """ | |
| 43 | + self.t0 = time.time() | |
| 44 | + StegBase._post_embed_actions(self, src_cover, src_hidden, tgt_stego) | |
| 45 | + | |
| 46 | + def extract_raw_data(self, src_steg, tgt_hidden): | |
| 47 | + """ This method extracts secret data from a stego image. It is | |
| 48 | + (obviously) the inverse operation of embed_raw_data. | |
| 49 | + | |
| 50 | + src_stego - A valid pathname to an image file which serves as stego image. | |
| 51 | + | |
| 52 | + tgt_hidden - A pathname denoting where the extracted data should be saved to. | |
| 53 | + """ | |
| 54 | + self.t0 = time.time() | |
| 55 | + StegBase._post_extract_actions(self, src_steg, tgt_hidden) | |
| 56 | + | |
| 57 | + def _raw_embed(self, cov_data, hid_data, status_begin=0): | |
| 58 | + """ | |
| 59 | + cov_data - 4-D numpy.int32 array | |
| 60 | + hid_data - 1-D numpy.uint8 array | |
| 61 | + """ | |
| 62 | + hid_data = bytes2bits(hid_data) | |
| 63 | + i = 0 | |
| 64 | + cnt = -1 | |
| 65 | + for x in np.nditer(cov_data, op_flags=['readwrite']): | |
| 66 | + cnt = cnt + 1 | |
| 67 | + if x == 0 or cnt % 64 == 0: continue | |
| 68 | + | |
| 69 | + m = (hid_data[i] & 1) | |
| 70 | + if x > 0 and x & 1 != m: | |
| 71 | + x[...] -= 1 | |
| 72 | + elif x < 0 and x & 1 == m: | |
| 73 | + x[...] += 1 | |
| 74 | + if x == 0: continue | |
| 75 | + i += 1 | |
| 76 | + if i == hid_data.size: break | |
| 77 | + | |
| 78 | + return cov_data | |
| 79 | + | |
| 80 | + def _raw_extract(self, steg_data, num_bits): | |
| 81 | + """ | |
| 82 | + Just a small helper function to extract hidden data. | |
| 83 | + """ | |
| 84 | + hid_data = np.zeros(num_bits, np.uint8) | |
| 85 | + j = 0 | |
| 86 | + cnt = -1 | |
| 87 | + for x in np.nditer(steg_data): | |
| 88 | + cnt = cnt + 1 | |
| 89 | + if x == 0 or cnt % 64 == 0: continue | |
| 90 | + if j >= num_bits: break | |
| 91 | + if x > 0: | |
| 92 | + hid_data[j] = x & 1 | |
| 93 | + else: | |
| 94 | + hid_data[j] = (x & 1) ^ 1 | |
| 95 | + | |
| 96 | + j = j + 1 | |
| 97 | + | |
| 98 | + return hid_data | |
| 99 | + | |
| 100 | + def __str__(self): | |
| 101 | + return "F4'" | ... | ... |
msteg/steganography/F4.py
| ... | ... | @@ -74,6 +74,10 @@ class F4(StegBase): |
| 74 | 74 | # recovering file size |
| 75 | 75 | header_size = 4 * 8 |
| 76 | 76 | size_data, bits_cnt = self._raw_extract(steg_data, header_size) |
| 77 | + if bits_cnt < header_size: | |
| 78 | + raise Exception("Expected embedded size is %db but actually %db." % ( | |
| 79 | + header_size, bits_cnt)) | |
| 80 | + | |
| 77 | 81 | size_data = bits2bytes(size_data) |
| 78 | 82 | |
| 79 | 83 | size_hd = 0 |
| ... | ... | @@ -120,6 +124,7 @@ class F4(StegBase): |
| 120 | 124 | if x == 0: continue |
| 121 | 125 | i += 1 |
| 122 | 126 | if i == hid_data.size: break |
| 127 | + | |
| 123 | 128 | return cov_data, i |
| 124 | 129 | |
| 125 | 130 | def _raw_extract(self, steg_data, num_bits): |
| ... | ... | @@ -127,6 +132,7 @@ class F4(StegBase): |
| 127 | 132 | Just a small helper function to extract hidden data. |
| 128 | 133 | steg_data - 1-D numpy.int16 array (permunated) |
| 129 | 134 | """ |
| 135 | + | |
| 130 | 136 | hid_data = np.zeros(num_bits, np.uint8) |
| 131 | 137 | j = 0 |
| 132 | 138 | for x in steg_data: | ... | ... |
msteg/steganography/F4.pyc
No preview for this file type
msteg/steganography/F5.py
| 1 | +__author__ = 'chunk' | |
| 2 | + | |
| 1 | 3 | """ |
| 2 | 4 | <p>This module implements the rather sophisticated F5 algorithm which was |
| 3 | 5 | invented by Andreas Westfeld.</p> |
| ... | ... | @@ -35,118 +37,134 @@ class F5(StegBase): |
| 35 | 37 | with the F5 algorithm and <i>extract_raw_data</i> to extract data |
| 36 | 38 | which was embedded previously. """ |
| 37 | 39 | |
| 38 | - def __init__(self, key=sample_key): | |
| 40 | + def __init__(self, key=sample_key, k=None): | |
| 39 | 41 | """ |
| 40 | 42 | Constructor of the F5 class. |
| 41 | 43 | """ |
| 42 | 44 | StegBase.__init__(self, key) |
| 43 | 45 | self._embed_fun = None |
| 44 | 46 | self.default_embedding = True |
| 45 | - self.steg_ind = -1 | |
| 46 | - self.excess_bits = None | |
| 47 | + | |
| 47 | 48 | # needed because k is embedded separately |
| 48 | - self.cov_ind = -1 | |
| 49 | - self.k_coeff = -1 | |
| 50 | - | |
| 51 | - @describe_annotate_convert((None, None, ident), | |
| 52 | - ("cover image", ImagePath, str), | |
| 53 | - ("hidden data", FilePath, str), | |
| 54 | - ("stego image", NewFilePath, str), | |
| 55 | - ("seed", int, int), | |
| 56 | - ("embedding behavior", | |
| 57 | - ['Default', 'F3', 'JSteg'], str)) | |
| 58 | - def embed_raw_data(self, src_cover, src_hidden, tgt_stego, seed, | |
| 59 | - embed_fun): | |
| 60 | - """<p>This method embeds arbitrary data into a cover image. | |
| 61 | - The cover image must be a JPEG.</p> | |
| 62 | - | |
| 63 | - <p>Parameters: | |
| 64 | - <ol> | |
| 65 | - <li><pre>src_cover</pre> | |
| 66 | - A valid pathname to an image file which serves as cover image | |
| 67 | - (the image which the secret image is embedded into).</li> | |
| 68 | - | |
| 69 | - <li><pre>src_hidden</pre> | |
| 70 | - A valid pathname to an arbitrary file that is supposed to be | |
| 71 | - embedded into the cover image.</li> | |
| 72 | - | |
| 73 | - <li><pre>tgt_stego</pre> | |
| 74 | - Target pathname of the resulting stego image. You should save to | |
| 75 | - a PNG or another lossless format, because many LSBs don't survive | |
| 76 | - lossy compression.</li> | |
| 77 | - | |
| 78 | - <li><pre>seed</pre> | |
| 79 | - A seed for the random number generator that is responsible scattering | |
| 80 | - the secret data within the cover image.</li> | |
| 81 | - | |
| 82 | - <li><pre>param embed_fun</pre> | |
| 49 | + self.k_coeff = k | |
| 50 | + | |
| 51 | + | |
| 52 | + def _get_cov_data(self, img_path): | |
| 53 | + """ | |
| 54 | + Returns DCT coefficients of the cover image. | |
| 55 | + """ | |
| 56 | + self.cov_jpeg = jpegObj.Jpeg(img_path, key=self.key) | |
| 57 | + | |
| 58 | + cov_data = self.cov_jpeg.getsignal(channel='Y') | |
| 59 | + self.cov_data = np.array(cov_data, dtype=np.int16) | |
| 60 | + return self.cov_data | |
| 61 | + | |
| 62 | + def embed_raw_data(self, src_cover, src_hidden, tgt_stego, embed_fun='Default'): | |
| 63 | + """This method embeds arbitrary data into a cover image. | |
| 64 | + The cover image must be a JPEG. | |
| 65 | + | |
| 66 | + @param embed_fun: | |
| 83 | 67 | Specifies which embedding function should be used. Must be one of |
| 84 | 68 | 'Default', 'F3', 'Jsteg'. If 'Default' is selected, the algorithm uses |
| 85 | 69 | the same behavior as Westfeld's implementation, i.e. decrementing |
| 86 | 70 | absolute values for n > 1 (F3) and using F4 in the special case n = 1. |
| 87 | - Selecting F3 or JSteg results in using that scheme for all n.</li> | |
| 88 | - </ol> | |
| 89 | - </p> | |
| 71 | + Selecting F3 or JSteg results in using that scheme for all n. | |
| 90 | 72 | """ |
| 91 | 73 | self.t0 = time.time() |
| 92 | - self.seed = seed | |
| 74 | + | |
| 93 | 75 | if embed_fun == 'F3': |
| 94 | 76 | self._embed_fun = self._f3_embed |
| 95 | 77 | self.default_embedding = False |
| 96 | - elif embed_fun == 'JSteg': | |
| 78 | + elif embed_fun == 'JSteg' or embed_fun == 'LSB': | |
| 97 | 79 | self._embed_fun = self._jsteg_embed |
| 98 | 80 | self.default_embedding = False |
| 99 | - elif embed_fun == 'Default': | |
| 81 | + else: | |
| 100 | 82 | self._embed_fun = self._f3_embed |
| 101 | 83 | self.default_embedding = True |
| 102 | 84 | |
| 103 | - self.cov_ind = -1 | |
| 104 | - JPEGSteg._post_embed_actions(self, src_cover, src_hidden, tgt_stego) | |
| 85 | + try: | |
| 86 | + cov_data = self._get_cov_data(src_cover) | |
| 87 | + hid_data = self._get_hid_data(src_hidden) | |
| 88 | + # print hid_data.dtype,type(hid_data),hid_data.tolist() | |
| 105 | 89 | |
| 106 | - @describe_annotate_convert((None, None, ident), | |
| 107 | - ("stego image", ImagePath, str), | |
| 108 | - ("hidden data", NewFilePath, str), | |
| 109 | - ("seed", int, int), | |
| 110 | - ("embedding behavior", ['Default', 'F3/JSteg'], | |
| 111 | - str)) | |
| 112 | - def extract_raw_data(self, src_steg, tgt_hidden, seed, embed_fun): | |
| 113 | - """<p>This method extracts secret data from a stego image. It is | |
| 114 | - (obviously) the inverse operation of embed_raw_data.</p> | |
| 90 | + cov_data, bits_cnt = self._raw_embed(cov_data, hid_data) | |
| 115 | 91 | |
| 116 | - <p>Parameters: | |
| 117 | - <ol> | |
| 118 | - <li><pre>src_stego</pre> | |
| 119 | - A valid pathname to an image file which serves as stego image.</li> | |
| 92 | + if bits_cnt < np.size(hid_data) * 8: | |
| 93 | + raise Exception("Expected embedded size is %db but actually %db." % ( | |
| 94 | + np.size(hid_data) * 8, bits_cnt)) | |
| 120 | 95 | |
| 121 | - <li><pre>tgt_hidden</pre> | |
| 122 | - A pathname denoting where the extracted data should be saved to.</li> | |
| 96 | + self.cov_jpeg.setsignal(cov_data, channel='Y') | |
| 97 | + self.cov_jpeg.Jwrite(tgt_stego) | |
| 123 | 98 | |
| 124 | - <li><pre>param seed</pre> | |
| 125 | - A seed for the random number generator that is responsible scattering | |
| 126 | - the secret data within the cover image.</li> | |
| 99 | + # size_cov = os.path.getsize(tgt_stego) | |
| 100 | + size_cov = np.size(cov_data) / 8 | |
| 101 | + size_embedded = np.size(hid_data) | |
| 127 | 102 | |
| 128 | - <li><pre>param embed_fun</pre> | |
| 129 | - Specifies which embedding function should be used. Must be one of | |
| 130 | - 'Default', 'F3', 'JSteg'. If 'Default' is selected, the algorithm uses | |
| 131 | - the same behavior as Westfeld's implementation, i.e. decrementing | |
| 132 | - absolute values for n > 1 (F3) and using F4 in the special case n = 1. | |
| 133 | - Selecting F3 or JSteg results in using that scheme for all n.</li> | |
| 134 | - </ol></pre> | |
| 135 | - """ | |
| 103 | + self._display_stats("embedded", size_cov, size_embedded, | |
| 104 | + time.time() - self.t0) | |
| 136 | 105 | |
| 106 | + except TypeError as e: | |
| 107 | + raise e | |
| 108 | + except Exception as expt: | |
| 109 | + print "Exception when embedding!" | |
| 110 | + raise | |
| 111 | + | |
| 112 | + | |
| 113 | + def extract_raw_data(self, src_steg, tgt_hidden, embed_fun='Default'): | |
| 137 | 114 | self.t0 = time.time() |
| 138 | - self.seed = seed | |
| 139 | - self.steg_ind = -1 | |
| 140 | - if embed_fun == 'F3/JSteg': | |
| 115 | + | |
| 116 | + if embed_fun == 'F3': | |
| 117 | + self._embed_fun = self._f3_embed | |
| 118 | + self.default_embedding = False | |
| 119 | + elif embed_fun == 'JSteg' or embed_fun == 'LSB': | |
| 120 | + self._embed_fun = self._jsteg_embed | |
| 141 | 121 | self.default_embedding = False |
| 142 | - elif embed_fun == 'Default': | |
| 122 | + else: | |
| 123 | + self._embed_fun = self._f3_embed | |
| 143 | 124 | self.default_embedding = True |
| 144 | 125 | |
| 145 | - # excess bits occur when the size of extracted data is not a multiple | |
| 146 | - # of k. if excess bits are available, they are prepended to hidden data | |
| 147 | - self.excess_bits = None | |
| 126 | + try: | |
| 127 | + steg_data = self._get_cov_data(src_steg) | |
| 128 | + # emb_size = os.path.getsize(src_steg) | |
| 129 | + emb_size = np.size(steg_data) / 8 | |
| 130 | + | |
| 131 | + # recovering file size | |
| 132 | + header_size = 4 * 8 | |
| 133 | + size_data, bits_cnt = self._raw_extract(steg_data, header_size) | |
| 134 | + | |
| 135 | + if bits_cnt < header_size: | |
| 136 | + raise Exception("Expected embedded size is %db but actually %db." % ( | |
| 137 | + header_size, bits_cnt)) | |
| 138 | + | |
| 139 | + size_data = bits2bytes(size_data[:header_size]) | |
| 140 | + print size_data | |
| 141 | + | |
| 142 | + size_hd = 0 | |
| 143 | + for i in xrange(4): | |
| 144 | + size_hd += size_data[i] * 256 ** i | |
| 145 | + | |
| 146 | + raw_size = size_hd * 8 | |
| 147 | + | |
| 148 | + if raw_size > np.size(steg_data): | |
| 149 | + raise Exception("Supposed secret data too large for stego image.") | |
| 150 | + | |
| 151 | + hid_data, bits_cnt = self._raw_extract(steg_data, raw_size) | |
| 152 | + | |
| 153 | + if bits_cnt < raw_size: | |
| 154 | + raise Exception("Expected embedded size is %db but actually %db." % ( | |
| 155 | + raw_size, bits_cnt)) | |
| 156 | + | |
| 157 | + hid_data = bits2bytes(hid_data) | |
| 158 | + # print hid_data.dtype,type(hid_data),hid_data.tolist() | |
| 159 | + hid_data[4:].tofile(tgt_hidden) | |
| 160 | + | |
| 161 | + self._display_stats("extracted", emb_size, | |
| 162 | + np.size(hid_data), | |
| 163 | + time.time() - self.t0) | |
| 164 | + except Exception as expt: | |
| 165 | + print "Exception when extracting!" | |
| 166 | + raise | |
| 148 | 167 | |
| 149 | - JPEGSteg._post_extract_actions(self, src_steg, tgt_hidden) | |
| 150 | 168 | |
| 151 | 169 | def _embed_k(self, cov_data, hid_data): |
| 152 | 170 | np.random.seed(self.seed) |
| ... | ... | @@ -218,153 +236,89 @@ class F5(StegBase): |
| 218 | 236 | |
| 219 | 237 | def _jsteg_embed(self, cov_data, ind): |
| 220 | 238 | m = 1 ^ (cov_data[ind] & 1) |
| 221 | - cov_data[ind] = (cov_data[ind] & 0xffffe) | m | |
| 239 | + cov_data[ind] = (cov_data[ind] & 0xfffffffe) | m | |
| 222 | 240 | |
| 223 | - def _raw_embed(self, cov_data, hid_data, status_begin=0): | |
| 241 | + def _raw_embed(self, cov_data, hid_data): | |
| 224 | 242 | k = self.k_coeff |
| 225 | 243 | n = (1 << k) - 1 |
| 244 | + | |
| 226 | 245 | if n == 1 and self.default_embedding: |
| 227 | - # in case k = n = 1, Westfeld's implementation uses F4 for | |
| 228 | - # embedding. Therefore, if 'default' embedding has been selected | |
| 229 | - # we will do the same | |
| 230 | - f4 = F4(self.ui, self.core) | |
| 231 | - f4.seed = self.seed | |
| 232 | - f4.dct_p = self.dct_p | |
| 233 | - f4.cov_ind = self.cov_ind | |
| 234 | - cov_data = f4._raw_embed(cov_data, hid_data, 30) | |
| 235 | - return cov_data | |
| 236 | - | |
| 237 | - cov_ind = self.cov_ind # preventing RSI by writing 'self' less often | |
| 238 | - hid_ind = 0 | |
| 239 | - remaining_bits = hid_data.size | |
| 240 | - hid_size = float(hid_data.size) | |
| 241 | - dct_p = self.dct_p | |
| 242 | - | |
| 243 | - update_cnt = int(hid_size / (70.0 * k)) | |
| 244 | - while remaining_bits > 0: | |
| 245 | - if update_cnt == 0: | |
| 246 | - self._set_progress(30 + int((( | |
| 247 | - hid_size - remaining_bits) / hid_size) * 70)) | |
| 248 | - update_cnt = int(hid_size / (70.0 * k)) | |
| 249 | - update_cnt -= 1 | |
| 250 | - msg_chunk_size = min(remaining_bits, k) | |
| 251 | - msg_chunk = np.zeros(k, np.int8) | |
| 252 | - cov_chunk = np.zeros(n, np.int32) | |
| 253 | - msg_chunk[0:msg_chunk_size] = hid_data[hid_ind:hid_ind + | |
| 254 | - msg_chunk_size] | |
| 255 | - hid_ind += k | |
| 256 | - | |
| 257 | - # get n DCT coefficients | |
| 258 | - for i in xrange(n): | |
| 259 | - cov_ind += 1 | |
| 260 | - while cov_data[dct_p[cov_ind]] == 0 \ | |
| 261 | - or dct_p[cov_ind] % 64 == 0: | |
| 262 | - cov_ind += 1 | |
| 263 | - cov_chunk[i] = dct_p[cov_ind] | |
| 246 | + # in case k = n = 1, Westfeld's implementation uses F4 for embedding. | |
| 247 | + f4 = F4(key=self.key) | |
| 248 | + return f4._raw_embed(cov_data, hid_data) | |
| 249 | + | |
| 250 | + hid_data = bytes2bits(hid_data) | |
| 251 | + if len(hid_data) % k != 0: | |
| 252 | + hid_data = list(hid_data) + [0 for x in range(k - len(hid_data) % k)] | |
| 253 | + | |
| 254 | + ind_nonzero = np.nonzero(cov_data)[0] | |
| 255 | + | |
| 256 | + if np.size(ind_nonzero) * k < len(hid_data) * n: | |
| 257 | + raise Exception("Supposed secret data too large for stego image.") | |
| 258 | + | |
| 259 | + ind_cov = 0 | |
| 260 | + for ind_hid in range(0, len(hid_data), k): | |
| 261 | + msg_chunk = hid_data[ind_hid:ind_hid + k] | |
| 262 | + cov_chunk = ind_nonzero[ind_cov:ind_cov + n] | |
| 263 | + ind_cov += n | |
| 264 | 264 | |
| 265 | 265 | success = False |
| 266 | - while not success: # loop necessary because of shrinkage | |
| 266 | + while not success: | |
| 267 | 267 | h = 0 |
| 268 | 268 | for i in xrange(n): |
| 269 | 269 | h ^= ((cov_data[cov_chunk[i]] & 1) * (i + 1)) |
| 270 | 270 | scalar_x = 0 |
| 271 | 271 | for i in xrange(k): |
| 272 | - scalar_x = (scalar_x << 1) + msg_chunk[i] | |
| 272 | + scalar_x = (scalar_x << 1) + msg_chunk[ | |
| 273 | + i] # N.B. hid_data[0]:high (that is x2), hid_data[1]:low (that is x1) | |
| 273 | 274 | s = scalar_x ^ h |
| 274 | 275 | if s != 0: |
| 275 | 276 | self._embed_fun(cov_data, cov_chunk[s - 1]) |
| 276 | 277 | else: |
| 277 | 278 | break |
| 278 | 279 | |
| 279 | - if cov_data[cov_chunk[s - 1]] == 0: # test for shrinkage | |
| 280 | - cov_chunk[s - 1:-1] = cov_chunk[s:] # adjusting | |
| 281 | - cov_ind += 1 | |
| 282 | - while cov_data[dct_p[cov_ind]] == 0 or \ | |
| 283 | - dct_p[cov_ind] % 64 == 0: | |
| 284 | - cov_ind += 1 | |
| 285 | - cov_chunk[n - 1] = dct_p[cov_ind] | |
| 280 | + if cov_data[cov_chunk[s - 1]] == 0: # shrinkage | |
| 281 | + cov_chunk[s - 1:-1] = cov_chunk[s:] | |
| 282 | + cov_chunk[-1] = ind_nonzero[ind_cov] | |
| 283 | + ind_cov += 1 | |
| 286 | 284 | else: |
| 287 | 285 | success = True |
| 288 | 286 | |
| 289 | - remaining_bits -= k | |
| 290 | - | |
| 291 | - self.k_coeff = -1 # prevent k being read from this instance | |
| 292 | - return cov_data | |
| 287 | + return cov_data, ind_hid + k | |
| 293 | 288 | |
| 294 | - def _raw_extract(self, num_bits): | |
| 289 | + def _raw_extract(self, steg_data, num_bits): | |
| 295 | 290 | k = self.k_coeff |
| 296 | 291 | n = (1 << k) - 1 |
| 297 | - if self.is_header == None: | |
| 298 | - self.is_header = True | |
| 299 | - if n == 1 and self.default_embedding: | |
| 300 | - f4 = F4(self.ui, self.core) | |
| 301 | - f4.seed = self.seed | |
| 302 | - f4.dct_p = self.dct_p | |
| 303 | - f4.steg_data = self.steg_data | |
| 304 | - f4.is_header = self.is_header | |
| 305 | - f4.steg_ind = self.steg_ind | |
| 306 | - hid_data = f4._raw_extract(num_bits) | |
| 307 | - self.steg_ind = f4.steg_ind | |
| 308 | - self.is_header = False | |
| 309 | - return hid_data | |
| 310 | - remaining_bits = num_bits | |
| 311 | - hid_data = np.zeros(num_bits, np.uint8) | |
| 312 | - hid_ind = 0 | |
| 313 | - | |
| 314 | - dct_p = self.dct_p | |
| 315 | - | |
| 316 | - is_header = False # signals whether or not extracting header | |
| 317 | - | |
| 318 | - if self.excess_bits != None: | |
| 319 | - hid_data[hid_ind:hid_ind + self.excess_bits.size] = \ | |
| 320 | - self.excess_bits | |
| 321 | - hid_ind += self.excess_bits.size | |
| 322 | - remaining_bits -= self.excess_bits.size | |
| 323 | - | |
| 324 | - curr_chunk = np.zeros(k, np.uint8) | |
| 325 | - | |
| 326 | - update_cnt = int(num_bits / (100.0 * k)) | |
| 327 | - | |
| 328 | - while remaining_bits > 0: | |
| 329 | 292 | |
| 330 | - if update_cnt == 0 and not is_header: | |
| 331 | - self._set_progress(int(((float(num_bits) \ | |
| 332 | - - remaining_bits) / num_bits) * 100)) | |
| 333 | - update_cnt = int(num_bits / (100.0 * k)) | |
| 293 | + if n == 1 and self.default_embedding: | |
| 294 | + f4 = F4(key=self.key) | |
| 295 | + return f4._raw_extract(steg_data, num_bits) | |
| 334 | 296 | |
| 335 | - update_cnt -= 1 | |
| 297 | + num_bits_ceil = num_bits | |
| 298 | + if num_bits % k != 0: | |
| 299 | + num_bits_ceil = k * (num_bits / k + 1) | |
| 336 | 300 | |
| 337 | - steg_chunk = [0 for i in xrange(n)] | |
| 338 | - for i in xrange(n): | |
| 339 | - self.steg_ind += 1 | |
| 340 | - while self.steg_data[dct_p[self.steg_ind]] == 0 or \ | |
| 341 | - dct_p[self.steg_ind] % 64 == 0: | |
| 342 | - self.steg_ind += 1 | |
| 343 | - steg_chunk[i] = self.steg_data[dct_p[self.steg_ind]] | |
| 301 | + hid_data = np.zeros(num_bits_ceil, np.uint8) | |
| 302 | + curr_chunk = np.zeros(k, np.uint8) | |
| 303 | + steg_data = steg_data[np.nonzero(steg_data)] | |
| 304 | + ind_hid = 0 | |
| 305 | + for ind_cov in range(0, len(steg_data), n): | |
| 306 | + steg_chunk = steg_data[ind_cov:ind_cov + n] | |
| 344 | 307 | |
| 345 | 308 | h = 0 # hash value |
| 346 | 309 | for i in xrange(n): |
| 347 | 310 | h ^= ((steg_chunk[i] & 1) * (i + 1)) |
| 348 | 311 | |
| 349 | 312 | for i in xrange(k): |
| 350 | - curr_chunk[k - i - 1] = h % 2 | |
| 351 | - h /= 2 | |
| 352 | - | |
| 353 | - l = min(k, remaining_bits) | |
| 354 | - for i in xrange(l): | |
| 355 | - hid_data[hid_ind] = curr_chunk[i] | |
| 356 | - hid_ind += 1 | |
| 313 | + curr_chunk[k - i - 1] = h & 1 # N.B. hid_data[0]:high (that is x2), hid_data[1]:low (that is x1) | |
| 314 | + h >>= 1 | |
| 357 | 315 | |
| 358 | - # save excess bits (for later calls) | |
| 359 | - if k > remaining_bits: | |
| 360 | - self.excess_bits = curr_chunk[remaining_bits:] | |
| 361 | - else: | |
| 362 | - self.excess_bits = None | |
| 316 | + hid_data[ind_hid:ind_hid + k] = curr_chunk[0:k] | |
| 317 | + ind_hid += k | |
| 363 | 318 | |
| 364 | - remaining_bits -= k | |
| 319 | + if ind_hid >= num_bits_ceil: break | |
| 365 | 320 | |
| 366 | - self.is_header = False | |
| 367 | - return hid_data | |
| 321 | + return hid_data, num_bits_ceil | |
| 368 | 322 | |
| 369 | 323 | def __str__(self): |
| 370 | 324 | return 'F5' | ... | ... |
msteg/steganography/F5.pyc
No preview for this file type
msteg/steganography/LSB.pyc
No preview for this file type
res/steged.jpg
test_jpeg.py
| ... | ... | @@ -17,7 +17,9 @@ sample = [[7, 12, 14, -12, 1, 0, -1, 0], |
| 17 | 17 | [0, 0, 0, 0, 0, 0, 0, 0], |
| 18 | 18 | [0, 0, 0, 0, 0, 0, 0, 0]] |
| 19 | 19 | |
| 20 | -sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, 5908L, 59816L, 56765L] | |
| 20 | +sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, | |
| 21 | + 5908L, 59816L, 56765L] | |
| 22 | + | |
| 21 | 23 | |
| 22 | 24 | def diffblock(c1, c2): |
| 23 | 25 | diff = False |
| ... | ... | @@ -32,6 +34,7 @@ def diffblock(c1, c2): |
| 32 | 34 | |
| 33 | 35 | def diffblocks(a, b): |
| 34 | 36 | diff = False |
| 37 | + cnt = 0 | |
| 35 | 38 | for comp in range(a.image_components): |
| 36 | 39 | xmax, ymax = a.Jgetcompdim(comp) |
| 37 | 40 | for y in range(ymax): |
| ... | ... | @@ -39,7 +42,8 @@ def diffblocks(a, b): |
| 39 | 42 | if a.Jgetblock(x, y, comp) != b.Jgetblock(x, y, comp): |
| 40 | 43 | print("blocks({},{}) in component {} not match".format(y, x, comp)) |
| 41 | 44 | diff = True |
| 42 | - return diff | |
| 45 | + cnt += 1 | |
| 46 | + return diff, cnt | |
| 43 | 47 | |
| 44 | 48 | |
| 45 | 49 | def test_setblocks(): |
| ... | ... | @@ -131,17 +135,18 @@ def test_rawfile(): |
| 131 | 135 | raw[i] = raw_size % 256 |
| 132 | 136 | raw_size /= 256 |
| 133 | 137 | raw = np.array(raw) |
| 134 | - print raw.shape,raw | |
| 138 | + print raw.shape, raw | |
| 135 | 139 | # print raw.size |
| 136 | 140 | # print bytes2bits(raw) |
| 137 | 141 | |
| 142 | + | |
| 138 | 143 | def test_bitbyte(): |
| 139 | 144 | timer.mark() |
| 140 | 145 | raw = np.fromfile("res/test4.jpg", np.uint8) |
| 141 | 146 | timer.report() |
| 142 | 147 | print raw |
| 143 | 148 | |
| 144 | - bitsraw = bytes2bits(raw) | |
| 149 | + bitsraw = bytes2bits(raw) | |
| 145 | 150 | # bitsraw = bitsraw[:24] |
| 146 | 151 | timer.report() |
| 147 | 152 | print bitsraw |
| ... | ... | @@ -150,6 +155,7 @@ def test_bitbyte(): |
| 150 | 155 | timer.report() |
| 151 | 156 | print bytesraw |
| 152 | 157 | |
| 158 | + | |
| 153 | 159 | def test_iter(): |
| 154 | 160 | imb = jpegObj.Jpeg("res/test4.jpg") |
| 155 | 161 | blocks = imb.getCoefBlocks(channel='Y') |
| ... | ... | @@ -186,12 +192,12 @@ if __name__ == '__main__': |
| 186 | 192 | |
| 187 | 193 | # test_bitbyte() |
| 188 | 194 | |
| 189 | - ima = jpegObj.Jpeg("res/test3.jpg",key=sample_key) | |
| 195 | + ima = jpegObj.Jpeg("res/test3.jpg", key=sample_key) | |
| 190 | 196 | # imb = jpegObj.Jpeg("res/new.jpg",key=sample_key) |
| 191 | - imc = jpegObj.Jpeg("res/steged.jpg",key=sample_key) | |
| 197 | + imc = jpegObj.Jpeg("res/steged.jpg", key=sample_key) | |
| 192 | 198 | print ima.Jgetcompdim(0) |
| 193 | - print ima.getkey(),imc.getkey() | |
| 194 | - diffblocks(ima, imc) | |
| 199 | + print ima.getkey(), imc.getkey() | |
| 200 | + print diffblocks(ima, imc) | |
| 195 | 201 | |
| 196 | 202 | # c1 = ima.getCoefBlocks() |
| 197 | 203 | # c2 = imb.getCoefBlocks() | ... | ... |
test_steg.py
| ... | ... | @@ -7,7 +7,7 @@ import pylab as plt |
| 7 | 7 | import mjpeg |
| 8 | 8 | import mjsteg |
| 9 | 9 | import jpegObj |
| 10 | -from msteg.steganography import F3, F4, LSB | |
| 10 | +from msteg.steganography import LSB, F3, F4, F5 | |
| 11 | 11 | from common import * |
| 12 | 12 | |
| 13 | 13 | |
| ... | ... | @@ -22,13 +22,17 @@ 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] | |
| 25 | +sample_key = [46812L, 20559L, 31360L, 16681L, 27536L, 39553L, 5427L, 63029L, 56572L, 36476L, 25695L, 61908L, 63014L, | |
| 26 | + 5908L, 59816L, 56765L] | |
| 26 | 27 | |
| 27 | 28 | txtsample = [116, 104, 105, 115, 32, 105, 115, 32, 116, 111, 32, 98, 101, 32, 101, 109, 98, 101, 100, 101, 100, 46, 10] |
| 28 | 29 | |
| 29 | 30 | if __name__ == '__main__': |
| 30 | - f3test = F4.F4() | |
| 31 | + # f3test = F4.F4(sample_key) | |
| 32 | + f3test = F5.F5(sample_key, 3) | |
| 31 | 33 | f3test.embed_raw_data("res/test3.jpg", "res/embeded", "res/steged.jpg") |
| 34 | + | |
| 35 | + # f3test2 = F4.F4(sample_key) | |
| 32 | 36 | f3test.extract_raw_data("res/steged.jpg", "res/extracted") |
| 33 | 37 | print f3test.get_key() |
| 34 | 38 | pass | ... | ... |