うならぼ

どうも。

Exifのサムネイルの縦横比を修正して、デジカメできちんと表示されるように

SDカードに過去の写真が結構入っている*1のですが、RAWで撮ってるので結構場所をとります。原本をPCに転送したらJPEGに置き換えたい。

と思って適当にJPEGに変換して入れてみると、個別に表示することはできても一覧でサムネイルが表示されない。

f:id:unarist:20160811224057p:plain

いろいろ探った結果、サムネイルの大きさがDCF規格の160x120*2と違う縦横比だったのが原因だったようです。ちなみに比率が正しければもっと大きくても表示できました*3

プログラム書かない方へ:F6 Exifのインポート機能なども使えます(手間はかかりますが)。他にもサムネ再生成してくれるソフトはありそうですが、適当なものだと縦横比修正してくれないかも。

で、サムネイル再生成する何かをいつものようにC#で書こうとしたものの、System.DrawingやWPFメタデータ付きのJPEGを書き出すとビッグエンディアンになってしまうようなのです。これではサムネイルだけでなく全く表示できなくなってしまいました。

そこで今回はExiv2コマンドラインツールで埋め込むことにしました。exiv2 -it hoge.jpg とすると、hoge-thumb.jpg を hoge.jpg に埋め込んでくれます。

またRAWからのJPEG化・サムネイル生成はPhotoshopをCOM経由で叩いてみました。

参考 Automating Photoshop With C# // Josh Wright

ただうちの環境ではタイプライブラリの場所が正しく登録されていなかったようで、手動で C:\Program Files (x86)\Adobe\Adobe Photoshop CS5\Plug-ins\Extensions\ScriptingSupport.8li からtlbimpしました。

// using Photoshop;

var src_dir = @"G:\Src\";
var target_dir = @"G:\Thumb\";
var exiv2_exe = @"R:\exiv2.exe";
var thumb_width = 320;

var app = new Application();
//app.Visible = false;
app.BackgroundColor = new SolidColor { RGB = new RGBColor { HexValue = "000000" } };

// 後々の座標・長さ指定はこの単位が適用される
var ruler_old = app.Preferences.RulerUnits;
app.Preferences.RulerUnits = PsUnits.psPixels;

var export_opts = new ExportOptionsSaveForWeb
{
    Format = PsSaveDocumentType.psJPEGSave,
    Blur = 0.2,
    Quality = 50,
    Optimized = true
};
var save_opts = new JPEGSaveOptions
{
    Quality = 8,
    FormatOptions = PsFormatOptionsType.psOptimizedBaseline
};

foreach (var src in Directory.EnumerateFiles(src_dir, "*.cr2"))
{
    var basename = Path.GetFileNameWithoutExtension(src);
    var img_path = $"{target_dir}{basename}.jpg";
    var thumb_path = $"{target_dir}{basename}-thumb.jpg";
    
    if (File.Exists(img_path)) File.Delete(img_path);
    if (File.Exists(thumb_path)) File.Delete(thumb_path);

    // サムネイル生成で使うExportはファイル名を指定できないので、後で移動する。
    // ので、先にサムネ生成してから縮小前までUndoしてフルサイズ画像を書き出す。
    
    var doc = app.Open(src);
    var state = doc.ActiveHistoryState;
    
    if (doc.Width > doc.Height)
    {
        doc.ResizeImage(Width: thumb_width, ResampleMethod: PsResampleMethod.psBicubicSharper);
    }
    else
    {
        // doc.ResizeImage(Height: thumb_width * 3 / 4, ResampleMethod: PsResampleMethod.psBicubicSharper);
        // サムネはこれでいいが、フルサイズ画像も(横長のまま)Exifで回転指定しないといけない・・・
        throw new NotSupportedException("縦長画像は今後の課題。");
    }
    doc.ResizeCanvas(thumb_width, thumb_width * 3 / 4, PsAnchorPosition.psMiddleCenter);
    doc.Export(target_dir, PsExportType.psSaveForWeb, export_opts);
    File.Move(img_path, thumb_path);
    
    doc.ActiveHistoryState = state;
    doc.SaveAs(img_path, save_opts);
    doc.Close();

    // exiv2 -it foo.jpg
    // => foo.jpg に foo-thumb.jpg をサムネイル画像として埋め込む
    Process.Start(new ProcessStartInfo {
        FileName = exiv2_exe,
        Arguments = $"-it \"{img_path}\"",
        UseShellExecute = false,
        CreateNoWindow = true
    }).WaitForExit();
    File.Delete(thumb_path);
}

app.Preferences.RulerUnits = ruler_old;
//app.Quit();

ここまで書いてから、WICなんだからRAW形式もBitmapImageとかで読み込めて、Photoshopいらないのではと気付いた。面倒くささはいい勝負のような気がするけど…。

*1:撮った写真を人に見せられるようにってことなんですが、本当は画面大きくて操作しやすいタブレットに移行したい。

*2:CIPAのサイトからダウンロードできるDC-009-2010の§4.4.6.3参照

*3:うちのKiss X4でも液晶ディスプレイが横1400pxぐらいはあるようなので、3枚横に並べるとしても一枚160pxは余裕すぎる。ファイルサイズや表示速度に影響しそうではあるけど。