From 9e4df438dfa1a4fd5c0fe06a5954d3d03048bdef Mon Sep 17 00:00:00 2001 From: devoalda Date: Tue, 23 May 2023 11:53:11 +0800 Subject: [PATCH] Updated image Steg to allow for multiple chosen bits as bits to use for hiding --- Image_Steg/img_steg.py | 202 ++++++++++++++++++++++------------------- Image_Steg/pokemon.png | Bin 0 -> 4242 bytes 2 files changed, 110 insertions(+), 92 deletions(-) create mode 100644 Image_Steg/pokemon.png diff --git a/Image_Steg/img_steg.py b/Image_Steg/img_steg.py index 4aaf542..ad1a303 100644 --- a/Image_Steg/img_steg.py +++ b/Image_Steg/img_steg.py @@ -3,84 +3,26 @@ import numpy as np class img_steg: - def __init__(self, image_name: str = "image.png", bit_to_hide: int = 2): + def __init__(self, image_name: str = "image.png", bit_to_hide: list[int] = None): """ Initialize the class :param image_name: Name of the image to encode or decode :type image_name: str - :param bit_to_hide: Bit to hide the data in (1 - LSB to 8 - MSB) - :type bit_to_hide: int + :param bit_to_hide: List of bit position to hide the data (LSB is 1, MSB is 8) + :type bit_to_hide: list[int] """ self.image_name = image_name - self.bit_to_hide = 8 - bit_to_hide - self.delimiter = "\n" - - def to_bin(self, data: str) -> str | list[str]: - """ - Convert data to binary format as string - Args: - data: String - - Returns: - Binary Data: String | List[String] - """ - if isinstance(data, str): - return ''.join([format(ord(i), "08b") for i in data]) - elif isinstance(data, bytes) or isinstance(data, np.ndarray): - return [format(i, "08b") for i in data] - elif isinstance(data, int) or isinstance(data, np.unit8): - return format(data, "08b") - else: - raise TypeError("Type not supported") - - def from_bin(self, data: str) -> str: - """ - Convert binary `data` back to the original format - Args: - data: String - - Returns: - Original Data: String - """ - return ''.join([chr(int(data[i:i + 8], 2)) for i in range(0, len(data), 8)]) - - def decode(self) -> str: - """ - Decode the data hidden in the image - Args: - None - :return: Decoded Data: String - """ - print("[+] Decoding...") - image = cv2.imread(self.image_name) # read image - binary_data = "" - bit_to_hide = self.bit_to_hide - for row in image: - for pixel in row: - r, g, b = self.to_bin(pixel) - binary_data += r[bit_to_hide] - binary_data += g[bit_to_hide] - binary_data += b[bit_to_hide] - # 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 - decoded_data = "" - for byte in all_bytes: - decoded_data += chr(int(byte, 2)) - if decoded_data[-len(self.delimiter):] == self.delimiter: - break - - return decoded_data[:-len(self.delimiter)] + self.bit_to_hide = [8 - bit_pos for bit_pos in bit_to_hide] + self.delimiter = "=====" def encode(self, secret_data: str = "Hello World") -> np.ndarray: """ - Encode the secret data in the image - Args: - secret_data: str - - Returns: - Encoded Image: np.ndarray - """ + Encode the secret data in the image + :param secret_data: Data to hide in the image + :type secret_data: str + :return: Encoded Image + :rtype: np.ndarray + """ image = cv2.imread(self.image_name) # read image n_bytes = image.shape[0] * image.shape[1] * 3 // 8 # Max bytes to encode print("[*] Maximum bytes to encode:", n_bytes) @@ -97,43 +39,119 @@ class img_steg: for row in image: for pixel in row: r, g, b = self.to_bin(pixel) # Convert RGB Values to binary format - if data_index < data_len: - r = list(r) - r[bit_to_hide] = binary_secret_data[data_index] # hide data into least significant bit of red pixel - pixel[0] = int(''.join(r), 2) - data_index += 1 - if data_index < data_len: - g = list(g) - g[bit_to_hide] = binary_secret_data[data_index] - pixel[1] = int(''.join(g), 2) - data_index += 1 - if data_index < data_len: - b = list(b) - b[bit_to_hide] = binary_secret_data[data_index] - pixel[2] = int(''.join(b), 2) - data_index += 1 + for bit_pos in bit_to_hide: + if data_index < data_len: + r = list(r) + r[bit_pos] = binary_secret_data[ + data_index] # hide data into specified bit position of red pixel + pixel[0] = int(''.join(r), 2) + data_index += 1 + if data_index < data_len: + g = list(g) + g[bit_pos] = binary_secret_data[ + data_index] + pixel[1] = int(''.join(g), 2) + data_index += 1 + if data_index < data_len: + b = list(b) + b[bit_pos] = binary_secret_data[ + data_index] + pixel[2] = int(''.join(b), 2) + data_index += 1 + if data_index >= data_len: + break + + pixel[0] = int(''.join(r), 2) # convert modified binary back to integer for red pixel + pixel[1] = int(''.join(g), 2) # convert modified binary back to integer for green pixel + pixel[2] = int(''.join(b), 2) # convert modified binary back to integer for blue pixel + if data_index >= data_len: break return image + def decode(self) -> str: + """ + Decode the data hidden in the image + + :return: Decoded Data: String + """ + print("[+] Decoding...") + image = cv2.imread(self.image_name) # read image + binary_data = "" + bit_to_hide = self.bit_to_hide + for row in image: + for pixel in row: + r, g, b = self.to_bin(pixel) + for bit_pos in bit_to_hide: + binary_data += r[bit_pos] # retrieve data from specified bit position of red pixel + binary_data += g[bit_pos] # retrieve data from specified bit position of green pixel + binary_data += b[bit_pos] # retrieve data from specified bit position of blue pixel + + # 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 + decoded_data = "" + for byte in all_bytes: + decoded_data += chr(int(byte, 2)) + if decoded_data[-len(self.delimiter):] == self.delimiter: + break + + return decoded_data[:-len(self.delimiter)] + + def to_bin(self, data: str) -> str | list[str]: + """ + Convert data to binary format as string + + :param data: Data to convert + :type data: str + :return: Binary Data + :rtype: str | list[str] + """ + if isinstance(data, str): + return ''.join([format(ord(i), "08b") for i in data]) + elif isinstance(data, bytes) or isinstance(data, np.ndarray): + return [format(i, "08b") for i in data] + elif isinstance(data, int) or isinstance(data, np.unit8): + return format(data, "08b") + else: + raise TypeError("Type not supported") + + def from_bin(self, data: str) -> str: + """ + # UNUSED + Convert binary `data` back to the original format + + :param data: Binary Data + :type data: str + :return: Original Data + :rtype: str + """ + return ''.join([chr(int(data[i:i + 8], 2)) for i in range(0, len(data), 8)]) + def main(): + # Variables + image_name = "pokemon.png" + encoded_image_name = "encoded_image.png" + secret_data = "" + with open("../Txt_Steg/test.txt", "r") as f: + secret_data = f.read() + bit_to_hide = [1, 2, 3, 4, 5, 6] + print("Welcome to Image Steganography") print("1. Encode\n2. Decode\n3. Exit") choice = int(input("Enter your choice: ")) + if choice == 1: - image_name = input("Enter name of image to encode: ") - secret_data = input("Enter data to encode: ") - bit_to_hide = int(input("Enter bit to hide (1 - LSB to 8 - MSB): ")) - encoded_image = img_steg(image_name, bit_to_hide).encode(secret_data) + encoded_image = img_steg(image_name=image_name, bit_to_hide=bit_to_hide).encode(secret_data) extension = image_name.split(".")[-1] cv2.imwrite("encoded_image." + extension, encoded_image) print("Image Encoded Successfully") elif choice == 2: - image_name = input("Enter name of image to decode: ") - bit_to_hide = int(input("Enter bit to hide (1 - LSB to 8 - MSB): ")) - decoded_data = img_steg(image_name, bit_to_hide).decode() + decoded_data = img_steg(image_name=image_name, bit_to_hide=bit_to_hide).decode() print("Decoded Data:", decoded_data) + else: + exit(0) if __name__ == "__main__": @@ -151,7 +169,7 @@ if __name__ == "__main__": # Initialise class with image name and bit to hide # # image_name = image name with extension (String) -# bit_to_hide = bit to hide the data in (1 - LSB to 8 - MSB) (int) +# bit_to_hide = [1, 2, 3] (Please Sort the list in ascending order) # # To encode: # encoded_image = img_steg(image_name, bit_to_hide).encode(secret_data) diff --git a/Image_Steg/pokemon.png b/Image_Steg/pokemon.png new file mode 100644 index 0000000000000000000000000000000000000000..1712e56414e1e72e4ee03ee251c2374ccd60915f GIT binary patch literal 4242 zcmV;D5N+>?P)>|jw0{`wJ%en&p@FLGc5dZQc{r&&*;2qJx2lnG0^Yio8#Q=_j z66xyi>)IdX)E?N_*eE3ho|6@degK1c5yiF|_TmMimH?@n0MOCb*T@{buNmUv;>*j+ zz`($bjg7Xpwl_C7c6N4ycmPdA0DgXcp`oF%q!(6JRwpMX#m3HHR{*cDykuiyac2NJ zGyokP9Y`qyy$1@4i5CTj|KSV@DSyB}l8X+7S z13EY+ou8>pMj%T}Q+0A)cyU8xW`BW$ly7o|iGVGQiE)jGS#D}cl8=L9U`%9RGIee! ze|>VcsW^syJpcd?pp`i`9s(s302>VeSTYA91OPf015jEwglz_v00`L>40k99sjV14 zL~3;a0G1^QsQ?IeMhjvo2xkBVKL7x6Pz+&DPisU&q=yvi=f#vZ3#K6eheI@tT~(5G zaiX4ny}GBEkyWUlQhp-ma{vGi&`Cr=RCt`tT>XFBMj4OPJDpTnk|pa>S@Mh2CT<+t zo$aV}na9@B46-_uCTU(6gRY~^1}%(1VL(^@c24h-eX`}3F`gg#`J|HWzW2PpT(Yro zb9?L7&5i#LYOyT)-kr{7`NsxAXp0T#_V$m@oiZEKqyCT0oV^&!pz*=*f46Um@_$<~ zmeGfx@zcW}9K>d;yLX#x_wmm3v-e^wA_JAiNBtiVM|pGm_Q%nF6fCXi*{;Yi43gUS zD?b#H?K^CWHYu+}8gs+L!Z4z~yH6A5sCj%f89safd@KwA&d=6u;N{js zmOXGJGr>aV{&x5jhhP|_AFS7+x}UJDCJRtFn9UXf`gZWxf*}?Lpz6-r=$$5_Z$&fc0-_|_x#Mo&V)vN2n_x$p1z&rOhEVFdLT>rk3kU?TiFN){E2 zd^eXO-KxPum3Y;n5VKtE9w6vS=anrQv zd0q@Az(8|pFq@ySR+i7e4xnt${nk^N-XvofFj!K7Z?U`j#A>KlKv@DJDsv!CVF91c z<0zF{zcHoBgNXwV%AGQ%IaX_)4((qSDbzVXrKA{S4&k?595scW&l=`jXQMgw}N9D6he=UDKSCbJ5{q&#Ui9i002R%ANMz&>a@oqx08`$dAQT&YAIur za-H#T1f~L$TWXH$H9C+G4y-xnn4?8uIy>eK2l{fHND->FJJBxv`)zqrHZhiqdGYs`^+1Jzq%Vg=s^1S? zSZVk{RiOo`5Xc zFL>T2;K|{61I`w=^msOmfGXRLDJ`zVLVFxar_#Mp^VB4Ut`@8}c4(1%9s&I_K(bGe z%smQpiX|n@`gEu)K*(FZ*>SO0fT@C3D5oy%E$L;1lm6Jy=kvq|J}Q(SRjNx!$Rog; z5-NI*^Fku(^2>T(piNf-Id9_2LRc2c?-l((dQ*AsgyX`n+h_uULz z^n$H}01I@zZiSb6-J-OvLblX@;{~|#Pspn-S^x_y=;<~ppCkXsQP+Hn9pz+2;;zp) zf6#H)&q)i)NO)4P*VOdb0Pu})noz#Y-pxrN#2X&x%KiW-=s_040)(StP&WFUx2o=A zVSnO{q7L%-;x*$Q293(FA3E2L02FI-JHKSi(opqQRR-*XXJN}a&#{B3zy9nQd*Cx< zHeUdOy4*V&cMEr_p8AqUb@sPk{LbHowNvgc0UQu8U)=lRUZzbCY|c)8BL3xfUwHKVZooR&vuHeo0#F+lJNt{aYAa#q z4M+p_`MocnKQq!n0PeC6QaK8=Zan|;-k0n_*!BR}oYXCV7@d1^w%uH!P5XUxGQji( zcL#=@F(QC1JN+}ljnPQ6V!;WcjF12lu`%ilmmEu7N<^}Pj|Uo)t5dOD>>Um^R%IT_8-R-Lg%e9wQ!UF88EJT)TnRUtUsz98yXpr%G>MKw? zUlB|S%e6nnz5>_=mL%PA>k?5T<&uai%%23a2o z%oVOkl>ZX1``Rv41p=H{QN|JY50&4qU`(xe(W>xiJIx;Pk$y!z`Himz2H;sRMRs~Z&o{AB@Xy)55PDk-h@!DU;pRI`dhik>AL_q+x?<-Aqe4r z1W3O7=Zvd11?e(xdI|V6!vM~iz9G#=@~<_hOTV5UnY9!rJWoQ0EdBBNtAF2GgMTTV z=PQe#rF@ED=0!|YT|RHh(7u|F1z1U!1K9cela^0y)6|?bcL&%=Ix9y0P#pDEZq#ke zmXLSgq?r7U)ElUVFP5mLV6nOmnb^Zus1$r#OjnLu1+c?CD9X6wN$+spo(Z z!f7JNQC$?xYZz!Pm3x|!?Hvf>$Z6zWkN{2~1cCYjOx2eJ%g|5|?tOT$fUufYPZkZ& zM*+nQZjl2krxE~8U~1xssxB$f3j9Z$$Ovu8alS8(rTk7L*5Udrfktrc22uA)>_?y} z6yRc?Kpc&!00kHr*R5igkpddZx+!8eSSLtJ1dX0R#xkNsX=ubsgv{$|T>$(&rNYJIn zzMh?{FNx2^rj1pMWepn~w`eKbssoHwLRbpdOD5jg{X!ony3<(ns9sVGZ!uX`aot#A z5^&hVekVg!^^h}gL}LsxY(*l3rf=M!UgWhUUGtMfB`&> zF-Wt$;$CN_eVr7gHj0i_e=(ku2g~%x${)$R-^_@?2 z@W}5?@p8Q(WxyaUWD9vGXc%#Fb@|`%-5RJ7XNYl2k|K^K$}vT9s&ddJv$RY<$g3#* z10+z?>Y~4tn5Ll{Ho%B&6F9nLPk0eUF&ZHjd8B6;)7E|GWMDWB=jMXj=5HM~4hQP6^#9(mS0U0c9u3;F}YQ4c__+lBV z?+{c~j&x-e0fk@;Il#hmZO>cb5trtS(tf(N)#`7xx?@91u;`}m5FB0(n6efq8yh=q zH57x7Ma6Dv77GL;dhfGZ0tITEuSKpL_J3t}9Z*O#aCzT@a08;C-ood8*$RjUVbphV zzS#Oa4iUh4qIz#OjF2hL`MUmU%5Y$LfywQ@Jvljk`+c)-h&2GPgihWaRgzSs@;M^^ z4GBivYMxe!h>TufVb!zs{mGQd<8!y2{ArJy7bn|}y8qO@S