diff --git a/custom_lib/Custom_Lib.py b/custom_lib/Custom_Lib.py index b2f7835..ae7679b 100644 --- a/custom_lib/Custom_Lib.py +++ b/custom_lib/Custom_Lib.py @@ -1,21 +1,27 @@ -__version__ = "1.0.0" +__version__ = "1.0.2" # --- custom --- from custom_lib import my_file_config from custom_lib import my_logger from custom_lib import my_uuid +# 20260527 tak add my image +from custom_lib import my_image MODULES = [ my_file_config, my_logger, my_uuid, + my_image, ] def print_versions(): for module in MODULES: version = getattr(module, "__version__", "unknown") - print(f"[MODULE] {module.__name__} v{version}") + print(f"# [MODULE] {module.__name__} v{version}") def init(): - print(f"Custom_Lib v{__version__}") - print_versions() \ No newline at end of file + print("\n" + "#" * 60) + print(f"# Custom_Lib v{__version__}") + print("=" * 60) + print_versions() + print("#" * 60) \ No newline at end of file diff --git a/custom_lib/__pycache__/Custom_Lib.cpython-39.pyc b/custom_lib/__pycache__/Custom_Lib.cpython-39.pyc deleted file mode 100644 index 2279c45..0000000 Binary files a/custom_lib/__pycache__/Custom_Lib.cpython-39.pyc and /dev/null differ diff --git a/custom_lib/__pycache__/my_file_config.cpython-39.pyc b/custom_lib/__pycache__/my_file_config.cpython-39.pyc deleted file mode 100644 index aa25096..0000000 Binary files a/custom_lib/__pycache__/my_file_config.cpython-39.pyc and /dev/null differ diff --git a/custom_lib/__pycache__/my_logger.cpython-39.pyc b/custom_lib/__pycache__/my_logger.cpython-39.pyc deleted file mode 100644 index a5a78e4..0000000 Binary files a/custom_lib/__pycache__/my_logger.cpython-39.pyc and /dev/null differ diff --git a/custom_lib/__pycache__/my_uuid.cpython-39.pyc b/custom_lib/__pycache__/my_uuid.cpython-39.pyc deleted file mode 100644 index f20eae2..0000000 Binary files a/custom_lib/__pycache__/my_uuid.cpython-39.pyc and /dev/null differ diff --git a/custom_lib/my_image.py b/custom_lib/my_image.py new file mode 100644 index 0000000..8a4e688 --- /dev/null +++ b/custom_lib/my_image.py @@ -0,0 +1,478 @@ +__version__ = "1.0.0" + +# --- package --- +# --- color --- +import webcolors + +# --- image --- +from pathlib import Path +from PIL import Image, ImageSequence +import cv2 +import numpy as np +from io import BytesIO + +# --- base64 --- +import base64 + +# --- common --- +import os +from datetime import datetime +import shutil +import time +import inspect + +def hex_to_rgb(hex_code): + """Convert a hex code to RGB values.""" + hex_code = hex_code.lstrip('#') + return tuple(int(hex_code[i:i+2], 16) for i in (0, 2, 4)) + +def rgb_to_hex(rgb_values): + """Convert RGB values to a hex code.""" + r, g, b = rgb_values + hex_code = "{:02x}{:02x}{:02x}".format(r, g, b) + return hex_code + +def get_color_name(rgb_values): + """Return the name of the closest matching color.""" + min_colors = {} + for key, name in webcolors.CSS3_HEX_TO_NAMES.items(): + r_c, g_c, b_c = hex_to_rgb(key) + rd = (r_c - rgb_values[0]) ** 2 + gd = (g_c - rgb_values[1]) ** 2 + bd = (b_c - rgb_values[2]) ** 2 + min_colors[(rd + gd + bd)] = name + + return min_colors[min(min_colors.keys())] + +def image_resize(import_path, import_raw_path, export_path, img, resize_target, resize_resolution): + image_ext = os.path.splitext(img)[1].split('.')[1] + + move_date = datetime.now().strftime('%y%m%d%H%M%S') + + img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') + + shutil.move(img, f"{import_path}{img_new_nm}") + + image = cv2.imread(f'{import_path}{img_new_nm}') + + img_width = image.shape[1] + img_height = image.shape[0] + + if resize_target == 'W': + resize_height = int(img_height * (resize_resolution / img_width)) + dim = (resize_resolution, resize_height) + + elif resize_target == 'H': + resize_width = int(img_width * (resize_resolution / img_height)) + dim = (resize_width, resize_resolution) + + resized_img = cv2.resize(image, dim, interpolation=cv2.INTER_AREA) + + cv2.imwrite(f"{export_path}export_{move_date}.{image_ext}", resized_img) + + move_date = datetime.now().strftime('%y%m%d%H%M%S') + + img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') + + shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}") + + time.sleep(1) + + return 'Success Image Resize' + +def distribute_serveral_png(import_path, import_raw_path, export_path, img): + image_ext = os.path.splitext(img)[1].split('.')[1] + + if image_ext == 'png': + move_date = datetime.now().strftime('%y%m%d%H%M%S') + + img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') + + shutil.move(img, f"{import_path}{img_new_nm}") + + pc_sizes = [16, 24, 32, 48, 64, + 72, 80, 96, 128, 256] + and_sizes = [36, 48, 72, 96, 144] + ios_sizes = [20, 29, 40, 40, 48, + 50, 55, 57, 58, 60, + 60, 72, 76, 80, 87, + 88, 100, 114, 120, 144, + 152, 167, 172, 180, 196, + 1024] + + android_names = ['launcher_ldpi', + 'launcher_mdpi', + 'launcher_hdpi', + 'launcher_xhdpi', + 'launcher_xxhdpi'] + ios_names = ['icon-20', + 'icon-29', + 'icon-20@2x', + 'icon-40', + 'icon-24@2x', + 'icon-50', + 'icon-27.5@2x', + 'icon', + 'icon-29@2x', + 'icon-20@3x', + 'icon-60', + 'icon-72', + 'icon-76', + 'icon-40@2x', + 'icon-83.5@2x', + 'icon-44@2x', + 'icon-50@2x', + 'icon@2x', + 'icon-60@2x', + 'icon-72@2x', + 'icon-76@2x', + 'icon-29@3x', + 'icon-86@2x', + 'icon-60@3x', + 'icon-98@2x', + 'icon-1024'] + + pc_icon_sizes = [(x, x) for x in pc_sizes] + and_icon_sizes = [(x, x) for x in and_sizes] + ios_icon_sizes = [(x, x) for x in ios_sizes] + + image = Image.open(f'{import_path}{img_new_nm}') + + for size in pc_icon_sizes: + exp_img_new_nm = f"{export_path}export_{move_date}_{str(size[0])}.{image_ext}" + new_image = image.resize(size) + new_image.save(exp_img_new_nm) + + new_logo_ico_filename = f"{export_path}export_{move_date}_main.ico" + new_logo_ico = image.resize(size) + new_logo_ico.save(new_logo_ico_filename, format="ICO", quality=100) + + for num, label in zip(and_icon_sizes, android_names): + exp_img_new_nm = f"{export_path}export_{move_date}_{label}.{image_ext}" + new_image = image.resize(num) + new_image.save(exp_img_new_nm) + + for num, label in zip(ios_icon_sizes, ios_names): + exp_img_new_nm = f"{export_path}export_{move_date}_{label}.{image_ext}" + new_image = image.resize(num) + new_image.save(exp_img_new_nm) + + time.sleep(1) + + shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}") + + return 'Success Image to Icon' + + else: + return 'Only Convert .PNG' + +def bake_one_big_png_to_ico(import_path, import_raw_path, export_path, img): + """Converts one big PNG into one ICO file. + + args: + sourcefile (str): Pathname of a PNG file. + targetfile (str): Pathname of the resulting ICO file. + sizes (list of int): Requested sizes of the resulting + icon file, defaults to [16, 32, 48]. + + Use this function if you have one big, square PNG file + and don’t care about fine-tuning individual icon sizes. + + Example:: + + sourcefile = "Path/to/high_resolution_logo_512x512.png" + targetfile = "Path/to/logo.ico" + sizes = [16, 24, 32, 48, 256] + bake_one_big_png_to_ico(sourcefile, targetfile, sizes) + """ + # if sizes is None: + # sizes = [16, 24, 32, 48, 256, 512] + # icon_sizes = [(x, x) for x in sizes] + # Image.open(sourcefile).save(targetfile, icon_sizes=icon_sizes) + + image_ext = os.path.splitext(img)[1].split('.')[1] + + if image_ext == 'png': + move_date = datetime.now().strftime('%y%m%d%H%M%S') + + img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') + + shutil.move(img, f"{import_path}{img_new_nm}") + + sizes = [16, 24, 32, 48, 64, 72, 80, 96, 128, 256] + + icon_sizes = [(x, x) for x in sizes] + + print(icon_sizes) + + # img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') + + Image.open(f'{import_path}{img_new_nm}').save(f"{export_path}export_{move_date}.ico", icon_sizes=icon_sizes) + + # shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}") + + time.sleep(1) + + return 'Success Image to Icon' + + else: + return 'Only Convert .PNG' + +def bake_several_pngs_to_ico(sourcefiles, targetfile): + """Converts several PNG files into one ICO file. + + args: + sourcefiles (list of str): A list of pathnames of PNG files. + targetfile (str): Pathname of the resulting ICO file. + + Use this function if you want to have fine-grained control over + the resulting icon file, providing each possible icon resolution + individually. + + Example:: + + sourcefiles = [ + "Path/to/logo_16x16.png", + "Path/to/logo_32x32.png", + "Path/to/logo_48x48.png" + ] + targetfile = "Path/to/logo.ico" + bake_several_pngs_to_ico(sourcefiles, targetfile) + """ + + # Write the global header + number_of_sources = len(sourcefiles) + data = bytes((0, 0, 1, 0, number_of_sources, 0)) + offset = 6 + number_of_sources * 16 + + # Write the header entries for each individual image + for sourcefile in sourcefiles: + img = Image.open(sourcefile) + data += bytes((img.width, img.height, 0, 0, 1, 0, 32, 0, )) + bytesize = Path(sourcefile).stat().st_size + data += bytesize.to_bytes(4, byteorder="little") + data += offset.to_bytes(4, byteorder="little") + offset += bytesize + + # Write the individual image data + for sourcefile in sourcefiles: + data += Path(sourcefile).read_bytes() + + # Save the icon file + Path(targetfile).write_bytes(data) + +# def image_to_icon(import_path, import_raw_path, export_path, img, resize_resolution): +# image_ext = os.path.splitext(img)[1].split('.')[1] +# +# if image_ext == 'png': +# move_date = datetime.now().strftime('%y%m%d%H%M%S') +# +# img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') +# +# shutil.move(img, f"{import_path}{img_new_nm}") +# +# image = cv2.imread(f'{import_path}{img_new_nm}') +# +# resized_img = cv2.resize(image, (resize_resolution, resize_resolution)) +# +# cv2.imwrite(f"{export_path}export_{image_ext}_{move_date}.ico", resized_img, [cv2.IMWRITE_PNG_COMPRESSION, 9]) +# +# move_date = datetime.now().strftime('%y%m%d%H%M%S') +# +# img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') +# +# shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}") +# +# time.sleep(1) +# +# return 'Success Image to Icon' +# +# else: +# return 'Only Convert .PNG' + +def image_to_extension(import_path, import_raw_path, export_path, img, extension): + image_ext = os.path.splitext(img)[1].split('.')[1] + + move_date = datetime.now().strftime('%y%m%d%H%M%S') + + img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') + + shutil.move(img, f"{import_path}{img_new_nm}") + + if image_ext == 'gif': + # Load the GIF into a PIL Image object + image = Image.open(f"{import_path}{img_new_nm}") + + if extension == 'png': + # Split the GIF into individual frames + frames = [] + for frame in ImageSequence.Iterator(image): + frames.append(frame.copy()) + + # Save each frame as an individual image file + for i, frame in enumerate(frames): + move_date = datetime.now().strftime('%y%m%d%H%M%S') + frame.save(f"{export_path}export_{move_date}_{i}.{extension}", format=extension) + + else: + image = image.convert("RGB") + + image.save(f"{export_path}export_{move_date}.{extension}") + + elif image_ext == 'png': + # Load the GIF into a PIL Image object + image = Image.open(f"{import_path}{img_new_nm}") + + background = Image.new('RGB', image.size, (255, 255, 255)) + + background.paste(image, mask=image) + + background.save(f"{export_path}export_{move_date}.{extension}") + + else: + image = cv2.imread(f'{import_path}{img_new_nm}') + + cv2.imwrite(f"{export_path}export_{move_date}.{extension}", image) + + shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}") + + time.sleep(1) + + return 'Success Image Extension' + +def base64_to_image(import_raw_path, export_path, base64_str, txt_file, file): + try: + # ImageFile.LOAD_TRUNCATED_IMAGES = True + # Remove the "data:image/png;base64," prefix from the string + base64_string = base64_str.split(',')[1] + + # Determine the length of the input string + length = len(base64_string) + + # Add padding characters if necessary + if length % 4 != 0: + base64_string += '=' * (4 - length % 4) + + # Decode the base64-encoded string into bytes + image_bytes = base64.b64decode(base64_string) + + image = Image.open(BytesIO(image_bytes)) + + image_ext = base64_str.split('/')[1].split(';')[0] + + move_date = datetime.now().strftime('%y%m%d%H%M%S') + + if image_ext == 'gif': + if image.mode == "RGBA": + # If the image has an alpha channel, convert it to RGB mode + image = image.convert("RGB") + + # Get the duration of each frame in the animation + frame_durations = [] + frames = [] + for frame in ImageSequence.Iterator(image): + frame_durations.append(frame.info.get("duration", 100)) # default duration is 100ms + frames.append(frame.copy()) + + # Calculate the speed of the animation + average_duration = sum(frame_durations) / len(frame_durations) + # if not average_duration == 0.0: + if average_duration != 0: + speed = 1 / (average_duration / 1000) # speed in frames per second + else: + speed = 0 + + frames[0].save(f"{export_path}export_{move_date}.{image_ext}", format="GIF", save_all=True, append_images=frames[1:], duration=speed, loop=0) + + # Check if the image is a PNG with transparency + elif image_ext == "png": + # Convert the image to RGB mode to remove the alpha channel + image = image.convert("RGBA") + + # Save the image to file + image.save(f"{export_path}export_{move_date}.{image_ext}") + + else: + # Convert the bytes to a numpy array + image_array = np.frombuffer(image_bytes, dtype=np.uint8) + + # Decode the numpy array into an OpenCV image + image = cv2.imdecode(image_array, cv2.IMREAD_UNCHANGED) + + # Save the image to a file + cv2.imwrite(f"{export_path}export_{move_date}.{image_ext}", image) + + file.close() + + image_ext = os.path.splitext(txt_file)[1].split('.')[1] + + img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') + + shutil.move(txt_file, f"{import_raw_path}{img_new_nm}") + + time.sleep(1) + + return 'Success Image Convert' + + except Exception as e: + return e + +def image_to_base64(import_path, import_raw_path, export_path, img): + image_ext = os.path.splitext(img)[1].split('.')[1] + + move_date = datetime.now().strftime('%y%m%d%H%M%S') + + img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}') + + shutil.move(img, f"{import_path}{img_new_nm}") + + if image_ext == 'gif': + image_pil = Image.open(f"{import_path}{img_new_nm}") + + # Convert the image to a sequence of frames + frames = [] + try: + while True: + frames.append(image_pil.copy()) + image_pil.seek(len(frames)) # skip to the next frame + + except EOFError: + pass # end of sequence + + # Encode the frames as GIF bytes + with BytesIO() as output: + frames[0].save(output, format='GIF', save_all=True, append_images=frames[1:]) + image_bytes = output.getvalue() + + elif image_ext == 'png': + image = Image.open(f'{import_path}{img_new_nm}') + + if image.mode != 'RGBA': + image = image.convert('RGBA') + + buffer = BytesIO() + image.save(buffer, format='PNG') + + # Encode the image data as a base64 string + image_bytes = buffer.getvalue() + + else: + # Load the image file + image = cv2.imread(f'{import_path}{img_new_nm}') + + # Convert the image to bytes + success, image_bytes = cv2.imencode(f'.{image_ext}', image) + image_bytes = image_bytes.tobytes() + + # Encode the bytes as a base64 string + base64_str = base64.b64encode(image_bytes).decode() + + # Create the base64-encoded image string + base64_str_dec = f"data:image/{image_ext};base64," + base64_str + + shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}") + + time.sleep(1) + + # Print the base64-encoded image string + return base64_str_dec, 'Success Base64 Convert', f"{export_path}export_{move_date}" \ No newline at end of file