First commit
This commit is contained in:
commit
6b00f62a2c
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
**/.idea/
|
||||||
|
**/__pycache__/
|
||||||
|
|
||||||
|
|
110
ApplyImageOrientation.py
Normal file
110
ApplyImageOrientation.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import exif
|
||||||
|
from exif import Image as ExifImage
|
||||||
|
|
||||||
|
|
||||||
|
from PIL import Image as PilImage
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class ApplyImageOrientation:
|
||||||
|
|
||||||
|
def __init__(self, input_folder, output_folder):
|
||||||
|
|
||||||
|
self.ensure_folder(output_folder)
|
||||||
|
|
||||||
|
assert os.path.isdir(input_folder), "Invalid input folder: %s" % input_folder
|
||||||
|
assert os.path.isdir(output_folder), "Invalid output folder: %s" % output_folder
|
||||||
|
|
||||||
|
self.__input_folder = os.path.abspath(input_folder)
|
||||||
|
self.__output_folder = os.path.abspath(output_folder)
|
||||||
|
|
||||||
|
self.__valid_image_extensions = [
|
||||||
|
"jpeg", "jpg",
|
||||||
|
"png",
|
||||||
|
"tiff"
|
||||||
|
]
|
||||||
|
|
||||||
|
def run(self, jpeg=False):
|
||||||
|
|
||||||
|
images_paths = self.get_input_images()
|
||||||
|
|
||||||
|
for image_path in images_paths:
|
||||||
|
self.apply_orientation(
|
||||||
|
image_path=image_path,
|
||||||
|
jpeg=jpeg
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ensure_folder(folder_path):
|
||||||
|
|
||||||
|
os.makedirs(folder_path, exist_ok=True)
|
||||||
|
|
||||||
|
def get_input_images(self):
|
||||||
|
|
||||||
|
return self.get_directory_images(self.__input_folder)
|
||||||
|
|
||||||
|
def get_directory_images(self, path):
|
||||||
|
|
||||||
|
images_files_paths = []
|
||||||
|
|
||||||
|
for current_dir, subdirs, files in os.walk(path):
|
||||||
|
|
||||||
|
for file_name in files:
|
||||||
|
|
||||||
|
file_path = os.path.join(current_dir, file_name)
|
||||||
|
file_basename, file_extension = os.path.splitext(file_path)
|
||||||
|
if file_extension:
|
||||||
|
file_extension = file_extension[1:]
|
||||||
|
file_extension = file_extension.lower()
|
||||||
|
if file_extension in self.__valid_image_extensions:
|
||||||
|
images_files_paths.append(file_path)
|
||||||
|
|
||||||
|
return images_files_paths
|
||||||
|
|
||||||
|
def apply_orientation(self, image_path, jpeg=False):
|
||||||
|
|
||||||
|
image_basename = os.path.basename(image_path)
|
||||||
|
|
||||||
|
with open(image_path, "rb") as f:
|
||||||
|
|
||||||
|
exif_image = ExifImage(f)
|
||||||
|
orientation = exif_image.orientation
|
||||||
|
|
||||||
|
pil_image = PilImage.open(image_path)
|
||||||
|
|
||||||
|
rotation_degrees = 0
|
||||||
|
if orientation == exif.Orientation.LEFT_BOTTOM:
|
||||||
|
rotation_degrees = 90
|
||||||
|
|
||||||
|
if rotation_degrees != 0:
|
||||||
|
pil_image_rotated = pil_image.rotate(rotation_degrees, expand=True)
|
||||||
|
else:
|
||||||
|
pil_image_rotated = pil_image
|
||||||
|
|
||||||
|
print(image_basename, pil_image.size, "==>", orientation, "==>", rotation_degrees)
|
||||||
|
|
||||||
|
image_rotated_file_path = self.make_image_output_path(image_path=image_path, jpeg=jpeg)
|
||||||
|
self.ensure_folder(os.path.dirname(image_rotated_file_path))
|
||||||
|
pil_image_rotated.save(image_rotated_file_path, None if jpeg else "png")
|
||||||
|
|
||||||
|
def get_image_relative_path(self, image_path):
|
||||||
|
|
||||||
|
image_path_relative = image_path[len(self.__input_folder):]
|
||||||
|
|
||||||
|
return image_path_relative
|
||||||
|
|
||||||
|
def make_image_output_path(self, image_path, jpeg=False):
|
||||||
|
|
||||||
|
image_path_relative = self.get_image_relative_path(image_path=image_path)
|
||||||
|
|
||||||
|
image_output_path = self.__output_folder + image_path_relative
|
||||||
|
|
||||||
|
if not jpeg:
|
||||||
|
path_name, extension = os.path.splitext(image_output_path)
|
||||||
|
image_output_path = path_name + ".png"
|
||||||
|
|
||||||
|
return image_output_path
|
13
Pipfile
Normal file
13
Pipfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.python.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
exif = "*"
|
||||||
|
pillow = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3"
|
69
Pipfile.lock
generated
Normal file
69
Pipfile.lock
generated
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "1900f06584008e6cfdf5a311b77e1a8370d743e22ffdead6bbf0ce186dad4e9f"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.python.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"exif": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:87699a3032b902dc72b6e565c51ce2711390cb6d5a7689ff12e04ba427dc95c2",
|
||||||
|
"sha256:dc53ffc2e09245676944367b0b7038d6cb7d73175d80b4ec9d84333d1af17500"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.0.4"
|
||||||
|
},
|
||||||
|
"pillow": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6",
|
||||||
|
"sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865",
|
||||||
|
"sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174",
|
||||||
|
"sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032",
|
||||||
|
"sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a",
|
||||||
|
"sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e",
|
||||||
|
"sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378",
|
||||||
|
"sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17",
|
||||||
|
"sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c",
|
||||||
|
"sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913",
|
||||||
|
"sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7",
|
||||||
|
"sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0",
|
||||||
|
"sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820",
|
||||||
|
"sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba",
|
||||||
|
"sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2",
|
||||||
|
"sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b",
|
||||||
|
"sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9",
|
||||||
|
"sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234",
|
||||||
|
"sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d",
|
||||||
|
"sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5",
|
||||||
|
"sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206",
|
||||||
|
"sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9",
|
||||||
|
"sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8",
|
||||||
|
"sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59",
|
||||||
|
"sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d",
|
||||||
|
"sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a",
|
||||||
|
"sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b",
|
||||||
|
"sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==8.1.0"
|
||||||
|
},
|
||||||
|
"plum-py": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:9328ec7502046134c5f16b04d85947559006e200d7268b7d5ed30f88735a48e5"
|
||||||
|
],
|
||||||
|
"version": "==0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
53
main.py
Executable file
53
main.py
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
from ApplyImageOrientation import ApplyImageOrientation
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="Mike's Auto Image Orientation Applier"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--input", "--dir", "--folder",
|
||||||
|
dest="input_folder",
|
||||||
|
required=True,
|
||||||
|
type=str,
|
||||||
|
help="Specify an input folder of images"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--output", "--output-dir", "--output-folder",
|
||||||
|
dest="output_folder",
|
||||||
|
required=True,
|
||||||
|
type=str,
|
||||||
|
help="Specify an output folder for the images"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--jpg", "--jpeg",
|
||||||
|
dest="output_jpeg",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Output to jpeg files, not png"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
assert args.input_folder != args.output_folder, "Input and Output folders must be different"
|
||||||
|
|
||||||
|
app = ApplyImageOrientation(
|
||||||
|
input_folder=args.input_folder,
|
||||||
|
output_folder=args.output_folder
|
||||||
|
)
|
||||||
|
app.run(
|
||||||
|
jpeg=args.output_jpeg
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user