Python Image Steganography – Learn How To Hide Data in Images

Python course with 57 real-time projects - Learn Python

Image Steganography is a python project in which we hide the secret message inside any image using Tkinter and the PIL module. Let’s start the development.

What is Image Steganography?

Steganography is the process of hiding a secret message which is text-based data within non-text files like image file, audio, video file etc. In such a way someone cannot know the presence of a hidden message in a particular file. The main purpose of Steganography is to maintain secret communication between two people. It generally refers to encoding and decoding. So in this image Steganography project, we will encode and decode the data into an image file.

Python Image Steganography – Project Details

Image steganography is a GUI-based project in which we are hiding a secret message within the image using encoding and decoding functions. We are creating a window in which there are two buttons: encoding and decoding.

For encoding, select any image, this image will be converted into png format. Type message in the message box then it will convert into base64, merge this encoded string into image and the user can save the image where he/she wants.

For decoding, select the image which is encoded, the base64 string will get separated by decoding, and by Tkinter module hidden text is shown in the textbox.

Project Prerequisite

This project requires good knowledge of python and the Tkinter library. Tkinter is the python binding to the Tk toolkit which is used across many programming languages for building the Graphical user interface which is a GUI.

Also, we require a PIL module. This is the images module from the pillow. The PIL module helps to open, manipulate and save many different forms of images.

Use the following command to install PIL

python -m pip install pillow

Download Image Steganography Project Code

Please download the source code of Python Image Steganography from the following link: Python Image Steganography Project

Steps to Build a Python Image Steganography Project

Technology is evolving rapidly!
Stay updated with DataFlair on WhatsApp!!

Below are steps to implement Python Image Steganography Project:

1. Import Modules.
2. Create a Function to make a main frame
3. Function to go back to the main frame
4. Function to Encoding and Decoding frame.
5. Create function for encoding image
6. Create function for decoding image
7. Function to decoding and generation of data
8. Function to modify the pixels of image
9. Function to enter the data pixels in image
10. Function to enter hidden text
11. GUI loop

Step 1- Importing Modules

#DataFlair Python Image Stegangraphy project
from tkinter import *
import tkinter.filedialog
from tkinter import messagebox
from PIL import ImageTk
from PIL import Image
from io import BytesIO
import os

Code Explanation-

  • Tkinter module – Tkinter is the standard interface in python for creating a GUI that is Graphical User Interface.
  • tkinter import * – import everything from the module.
  • tkinter.filedialog – This module is used to work with files.
  • from tkinter import messagebox – Import message box separately for showing messages on the screen.
  • PIL module – This is the images module from the pillow. The PIL module helps to open, manipulate and save many different forms of images.
  • Import ImageTk – ImageTk module used to create and modify Tkinter photoimage from PIL images.
  • io import Bytesio – Bytes data in the memory.
  • Import os – This module is used for creating and removing any directory.

Step 2- Create a Function to make a Main frame

#main frame or start page
    def main(self, root):
        root.title('ImageSteganography by DataFlair')
        root.geometry('500x600')
        root.resizable(width =False, height=False)
        root.config(bg = '#e3f4f1')
        frame = Frame(root)
        frame.grid()
       
        title = Label(frame,text='DataFlair Image Steganography')
        title.config(font=('Times new roman',25, 'bold'),bg = '#e3f4f1')
        title.grid(pady=10)
        title.grid(row=1)
 
        encode = Button(frame,text="Encode",command= lambda :self.encode_frame1(frame), padx=14,bg = '#e3f4f1' )
        encode.config(font=('Helvetica',14), bg='#e8c1c7')
        encode.grid(row=2)
        decode = Button(frame, text="Decode",command=lambda :self.decode_frame1(frame), padx=14,bg = '#e3f4f1')
        decode.config(font=('Helvetica',14), bg='#e8c1c7')
        decode.grid(pady = 12)
        decode.grid(row=3)
 
        root.grid_rowconfigure(1, weight=1)
        root.grid_columnconfigure(0, weight=1)

