├── .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 | License: MIT 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) --------------------------------------------------------------------------------