diff --git a/ImageInputHandler/ImageInputHandler.py b/ImageInputHandler/ImageInputHandler.py new file mode 100644 index 0000000..379b576 --- /dev/null +++ b/ImageInputHandler/ImageInputHandler.py @@ -0,0 +1,301 @@ +import numpy as np +import cv2 +import base64 +import urllib3 +import re +from PIL import Image, UnidentifiedImageError +import io + +class UniversalImageInputHandler: + def __init__(self, img_input, img_is_a_mask=False, debug=False): + self.img_input = img_input + self.img_is_a_mask = img_is_a_mask + self.img = None + self.COMPATIBLE = False + self.debug=debug + + if self.debug: + print("debug On") + + self.read_image() + + def adjust_image_channels(self, img): + if img.ndim == 3 and img.shape[2] == 4: + img = img[:, :, :3] # Remove the alpha channel if present + if self.img_is_a_mask and img.ndim == 3: + img = img[:, :, 0] # Use the first channel for masks + return img + + def read_image(self): + + if self.debug: + print("checking image input type") + print(self.img_input) + + if isinstance(self.img_input, np.ndarray): + + if self.debug : + print("input is ndarray") + self.process_image(self.img_input) + elif isinstance(self.img_input, str): + if self.debug: + print("input is string") + if self.is_url(self.img_input): + if self.debug: + print("input is url") + self.handle_url_image(self.img_input) + elif self.is_path(self.img_input): + if self.debug: + print("input is path") + self.handle_path_image(self.img_input) + elif self.is_base64(self.img_input): + if self.debug: + print("input is base64 image") + self.handle_base64_image(self.img_input) + + def handle_url_image(self, url): + try: + user_agent = {'user-agent': 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) ..'} + http = urllib3.PoolManager(10, headers=user_agent) + response = http.urlopen('GET', url) + image = Image.open(io.BytesIO(response.data)) + img_arr = np.array(image) + self.process_image(img_arr) + except (UnidentifiedImageError, urllib3.exceptions.HTTPError) as e: + print(f"Failed to load image from URL: {e}") + + def handle_path_image(self, path): + img = cv2.imread(path, cv2.IMREAD_UNCHANGED) + if img is not None: + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + self.process_image(img) + else: + print("Failed to load image from path.") + + def handle_base64_image(self, encoded_img): + try: + decoded_img = base64.b64decode(encoded_img) + img_np_arr = np.frombuffer(decoded_img, np.uint8) + img = cv2.imdecode(img_np_arr, cv2.IMREAD_UNCHANGED) + self.process_image(img) + except ValueError: + print("Invalid Base64 encoding.") + + def process_image(self, img): + img = self.adjust_image_channels(img) + self.img = img + self.COMPATIBLE = True + + # def is_path(self, s): + # path_regex = re.compile( + # r'^(/|\\|[a-zA-Z]:\\|\.\\|..\\|./|../)' + # r'(?:(?:[^\\/:*?"<>|\r\n]+\\|[^\\/:*?"<>|\r\n]+/)*' + # r'[^\\/:*?"<>|\r\n]*)$', + # re.IGNORECASE) + # return re.match(path_regex, s) is not None + + def is_path(self, s): + path_regex = re.compile( + r'^(/|\\|[a-zA-Z]:\\|\.\\|..\\|./|../)?' # Optional start with /, \, C:\, .\, ..\, ./, or ../ + r'(?:(?:[^\\/:*?"<>|\r\n]+\\|[^\\/:*?"<>|\r\n]+/)*' # Directory names + r'[^\\/:*?"<>|\r\n]*)$', # Last part of the path which can be a file + re.IGNORECASE) + return re.match(path_regex, s) is not None + + def is_url(self, s): + url_regex = re.compile( + r'^(https?://|ftp://)' + r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' + r'localhost|' + r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' + r'(?::\d+)?' + r'(?:/?|[/?]\S+)$', re.IGNORECASE) + return re.match(url_regex, s) is not None + + def is_base64(self, s): + try: + s = s.strip() + if len(s) % 4 != 0: + return False + base64.b64decode(s, validate=True) + return True + except ValueError: + return False + + + + + + + + + + + + + + + + + + +# import urllib3 +# import logging +# from PIL import Image,UnidentifiedImageError +# import io +# import re +# import numpy as np +# import base64 +# import cv2 +# +# +# +# +# +# class ImageObject: +# def __init__(self, img_input, img_is_a_mask=False): +# self.IMG_IS_NP_ARRAY = False +# self.IMG_HAS_3_CHANNEL = False +# self.IMG_HAS_4_CHANNEL = False +# self.IMG_IS_BASE64_ENCODED = False +# self.IMG_IS_LINK = False +# self.img_input = img_input +# self.img_is_a_mask = img_is_a_mask +# self.COMPATIBLE = False +# self.img_input_format = False +# +# def is_numpy_array(self, img): +# return isinstance(img, np.ndarray) +# +# def has_three_channels(self, img): +# return img.ndim == 3 and img.shape[-1] == 3 +# +# def has_four_channels(self, img): +# return img.ndim == 3 and img.shape[-1] == 4 +# +# def read_image(self): +# pass +# +# def load_img(self, path, COLORTRANSFORMATION): +# temp = cv2.imread(path, cv2.IMREAD_UNCHANGED) +# temp = cv2.cvtColor(temp, COLORTRANSFORMATION) +# return temp +# +# def read_img_with_user_agent(self, url): +# user_agent = {'user-agent': 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) ..'} +# http = urllib3.PoolManager(10, headers=user_agent) +# r1 = http.urlopen('GET', url) +# LINK_IS_IMAGE = False +# im2arr = [] +# try: +# image = Image.open(io.BytesIO(r1.data)) +# im2arr = np.array(image) +# LINK_IS_IMAGE = True +# except UnidentifiedImageError: +# pass +# return LINK_IS_IMAGE, im2arr, +# +# def is_path(self, s): +# # Regular expression for validating file paths +# path_regex = re.compile( +# r'^(/|\\|[a-zA-Z]:\\|\.\\|..\\|./|../)' # Starts with /, \, C:\, .\, ..\, ./, or ../ +# r'(?:(?:[^\\/:*?"<>|\r\n]+\\|[^\\/:*?"<>|\r\n]+/)*' # Directory names +# r'[^\\/:*?"<>|\r\n]*)$', # Last part of the path +# re.IGNORECASE) +# return re.match(path_regex, s) is not None +# +# def is_url(self, s): +# # A simple regular expression for validating URLs +# url_regex = re.compile( +# r'^(https?://|ftp://)' # HTTP, HTTPS, or FTP protocols +# r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|' # domain +# r'localhost|' # localhost +# r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or IP +# r'(?::\d+)?' # optional port +# r'(?:/?|[/?]\S+)$', re.IGNORECASE) +# return re.match(url_regex, s) is not None +# +# def is_base64(self, s): +# """Check if the input string is Base64-encoded.""" +# try: +# s = s.strip() +# if len(s) % 4 != 0: +# return False +# base64.b64decode(s, validate=True) +# return True +# except ValueError: +# return False +# +# def decode_img(self, encoded_img, mask=False): +# +# if self.is_base64(encoded_img): +# decoded_img = base64.b64decode(encoded_img) +# img_np_arr = np.frombuffer(decoded_img, np.uint8) +# if mask: +# img = cv2.imdecode(img_np_arr, cv2.IMREAD_UNCHANGED) +# if img is not None and len(img.shape) == 3 and img.shape[2] == 2: +# pass # Not sure what 'pass' is intended for, possibly apply some processing here? +# else: +# img = img[:, :, 2] # Assuming this intends to extract a specific channel, needs more clarification. +# else: +# img = cv2.imdecode(img_np_arr, cv2.IMREAD_COLOR) +# return True, img +# else: +# return False, None +# +# def encode_img(self, img): +# _, img_buffer = cv2.imencode('.webp', img) +# encoded_img = base64.b64encode(img_buffer) +# # return encoded_img +# return encoded_img.decode('utf-8') +# +# def read_img_input(self): +# if isinstance(self.img_input, str): +# if self.is_url(self.img_input): +# LINK_IS_IMAGE, img = self.read_img_with_user_agent(self.img_input) +# if LINK_IS_IMAGE: +# if self.has_four_channels(img): +# img = img[:, :, :3] +# self.img = img +# elif self.has_three_channels(img): +# self.img = img +# if self.img_is_a_mask: +# self.img = self.img[:, :, 0] +# +# self.COMPATIBLE = True +# else: +# pass +# else: +# is_img_input_a_base64_img, img = self.decode_img(self.img_input) +# if is_img_input_a_base64_img: +# if self.has_four_channels(img): +# img = img[:, :, :3] +# self.img = img +# elif self.has_three_channels(img): +# self.img = img +# if self.img_is_a_mask: +# self.img = self.img[:, :, 0] +# self.COMPATIBLE = True +# elif self.is_path(self.img_input): +# img = self.load_img(self.img_input, cv2.COLOR_BGR2RGB) +# if self.has_four_channels(img): +# img = img[:, :, :3] +# self.img = img +# elif self.has_three_channels(img): +# self.img = img +# if self.img_is_a_mask: +# self.img = self.img[:, :, 0] +# self.COMPATIBLE = True +# +# elif isinstance(self.img_input, np.ndarray): +# img=self.img_input +# if self.has_four_channels(img): +# img = img[:, :, :3] +# self.img = img +# elif self.has_three_channels(img): +# self.img = img +# if self.img_is_a_mask: +# self.img = self.img[:, :, 0] +# self.COMPATIBLE = True +# +# diff --git a/ImageInputHandler/__init__.py b/ImageInputHandler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c79a15 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ + Image Input Handler + +`image-input-handler` is a versatile Python package designed to simplify the handling of various image inputs. It supports images from multiple sources including file paths, URLs, and base64 encoded strings. It adjusts image channels according to user needs and includes functionality for checking compatibility and enabling debug mode, making it an essential tool for projects involving image processing. + +## Features + +- Handle images from URLs, file paths, and base64 strings. +- Automatic adjustment of image channels. +- Support for image masks. +- Compatibility checks to ensure image inputs can be processed. +- Debug mode for detailed operational logging, aiding in troubleshooting and development. +- Easy integration with popular libraries such as NumPy, OpenCV, and PIL. + +## Installation + +Install `image-input-handler` using pip: + +```bash``` pip install image-input-handler + +# Usage +Below is a basic example of how to use image-input-handler: +``` +from image_input_handler import UniversalImageInputHandler + +# Initialize the handler with a local file path, enabling debug mode +handler = UniversalImageInputHandler('path/to/your/image.png', debug=True) + +# For URL based images +url_handler = UniversalImageInputHandler('http://example.com/image.png') + +# For base64 encoded images +base64_handler = UniversalImageInputHandler('base64_encoded_string_here', img_is_a_mask=True) + +# Access the processed image +processed_image = handler.img + +# Check if the image is compatible +if handler.COMPATIBLE: + print("The image is compatible.") +else: + print("The image is not compatible.") + + + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..78b4801 --- /dev/null +++ b/setup.py @@ -0,0 +1,60 @@ +from setuptools import setup, find_packages +import setuptools +import subprocess +import os + +# cf_remote_version = ( +# subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE) +# .stdout.decode("utf-8") +# .strip() +# ) +# +# if "-" in cf_remote_version: +# # when not on tag, git describe outputs: "1.3.3-22-gdf81228" +# # pip has gotten strict with version numbers +# # so change it to: "1.3.3+22.git.gdf81228" +# # See: https://peps.python.org/pep-0440/#local-version-segments +# v,i,s = cf_remote_version.split("-") +# cf_remote_version = v + "+" + i + ".git." + s +# +# assert "-" not in cf_remote_version +# assert "." in cf_remote_version +# +# assert os.path.isfile("cf_remote/version.py") +# with open("cf_remote/VERSION", "w", encoding="utf-8") as fh: +# fh.write("%s\n" % cf_remote_version) +# +# with open("README.md", "r", encoding="utf-8") as fh: +# long_description = fh.read() + + + + + + +setup( + name='universal_image_input_handler', # Package name + version='0.1.0', # Version of your package + author='Enes Kuzucu', # Your name + + description='A module to handle different formats of image input', # Short description + long_description=open('README.md').read(), # Long description from a README file + long_description_content_type='text/markdown', # Type of the long description + url='https://github.com/karaposu/image-input-handler', # URL to the repository + packages=find_packages(), # Automatically find packages in the directory + install_requires=[ + 'numpy', 'opencv-python', 'Pillow', 'urllib3', 'base64', 're' # List of dependencies + ], + classifiers=[ + 'Development Status :: 3 - Alpha', # Development status + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', # License as you choose + 'Programming Language :: Python :: 3', # Supported Python versions + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Operating System :: OS Independent', + ], + python_requires='>=3.7', # Minimum version requirement of Python +)