Code Explanation-

  • main() – Function for making the main frame.
  • root – Initializing the root window of Python Image Steganography Project.
  • .title – Use to set title to window.
  • .geometry – For setting dimensions of a window in pixels.
  • .config – Used to configure attributes to the window, such as background color.
  • .grid – for making grid frame on python image steganography project window
  • title.grid – Used to make a grid and here we are giving padding as 10 pixels. For the first row which is row equals to 1.
  • encode – encode variable creates the encode button using the command lambda: self and also gives padding as 14 and background color.
  • decode – decode variable creates the decode button using the command lambda: self and also gives padding as 14 and background color.
  • command lambda – It is used to pass data to the callback function.
  • .grid_rowconfigure() – This method configures the row index of the grid and weight determines how large the row will occupy.
  • .grid_columnconfigure() – This method configures the column index of the grid and weight determines how large the column will occupy.

Step 3- Function to go back to the main frame

#Back function to loop back to main screen
    def back(self,frame):
        frame.destroy()
        self.main(root)

Code Explanation-

  • back() – Function to get back on the main screen after encoding or decoding.
  • .destroy() – Used when the process is completed by the user, then for destroying GUI components as well as clearing the screen.

Step 4- Function to Encoding and decoding frame

#frame for encode page
    def encode_frame1(self,F):
        F.destroy()
        F2 = Frame(root)
        label1= Label(F2,text='Select the Image in which \n you want to hide text :')
        label1.config(font=('Times new roman',25, 'bold'),bg = '#e3f4f1')
        label1.grid()
 
        button_bws = Button(F2,text='Select',command=lambda : self.encode_frame2(F2))
        button_bws.config(font=('Helvetica',18), bg='#e8c1c7')
        button_bws.grid()
        button_back = Button(F2, text='Cancel', command=lambda : IMG_Stegno.back(self,F2))
        button_back.config(font=('Helvetica',18),bg='#e8c1c7')
        button_back.grid(pady=15)
        button_back.grid()
        F2.grid()
 
#frame for decode page
    def decode_frame1(self,F):
        F.destroy()
        d_f2 = Frame(root)
        lablel1 = Label(d_f2, text='Select Image with Hidden text:')
        label1.config(font=('Times new roman',25,'bold'),bg = '#e3f4f1')
        label1.grid()
        label1.config(bg = '#e3f4f1')
        button_bws = Button(d_f2, text='Select', command=lambda :self.decode_frame2(d_f2))
        button_bws.config(font=('Helvetica',18), bg='#e8c1c7')
        button_bws.grid()
        button_back = Button(d_f2, text='Cancel', command=lambda : IMG_Stegno.back(self,d_f2))
        button_back.config(font=('Helvetica',18), bg='#e8c1c7')
        button_back.grid(pady=15)
        button_back.grid()
        d_f2.grid()

Code Explanation-

  • encode_frame1() – function for making encoded frame. Using parameters self and f. First, we destroy the f frame.
  • f2- creating a new frame in an encoded frame.
  • decode_frame1() – function for making an encoded frame. Using parameters self and f. First, we destroy the f frame.
  • d_F2 – creating a new frame in the decode frame.
  • label1 – In this variable, we use the label widget for displaying the box in which we give text. Then call configure to set font, font size, and background color.
  • .grid() – make a grid of label1
  • Here we are creating two buttons: select and cancel using variable button_bws and button_back using command lambda to callback the data.
  • Then call configure to set font, font size, and background color in python image steganography project.

Step 5- Create function for encoding image

