質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.70%

Pythonで写真の縦横を判別して画像処理したい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 330

TakayukiMoriya

score 11

何をやっているか

Pythonの画像処理モジュールPillowを使って指定したフォルダ内の写真のをリサイズして、右下にロゴマークを合成して保存する、というプログラムを作っています。

ここまでの進捗

以下コードで横向きの写真にロゴの挿入ができました。ここからさらに進化させたいと考えております。

import os
import sys
import shutil
from datetime import datetime
from enum import Enum, auto

import tkinter, tkinter.filedialog
from PIL import Image

WM_FILE = 'logo.png' #横写真の場合のウォーターマーク画像

class Mode(Enum):
    BLG = auto() #ブログ用

def mkdir_dto(img, output_dir_path):
        """画像の撮影日のフォルダを作成する
        :param img: 画像(PIL)
        :param output_dir_path: 出力先のフォルダーのパス
        :return: 作成したフォルダーのパス
        """
        EXIF_DTO = 36867 #Exifの撮影日のタグ番号

        # 移動先フォルダー作成。フォルダー名はExifの撮影日からyyyymmdd形式で生成
        exif = img._getexif()
        dt = datetime.strptime(exif[EXIF_DTO], '%Y:%m:%d %H:%M:%S')
        output_sub_dir = dt.strftime('%Y%m%d')
        output_path = os.path.join(output_dir_path, output_sub_dir)
        os.makedirs(output_path, exist_ok=True)

        return output_path

def make_img(img, img_name, mode, watermark, output_path):
    """画像をリサイズし、ウォーターマークを貼り付け、別名で保存する
    :param img: 画像(PIL)
    :param img_name: 画像(PIL)ファイル名
    :param mode: Mode.BLGならブログ用、Mode.TMBならサムネイル用
    :param watermark: ウォーターマーク画像(PIL)
    :param output_path: 出力先フォルダーのパス
    """
    BLG_CHAR = '_s' #ブログ画像のファイル名に付加する文字列
    MAX_W_BLG = 4000 #ブログ画像の幅の上限
    MAX_H_BLG = 2400 #ブログ画像の高さの上限 ※こちらはあまり意味のない数値です

    #  サイズ、ファイル名の末尾に付加する文字列を設定
    if (mode == Mode.BLG): #ブログ用
        w, h = MAX_W_BLG, MAX_H_BLG
        add_chr = BLG_CHAR

    else:
        return None

    #リサイズ
    img.thumbnail((w, h))

    #ウォーターマークを付加
    w_img, h_img = img.size
    w_wm, h_wm = watermark.size
    img.paste(watermark, (3000, 300), watermark)#ロゴの位置

    #ファイル名に文字列を付加して保存
    fname, ext = os.path.splitext(img_name)
    img.save(os.path.join(output_path, fname + add_chr + ext), quality=95, exif=img.info['exif'])#追記


# ウォーターマーク画像読み込み
watermark = Image.open(WM_FILE)

#元画像フォルダー選択
root = tkinter.Tk()
root.withdraw()
msg = '画像フォルダーを選択してください。'
img_dir_path = tkinter.filedialog.askdirectory(title=msg)
if (not img_dir_path): #[キャンセル]クリック時の処理
    print('フォルダーを選んでください。')
    sys.exit()

#出力先フォルダー選択    
msg = '出力先フォルダーを選択してください。'    
output_dir_path = tkinter.filedialog.askdirectory(title=msg)    
if (not output_dir_path):#[キャンセル]クリック時の処理    
    print('フォルダーを選んでください。')
    sys.exit()    

# 元画像フォルダー内のファイル1つずつ処理
for img_file in os.listdir(img_dir_path):
    #元画像読み込み (PIL)
    img_path = os.path.join(img_dir_path, img_file)
    img_pil = Image.open(img_path)

    #ファイルの移動先フォルダー作成
    output_path = mkdir_dto(img_pil, output_dir_path)

    #ブログ用画像とサムネイル用画像を作成
    make_img(img_pil.copy(), img_file, Mode.BLG, watermark, output_path)
