-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsteganography_advanced.py
149 lines (140 loc) · 6.43 KB
/
steganography_advanced.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import cv2
import numpy as np
import os
def to_bin(data):
"""Convert `data` to binary format as string"""
if isinstance(data, str):
return ''.join([ format(ord(i), "08b") for i in data ])
elif isinstance(data, bytes):
return ''.join([ format(i, "08b") for i in data ])
elif isinstance(data, np.ndarray):
return [ format(i, "08b") for i in data ]
elif isinstance(data, int) or isinstance(data, np.uint8):
return format(data, "08b")
else:
raise TypeError("Type not supported.")
def encode(image_name, secret_data, n_bits=2):
# read the image
image = cv2.imread(image_name)
# maximum bytes to encode
n_bytes = image.shape[0] * image.shape[1] * 3 * n_bits // 8
print("[*] Maximum bytes to encode:", n_bytes)
print("[*] Data size:", len(secret_data))
if len(secret_data) > n_bytes:
raise ValueError(f"[!] Insufficient bytes ({len(secret_data)}), need bigger image or less data.")
print("[*] Encoding data...")
# add stopping criteria
if isinstance(secret_data, str):
secret_data += "====="
elif isinstance(secret_data, bytes):
secret_data += b"====="
data_index = 0
# convert data to binary
binary_secret_data = to_bin(secret_data)
# size of data to hide
data_len = len(binary_secret_data)
for bit in range(1, n_bits+1):
for row in image:
for pixel in row:
# convert RGB values to binary format
r, g, b = to_bin(pixel)
# modify the least significant bit only if there is still data to store
if data_index < data_len:
if bit == 1:
# least significant red pixel bit
pixel[0] = int(r[:-bit] + binary_secret_data[data_index], 2)
elif bit > 1:
# replace the `bit` least significant bit of the red pixel with the data bit
pixel[0] = int(r[:-bit] + binary_secret_data[data_index] + r[-bit+1:], 2)
data_index += 1
if data_index < data_len:
if bit == 1:
# least significant green pixel bit
pixel[1] = int(g[:-bit] + binary_secret_data[data_index], 2)
elif bit > 1:
# replace the `bit` least significant bit of the green pixel with the data bit
pixel[1] = int(g[:-bit] + binary_secret_data[data_index] + g[-bit+1:], 2)
data_index += 1
if data_index < data_len:
if bit == 1:
# least significant blue pixel bit
pixel[2] = int(b[:-bit] + binary_secret_data[data_index], 2)
elif bit > 1:
# replace the `bit` least significant bit of the blue pixel with the data bit
pixel[2] = int(b[:-bit] + binary_secret_data[data_index] + b[-bit+1:], 2)
data_index += 1
# if data is encoded, just break out of the loop
if data_index >= data_len:
break
return image
def decode(image_name, n_bits=1, in_bytes=False):
print("[+] Decoding...")
# read the image
image = cv2.imread(image_name)
binary_data = ""
for bit in range(1, n_bits+1):
for row in image:
for pixel in row:
r, g, b = to_bin(pixel)
binary_data += r[-bit]
binary_data += g[-bit]
binary_data += b[-bit]
# split by 8-bits
all_bytes = [ binary_data[i: i+8] for i in range(0, len(binary_data), 8) ]
# convert from bits to characters
if in_bytes:
# if the data we'll decode is binary data,
# we initialize bytearray instead of string
decoded_data = bytearray()
for byte in all_bytes:
# append the data after converting from binary
decoded_data.append(int(byte, 2))
if decoded_data[-5:] == b"=====":
# exit out of the loop if we find the stopping criteria
break
else:
decoded_data = ""
for byte in all_bytes:
decoded_data += chr(int(byte, 2))
if decoded_data[-5:] == "=====":
break
return decoded_data[:-5]
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Steganography encoder/decoder, this Python scripts encode data within images.")
parser.add_argument("-t", "--text", help="The text data to encode into the image, this only should be specified for encoding")
parser.add_argument("-f", "--file", help="The file to hide into the image, this only should be specified while encoding")
parser.add_argument("-e", "--encode", help="Encode the following image")
parser.add_argument("-d", "--decode", help="Decode the following image")
parser.add_argument("-b", "--n-bits", help="The number of least significant bits of the image to encode", type=int, default=2)
args = parser.parse_args()
if args.encode:
# if the encode argument is specified
if args.text:
secret_data = args.text
elif args.file:
with open(args.file, "rb") as f:
secret_data = f.read()
input_image = args.encode
# split the absolute path and the file
path, file = os.path.split(input_image)
# split the filename and the image extension
filename, ext = file.split(".")
output_image = os.path.join(path, f"{filename}_encoded.{ext}")
# encode the data into the image
encoded_image = encode(image_name=input_image, secret_data=secret_data, n_bits=args.n_bits)
# save the output image (encoded image)
cv2.imwrite(output_image, encoded_image)
print("[+] Saved encoded image.")
if args.decode:
input_image = args.decode
if args.file:
# decode the secret data from the image and write it to file
decoded_data = decode(input_image, n_bits=args.n_bits, in_bytes=True)
with open(args.file, "wb") as f:
f.write(decoded_data)
print(f"[+] File decoded, {args.file} is saved successfully.")
else:
# decode the secret data from the image and print it in the console
decoded_data = decode(input_image, n_bits=args.n_bits)
print("[+] Decoded data:", decoded_data)