# function to encode image
    def encode_frame2(self,e_F2):
        e_pg= Frame(root)
        myfile = tkinter.filedialog.askopenfilename(filetypes = ([('png', '*.png'),('jpeg', '*.jpeg'),('jpg', '*.jpg'),('All Files', '*.*')]))
        if not myfile:
            messagebox.showerror("Error","You have selected nothing !")
        else:
            my_img = Image.open(myfile)
            new_image = my_img.resize((300,200))
            img = ImageTk.PhotoImage(new_image)
            label3= Label(e_pg,text='Selected Image')
            label3.config(font=('Helvetica',14,'bold'))
            label3.grid() 
            board = Label(e_pg, image=img)
            board.image = img
            self.output_image_size = os.stat(myfile)
            self.o_image_w, self.o_image_h = my_img.size
            board.grid()
            label2 = Label(e_pg, text='Enter the message')
            label2.config(font=('Helvetica',14,'bold'))
            label2.grid(pady=15)
            text_a = Text(e_pg, width=50, height=10)
            text_a.grid()
            encode_button = Button(e_pg, text='Cancel', command=lambda : IMG_Stegno.back(self,e_pg))
            encode_button.config(font=('Helvetica',14), bg='#e8c1c7')
            data = text_a.get("1.0", "end-1c")
            button_back = Button(e_pg, text='Encode', command=lambda : [self.enc_fun(text_a,my_img),IMG_Stegno.back(self,e_pg)])
            button_back.config(font=('Helvetica',14), bg='#e8c1c7')
            button_back.grid(pady=15)
            encode_button.grid()
            e_pg.grid(row=1)
            e_F2.destroy()

Code Explanation-

  • encode_frame2() – function for making a second encoding frame. Using parameters self and e_F2.
  • .filedialog – this function is to create a directory and also helps to open save files in our directories.
  • myfile – We create myfiles for opening the png, jpeg, jpg types of files from our directory using the tkinter tool filedialog.
  • Here we have created an if-else loop. If you haven’t selected anything from your directory. Then we pass a message box that shows an error that you have selected nothing.
  • Else we will select a proper image from our directory.
  • .open – This function is used to open files that are stored in our directory.
  • .resize – this function returns a resize copy of the selected image that we have given 300×200
  • ImageTk.PhotoImage – For displaying images on the screen.
  • label3 – In this variable, we use the label widget for displaying the box in which we give text. Then call configure to set font, font size, and background color.
  • text_a – To create a text box in which we have to add our text, having width 50 and height 10.
  • Board.image – displaying output image.
  • os.stat – this method is used to call a specific path of the image. We are setting our image on the screen.
  • label2 – label2 is created to enter the text. In this variable, we use the label widget for displaying the box in which we give text. Then call configure to set font, font size, and
  • background color. And padding of 15 pixels.
  • Here we are creating two encoding buttons: Encode and cancel using variable encode_button and button_back using command lambda to callback the data. Then call configure to set font, font size and background color.
  • Apply the grid to the e_pg image variable and then destroy the e_F2 frame.

Step 6- Create function for decoding image

# function to decode image
    def decode_frame2(self,d_F2):
        d_F3 = Frame(root)
        myfiles = tkinter.filedialog.askopenfilename(filetypes = ([('png', '*.png'),('jpeg', '*.jpeg'),('jpg', '*.jpg'),('All Files', '*.*')]))
        if not myfiles:
            messagebox.showerror("Error","You have selected nothing !")
        else:
            my_img = Image.open(myfiles, 'r')
            my_image = my_img.resize((300, 200))
            img = ImageTk.PhotoImage(my_image)
            label4= Label(d_F3,text='Selected Image :')
            label4.config(font=('Helvetica',14,'bold'))
            label4.grid()
            board = Label(d_F3, image=img)
            board.image = img
            board.grid()
            hidden_data = self.decode(my_img)
            label2 = Label(d_F3, text='Hidden data is :')
            label2.config(font=('Helvetica',14,'bold'))
            label2.grid(pady=10)
            text_a = Text(d_F3, width=50, height=10)
            text_a.insert(INSERT, hidden_data)
            text_a.configure(state='disabled')
            text_a.grid()
            button_back = Button(d_F3, text='Cancel', command= lambda :self.Page_3(d_F3))
            button_back.config(font=('Helvetica',14),bg='#e8c1c7')
            button_back.grid(pady=15)
            button_back.grid()
            d_F3.grid(row=1)
            d_F2.destroy()

