├── .gitignore ├── LICENSE ├── README.md ├── image_tools ├── __init__.py ├── downloader.py └── version.py ├── setup.cfg ├── setup.py └── tests ├── __init__.py └── test_downloader.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.pot 48 | 49 | # Django stuff: 50 | *.log 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | # PyBuilder 56 | target/ 57 | screenshots 58 | TODO.txt 59 | 60 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Amirhossein Amiri 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-image-tools 2 | The python-image-tools is a python package that allows you to easily download an image from a given URL and manipulate it. 3 | 4 | ## Installation 5 | - Run `pip install python-image-tools` 6 | 7 | ### Usage 8 | To use the class, you first need to import it: 9 | 10 | ```python 11 | from image_tools import ImageDownloader 12 | 13 | img = ImageDownloader("https://www.example.com/image.jpg") 14 | ``` 15 | ### Download an Image 16 | To download an image, you can use the `download_image` method: 17 | ```python 18 | img.download_image() 19 | ``` 20 | 21 | ### Resize an Image 22 | To resize an image, you can use the `resize_image` method, passing in the desired width and height of the image: 23 | ```python 24 | img.resize_image(500, 500) 25 | ``` 26 | 27 | ### Resize an Image by Percent 28 | To resize an image by a certain percent, you can use the `resize_image_by_percent` method, passing in the desired percent: 29 | ```python 30 | img.resize_image_by_percent(50) 31 | ``` 32 | 33 | ### Change Image format 34 | To change image format, you can use the `change_image_format` method, passing in the desired format: 35 | ```python 36 | img.change_image_format("png") 37 | ``` 38 | 39 | ### Reduce Image size 40 | To reduce image size, you can use the `reduce_image_size` method, passing in the desired size in kilobytes: 41 | ```python 42 | img.reduce_image_size(50) 43 | ``` 44 | 45 | ### Rename Image 46 | To rename image, you can use the `rename_image` method, passing in the desired name: 47 | ```python 48 | img.rename_image('test') 49 | ``` 50 | ### Note 51 | - If you want to resize the image to a specific file size, you should first reduce the image file size to your desired file size and then change the image format to a format that supports compression such as JPEG. 52 | 53 | - The class will overwrite the original image file, so make sure to save a copy of the original image before using the class. 54 | 55 | ### Support 56 | If you have any issues or questions, please don't hesitate to reach out to us at amirhamiri74@gmail.com. -------------------------------------------------------------------------------- /image_tools/__init__.py: -------------------------------------------------------------------------------- 1 | from image_tools.downloader import ImageDownloader -------------------------------------------------------------------------------- /image_tools/downloader.py: -------------------------------------------------------------------------------- 1 | import math 2 | import os 3 | import requests 4 | from PIL import Image 5 | 6 | 7 | class ImageDownloader: 8 | def __init__(self, image_url): 9 | self.current_directory = os.getcwd() 10 | self.image_url = image_url 11 | 12 | def download_image(self): 13 | image_name = self.image_url.split("/")[-1] 14 | 15 | # Combine the current working directory and image name to get the full path to save the image 16 | self.save_path = os.path.join(self.current_directory, image_name) 17 | 18 | # Make a request to download the image 19 | try: 20 | response = requests.get(self.image_url) 21 | except ConnectionError: 22 | raise Exception('Check your internet connection') 23 | 24 | # Open the file and write the content 25 | with open(self.save_path, "wb") as f: 26 | f.write(response.content) 27 | 28 | print(f"Image downloaded successfully and saved in {self.current_directory}") 29 | 30 | def resize_image(self, width, height): 31 | # Open the image file 32 | with Image.open(self.save_path) as im: 33 | # Resize the image 34 | im = im.resize((width, height)) 35 | # Save the resized image 36 | im.save(self.save_path) 37 | print(f"Image resized successfully to {width}x{height}") 38 | 39 | def resize_image_by_percent(self, percent): 40 | # Open the image file 41 | with Image.open(self.save_path) as im: 42 | original_width, original_height = im.size 43 | # Calculate the new width and height based on the percent 44 | new_width = int(original_width * percent / 100) 45 | new_height = int(original_height * percent / 100) 46 | # Resize the image 47 | im = im.resize((new_width, new_height)) 48 | # Save the resized image 49 | im.save(self.save_path) 50 | print(f"Image resized successfully to {percent}% of original size") 51 | 52 | def reduce_image_filesize(self, target_filesize): 53 | with Image.open(self.save_path) as im: 54 | original_filesize = os.path.getsize(self.save_path) 55 | current_filesize = original_filesize 56 | quality = 100 57 | while current_filesize > target_filesize * 1000: 58 | im.save(self.save_path, quality=quality) 59 | current_filesize = os.path.getsize(self.save_path) 60 | quality -= 1 61 | print(f"Image filesize reduced successfully to {target_filesize} KB") 62 | 63 | def change_image_format(self, format): 64 | with Image.open(self.save_path) as im: 65 | # get format of current image 66 | original_format = im.format 67 | # check if current format is same as desired format 68 | if original_format != format: 69 | im.save(self.save_path, format=format) 70 | print(f"Image format changed successfully to {format}") 71 | 72 | def rename_image(self, new_name): 73 | # Get the current format of the image 74 | current_format = os.path.splitext(self.save_path)[1] 75 | # Combine the new name and the current format to get the new full path 76 | new_path = os.path.join(self.current_directory, new_name + current_format) 77 | # Rename the file 78 | os.rename(self.save_path, new_path) 79 | self.save_path = new_path 80 | print(f"Image renamed successfully to {new_name}{current_format}") 81 | 82 | -------------------------------------------------------------------------------- /image_tools/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __version__ = "0.3" -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | import os, sys 4 | 5 | exec(open("image_tools/version.py").read()) 6 | 7 | github_url = "https://github.com/amirhamiri" 8 | package_name = "python-image-tools" 9 | package_url = "{}/{}".format(github_url, package_name) 10 | package_path = os.path.abspath(os.path.dirname(__file__)) 11 | long_description_file_path = os.path.join(package_path, "README.md") 12 | long_description_content_type = "text/markdown" 13 | long_description = "" 14 | try: 15 | long_description_file_options = ( 16 | {} if sys.version_info[0] < 3 else {"encoding": "utf-8"} 17 | ) 18 | with open(long_description_file_path, "r", **long_description_file_options) as f: 19 | long_description = f.read() 20 | except IOError: 21 | pass 22 | 23 | setup( 24 | name=package_name, 25 | include_package_data=True, 26 | version=__version__, 27 | description="python-image-tools is a python package for working with images", 28 | long_description=long_description, 29 | long_description_content_type=long_description_content_type, 30 | author="Amirhossein Amiri", 31 | author_email="amirhamiri74@gmail.com", 32 | url=package_url, 33 | packages=find_packages(), 34 | download_url="{}/archive/{}.tar.gz".format(package_url, __version__), 35 | project_urls={ 36 | "Documentation": "{}#readme".format(package_url), 37 | "Issues": "{}/issues".format(package_url), 38 | }, 39 | keywords=[ 40 | "image", 41 | "download", 42 | "python", 43 | "resize" 44 | ], 45 | install_requires=[ 46 | "Pillow==9.4.0", "requests==2.28.2" 47 | ], 48 | classifiers=[ 49 | "Development Status :: 5 - Production/Stable", 50 | "Intended Audience :: Developers", 51 | "License :: OSI Approved :: MIT License", 52 | "Natural Language :: English", 53 | "Operating System :: OS Independent", 54 | "Programming Language :: Python :: 3", 55 | "Programming Language :: Python :: 3.4", 56 | "Programming Language :: Python :: 3.5", 57 | "Programming Language :: Python :: 3.6", 58 | "Programming Language :: Python :: 3.7", 59 | "Programming Language :: Python :: 3.8", 60 | "Programming Language :: Python :: 3.9", 61 | "Programming Language :: Python :: 3.10", 62 | "Topic :: Software Development :: Build Tools", 63 | ], 64 | license="MIT", 65 | test_suite="runtests.runtests", 66 | ) 67 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirhamiri/python-image-tools/af0fb1d0cf07b634b3f3b0d555dfcce871cd48f1/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_downloader.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | from PIL import Image 4 | from image_tools import ImageDownloader 5 | 6 | 7 | class TestImageDownloader(unittest.TestCase): 8 | def setUp(self): 9 | # Create an instance of the ImageDownloader class 10 | self.test_image_url = 'https://i.ytimg.com/vi/4vn40Wdcsmc/maxresdefault.jpg' 11 | self.image_downloader = ImageDownloader(self.test_image_url) 12 | self.test_image_name = self.test_image_url.split("/")[-1] 13 | self.test_image_path = os.path.join(self.image_downloader.current_directory, self.test_image_name) 14 | 15 | def test_download_image(self): 16 | # Test if the image is downloaded successfully 17 | self.image_downloader.download_image() 18 | self.assertTrue(os.path.exists(self.test_image_path)) 19 | 20 | def test_resize_image(self): 21 | # Test if the image is resized successfully 22 | width = 200 23 | height = 300 24 | self.image_downloader.download_image() 25 | self.image_downloader.resize_image(width, height) 26 | with Image.open(self.test_image_path) as im: 27 | self.assertEqual(im.size, (width, height)) 28 | 29 | def test_resize_image_by_percent(self): 30 | # Download the image 31 | self.image_downloader.download_image() 32 | 33 | # Open the image and save old size 34 | with Image.open(self.image_downloader.save_path) as im: 35 | # Check if the image size is equal to the expected size 36 | old_size = im.size 37 | 38 | # Resize the image 39 | percent = 50 40 | self.image_downloader.resize_image_by_percent(percent) 41 | with Image.open(self.image_downloader.save_path) as im: 42 | # Check if the image size is equal to the expected size 43 | self.assertEqual(im.size, (old_size[0] * (percent / 100), old_size[1] * (percent / 100))) 44 | 45 | def test_resize_image_by_filesize(self): 46 | # Download the image 47 | self.image_downloader.download_image() 48 | old_size = os.path.getsize(self.image_downloader.save_path) 49 | # Resize the image 50 | new_size = 30 51 | self.image_downloader.reduce_image_filesize(new_size) 52 | 53 | # Open the image 54 | with Image.open(self.image_downloader.save_path) as im: 55 | # Check if the image size is equal to the expected size 56 | self.assertTrue(old_size > new_size) 57 | 58 | def test_change_image_format(self): 59 | # Test if the image format is changed successfully 60 | format = "PNG" 61 | self.image_downloader.download_image() 62 | self.image_downloader.change_image_format(format) 63 | with Image.open(self.test_image_path) as im: 64 | self.assertEqual(im.format, format) 65 | 66 | 67 | def tearDown(self): 68 | # Delete the tests image 69 | os.remove(self.test_image_path) 70 | 71 | 72 | if __name__ == "__main__": 73 | unittest.main() 74 | --------------------------------------------------------------------------------