@ -0,0 +1,7 @@ | |||
# | |||
**/.idea/ | |||
**/__pycache__/ | |||
@ -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 |
@ -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" |
@ -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": {} | |||
} |
@ -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() |