Code Explanation-

  • decode_frame2() – function for making a second decoding frame using parameters self and d_F2.
  • Here we follow the same procedure that we did in encoding. step5.

Step 7- Function to decoding and generation of data

#function to decode data
    def decode(self, image):
        image_data = iter(image.getdata())
        data = ''
 
        while (True):
            pixels = [value for value in image_data.__next__()[:3] +
                      image_data.__next__()[:3] +
                      image_data.__next__()[:3]]
            # string of binary data
            binary_str = ''
            for i in pixels[:8]:
                if i % 2 == 0:
                    binary_str += '0'
                else:
                    binary_str += '1'
 
            data += chr(int(binary_str, 2))
            if pixels[-1] % 2 != 0:
                return data
 
#function to generate data
    def generate_Data(self,data):
        # list of binary codes of given data
        new_data = []
 
        for i in data:
            new_data.append(format(ord(i), '08b'))
        return new_data

Code Explanation-

  • decode() – function to decode the image using the parameters self and image.
  • iter – This iter function creates an object called image_data which iterates one element at a time.
  • Data – containing the empty string of the data object.
  • Here we are making a while loop for decoding the data. In the pixel variable, we are using a for loop for assigning a pixel to a specific image. Extracting 3 pixels at a time.
  • Binary data string stored in the object then iterate by i.
  • generate_data – for generating the data that we have passed.
  • new_data – empty list of binary code of the given data.
  • .append – use to add new data in the existing list.
  • ord – this function returns the unicode from the given character.

Step 8- Function to modify the pixels of image

#function to modify the pixels of image
    def modify_Pix(self,pix, data):
        dataList = self.generate_Data(data)
        dataLen = len(dataList)
        imgData = iter(pix)
        for i in range(dataLen):
            
            pix = [value for value in imgData.__next__()[:3] +
                   imgData.__next__()[:3] +
                   imgData.__next__()[:3]]
                        for j in range(0, 8):
                if (dataList[i][j] == '0') and (pix[j] % 2 != 0):
 
                    if (pix[j] % 2 != 0):
                        pix[j] -= 1
 
                elif (dataList[i][j] == '1') and (pix[j] % 2 == 0):
                    pix[j] -= 1
                       if (i == dataLen - 1):
                if (pix[-1] % 2 == 0):
                    pix[-1] -= 1
            else:
                if (pix[-1] % 2 != 0):
                    pix[-1] -= 1
 
            pix = tuple(pix)
            yield pix[0:3]
            yield pix[3:6]
            yield pix[6:9]

Code Explanation-

  • modify_pix() – function for modifying pixels of images. Calling the data generation function in the datalist variable.
  • len – Used to count the length of data.
  • iter – This iter function creates an object which iterates one element at a time. Here we are iterating pixels.
  • Here we are using the next for loop, in the pix object we are extracting three pixels at a time. And we are assigning pixel values 1 for odd and 0 for even.
  • We give a range from 0 to 8. The eighth pixel of every set tells whether to stop and whether to read further and 0 means keep reading until the loop ends. And 1 denotes the message is over.
  • yield – yield returns the function without destroying.

Step 9- Function to enter the data pixels in image

#function to enter the data pixels in image
    def encode_enc(self,newImg, data):
        w = newImg.size[0]
        (x, y) = (0, 0)
 
        for pixel in self.modify_Pix(newImg.getdata(), data):
 
            # Putting modified pixels in the new image
            newImg.putpixel((x, y), pixel)
            if (x == w - 1):
                x = 0
                y += 1
            else:
                x += 1

Code Explanation-

  • encode_enc – this is the function to enter the data pixels in an image.
  • .size – Is used to count the number of pixels along with the image.
  • We have created a for loop in which we have called the modify_pix function and getting data from the new image.
  • put.pixel – putting modified pixels in the new image.

