Issue
Code backup
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
dist
|
||||
build
|
||||
*.spec
|
||||
.idea
|
||||
tests
|
||||
*venv/
|
||||
__pycache__/
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Sascha Schiwy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,94 @@
|
||||
# HeicConverter
|
||||
|
||||
## Introduction
|
||||
|
||||
A very simple command line tool to convert *.heic files to jpg. Since the available programs for windows are either paid
|
||||
or not working for me, I decided to make a simple python script to help convert these files.
|
||||
|
||||
## Features
|
||||
|
||||
- Convert all HEIC files to jpg in a folder and sub-folders recursively
|
||||
- Skips already existing conversions
|
||||
- Keep Metadata of the original file
|
||||
- Optional: Remove source files
|
||||
- Optional: Overwrite existing files
|
||||
|
||||
## Quick Usage
|
||||
|
||||
1. Copy the prepared exe to the folder with heic files for convert and double click it.
|
||||
2. Use Command line and append the path of interest:
|
||||
|
||||
~~~~
|
||||
./heicConverter.exe path/to/pictures
|
||||
~~~~
|
||||
|
||||
## Command Line
|
||||
|
||||
Start the script (or exe) from command line for additional options:
|
||||
|
||||
~~~~
|
||||
heicConverter.exe [-h] [-r] [-o] [--not-recursive] [--skip-prompt] path
|
||||
|
||||
Convert HEIC files to JPEG
|
||||
|
||||
positional arguments:
|
||||
path the path to the file or directory to convert
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-r, --remove Remove converted HEIC Files
|
||||
-o, --overwrite Overwrite existing JPEG files
|
||||
--not-recursive Do not search subdirectories
|
||||
--skip-prompt Skip the prompt at the end
|
||||
~~~~
|
||||
|
||||
## GUI
|
||||
|
||||
Also a GUI Version is available. Just start HeicConverterGui.exe.
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
### Windows
|
||||
|
||||
Download the latest release from the [Release Page](https://github.com/saschiwy/HeicConverter/releases) and extract it
|
||||
somewhere on your machine.
|
||||
|
||||
### Linux / Mac
|
||||
|
||||
Download the repo, install the dependencies and run the script.
|
||||
|
||||
## Development Dependencies
|
||||
|
||||
Install the python package dependencies with:
|
||||
|
||||
~~~~
|
||||
pip install -r requirements.txt
|
||||
~~~~
|
||||
|
||||
## Create your own executable
|
||||
|
||||
Install pyinstaller with:
|
||||
|
||||
~~~~
|
||||
pip install pyinstaller
|
||||
~~~~
|
||||
|
||||
Install the dependencies, navigate with a console to the source dir and run the following commands:
|
||||
|
||||
~~~~
|
||||
python -m PyInstaller --onefile --console heicConverter.py
|
||||
python -m PyInstaller --onefile --windowed heicConverterGui.py
|
||||
~~~~
|
||||
|
||||
## Remarks
|
||||
|
||||
This software was mainly created by people at StackOverflow:
|
||||
https://stackoverflow.com/questions/54395735/how-to-work-with-heic-image-file-types-in-python
|
||||
|
||||
## Example
|
||||
|
||||

|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JBK73YUVW7MGW&source=url)
|
||||
@@ -0,0 +1,115 @@
|
||||
import os
|
||||
from PIL import Image, ExifTags, UnidentifiedImageError
|
||||
from pillow_heif import register_heif_opener
|
||||
from datetime import datetime
|
||||
import piexif
|
||||
import fnmatch
|
||||
|
||||
register_heif_opener(allow_incorrect_headers=True)
|
||||
|
||||
|
||||
def get_file_list(dir_of_interest, recursive):
|
||||
"""
|
||||
Get a list of all files in the directory of interest
|
||||
|
||||
:param dir_of_interest: the directory to search
|
||||
:param recursive: search subdirectories
|
||||
|
||||
:return: a list of files
|
||||
"""
|
||||
file_list = []
|
||||
|
||||
if os.path.isdir(dir_of_interest):
|
||||
for root, dirs, files in os.walk(dir_of_interest):
|
||||
if not recursive:
|
||||
dirs.clear()
|
||||
for file in files:
|
||||
if fnmatch.fnmatch(file.lower(), '*.heic'):
|
||||
file_list.append([os.path.normpath(root), file])
|
||||
return file_list
|
||||
else:
|
||||
print("Path {} is not a valid directory.".format(dir_of_interest))
|
||||
return None
|
||||
|
||||
|
||||
def convert_heic_file(source_file, target_file, overwrite, remove):
|
||||
"""
|
||||
Convert a single heic file to jpeg
|
||||
|
||||
:param source_file: the source file
|
||||
:param target_file: the target file
|
||||
:param overwrite: overwrite existing jpeg files
|
||||
:param remove: remove converted heic files
|
||||
|
||||
:return: True if successful, False otherwise
|
||||
"""
|
||||
|
||||
if os.path.exists(target_file) and not overwrite:
|
||||
print(f'File {target_file} already exists, skip')
|
||||
return False
|
||||
|
||||
try:
|
||||
image = Image.open(source_file)
|
||||
image_exif = image.getexif()
|
||||
if image_exif:
|
||||
# Make a map with tag names and grab the datetime
|
||||
exif = {ExifTags.TAGS[k]: v for k, v in image_exif.items() if k in ExifTags.TAGS and type(v) is not bytes}
|
||||
if 'DateTime' in exif:
|
||||
date = datetime.strptime(exif['DateTime'], '%Y:%m:%d %H:%M:%S')
|
||||
else:
|
||||
date = datetime.now()
|
||||
|
||||
# Load exif data via piexif
|
||||
exif_dict = piexif.load(image.info["exif"])
|
||||
|
||||
# Update exif data with orientation and datetime
|
||||
exif_dict["0th"][piexif.ImageIFD.DateTime] = date.strftime("%Y:%m:%d %H:%M:%S")
|
||||
exif_dict["0th"][piexif.ImageIFD.Orientation] = 1
|
||||
exif_bytes = piexif.dump(exif_dict)
|
||||
|
||||
# Save image as jpeg
|
||||
image.save(target_file, "jpeg", exif=exif_bytes)
|
||||
print(f'Converted image: {source_file}')
|
||||
if remove:
|
||||
os.remove(source_file)
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"Unable to get exif data for {source_file}")
|
||||
|
||||
except UnidentifiedImageError as e:
|
||||
print(f"{source_file} is not a valid image : {e}")
|
||||
except Exception as e:
|
||||
print(f"Unable to convert {source_file}: {e}")
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def convert_heic_to_jpeg(dir_of_interest, recursive, overwrite, remove):
|
||||
"""
|
||||
Convert all heic files in the directory of interest to jpeg
|
||||
|
||||
:param dir_of_interest: The directory to search
|
||||
:param recursive: search subdirectories
|
||||
:param overwrite: overwrite existing jpeg files
|
||||
:param remove: remove converted heic files
|
||||
|
||||
:return: a list of successfully converted files
|
||||
"""
|
||||
heic_files = get_file_list(dir_of_interest, recursive)
|
||||
|
||||
# Extract files of interest
|
||||
success_files = []
|
||||
|
||||
print(f'Found {len(heic_files)} files to convert in folder {dir_of_interest}')
|
||||
|
||||
# Convert files to jpg while keeping the timestamp
|
||||
for root, filename in heic_files:
|
||||
|
||||
target_filename = os.path.splitext(filename)[0] + ".jpg"
|
||||
target_file = os.path.join(root, target_filename)
|
||||
source_file = os.path.join(root, filename)
|
||||
if convert_heic_file(source_file, target_file, overwrite, remove):
|
||||
success_files.append(target_filename)
|
||||
|
||||
return success_files
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
@@ -0,0 +1 @@
|
||||
root@PC284:/mnt/e/Code/VSC/Python3/_sftw/HeicConverter# python3 heicConverter.py -r -o fc/Mario
|
||||
@@ -0,0 +1,46 @@
|
||||
import os
|
||||
import argparse
|
||||
|
||||
from converter import convert_heic_to_jpeg, convert_heic_file
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""
|
||||
Parse command line arguments
|
||||
|
||||
:return: the parsed arguments
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description='Convert HEIC files to JPEG')
|
||||
parser.add_argument('path', help='the path to the file or directory to convert', default='./', nargs='?')
|
||||
parser.add_argument('-r', '--remove', help='Remove converted HEIC Files', action='store_true')
|
||||
parser.add_argument('-o', '--overwrite', help='Overwrite existing JPEG files', action='store_true')
|
||||
parser.add_argument('--not-recursive', help='Do not search subdirectories', action='store_true')
|
||||
parser.add_argument('--skip-prompt', help='Skip the prompt at the end', action='store_true')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
path = os.path.abspath(os.getcwd())
|
||||
|
||||
args = parse_args()
|
||||
if args.path:
|
||||
path = args.path
|
||||
|
||||
path = os.path.abspath(path)
|
||||
|
||||
if os.path.isdir(path):
|
||||
print(f'Converting HEIC files in directory {path}')
|
||||
converted = convert_heic_to_jpeg(path, not args.not_recursive, args.overwrite, args.remove)
|
||||
print(f'\nSuccessfully converted {len(converted)} files')
|
||||
|
||||
elif os.path.isfile(path):
|
||||
print(f'Converting HEIC file {path}')
|
||||
convert_heic_file(path, os.path.splitext(path)[0] + ".jpg", args.overwrite, args.remove)
|
||||
print(f'\nSuccessfully converted file')
|
||||
|
||||
else:
|
||||
print(f'Dont know what to do with {path}')
|
||||
|
||||
if not args.skip_prompt:
|
||||
input("Press Enter to continue...")
|
||||
@@ -0,0 +1,87 @@
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog
|
||||
from tkinter import scrolledtext
|
||||
from tkinter import ttk # ttk for modern themed widgets
|
||||
from converter import convert_heic_to_jpeg, convert_heic_file
|
||||
from ctypes import windll
|
||||
|
||||
windll.shcore.SetProcessDpiAwareness(1)
|
||||
|
||||
class HEICConverterGUI:
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
master.title("HEIC to JPEG Converter")
|
||||
master.geometry("600x500") # Set a default size for the window
|
||||
|
||||
# Style configuration
|
||||
style = ttk.Style()
|
||||
style.configure("TButton", font=("Helvetica", 12), padding=10)
|
||||
style.configure("TLabel", font=("Helvetica", 12), padding=5)
|
||||
style.configure("TCheckbutton", font=("Helvetica", 12), padding=5)
|
||||
style.configure("TEntry", font=("Helvetica", 12), padding=5)
|
||||
|
||||
self.path_label = ttk.Label(master, text="File or Directory Path:")
|
||||
self.path_label.pack(anchor='w', padx=10, pady=5)
|
||||
|
||||
self.path_entry = ttk.Entry(master, width=50)
|
||||
self.path_entry.pack(anchor='w', padx=10, pady=5)
|
||||
|
||||
self.browse_button = ttk.Button(master, text="Browse", command=self.browse)
|
||||
self.browse_button.pack(anchor='w', padx=10, pady=5)
|
||||
|
||||
self.remove_var = tk.BooleanVar()
|
||||
self.remove_check = ttk.Checkbutton(master, text="Remove converted HEIC Files", variable=self.remove_var)
|
||||
self.remove_check.pack(anchor='w', padx=10, pady=5)
|
||||
|
||||
self.overwrite_var = tk.BooleanVar()
|
||||
self.overwrite_check = ttk.Checkbutton(master, text="Overwrite existing JPEG files", variable=self.overwrite_var)
|
||||
self.overwrite_check.pack(anchor='w', padx=10, pady=5)
|
||||
|
||||
self.recursive_var = tk.BooleanVar(value=True)
|
||||
self.recursive_check = ttk.Checkbutton(master, text="Search subdirectories", variable=self.recursive_var)
|
||||
self.recursive_check.pack(anchor='w', padx=10, pady=5)
|
||||
|
||||
self.convert_button = ttk.Button(master, text="Convert", command=self.convert)
|
||||
self.convert_button.pack(anchor='w', padx=10, pady=5)
|
||||
|
||||
# Console Output
|
||||
self.console_output = scrolledtext.ScrolledText(master, width=80, height=20, wrap=tk.WORD)
|
||||
self.console_output.pack(anchor='w', padx=10, pady=10)
|
||||
|
||||
def browse(self):
|
||||
file_path = filedialog.askdirectory()
|
||||
self.path_entry.delete(0, tk.END)
|
||||
self.path_entry.insert(0, file_path)
|
||||
|
||||
def convert(self):
|
||||
path = self.path_entry.get()
|
||||
remove = self.remove_var.get()
|
||||
overwrite = self.overwrite_var.get()
|
||||
recursive = self.recursive_var.get()
|
||||
|
||||
output_text = ""
|
||||
|
||||
if os.path.isdir(path):
|
||||
output_text += f'Converting HEIC files in directory {path}\n'
|
||||
converted = convert_heic_to_jpeg(path, recursive, overwrite, remove)
|
||||
output_text += f'Successfully converted {len(converted)} files\n'
|
||||
|
||||
elif os.path.isfile(path):
|
||||
output_text += f'Converting HEIC file {path}\n'
|
||||
convert_heic_file(path, os.path.splitext(path)[0] + ".jpg", overwrite, remove)
|
||||
output_text += 'Successfully converted file\n'
|
||||
|
||||
else:
|
||||
output_text += f'Don\'t know what to do with {path}\n'
|
||||
|
||||
self.console_output.insert(tk.END, output_text)
|
||||
self.console_output.see(tk.END)
|
||||
|
||||
def main():
|
||||
root = tk.Tk()
|
||||
gui = HEICConverterGUI(root)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,3 @@
|
||||
pillow>=9.2.0
|
||||
pillow_heif>=0.18.0
|
||||
piexif==1.1.3
|
||||
@@ -0,0 +1,46 @@
|
||||
import os
|
||||
import argparse
|
||||
|
||||
from converter import convert_heic_to_jpeg, convert_heic_file
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""
|
||||
Parse command line arguments
|
||||
|
||||
:return: the parsed arguments
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description='Convert HEIC files to JPEG')
|
||||
parser.add_argument('path', help='the path to the file or directory to convert', default='./', nargs='?')
|
||||
parser.add_argument('-r', '--remove', help='Remove converted HEIC Files', action='store_true')
|
||||
parser.add_argument('-o', '--overwrite', help='Overwrite existing JPEG files', action='store_true')
|
||||
parser.add_argument('--not-recursive', help='Do not search subdirectories', action='store_true')
|
||||
parser.add_argument('--skip-prompt', help='Skip the prompt at the end', action='store_true')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
path = os.path.abspath(os.getcwd())
|
||||
|
||||
args = parse_args()
|
||||
if args.path:
|
||||
path = args.path
|
||||
|
||||
path = os.path.abspath(path)
|
||||
|
||||
if os.path.isdir(path):
|
||||
print(f'Converting HEIC files in directory {path}')
|
||||
converted = convert_heic_to_jpeg(path, not args.not_recursive, args.overwrite, args.remove)
|
||||
print(f'\nSuccessfully converted {len(converted)} files')
|
||||
|
||||
elif os.path.isfile(path):
|
||||
print(f'Converting HEIC file {path}')
|
||||
convert_heic_file(path, os.path.splitext(path)[0] + ".jpg", args.overwrite, args.remove)
|
||||
print(f'\nSuccessfully converted file')
|
||||
|
||||
else:
|
||||
print(f'Dont know what to do with {path}')
|
||||
|
||||
if not args.skip_prompt:
|
||||
input("Press Enter to continue...")
|
||||
Reference in New Issue
Block a user