1
+ import numpy as np
2
+
3
+ from constants .constants import *
4
+ from utils .bit_storing import store_bits , retrieve_bits
5
+ from utils .conversor import conv
6
+ from utils .ctxt import *
7
+ from utils .progress_bar import progress_bar
8
+
9
+ def inject_file (img_arr :np .ndarray , file :str , filename :str , store_random :bool ) -> np .ndarray :
10
+ print (ctxt ("\n Preparing...\n " , Fore .MAGENTA ))
11
+
12
+ img_arr_flat = img_arr .flatten ()
13
+
14
+ #-------------------------------------------------------
15
+ # Get the maximum size the file can have
16
+ max_file_size = len (img_arr_flat ) - MAX_FN_SIZE_BIN - 1
17
+ # Get the length of the maximum size in binary
18
+ max_file_size_len = len (conv (max_file_size , 2 ))
19
+ # Get the available pixels for storing the file
20
+ available_channels = max_file_size - max_file_size_len
21
+ #-------------------------------------------------------
22
+ # Get the base to use and the mask
23
+ times = available_channels // len (file )
24
+ if times < 1 :
25
+ needed_img_size = (len (file ) + MAX_FN_SIZE_BIN + 1 + 50 )/ 3 # 50 is a really big image size ((2^50)/3 pixels)
26
+ raise Exception (f"Image is too small to store the file. Needed size: { ctxt (f'{ round (needed_img_size / 1000000 ,4 )} MP' , Fore .YELLOW )} " )
27
+ times = times if times <= 4 else 4
28
+ base_exp = 5 - times
29
+ base = 2 ** base_exp
30
+ mask = 256 - base
31
+ #-------------------------------------------------------
32
+ # Convert the filename to base 2 and fill it left with 0s
33
+ filename_conv = conv (int (filename ,16 ), 2 ).zfill (MAX_FN_SIZE_BIN )
34
+
35
+ # Convert the file to base {base}
36
+ file_conv = conv (int (file ,16 ), base )
37
+
38
+ # Get the file size and convert it to base 2
39
+ file_size = len (file_conv )
40
+ file_size_conv = conv (file_size , 2 ).zfill (max_file_size_len ) # image resolution in pixels
41
+ #-------------------------------------------------------
42
+ print (f"Modified bits per channel: { ctxt (base_exp , Fore .YELLOW )} " )
43
+ print (f"Image modification: { ctxt (f'{ round (base / 256 * 100 , 2 )} %' , Fore .YELLOW )} \n " )
44
+ #-------------------------------------------------------
45
+ # (1) Store the (base exponent) in idx 0 (e.g. 3 = 2^3 = 8)
46
+ img_arr_flat [0 ] = store_bits (img_arr_flat [0 ], base_exp , MAX_BASE_EXP_MASK )
47
+ idx = 1
48
+ # Store the file size in the next {max_file_size_len} pixels
49
+ for i ,h in enumerate (file_size_conv ):
50
+ img_arr_flat [idx + i ] = store_bits (img_arr_flat [idx + i ], int (h , 2 ), 256 - 2 )
51
+ idx += len (file_size_conv )
52
+ #-------------------------------------------------------
53
+ # Store the filename
54
+ for i ,h in enumerate (filename_conv ):
55
+ img_arr_flat [idx + i ] = store_bits (img_arr_flat [idx + i ], int (h , 2 ), 256 - 2 )
56
+ idx += len (filename_conv )
57
+
58
+ # Store the input file
59
+ for i in range (len (file_conv )):
60
+ if i % 10000 == 0 or i == len (file_conv )- 1 :
61
+ progress_bar (i / (len (file_conv )- 1 ), ctxt ("Storing..." , Fore .GREEN ))
62
+ img_arr_flat [idx + i ] = store_bits (img_arr_flat [idx + i ], int (file_conv [i ], base ), mask )
63
+ idx += len (file_conv )
64
+ #-------------------------------------------------------
65
+ # Store random values on the rest of the pixels
66
+ if store_random :
67
+ print (ctxt ("\n Generating random values..." , Fore .MAGENTA ))
68
+ rands = list (np .random .randint (low = 0 , high = base , size = len (img_arr_flat [idx :])))
69
+ print (ctxt ("\n Storing random values..." , Fore .MAGENTA ))
70
+ img_arr_flat [idx :] = np .array ([store_bits (x , rands [i ], mask ) for i ,x in enumerate (img_arr_flat [idx :].tolist ())])
71
+ #-------------------------------------------------------
72
+ # Return flat_img to original shape
73
+ print (ctxt ("\n Reshaping..." , Fore .MAGENTA ))
74
+ img_arr = img_arr_flat .reshape (img_arr .shape )
75
+
76
+ return img_arr
77
+
78
+
79
+
80
+ def extract_file (mod_img_arr_flat :np .ndarray ) -> str :
81
+ print (ctxt ("\n Preparing..." , Fore .MAGENTA ))
82
+
83
+ # Get the maximum size the file can have
84
+ max_file_size = len (mod_img_arr_flat ) - MAX_FN_SIZE_BIN - 1
85
+ # Get the length of the maximum size in binary
86
+ max_file_size_len = len (conv (max_file_size , 2 ))
87
+
88
+ # Get the base
89
+ base_exp = retrieve_bits (mod_img_arr_flat [0 ], MAX_BASE_EXP_BASE )
90
+ base = 2 ** base_exp
91
+ idx = 1
92
+
93
+ # Get the file size
94
+ file_size_str = "" .join ([f'{ retrieve_bits (x , 2 ):b} ' for x in mod_img_arr_flat [idx :idx + max_file_size_len ]])
95
+ file_size = int (file_size_str , 2 )
96
+ idx += max_file_size_len
97
+
98
+ # Get the filename
99
+ print (ctxt ("\n Retrieving filename..." , Fore .MAGENTA ))
100
+ filename_str = "" .join ([f'{ retrieve_bits (x , 2 ):b} ' for x in mod_img_arr_flat [idx :idx + MAX_FN_SIZE_BIN ]])
101
+ filename_hex = f"{ int (filename_str , 2 ):x} "
102
+ idx += MAX_FN_SIZE_BIN
103
+
104
+ # Get the file
105
+ print (ctxt ("\n Retrieving input file..." , Fore .MAGENTA ))
106
+ file_str = "" .join ([f'{ retrieve_bits (x , base ):x} ' for x in mod_img_arr_flat [idx :idx + file_size ]])
107
+ file_hex = f"{ int (file_str , base ):x} "
108
+
109
+ # Return the filename and the input file
110
+ return {
111
+ "filename" : bytes .fromhex (filename_hex ),
112
+ "file" : bytes .fromhex (file_hex )
113
+ }
0 commit comments