#    make_img(img_pil, img_file, Mode.TMB, watermark, output_path)

    #元画像(PIL)を閉じる
    img_pil.close()

どう進化させたいか&発生しているエラー

こちらのプログラムに、写真のEixf情報を元に縦写真か横写真かの判別をして横写真ならロゴを右下に、縦写真なら270°回転させたロゴを右上に合成する、という機能を追加したいです。

自分なりに考えて、画像処理の部分にExifの回転に関する情報を取得して条件分岐させるというプログラムを書きましたが、
'Image' object has no attribute '_getexif'
というエラーになってしまいます。

def make_img(img, img_name, mode, watermark, output_path):
    """画像をリサイズし、ウォーターマークを貼り付け、別名で保存する
    :param img: 画像(PIL)
    :param img_name: 画像(PIL)ファイル名
    :param mode: Mode.BLGならブログ用、Mode.TMBならサムネイル用
    :param watermark: ウォーターマーク画像(PIL)
    :param output_path: 出力先フォルダーのパス
    """
    EXIF_ROT = 274#Exifの回転のタグ番号
    exif = img._getexif()
    BLG_CHAR = '_s' #ブログ画像のファイル名に付加する文字列
    MAX_W_BLG = 4000 #幅の上限
    MAX_H_BLG = 2400 #高さの上限 

    #  サイズ、ファイル名の末尾に付加する文字列を設定
    if (mode == Mode.BLG): #ブログ用
        w, h = MAX_W_BLG, MAX_H_BLG
        add_chr = BLG_CHAR

    else:
        return None

    #リサイズ
    img.thumbnail((w, h))

    #ウォーターマークを付加
    _orientation = exif[EXIF_ROT]
    w_img, h_img = img.size
    w_wm, h_wm = watermark.size

    if _orientation == 1:
        img.paste(watermark, (3000, 300), watermark)#ロゴ位置は仮です

    elif _orientation == 6:
        img.paste(watermark, (2800, 2000), watermark, ROTATE_270)#ロゴ位置は仮です

    elif _orientation == 8:
        img.paste(watermark, (400, 2000), watermark, ROTATE_90)#  ロゴ位置は仮です


    #ファイル名に文字列を付加して保存
    fname, ext = os.path.splitext(img_name)
    img.save(os.path.join(output_path, fname + add_chr + ext), quality=95, exif=img.info['exif'])#


条件分岐の手前のExif情報を取得するやり方が間違っているのでしょうか?
小生の力不足で恐縮ですが、解決方法があればご教授いただければ幸いです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • meg_

    2019/09/10 19:07

    画像の形式(拡張子)は何ですか?

    キャンセル

  • TakayukiMoriya

    2019/09/10 19:21

    JPEGです。そこにPNGのロゴを合成します。

    キャンセル

回答 1

checkベストアンサー

0

貼り付けるロゴを回転させるのではなく、貼り付ける画像の方を回転させて本来の向きにするほうがロゴの貼り付け位置は同じになるので、いいのではないでしょうか。

以下の記事で Pillow で EXIF タグから回転方向を判定して、画像を本来の向きにする方法が紹介されています。

PILでEXIF Orientationタグを考慮して処理 - Qiita

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/10 15:15

    ご回答ありがとうございます。できればEXIFデータを変えることなく処理を行いたいんですよね。何万枚という写真を扱うため、回転させる分処理に時間が掛かりそうな点も心配で…
    ニッチな悩みですみません

    キャンセル

  • 2019/09/10 15:22

    今回のエラーの直接の原因は img.copy() した PIL.Image オブジェクトでは EXIF 情報はコピーされないため、そのコピーした画像に対して、_getexif() は呼び出せません。
    コピーする前に exif 情報を別の変数に保存しておくなどの修正が必要と思われます。

    キャンセル

  • 2019/09/10 16:07

    ありがとうございます。もう少し頑張ってみます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.70%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる