├── .gitignore
├── LICENSE
├── README.md
├── amongus_images
├── Banana.webp
├── Black.webp
├── Blue.webp
├── Brown.webp
├── Coral.webp
├── Cyan.webp
├── Gray.webp
├── Green.webp
├── Lime.webp
├── Maroon.webp
├── Orange.webp
├── Pink.webp
├── Purple.webp
├── Red.webp
├── Rose.webp
├── Skin.png
├── Tan.webp
├── White.webp
├── Yellow.webp
├── kekw.webp
└── shaddizzy.webp
├── assets
├── lonamisa.png
└── monalisa.jpg
├── output.png
├── requirements.txt
└── src
├── __pycache__
└── main.cpython-39.pyc
└── main.py
/.gitignore:
--------------------------------------------------------------------------------
1 | env/
2 | images/
3 | .vscode/
4 | .env
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2022 Dhravya Shah
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
SussyImage
4 |

5 |
6 |
7 | Converts an image into funny, smaller amongus characters
8 |
9 |
10 |
11 | ***
12 |
13 | ### Demo
14 |
15 | Mona Lisa | Lona Misa (Made up of AmongUs characters)
16 |
17 |
18 | ### API
19 | I've also added an API that you can use to convert images to amongus characters - it's completely free and open source on the `api` branch.
20 |
21 | POST https://sussy.api.dhravya.dev (Pass input_image as a base64 encoded image in the body)
22 |
23 | You can also specify the `atol` by passing it as a query parameter.
24 |
25 | Docs - https://sussy.api.dhravya.dev/docs
26 |
27 |
28 | ### Installation
29 | ```
30 | git clone https://github.com/Dhravya/sussyimage
31 | cd SussyImage
32 | pip install -r requirements.txt
33 | ```
34 |
35 | ### Usage
36 | ```
37 | python ./src/main.py
38 | ```
39 |
40 | ### License
41 | This project is licensed under the MIT license
42 | ### Show your support
43 | Leave a ⭐ if you like this project
44 |
45 | ***
46 | Readme made with 💖 using [README Generator by Dhravya Shah](https://github.com/Dhravya/readme-generator)
--------------------------------------------------------------------------------
/amongus_images/Banana.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Banana.webp
--------------------------------------------------------------------------------
/amongus_images/Black.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Black.webp
--------------------------------------------------------------------------------
/amongus_images/Blue.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Blue.webp
--------------------------------------------------------------------------------
/amongus_images/Brown.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Brown.webp
--------------------------------------------------------------------------------
/amongus_images/Coral.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Coral.webp
--------------------------------------------------------------------------------
/amongus_images/Cyan.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Cyan.webp
--------------------------------------------------------------------------------
/amongus_images/Gray.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Gray.webp
--------------------------------------------------------------------------------
/amongus_images/Green.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Green.webp
--------------------------------------------------------------------------------
/amongus_images/Lime.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Lime.webp
--------------------------------------------------------------------------------
/amongus_images/Maroon.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Maroon.webp
--------------------------------------------------------------------------------
/amongus_images/Orange.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Orange.webp
--------------------------------------------------------------------------------
/amongus_images/Pink.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Pink.webp
--------------------------------------------------------------------------------
/amongus_images/Purple.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Purple.webp
--------------------------------------------------------------------------------
/amongus_images/Red.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Red.webp
--------------------------------------------------------------------------------
/amongus_images/Rose.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Rose.webp
--------------------------------------------------------------------------------
/amongus_images/Skin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Skin.png
--------------------------------------------------------------------------------
/amongus_images/Tan.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Tan.webp
--------------------------------------------------------------------------------
/amongus_images/White.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/White.webp
--------------------------------------------------------------------------------
/amongus_images/Yellow.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/Yellow.webp
--------------------------------------------------------------------------------
/amongus_images/kekw.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/kekw.webp
--------------------------------------------------------------------------------
/amongus_images/shaddizzy.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/amongus_images/shaddizzy.webp
--------------------------------------------------------------------------------
/assets/lonamisa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/assets/lonamisa.png
--------------------------------------------------------------------------------
/assets/monalisa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/assets/monalisa.jpg
--------------------------------------------------------------------------------
/output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/output.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | opencv-python==4.5.5.64
2 | numpy==1.22.3
3 | rich==12.2.0
4 | fastapi==0.75.1
5 | uvicorn
6 |
--------------------------------------------------------------------------------
/src/__pycache__/main.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dhravya/SussyImage/cb4b5f7186181975c019327737aa27c573ddb71a/src/__pycache__/main.cpython-39.pyc
--------------------------------------------------------------------------------
/src/main.py:
--------------------------------------------------------------------------------
1 | """
2 | Converts an image into funny, smaller amongus characters
3 | """
4 |
5 | import os
6 | from typing import List
7 |
8 | import numpy as np
9 | from rich import print
10 | from rich.progress import track
11 | from cv2 import imread, imwrite, imshow, resize, waitKey, destroyAllWindows, INTER_AREA
12 |
13 |
14 | class SussyImage:
15 | """
16 | Makes the input image into smaller amongus characters (or any other emoji/pictures you want)
17 |
18 | Attributes
19 | ----------
20 | i_image (np.ndarray): The input image
21 | emoji_size (int): The size of the emoji
22 | width (int): The width of the output image
23 | emoji_path (str): The path to the emoji images
24 | emojis (List[np.ndarray]): The emojis
25 |
26 | Methods
27 | -------
28 | run(): Runs the program
29 | __check_if_close_and_replace(): Checks if the image color is similar to the average color of the input image. if yes, replaces it.
30 | initialise_picture(): Resizes image to a width. Defaults to 3000, Returns the resized image
31 | initialise_emojis(): Returns a list of cv2.imread()'ed emojis
32 | """
33 |
34 | def __init__(
35 | self,
36 | /,
37 | input_img_path: os.PathLike,
38 | images_path: os.PathLike = "amongus_images/",
39 | width: int = 3000,
40 | emoji_size=30,
41 | ) -> None:
42 | # Check if the input image exists
43 | if not os.path.exists(input_img_path):
44 | print(f"[bold red]Input image does not exist:[/bold red] {input_img_path}")
45 | exit()
46 |
47 | # Check if the images folder exists
48 | if not os.path.exists(images_path):
49 | print(f"[bold red]Images folder does not exist:[/bold red] {images_path}")
50 | exit()
51 |
52 | self.i_path = input_img_path
53 | self.emoji_path = images_path
54 |
55 | self.emoji_size = emoji_size
56 | self.width = width
57 |
58 | # Initialises the image and "emojis"
59 | self.input_image = self.initialise_picture()
60 | self.emojis = self.initialise_emojis()
61 |
62 | def initialise_emojis(self) -> List[np.ndarray]:
63 | """Returns a list of cv2.imread()'ed emojis"""
64 | images = [self.emoji_path + image for image in os.listdir(self.emoji_path)]
65 | size = self.emoji_size, self.emoji_size
66 | for image in images:
67 | if not os.path.exists(image):
68 | print("Image does not exist: {}".format(image))
69 | exit()
70 | images = [
71 | resize(imread(image), size, interpolation=INTER_AREA) for image in images
72 | ]
73 |
74 | return images
75 |
76 | def initialise_picture(self) -> np.ndarray:
77 | """Resizes image to a width provided"""
78 | input_image = imread(self.i_path)
79 |
80 | input_image = resize(
81 | input_image,
82 | (
83 | int((self.width / input_image.shape[0]) * input_image.shape[1]),
84 | self.width,
85 | ),
86 | )
87 |
88 | return input_image
89 |
90 | def _check_if_close_and_replace(
91 | self,
92 | image: np.ndarray,
93 | /,
94 | average_col: float,
95 | output_img: np.ndarray,
96 | dim: tuple,
97 | avgs: dict,
98 | atol: float = 50,
99 | ) -> None:
100 | """Checks if the image color is similar to the average color of the input image. if yes, replaces it.
101 |
102 | Args:
103 | ----------
104 | image (np.ndarray): The input image
105 | average_col (float): Average color of the input image
106 | output_img (np.ndarray): The image on which the image/emoji will be placed
107 | dim (tuple): The dimensions of the image/emoji
108 | avgs (dict): The average colors of the emojis, so we don't have to calculate them every time
109 | atol (float, optional): The tolerance level. Defaults to 50.
110 | """
111 | i, j = dim
112 |
113 | average_col_image = avgs[str(image)]
114 |
115 | if np.allclose(average_col, average_col_image, atol=atol):
116 | try:
117 | output_img[i : i + self.emoji_size, j : j + self.emoji_size] = image
118 | except Exception:
119 | pass
120 |
121 | def run(
122 | self,
123 | atol: float = 50,
124 | /,
125 | show: bool = False,
126 | save: bool = True,
127 | save_path="output.png",
128 | compare: bool = False
129 | ) -> np.ndarray:
130 | """Runs the entire program
131 |
132 | Args:
133 | ----------
134 | atol (float, optional): Average tolerance. Defaults to 50.
135 | show (bool, optional): Whether or not to show the image after completion. Defaults to False.
136 | save (bool, optional): Whether of not to save the image. Saves to the provided save_path. Defaults to False.
137 | save_path (str, optional): Where to save the output. Defaults to "output.png".
138 | compare (bool, optional): Stacks the input image and the output image side by side. Defaults to False.
139 |
140 | Returns:
141 | ----------
142 | (np.ndarray, None): Either returns the array of the output image, or None if save is True
143 | """
144 | input_image = self.input_image
145 | images = self.emojis
146 |
147 | get_average_color = lambda image: np.average(np.mean(image, axis=0), axis=0)
148 |
149 | output_img = np.zeros(input_image.shape, dtype=np.uint8)
150 | avgs = {str(image): get_average_color(image) for image in images}
151 |
152 | # Loops through 50 * 50 pixels of the input image
153 | for i in track(
154 | range(0, input_image.shape[0], self.emoji_size),
155 | description="⚡ Making the image...",
156 | transient=True,
157 | ):
158 | for j in range(0, input_image.shape[1], self.emoji_size):
159 | # Gets the average color of the input image
160 | average_col = get_average_color(
161 | input_image[i : i + self.emoji_size, j : j + self.emoji_size]
162 | )
163 |
164 | for image in images:
165 | self._check_if_close_and_replace(
166 | image,
167 | average_col=average_col,
168 | output_img=output_img,
169 | dim=(i, j),
170 | avgs=avgs,
171 | atol=atol,
172 | )
173 |
174 | if compare:
175 | output_img = np.hstack((input_image, output_img))
176 |
177 | if show:
178 | imshow("Output", output_img)
179 | waitKey(0)
180 | destroyAllWindows()
181 |
182 | if save:
183 | imwrite(save_path, output_img)
184 | print(
185 | f"[green] Saved the image to [underlined]{save_path}[/underlined][/green]"
186 | )
187 | return None
188 |
189 | return output_img
190 |
191 |
192 | if __name__ == "__main__":
193 | sussy = SussyImage(input_img_path="./assets/monalisa.jpg", emoji_size=15)
194 |
195 | sussy.run(30, compare=True)
--------------------------------------------------------------------------------