diff --git a/.idea/blur-exif-face-tags.iml b/.idea/blur-exif-face-tags.iml
index 6807b60..76cf61c 100644
--- a/.idea/blur-exif-face-tags.iml
+++ b/.idea/blur-exif-face-tags.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 127b305..1f0a481 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/README.md b/README.md
index f10d1f1..77db2c8 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,6 @@
Many thanks to
-* https://www.thregr.org/~wavexx/software/facedetect/#blurring-faces-within-an-image
-* "Make blur all around a rectangle in image with PIL", https://stackoverflow.com/q/56987112/6334421
-* Example image: https://unsplash.com/photos/1qfy-jDc_jo?utm_source=unsplash&utm_medium=referral&utm_content=creditShareLink
+* [https://www.thregr.org/~wavexx/software/facedetect/#blurring-faces-within-an-image]()
+* "Make blur all around a rectangle in image with PIL", [https://stackoverflow.com/q/56987112/6334421]()
+* Example image: [https://unsplash.com/photos/1qfy-jDc_jo?utm_source=unsplash&utm_medium=referral&utm_content=creditShareLink]()
diff --git a/blur.py b/blur.py
index 7ae8916..75aeb96 100644
--- a/blur.py
+++ b/blur.py
@@ -35,23 +35,24 @@ class NormalizedRectangle:
raise Exception
-def blur_rectangle0(image_src: exif.Image, region: exif.ExifImageRegion, image_dst: Path = None):
+def blur_rectangle0(image_src: exif.Image, region: exif.ExifImageRegion, image_dst: Path = None) -> Path:
if region.area_unit == 'normalized':
- blur_rectangle1(image_src, normalized_rectangles=[NormalizedRectangle.of_exif_image_region(region=region)],
- image_dst=image_dst)
+ return blur_rectangle1(image_src,
+ normalized_rectangles=[NormalizedRectangle.of_exif_image_region(region=region)],
+ image_dst=image_dst)
else:
- raise Exception
+ raise Exception(f'Unknown area_unit: {region.area_unit}')
-def blur_rectangle1(image_src: exif.Image, normalized_rectangles: List[NormalizedRectangle], image_dst: Path = None):
- blur_rectangle2(image_src.get_image_file(), normalized_rectangles,
- image_dst=image_dst)
+def blur_rectangle1(image_src: exif.Image, normalized_rectangles: List[NormalizedRectangle], image_dst: Path = None) -> Path:
+ return blur_rectangle2(image_src.get_image_file(),
+ normalized_rectangles,
+ image_dst=image_dst)
-def blur_rectangle2(image_src: Path, normalized_rectangles: List[NormalizedRectangle], image_dst: Path = None):
+def blur_rectangle2(image_src: Path, normalized_rectangles: List[NormalizedRectangle], image_dst: Path = None) -> Path:
if len(normalized_rectangles) == 0:
- print('No rectangles to blur')
- return
+ raise Exception('No rectangles to blur')
# Open an image
im = Image.open(image_src)
@@ -85,6 +86,8 @@ def blur_rectangle2(image_src: Path, normalized_rectangles: List[NormalizedRecta
image_dst = get_image_dst(image_src)
im.save(image_dst)
+ return image_dst
+
def get_image_dst(image: Path):
return image.parent.joinpath(f'{image.stem}{stem_suffix()}{image.suffix}')
diff --git a/exec.py b/exec.py
index 497acf9..51ea021 100644
--- a/exec.py
+++ b/exec.py
@@ -2,12 +2,13 @@ from typing import List
import sys
import subprocess
+
def execute_save(command: List[str]):
returncode, stdout, stderr = execute(command)
if returncode == 0:
return stdout
else:
- raise Exception
+ raise Exception(f'Non-zero returncode: {returncode}\n{stderr}')
def execute(command: List[str]):
diff --git a/exif.py b/exif.py
index bebb52d..95c2476 100644
--- a/exif.py
+++ b/exif.py
@@ -86,6 +86,10 @@ def get_exif_image_regions(image: Image) -> List[ExifImageRegion]:
area_units_str = exec.execute_save(['exiftool', '-RegionAreaUnit', str(img_metadata_file)])
rectangles_str = exec.execute_save(['exiftool', '-RegionRectangle', str(img_metadata_file)])
+ if len(names_str) == len(r_types_str) == len(area_units_str) == len(rectangles_str) == 0:
+ # there are no tagged areas on this image
+ return []
+
names = names_str.strip().split(':', 1)[1].strip().split(', ')
r_types = r_types_str.strip().split(':', 1)[1].strip().split(', ')
area_units = area_units_str.strip().split(':', 1)[1].strip().split(', ')
diff --git a/main.py b/main.py
index 3cc278d..5a10fef 100644
--- a/main.py
+++ b/main.py
@@ -1,47 +1,78 @@
import os
from pathlib import Path
-from typing import List
-
-import exif, blur
+from typing import List, Union
+import blur
+import exec
+import exif
from exif import ExifImageRegion, get_exif_image_regions
+# ======================================================================================================= #
+
+# Adjust path to folder with images
+image_directory = Path('example')
+
+# Enter name of persons to be blurred. Leave empty to blur any face.
+names = ['A', 'B', 'C']
+# names = []
+
+delete_original: bool = True # deletes the original image after blurring image was created
+copy_metadata_gps: bool = True # copies gps location from original to blurred image
+copy_metadata_timestamp: bool = True # TODO: copies timestamp from original to blurred image
+
+# lower-case image extensions
image_extensions = ['.jpg', '.jpeg', '.png']
-image_directory = Path('example/') # TODO: Adjust path to folder with images
-r_type = 'Face'
-names = ['A', 'B', 'C'] # TODO: Enter name of persons to be blurred
+r_types = ['Face']
+
+# ======================================================================================================= #
-def blur_image(image: exif.Image):
+def blur_image(image: exif.Image) -> Union[Path, None]:
+ """
+ If at least one tagged area of the image matches the criteria,
+ a blurred image is created and it's path returned.
+ """
exif_image_regions: List[ExifImageRegion] = get_exif_image_regions(image=image)
- # Blur all tagged areas
- # normalized_rectangles = [blur.NormalizedRectangle.of_exif_image_region(region) for region in exif_image_regions]
-
# Blur only some faces
normalized_rectangles = []
for region in exif_image_regions:
- if region.r_type == r_type and region.name in names:
+ if len(r_types) > 0 and region.r_type not in r_types:
+ continue
+ if region.name in names or len(names) == 0:
normalized_rectangles += [blur.NormalizedRectangle.of_exif_image_region(region)]
- print(f'{image} contains {len(normalized_rectangles)} tagged faces to be blurred!')
- blur.blur_rectangle1(image_src=image, normalized_rectangles=normalized_rectangles)
+ if len(normalized_rectangles) > 0:
+ print(f' Blurring {len(normalized_rectangles)} areas ...')
+ return blur.blur_rectangle1(image_src=image, normalized_rectangles=normalized_rectangles)
+ else:
+ return None
def main():
- # Convert all images in the image_directory, including subdirectories.
+ # Convert all images in `image_directory`, including subdirectories.
for _, _, files in os.walk(image_directory):
for relative_file_str in files:
file: Path = Path.joinpath(image_directory, relative_file_str)
- if file.suffix.lower() in image_extensions:
+ if file.suffix.lower() not in image_extensions:
+ continue
+ if file.stem.endswith(blur.stem_suffix()):
+ print(f'Skipped the following image as it is already blurred:\n\t{file}')
+ continue
+ if blur.get_image_dst(file).exists():
+ print(f'Skipped the following image as it\'s blurred output does already exist:\n\t{file}')
+ continue
- if file.stem.endswith(blur.stem_suffix()):
- print(f'Skipped the following image as it is already blurred:\n\t{file}')
- elif blur.get_image_dst(file).exists():
- print(f'Skipped the following image as it\'s blurred output does already exist:\n\t{file}')
- else:
- blur_image(exif.Image(file))
+ print(f'{file}')
+ blurred_img = blur_image(exif.Image(file))
+
+ if blurred_img is not None and copy_metadata_gps:
+ print(f' Copying gps metadata to blurred file ...')
+ exec.execute_save(['exiftool', '-tagsfromfile', str(file), '-gps:all', str(blurred_img)])
+ if blurred_img is not None and delete_original:
+ print(f' Deleting original file ...')
+ file.unlink()
if __name__ == '__main__':