概要
個人的に撮り貯めた数万枚の写真の整理をするにあたり、
容量節約のためheic形式に変換して保存したいと考えました。
jpeg⇒heic形式への変換はGIMPのScript-Fu機能を使い実現
できたのですが、Exif情報は引き継がれませんでした。
写真を時系列で閲覧する際には撮影日時が無いと困るため、
jpegファイルのExif情報を取得しheicファイルのExif情報を
更新することにしました。更新には外部ツール’ExifTool’を
利用します。
C#のコードについては一部を除きChatGPTの生成を利用しています。
ChatGPT恐るべし!
また、私の環境では動きましたがPCの環境やExif情報によっては
不具合が出る可能性もあります。使用にあたってはバックアップを
確実に取ることをお勧めいたします。
環境
Windows11
VisualStudio C# .NET6.0
※NuGetで「ExifLib」をインストール。
jpeg形式⇒heic形式への変換
①まずはGIMPをインストールします。
ダウンロード先:https://www.gimp.org/downloads/
②続いて下記スクリプトコードをテキストエディタ等へ転記して
ファイル名「script-fu-export_jpeg_to_heic.scm」として
下記フォルダへ保存します。
C:\Users\ユーザー名\AppData\Roaming\GIMP\2.10\scripts
画質(70)は適宜設定ください。
(define (export_jpeg_to_heic inDir inLoadType outDir)
(let*
(
(varLoadStr "")
(varFileList 0)
)
(set! varLoadStr
(cond
(( equal? inLoadType 0 ) ".jpg" )
(( equal? inLoadType 1 ) ".bmp" )
(( equal? inLoadType 2 ) ".png" )
(( equal? inLoadType 3 ) ".gif" )
)
)
(set! varFileList (cadr (file-glob (string-append inDir "\\*" varLoadStr) 1)))
(while (not (null? varFileList))
(let*
((filename (car varFileList))
(short_filename (substring filename (+ (string-length inDir) 1) (string-length filename)))
(extNum 4)
(fileLenghNum (string-length short_filename))
(delExt (- fileLenghNum extNum))
(short_filename (substring short_filename 0 delExt))
(short_filename (string-append short_filename ".heic"))
(image (car (gimp-file-load RUN-NONINTERACTIVE filename filename)))
(drawable (car (gimp-image-get-active-layer image)))
;保存ファイル名の先頭に"r_"を追加
(newfilename (string-append outDir "\\" (string-append "r_" short_filename)))
)
;画質70で保存
(file-heif-save RUN-NONINTERACTIVE image drawable newfilename short_filename 70 0)
(gimp-image-delete image)
)
(set! varFileList (cdr varFileList))
)
(gimp-patterns-refresh)
)
)
(script-fu-register "export_jpeg_to_heic"
"<Image>/Script-Fu/Export Jpeg to Heic..."
"Export Jpeg to Heic."
"appin"
"appin"
"November 2018"
""
SF-DIRNAME "Load from" ""
SF-OPTION "Load File Type" (list "jpg" "bmp" "png" "gif")
SF-DIRNAME "Save to" ""
)
③先ほどのスクリプトを起動します。下記ウィンドウが立ち上がるので
jpegファイルのフォルダとheicファイルの保存先フォルダを指定します。
同一フォルダでも大丈夫です。
OKボタンをクリックすると処理が開始されます。
10MBのjpegファイルの変換に10秒程度かかりました。
ファイル名先頭に'r_'が付加されたheicファイルが生成されました。
ファイル容量は10.0MB⇒5.85MB(48%減)になっています。
jpegファイルからExifタグ情報取得
続いてjpegファイルのExif情報取得。
jpegファイルからExifタグ情報取得コード(C#)
using System;
using System.IO;
using ExifLib;
using System.Drawing.Imaging;
using System.Text;
namespace Exif情報取得
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string jpegFilePath = "C:\\Users\\ukiuk\\Desktop\\moto\\IMG_5348.JPG";
try
{
ExifReader reader = new ExifReader(jpegFilePath);
// 撮影日時
DateTime? takenDate = GetTakenDate(jpegFilePath);
// 製造元
string make = GetTagValue<string>(reader, ExifTags.Make);
// カメラモデル
string model = GetTagValue<string>(reader, ExifTags.Model);
// 絞り値
string apertureValue = GetApertureValue(reader);
// 露出時間
string exposureTime = GetExposureTime(reader);
// ISO速度
ushort? isoSpeed = GetTagValue<ushort?>(reader, ExifTags.ISOSpeedRatings);
// 露出補正
string exposureCompensation = GetExposureCompensation(reader);
// 焦点距離
string focalLength = GetFocalLength(reader);
// 測光モード
string meteringMode = GetMeteringMode(reader);
// フラッシュモード
string flashMode = GetFlashMode(reader);
// 取得した情報を表示
listBox1.Items.Add($"撮影日時: {takenDate}");
listBox1.Items.Add($"製造元: {make}");
listBox1.Items.Add($"カメラモデル: {model}");
listBox1.Items.Add($"絞り値: {apertureValue}");
listBox1.Items.Add($"露出時間: {exposureTime}");
listBox1.Items.Add($"ISO速度: {isoSpeed}");
listBox1.Items.Add($"露出補正: {exposureCompensation}");
listBox1.Items.Add($"焦点距離: {focalLength}");
listBox1.Items.Add($"測光モード: {meteringMode}");
listBox1.Items.Add($"フラッシュモード: {flashMode}");
reader.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
static T GetTagValue<T>(ExifReader reader, ExifTags tag)
{
object val;
if (reader.GetTagValue(tag, out val))
{
return val != null ? (T)val : default(T);
}
else
{
return default(T);
}
}
//撮影日時
static DateTime? GetTakenDate(string imgFile)
{
using (Bitmap bmp = new Bitmap(imgFile))
{
foreach (PropertyItem item in bmp.PropertyItems)
{
if (item.Id == 0x9003 && item.Type == 2) // Exif情報から撮影日時を取得
{
string val = Encoding.ASCII.GetString(item.Value).Trim('\0');
DateTime dt = DateTime.ParseExact(val, "yyyy:MM:dd HH:mm:ss", null);
//Console.WriteLine($"撮影日時: {dt}");
return dt;
}
}
return null;
}
}
//絞り値取得
static string GetApertureValue(ExifReader reader)
{
double? val;
if (reader.GetTagValue(ExifTags.FNumber, out val))
{
return val.HasValue ? $"f/{val.Value:F1}" : "N/A";
}
else
{
return "N/A";
}
}
//露出時間取得
static string GetExposureTime(ExifReader reader)
{
double? val;
if (reader.GetTagValue(ExifTags.ExposureTime, out val))
{
if (val.HasValue)
{
// 1/200 のような形式に変換する
return $"1/{(int)(1 / val.Value)}";
}
else
{
return "N/A";
}
}
else
{
return "N/A";
}
}
//露出補正取得
static string GetExposureCompensation(ExifReader reader)
{
double? val;
if (reader.GetTagValue(ExifTags.ExposureBiasValue, out val))
{
return val.HasValue ? val.Value.ToString() : "N/A";
}
else
{
return "N/A";
}
}
//焦点距離
static string GetFocalLength(ExifReader reader)
{
double? val;
if (reader.GetTagValue(ExifTags.FocalLength, out val))
{
return val.HasValue ? val.Value.ToString() : "N/A";
}
else
{
return "N/A";
}
}
//測光モード
static string GetMeteringMode(ExifReader reader)
{
ushort? val;
if (reader.GetTagValue(ExifTags.MeteringMode, out val))
{
if (val.HasValue)
{
switch (val.Value)
{
case 0: return "Unknown";
case 1: return "Average";
case 2: return "CenterWeightedAverage";
case 3: return "Spot";
case 4: return "MultiSpot";
case 5: return "Pattern";
case 6: return "Partial";
case 7: return "Other";
case 255: return "Not defined";
default: return val.Value.ToString(); // 未定義の値の場合、そのまま数値を返す
}
}
else
{
return "N/A";
}
}
else
{
return "N/A";
}
}
//フラッシュモード
static string GetFlashMode(ExifReader reader)
{
ushort? val;
if (reader.GetTagValue(ExifTags.Flash, out val))
{
if (val.HasValue)
{
switch (val.Value)
{
case 0: return "No flash";
case 1: return "Fired";
case 5: return "Fired, return not detected";
case 7: return "Fired, return detected";
case 9: return "On, did not fire";
case 13: return "On, return not detected";
case 15: return "On, return detected";
case 16: return "Off, did not fire";
case 24: return "Auto, did not fire";
case 25: return "Auto, fired";
case 29: return "Auto, fired, return not detected";
case 31: return "Auto, fired, return detected";
case 32: return "No flash function";
case 65: return "Off, no flash function";
case 69: return "Fired, red-eye reduction";
case 73: return "Fired, red-eye reduction, return not detected";
case 75: return "Fired, red-eye reduction, return detected";
case 77: return "On, red-eye reduction";
case 81: return "On, red-eye reduction, return not detected";
case 83: return "On, red-eye reduction, return detected";
case 93: return "Auto, did not fire, red-eye reduction";
case 95: return "Auto, fired, red-eye reduction";
case 99: return "Auto, fired, red-eye reduction, return not detected";
case 103: return "Auto, fired, red-eye reduction, return detected";
default: return val.Value.ToString(); // 未定義の値の場合、そのまま数値を返す
}
}
else
{
return "N/A";
}
}
else
{
return "N/A";
}
}
}
}
heicファイルのExifタグ情報更新
jpegファイルから取得した情報を元にheicファイルのExif情報を更新します。
外部ツールとして’ExifTool’をダウンロード、適当なフォルダに解凍します。
ダウンロード先:https://exiftool.org/
Windows Executableをダウンロードします。
heicファイルのExifタグ情報変更
先ほどjpegファイルから取得した情報を元に、
下記関数にファイルパスと共に投げると情報が更新されます。
また、同時にバックアップのファイルも生成されました。
UpdateExifInfoWithExifTool(heicFilePath, originalDateTime, make, model, apertureValue, exposureTime, isoSpeed.ToString(), exposureCompensation, focalLength, meteringMode, flashMode);
static void UpdateExifInfoWithExifTool(string filePath, DateTime newTakenDate, string newMake, string newModel, string newApertureValue, string newExposureTime, string newISO, string newExposureCompensation, string newFocalLength, string newMeteringMode, string newFlashMode)
{
// ExifToolのパスを指定します。適切なパスに変更してください。
string exifToolPath = @"C:\Users\ユーザー名\Desktop\exiftool-12.77\exiftool.exe";
// ExifToolを呼び出して、EXIF情報を書き換えます。
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = exifToolPath,
Arguments = $"-DateTimeOriginal=\"{newTakenDate:yyyy:MM:dd HH:mm:ss}\" " +
$"-Make=\"{newMake}\" " +
$"-Model=\"{newModel}\" " +
$"-FNumber=\"{newApertureValue}\" " +
$"-ExposureTime=\"{newExposureTime}\" " +
$"-ISO=\"{newISO}\" " +
$"-ExposureCompensation=\"{newExposureCompensation}\" " +
$"-FocalLength=\"{newFocalLength}\" " +
$"-MeteringMode=\"{newMeteringMode}\" " +
$"-Flash=\"{newFlashMode}\" " +
$"\"{filePath}\"",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (Process process = Process.Start(startInfo))
{
process.WaitForExit();
if (process.ExitCode == 0)
{
Console.WriteLine("EXIF information updated successfully.");
}
else
{
Console.WriteLine("Failed to update EXIF information.");
}
}
}
無事にheicファイルのExif情報が更新されました。
(同時に更新日時も修正していますがここでは割愛します。)
参考サイト