"""Main module."""
from PIL import Image, ImageFont, ImageDraw
from PIL.ExifTags import TAGS, GPSTAGS, IFD
from datetime import datetime
from pathlib import Path
import exifread
import os
import logging
from . import logging_config
from .constants import FILMMODE_DICT, FONT_NAME
logger = logging.getLogger(__name__)
logger.propagate = False
script_dir = os.path.dirname(__file__)
def _get_exif_from_img_file(image_path):
exif = {}
with open(image_path, "rb") as img:
exif = exifread.process_file(img)
return exif
[docs]def get_fuji_filmmode(image_path):
with open(image_path, "rb") as f:
tags = exifread.process_file(f)
filmmode = tags.get("MakerNote Tag 0x1401")
if filmmode is None:
return None
else:
filmmode = int(f"{filmmode}")
return FILMMODE_DICT.get(filmmode, None)
def _resize_image(img, width=0, height=0):
"""Resize image to the given width or height. If either `width` or `height` is 0,
the image is resized to `height` or `width` and keeps the original ratio. If both `width`
and `height` are greater than 0, the image is resized to the given `width`
Args:
img (object): Image.open() object.
width (int, optional): _description_. Defaults to 0.
height (int, optional): _description_. Defaults to 0.
Returns:
object: _description_
"""
ratio = img.width / img.height
if width > 0:
return img.resize((width, int(width / ratio)))
elif height > 0:
return img.resize((int(height * ratio), height))
else:
raise RuntimeError("At least one of width and height shoult be > 0")
[docs]def add_border_to_image(
image_path, output_path, camera_logo_dir=None, add_camera_logo=True, add_lens=True
):
"""给图像添加边框,并在边框内添加拍摄信息和相机logo(可选)。
Args:
image_path (str): 图像文件的路径。
output_path (str): 保存新图像的路径。
camera_logo_dir (str): 相机logo文件夹的路径,不传入时使用包内附带
add_camera_logo (bool, optional): 是否添加相机logo。默认为 True。
add_lens (bool, optional): 是否添加镜头信息。默认为 True。
"""
# 读取图片
image_path = Path(image_path)
image = Image.open(image_path)
# 读取 exif 信息
exif_data = _get_exif_from_img_file(image_path)
# 获取照片的拍摄机型、iso、快门速度、镜头参数和富士胶片模拟名称
camera_make = str(exif_data.get("Image Make", ""))
if camera_make == "":
image.save(output_path, "JPEG", quality=95)
return
camera_model = str(exif_data.get("Image Model", ""))
iso = str(exif_data.get("EXIF ISOSpeedRatings", ""))
shutter_speed = exif_data["EXIF ExposureTime"].printable
lens = exif_data.get("EXIF LensModel").printable
# 获取照片的拍摄日期
capture_date = datetime.strptime(
exif_data["EXIF DateTimeOriginal"].printable, "%Y:%m:%d %H:%M:%S"
).strftime("%Y-%m-%d %H:%M:%S %a")
film_mode = get_fuji_filmmode(image_path)
focal_length = exif_data.get("EXIF FocalLengthIn35mmFilm")
f_number = float(exif_data["EXIF FNumber"].values[0])
# 尺寸
margin_left = int(0.02 * image.width) # 左右边距
margin_top = int(0.01 * image.height) # 上下边距
banner_height = int(0.08 * image.height)
main_sub_gap = int(image.height * 0.005)
anchor = (margin_left, image.height + margin_top)
banner_start_y = image.height + margin_top
# 颜色
border_color = (255, 255, 255) # 设置边框颜色为白色
text_color = {"main": (0, 0, 0), "sub": (100, 100, 100)} # 主字体是黑色
# 字体
text_font = {
"main": ImageFont.truetype(FONT_NAME, int(image.height * 0.03)),
"sub": ImageFont.truetype(FONT_NAME, int(image.height * 0.02)),
}
new_image = Image.new(
"RGB", (image.width, image.height + banner_height), border_color
)
new_image.paste(image, (0, 0))
new_image_draw = ImageDraw.Draw(new_image)
if focal_length is not None:
iso_aperture_text = f"{focal_length}mm f/{f_number} {shutter_speed}s ISO{iso}"
# 先把文本框的位置都写好
_iso_text_bbox = new_image_draw.textbbox(
xy=(0, 0), text=iso_aperture_text, font=text_font["main"], language="en"
)
# iso/fnumber text
_iso_text_xy = (image.width - margin_left - _iso_text_bbox[2], anchor[1])
new_image_draw.text(
_iso_text_xy,
iso_aperture_text,
fill=text_color["main"],
font=text_font["main"],
)
# 拍摄时间
new_image_draw.text(
(_iso_text_xy[0] + 10, anchor[1] + _iso_text_bbox[3] + 10),
f"{capture_date}",
fill=text_color["sub"],
font=text_font["sub"],
)
capture_date_box = new_image_draw.textbbox(
xy=(0, 0), text=f"{capture_date}", font=text_font["sub"], language="en"
)
if film_mode is not None:
# draw filmmode logo
logger.debug(banner_height)
_filmmode_img = Image.open(
os.path.join(
script_dir, f"../../assets/fujifilmsimulations/{film_mode}.jpg"
)
)
_fimmode_img_resized = _resize_image(
_filmmode_img, height=int(banner_height - 2 * margin_top)
)
new_image.paste(
_fimmode_img_resized,
(_iso_text_xy[0] - _filmmode_img.width, image.height + margin_top),
)
if add_camera_logo:
if camera_make is None:
logger.warning("camera make is missing in exif.")
new_image.save(output_path, "JPEG", quality=95)
return
if Path.exists(Path(camera_logo_dir)):
# if the path specified by user exists, use that path.
camero_logo_jpg = os.path.join(
camera_logo_dir, f"{camera_make.lower()}.jpg"
)
else:
logger.warning("add_camera_logo is True, but logo dir doesn't exist.")
logger.warning("trying to use default dir.")
camero_logo_jpg = os.path.join(
f"{script_dir}/../../assets/cameralogos/", f"{camera_make.lower()}.jpg"
)
new_image.save(output_path, "JPEG", quality=95)
return
logger.debug(f"logo file path: {camero_logo_jpg}")
draw_camera_logo = Image.open(camero_logo_jpg)
_logo_new_height = capture_date_box[3] + _iso_text_bbox[3]
draw_camera_logo_resized = _resize_image(
draw_camera_logo, height=_logo_new_height
)
new_image.paste(draw_camera_logo_resized, (anchor[0], banner_start_y))
# write model text
make_model_text = f"{camera_model}"
new_image_draw.text(
(anchor[0] + draw_camera_logo_resized.width + 10, banner_start_y),
make_model_text,
fill=text_color["main"],
font=text_font["main"],
language="en",
)
make_model_xy = new_image_draw.textbbox(
xy=(0, 0), text=make_model_text, font=text_font["main"], language="en"
)
if add_lens:
_lens_text_xy_coor = (
anchor[0] + draw_camera_logo_resized.width + 10,
banner_start_y + make_model_xy[3] + 5,
)
new_image_draw.text(
_lens_text_xy_coor,
f"{lens}",
fill=text_color["sub"],
font=text_font["sub"],
language="en",
)
new_image.save(output_path, "JPEG", quality=95)
return