├── .gitignore
├── LICENSE
├── README.md
├── project
├── README.md
├── bomb.mp3
├── main.py
└── treasure.jpg
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
161 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Amirreza Hashemi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Image Decoding: Unveiling Secrets with Minesweeper
2 |
3 | Welcome to the Image Decoding repository! This interactive project implements a unique puzzle-solving approach inspired by the classic game of Minesweeper. You can use this program to unravel hidden messages concealed within images of your choice.
4 |
5 |
6 | https://github.com/Amirrezahmi/Image-Decoding/assets/89692207/c05a9c88-d416-4899-8145-2a2d7155e052
7 |
8 | ## Features
9 |
10 | - Decoding images using Minesweeper-based puzzles
11 | - Dynamic and visually appealing user interface
12 | - Background color transitions for an immersive experience
13 | - Human verification through logical reasoning and strategic thinking
14 |
15 | ## Prerequisites
16 |
17 | To run the Image Decoding program, make sure you have the following installed:
18 |
19 | - Python (version 3.6 or above)
20 | - tkinter package
21 | - PIL package
22 | - pygame package
23 |
24 |
25 | ## Getting Started
26 |
27 | 1. Clone this repository to your local machine.
28 |
29 | ```bash
30 | git clone https://github.com/Amirrezahmi/Image-Decoding.git
31 | ```
32 |
33 | 2. Navigate to the project directory.
34 |
35 |
36 | ```bash
37 | cd Image-Decoding
38 | ```
39 |
40 | 3. Install the required dependencies.
41 |
42 |
43 | ```bash
44 | pip install -r requirements.txt
45 | ```
46 |
47 | 4. Launch the program.
48 |
49 | ```bash
50 | python main.py
51 | ```
52 | 5. Select your image or press "Default Picture" button to begin the decoding process.
53 |
54 | ## How to Play
55 |
56 | 1. Left Click: Click on a cell with the left mouse button to reveal its content.
57 | - Pay attention to the number displayed on the revealed cell. This number represents the number of adjacent cells containing clues.
58 | - Utilize the clues to strategically identify safe cells and avoid hidden mines.
59 | 2. Right Click: Click on a cell with the right mouse button to mark it with a flag.
60 | - Flagging a cell indicates your suspicion of a mine.
61 | - Use this feature to mark cells you believe may contain mines, allowing you to avoid them during the decoding process.
62 | - $\textbf{Important}$: When you flag a cell correctly, the corresponding part of the image will be decoded, revealing a portion of the hidden message. However, if you flag a cell incorrectly, that part of the image won't decode, emphasizing that blindly flagging all cells won't help you uncover the entire secret message.
63 |
64 |
65 |

