プログラミング > Python >

Exifの新規追加

2023-04-15
   Exifを一度削除して入れたい情報を新たに追加する。基本的に入れて有効なのは「アーティスト情報」「著作権」「編集日時」と思う。以下にコードで、その後に解説。
                    
FIELDS = [
	{'tag': b'\x01\x3b', 'type': ['ASCII', b'\x00\x02'], 'count': 0, 'offset': b'',
	 'name': 'アーティスト', 'value': b'Ibuki SHITORI\x00'},
	{'tag': b'\x82\x98', 'type': ['ASCII', b'\x00\x02'], 'count': 0, 'offset': b'',
	 'name': '撮影著作権者/編集著作権者', 'value': b'Copyright 2023 Ibuki SHITORI\x00'},
	{'tag': b'\x01\x32', 'type': ['ASCII', b'\x00\x02'], 'count': 0, 'offset': b'',
	 'name': 'ファイル変更日時', 'value': b'yyyy:mm:dd hh:mm:ss\x00'}
]

def simple_new_add_exif(file_name, new_file_name, fields): simple_delete_exif(file_name, new_file_name) app_marker = b'\xff\xe1' app_length = 18 app_label = b'Exif\x00\x00MM\x00\x2a\x00\x00\x00\x08' app_field_num = len(fields) app_offset = 10 + app_field_num * 12 app_fields = b'' app_value = b'' for field in fields: if field['tag'] == b'\x01\x32': field['value'] = datetime.datetime.now().strftime('%Y:%m:%d %H:%M:%S').encode()+b'\x00' add_field = b'' if field['type'][0] in ['ASCII']: value_length = len(field['value']) if value_length > 4 and field['value'][-1] == 0: add_field = field['tag'] + field['type'][1] + value_length.to_bytes(4, 'big') + app_offset.to_bytes(4, 'big') app_offset += value_length app_value += field['value'] elif value_length == 4 and field['value'][-1] == 0: add_field = field['tag'] + field['type'][1] + value_length.to_bytes(4, 'big') + field['value'] app_fields += add_field app_length += app_field_num * 12 + len(app_value) app_data = app_marker + app_length.to_bytes(2, 'big') + app_label + app_field_num.to_bytes(2, 'big') + app_fields + app_value with open(new_file_name, 'rb') as f: new_data = f.read() start_of_image_index = new_data.index(b'\xff\xd8') new_data = new_data[start_of_image_index:start_of_image_index + 2] + app_data + new_data[start_of_image_index + 2:] with open(new_file_name, 'wb') as f: f.write(new_data)
def simple_delete_exif(file_name, new_file_name): with open(file_name, 'rb') as f: data = f.read() start_of_image_index = data.index(b'\xff\xd8') start_of_scan_index = data.index(b'\xff\xda') application_segment_marker = [] for header in range(start_of_image_index, start_of_scan_index, 2): tag = data[header:header + 2] if b'\xff\xe0' <= tag <= b'\xff\xef': # tag_length = struct.unpack('>H', data[header + 2:header + 4])[0] tag_length = int.from_bytes(data[header + 2:header + 4], 'big') application_segment_marker.append(data[header:header + tag_length + 2]) with open(new_file_name, 'wb') as g: new_data = data for marker in application_segment_marker: new_data = new_data.replace(marker, b'') g.write(new_data)

目次

フィールドの構造

   フィールドは「Tag」「Type」「Count」「Offset」に加えてフィールド名とその値をキーにした辞書のリスト。「Count」「Offset」はフィールドの値によって変わるので後から変更するという方針で辞書を作成する。

Exifの追加

   一旦simple_delete_exif()によって画像のみのデータにする。そのあとにExifアプリケーションのマーカー、データの長さ、ヘッダー、オフセットを定義し、追加していくフィールドとその値を初期化する。
   与えられたフィールドリストを元にfor文で「Tag」「Type」などの情報をもとにapp_fieldsapp_valueを更新する。
   最後にアプリケーション全体の長さを測り、app_dataとしてJPEGのSOIとSOSの間にアプリケーションを挿入して保存すればできあがり。

バイトデータからの変換とその逆

   バイトから整数に変換するとき最初はstructを使っていたが、int.from_bytesで簡単にできた。
   また、逆に整数からバイトはint.to_bytesでできてバイト文字数を設定すればその文字数で変換してくれる。
   整数の他に文字列をバイトにしたいときはstr.encodeで可能。