Step 10- Function to enter hidden text

# function to enter hidden text
    def enc_fun(self,text_a,myImg):
        data = text_a.get("1.0", "end-1c")
        if (len(data) == 0):
            messagebox.showinfo("Alert","Kindly enter text in TextBox")
        else:
            newImg = myImg.copy()
            self.encode_enc(newImg, data)
            my_file = BytesIO()
            temp=os.path.splitext(os.path.basename(myImg.filename))[0]
            newImg.save(tkinter.filedialog.asksaveasfilename (initialfile=temp, filetypes = ([('png', '*.png')]), defaultextension=".png"))
            self.d_image_size = my_file.tell()
            self.d_image_w,self.d_image_h = newImg.size
            messagebox.showinfo("Success","Encoding Successful\nFile is saved as Image_with_hiddentext.png in the same directory")
 
    def frame_3(self,frame):
        frame.destroy()
        self.main(root)

Code Explanation-

  • enc_fun() – function for entering the hidden text in the text box.
  • .get() – get function is used to return the value of an element with a specific key. And here we are fetching data from text_a.
  • If the length of data entered is equal to zero then we pass a message box with a message to kindly enter text in the box.
  • If not then we will go in else loop and copy myimg. And using the bytesio() method we manipulate bytes of data in memory.
  • os.path.splitext() – This method is used to split the path of an image into the root and extension. Here we are giving filetype png and the extension is .png
  • .save – used to save content of the python window in the python file or text file.
  • tkinter.filedialog – This module is used to work with files.
  • .tell() – it shows the location of the handle file and also returns the position of the file object.
  • After successfully adding text and encoding the image we pass a message box with message Encoding successful, the file is saved in your directory.
  • frame_3() – this is the function of third page
  • .destroy() – Used when the process is completed by the user, then for destroying GUI components as well as clearing the screen.

Step 11- GUI loop

#GUI loop
root = Tk()
o = IMG_Stegno()
o.main(root)
root.mainloop()

Code Explanation-

  • root – Initializing the root window.
  • IMG_Stegno() – End call of the main class in which all functions are present.
  • .mainloop() – calling mainloop.

Python Image Steganography Project Output

python image steganography output

Summary

We have successfully created a python Image Steganography project using the Graphical User Interface(GUI). We have learned about the Tkinter and PIL modules. By using this, you can communicate with your friend secretly.

Did we exceed your expectations?
If Yes, share your valuable feedback on Google

follow dataflair on YouTube

12 Responses

  1. cherry says:

    NameError Traceback (most recent call last)
    Input In [27], in ()
    1 #GUI loop
    2 root = Tk()
    —-> 3 o = IMG_Stegno()
    4 o.main(root)
    5 root.mainloop()

    NameError: name ‘IMG_Stegno’ is not defined

    I’m getting above error
    can you please help me

  2. RAJDEEP DUTTA says:

    GETTING THE SAME ERROR!!!!!

  3. harsha vardhan says:

    can you make a code for video and audio

  4. Rajeshwari says:

    thank u so much…

  5. Pulti says:

    Traceback (most recent call last):
    File “/content/sample_data/ImgStegno_GUI_DataFlair.py”, line 250, in
    root = Tk()
    File “/usr/lib/python3.8/tkinter/__init__.py”, line 2270, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
    _tkinter.TclError: no display name and no $DISPLAY environment variable

    Getting above error while running file from google colab

  6. sheetal says:

    decode is not propare

  7. Kit says:

    It’s giving me an error in label widget even tho i have installed tkinter . What should i do?

  8. Kritika Anand says:

    This project is so good. I have successfully encoded the message and created an encoded image but the encoded message is not shown in the decoded message box. I was wondering if you could spare a moment to assist me with this problem. Thank you for your time and for creating such valuable educational content. I look forward to hearing from you.

Leave a Reply

Your email address will not be published. Required fields are marked *