66 |
67 |
68 |
69 |
70 | 3. Decoding Strategy: Combine the information from the revealed numbers and your flagged cells to make informed decisions.
71 | - Deduce the contents of neighboring cells based on the numbers displayed.
72 | - Exercise caution! If you click on a cell containing a mine without flagging it, the game will restart, and you'll need to begin the decoding process again.
73 | 4. First Click: The first click on the game board is always safe, ensuring you have a chance to observe the initial clue and plan your decoding strategy accordingly.
74 | 5. Winning Condition: Uncover all non-mine cells to successfully decode the image and reveal its secret message.
75 |
76 |
77 | ## Human Verification
78 |
79 | Beyond the exciting decoding adventure, this program can also serve as a human verification tool. By requiring users to employ logical reasoning and strategic thinking to decode the image, it ensures that they are real people interacting with the system.
80 |
81 | ## Contributing
82 |
83 | Contributions are welcome! If you have any ideas, suggestions, or bug fixes, please open an issue or submit a pull request.
84 |
85 |
86 | ## License
87 |
88 | This project is licensed under the [MIT License](https://opensource.org/license/mit/).
89 |
90 |
91 | ## Acknowledgments
92 |
93 | The Minesweeper game concept and image decoding inspiration
94 |
95 |
96 | ## Contact
97 |
98 |
99 | For any inquiries or feedback, please contact amirrezahmi2002@gmail.com.
100 |
101 | Enjoy the adventure of image decoding!
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/project/README.md:
--------------------------------------------------------------------------------
1 | This directory contains the main Python code, a sample treasure picture, and the bomb sound.
2 |
--------------------------------------------------------------------------------
/project/bomb.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amirrezahmi/Image-Decoding/efe1387d09a872818a8f43391164e7b766e6114c/project/bomb.mp3
--------------------------------------------------------------------------------
/project/main.py:
--------------------------------------------------------------------------------
1 | from tkinter import *
2 | from PIL import Image, ImageTk, ImageFilter
3 | import random
4 | import colorsys
5 | from tkinter.filedialog import askopenfilename
6 | import pygame
7 |
8 |
9 | pygame.mixer.init()
10 |
11 | # Create the window
12 | window = Tk()
13 | window.title("Image Decoding")
14 | window.resizable(False, False)
15 |
16 | # Get the screen width and height
17 | screen_width = window.winfo_screenwidth()
18 | screen_height = window.winfo_screenheight()
19 |
20 | # Set the size and position of the first window
21 | window_width = 512
22 | window_height = 512
23 | window_x = (screen_width - window_width) // 2
24 | window_y = (screen_height - window_height) // 2
25 | window.geometry(f"{window_width}x{window_height}+{window_x}+{window_y}")
26 |
27 | # Define the starting HSL values for background color
28 | hsl = [0, 0.5, 0.3] # Adjust the lightness value (range: 0.0 to 1.0)
29 |
30 | # Define the color transition step size
31 | step = 0.001
32 |
33 | # Function to update the background color
34 | def update_bg_color():
35 | # Convert HSL to RGB
36 | rgb = colorsys.hls_to_rgb(hsl[0], hsl[2], hsl[1])
37 |
38 | # Convert RGB to hexadecimal color code
39 | hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}"
40 |
41 | # Set the window background color
42 | window.configure(bg=hex_color)
43 |
44 | # Update the HSL values for the next color
45 | hsl[0] += step
46 | hsl[0] %= 1.0
47 |
48 | # Schedule the next color update
49 | window.after(20, update_bg_color)
50 |
51 | # Start the color update loop
52 | update_bg_color()
53 |
54 | # Function to handle button 1 (Default Picture)
55 | def use_default_picture():
56 | global image
57 | # Load the default picture
58 | image = Image.open("treasure.jpg").convert("RGBA")
59 | image = image.resize((512, 512))
60 | window.destroy() # Close the window and proceed to the main window
61 |
62 | # Function to handle button 2 (Upload a Picture)
63 | def upload_picture():
64 | global image
65 | # Open a file dialog to select an image file
66 | filename = askopenfilename(filetypes=[("Image Files", "*.png;*.jpg;*.jpeg;*.gif")])
67 | if filename:
68 | # Load the selected image
69 | image = Image.open(filename).convert("RGBA")
70 | image = image.resize((512, 512))
71 | window.destroy() # Close the window and proceed to the main window
72 |
73 | # Create the buttons in the window
74 | default_button = Button(window, text="Default Picture", command=use_default_picture)
75 | default_button.pack(pady=10)
76 | upload_button = Button(window, text="Upload a Picture", command=upload_picture)
77 | upload_button.pack(pady=10)
78 |
79 | # Start the main loop
80 | window.mainloop()
81 |
82 | # After the window is closed, continue with the main program
83 |
84 | # Load the image and create the mask
85 | blurred_image = image.filter(ImageFilter.GaussianBlur(radius=20))
86 |
87 | # Create the main window
88 | window = Tk()
89 | window.title("Image decoding")
90 | window.resizable(False, False)
91 | # Define the starting HSL values
92 | hsl = [0, 0.5, 0.3] # Adjust the lightness value (range: 0.0 to 1.0)
93 |
94 | # Define the color transition step size
95 | step = 0.001
96 |
97 | # Function to update the background color
98 | def update_bg_color():
99 | # Convert HSL to RGB
100 | rgb = colorsys.hls_to_rgb(hsl[0], hsl[2], hsl[1])
101 |
102 | # Convert RGB to hexadecimal color code
103 | hex_color = f"#{int(rgb[0] * 255):02x}{int(rgb[1] * 255):02x}{int(rgb[2] * 255):02x}"
104 |
105 | # Set the window background color
106 | window.configure(bg=hex_color)
107 |
108 | # Update the HSL values for the next color
109 | hsl[0] += step
110 | hsl[0] %= 1.0
111 |
112 | # Schedule the next color update
113 | window.after(20, update_bg_color)
114 |
115 | # Start the color update loop
116 | update_bg_color()
117 | screen_width = window.winfo_screenwidth()
118 | screen_height = window.winfo_screenheight()
119 |
120 | # Set the window size to cover the entire screen
121 | window.geometry(f"{screen_width}x{screen_height}")
122 | left_frame = Frame(window)
123 | right_frame = Frame(window)
124 | left_frame.pack(side=LEFT)
125 | right_frame.pack(side=RIGHT)
126 | canvas = Canvas(right_frame, width=image.width, height=image.height)
127 | canvas.pack()
128 |
129 | # Calculate the number of cells in the game board based on the size of the image
130 | cell_size = 30
131 | num_cells_x = image.width // cell_size
132 | num_cells_y = image.height // cell_size
133 |
134 | # Create the game board buttons
135 | buttons = [[None for _ in range(num_cells_x)] for _ in range(num_cells_y)]
136 |
137 | first_click = True
138 | def start_game():
139 | global image_tk, mask, board, mines, first_click
140 |
141 | # Create the mask
142 | mask = Image.new("L", image.size, 0)
143 |
144 | # Display the image on the canvas
145 | image_tk = ImageTk.PhotoImage(blurred_image)
146 | canvas.create_image(0, 0, anchor=NW, image=image_tk)
147 |
148 | # Create the game board
149 | board = [[0 for _ in range(num_cells_x)] for _ in range(num_cells_y)]
150 | mines = random.sample([(i, j) for i in range(num_cells_x) for j in range(num_cells_y)], 10)
151 |
152 | # Move a mine from the randomly selected cell to a different cell
153 | if first_click:
154 | random_cell = random.choice(mines)
155 | new_cell = random.choice([(i, j) for i in range(num_cells_x) for j in range(num_cells_y) if (i, j) not in mines])
156 | mines.remove(random_cell)
157 | mines.append(new_cell)
158 |
159 | for mine in mines:
160 | board[mine[0]][mine[1]] = -1
161 |
162 | # Reset the game board buttons
163 | for i in range(num_cells_x):
164 | for j in range(num_cells_y):
165 | button = Button(window, text=" ")
166 | button.bind("", lambda event, i=i, j=j: open_cell(i, j))
167 | button.bind("", lambda event, i=i, j=j: flag_cell(i, j))
168 | button.place(x=i * cell_size, y=j * cell_size, width=cell_size, height=cell_size)
169 | buttons[i][j] = button
170 |
171 | first_click = True
172 |
173 |
174 |
175 | def open_cell(i, j):
176 | global first_click
177 |
178 | # If the cell is flagged, do nothing
179 | if buttons[i][j]['text'] == "🚩":
180 | return
181 |
182 | # If the cell is flagged or a mine (and not the first click), game over
183 | if board[i][j] == -1 and not first_click:
184 | # Display the bomb symbol and play the bomb sound
185 | buttons[i][j].config(text="💣", fg="red")
186 | pygame.mixer.music.load("bomb.mp3")
187 | pygame.mixer.music.play()
188 |
189 | # Restart the puzzle
190 | start_game()
191 | return
192 |
193 | if buttons[i][j]['state'] == DISABLED or (board[i][j] == -1 and not first_click):
194 | return
195 |
196 | if first_click:
197 | first_click = False
198 | # Ensure the first click is not a mine
199 | while board[i][j] == -1:
200 | start_game()
201 | # Open some cells around the first click
202 | for x in range(max(0, i - 1), min(num_cells_x, i + 2)):
203 | for y in range(max(0, j - 1), min(num_cells_y, j + 2)):
204 | open_cell(x, y)
205 |
206 | # Calculate the number of adjacent mines
207 | num_mines = sum(board[x][y] == -1 for x in range(max(0, i - 1), min(num_cells_x, i + 2)) for y in
208 | range(max(0, j - 1), min(num_cells_y, j + 2)))
209 |
210 | # Update the button text and color
211 | buttons[i][j].config(text=str(num_mines),
212 | fg="red" if num_mines == 1 else "blue" if num_mines == 2 else "purple" if num_mines == 3 else "black")
213 |
214 | # Update the mask and blend it with the original image
215 | mask.paste(255, (i * cell_size, j * cell_size, (i + 1) * cell_size, (j + 1) * cell_size))
216 | revealed_image = Image.composite(image, blurred_image, mask)
217 |
218 | # Update the image on the canvas
219 | global image_tk
220 | image_tk = ImageTk.PhotoImage(revealed_image)
221 | canvas.create_image(0, 0, anchor=NW, image=image_tk)
222 |
223 | def flag_cell(i, j):
224 | # If the cell is already opened or flagged, do nothing
225 | if buttons[i][j]['text'] != " ":
226 | return
227 |
228 | # Flag the cell
229 | buttons[i][j].config(text="🚩", state=DISABLED)
230 |
231 | # Check if the flagged cell is a bomb
232 | if (i, j) in mines:
233 | # Update the mask and blend it with the original image
234 | mask.paste(255, (i * cell_size, j * cell_size, (i + 1) * cell_size, (j + 1) * cell_size))
235 | revealed_image = Image.composite(image, blurred_image, mask)
236 |
237 | # Update the image on the canvas
238 | global image_tk
239 | image_tk = ImageTk.PhotoImage(revealed_image)
240 | canvas.create_image(0, 0, anchor=NW, image=image_tk)
241 |
242 |
243 | start_game()
244 | window.mainloop()
--------------------------------------------------------------------------------
/project/treasure.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Amirrezahmi/Image-Decoding/efe1387d09a872818a8f43391164e7b766e6114c/project/treasure.jpg
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pygame==2.0.1
2 | Pillow==9.5.0
3 | tkinter==8.6
--------------------------------------------------------------------------------