IIIF manifestファイルを簡単に作ってみよう

今回は、最低限のIIIF manifestファイルを簡単に作ってみる方法をご用意してみました。経緯等は後回しにして、とりあえずやり方をば。

画像ファイルは、前回記事のやり方でpyramid tiffになっているものとします。拡張子は.tif。 そして、一つの資料ごとに一つのフォルダ(ディレクトリ)にまとめられているものとします。

そこで、まずは以下のようなCSVファイルを作成します。エクセルで作ってCSV UTF-8で保存すれば 大丈夫かと思います。(一応実験済み)

dir,title,license,attribution,viewingDirection,translator,publisher
0040_01,大方廣佛華嚴經 序~巻第二,https://creativecommons.org/licenses/by/4.0/deed.ja,東京大学総合図書館・SAT大蔵経テキストデータベース研究会,right-to-left,實叉難陀,鉄眼禅師
0040_02,大方廣佛華嚴經 巻第三~六,https://creativecommons.org/licenses/by/4.1/deed.ja,東京大学総合図書館・SAT大蔵経テキストデータベース研究会,right-to-left,實叉難陀,鉄眼禅師
0040_03,大方廣佛華嚴經 巻第七~十,https://creativecommons.org/licenses/by/4.2/deed.ja,東京大学総合図書館・SAT大蔵経テキストデータベース研究会,right-to-left,實叉難陀,鉄眼禅師
0040_04,大方廣佛華嚴經 巻第十一~十四,https://creativecommons.org/licenses/by/4.3/deed.ja,東京大学総合図書館・SAT大蔵経テキストデータベース研究会,right-to-left,實叉難陀,鉄眼禅師
0040_05,大方廣佛華嚴經 巻第十五~十七,https://creativecommons.org/licenses/by/4.4/deed.ja,東京大学総合図書館・SAT大蔵経テキストデータベース研究会,right-to-left,實叉難陀,鉄眼禅師
0040_06,大方廣佛華嚴經 巻第十八~二十,https://creativecommons.org/licenses/by/4.5/deed.ja,東京大学総合図書館・SAT大蔵経テキストデータベース研究会,right-to-left,實叉難陀,鉄眼禅師

最も大事なのは、一番左の列です。ここには、画像のフォルダ(ディレクトリ)名を入れておきます。前後の スラッシュは無しです。ここの情報を使って画像フォルダと突合させますので、これは必ず正確に記述してください。 (コピペ推奨です)

その他の内容ですが、まず、一行目は項目を書いて、二行目からコンテンツに対応する各種情報を書いていきます。titleはタイトルを書いておきます。 license (利用条件), attribution (帰属)、viewingDirection(読んでいく方向)あたりは書いておいた方がよいです。特に 右から左にページめくりしていくような物(縦書きの日本語・漢文資料など)はviewingDirectionのところに right-to-leftを書いておくと後々いいことがあります。 licenseは、ライセンス(利用条件)情報が書かれたベージのURLを書いておくことに なっています。なお、項目名については、IIIF Presentation APIで定められているものは英語で書いた方がよいですが、 それ以外の独自の項目名は日本語でつけても今のところはあんまり問題ありません。

繰り返しになりますが、エクセルで作った場合はCSV UTF-8で、そうでなければ普通にUTF-8で保存して いただけば大丈夫かと思います。ここでは仮にファイル名を bib.csv としておきます。

それから、準備として、Python3から画像の大きさを取得できるように、以下のようにモジュールをインストールしておきます。

% sudo pip3 install pillow

さて、次に、以下のスクリプトを回して見ます。

#!/usr/bin/python3
import sys
import glob
import os
import csv
import json
from PIL import Image
base_url = 'https://candra.dhii.jp/iiif/tetsugen/'
#ここ↑はマニフェストが設置されるURLに応じて書き換える
base_image_url = 'https://candra.dhii.jp/iiifimgs/tetsugen/'
#ここ↑はIIIF Image APIのURLに応じて書き換える
all_bib = {}
all_bib2 = {}
bib_title = []
mani_keys = ['dir','title','license','attribution','within','logo', 'description','viewingHint','viewingDirection']
#ここ↑は、IIIF Presentation APIにて、トップレベルで規定される項目を入れておきます。必要に応じて適宜増やしてください。
with open(sys.argv[1], newline='', encoding='utf_8_sig') as csvfile:
  spamreader = csv.reader(csvfile, delimiter=',', quotechar='"')
  rn = 0
  for row in spamreader:
    if rn == 0:
      bib_title = row
    else:
      each_bib = {}
      each_bib.update(zip(bib_title,row))
      link_name = row[0]
      all_bib[link_name] = each_bib
    rn = rn + 1;
#print (all_bib)

for key in all_bib.keys():
  each_manifest = {}
  all_meta = []
  file_dir0 = key
  glob_name = key+"/*.tif"
  if os.path.isdir(key):
   list_file_names = sorted(glob.glob(glob_name))
   if len(list_file_names) == 0:
     glob_name = key+"/*.ptif"
     list_file_names = sorted(glob.glob(glob_name))
   for item in all_bib[key]:
     if item not in mani_keys:
       each_meta  = {}
       item_value = all_bib[key][item]
       each_meta['label'] = item
       each_meta['value'] = item_value
       all_meta.append(each_meta)
   each_manifest['@id'] = base_url+key+'/manifest.json'
   each_manifest['@type'] = 'sc:Manifest'
   each_manifest['@context'] = 'http://iiif.io/api/presentation/2/context.json'
   each_manifest['metadata'] = all_meta
   for mani_key in mani_keys:
     if all_bib[key].get(mani_key):
       if mani_key == 'title':
         each_manifest['label'] = all_bib[key][mani_key]
       elif mani_key != 'dir':
         each_manifest[mani_key] = all_bib[key][mani_key]
   cn = 0
   sequence = {}
   canvases = []
   for file_path in list_file_names:
     service = {}
     resource = {}
     mani_image = {}
     canvas = {}
     file_dir = os.path.split(file_path)[0]
     if os.path.isdir(file_dir):
       cn = cn + 1
       canvas_number = 'p'+str(cn)+'.json'
       image_url_id = base_image_url+file_path
       service['@context'] = 'http://iiif.io/api/image/2/context.json'
       service['@id']  = image_url_id
       service['profile'] = 'http://iiif.io/api/image/2/level1.json'
       img = Image.open(file_path)
       width, height = img.size
       resource['@type'] = 'dctypes:Image'
       resource['format'] = 'image/jpeg'
       resource['width'] = width
       resource['height'] = height
       resource['@id'] = image_url_id+'/full/full/0/default.jpg'
       resource['service'] = service
       mani_image['@type']  = 'oa:Annotation'
       mani_image['motivation']  = 'sc:painting'
       mani_image['resource']  = resource
       mani_image['@id']  = base_url+file_dir+'/annotation/'+canvas_number
       mani_image['on']  = base_url+file_dir+'/canvas/'+canvas_number
       canvas['label'] = 'p. '+str(cn)
       canvas['images'] = []
       canvas['images'].append(mani_image)
       canvas['width'] = width
       canvas['height'] = height
       canvas['@type'] = 'sc:Canvas'
       canvas['@id'] = base_url+file_dir+'/canvas/'+canvas_number
       canvases.append(canvas)
   sequence['@id'] =  base_url+file_dir0+'/sequence/s1.json'
   sequence['@type'] =  'sc:Sequence'
   sequence['label'] =  'Current Page Order'
   sequence['canvases'] = canvases
   each_manifest['sequences'] = []
   each_manifest['sequences'].append(sequence)
   write_file_path = file_dir0+'/manifest.json'
   with open(write_file_path, mode='w') as f:
     json.dump(each_manifest, f, ensure_ascii=False)

ちょっと長いですが、こちらを mk_manifest.pyというファイル名で保存します。 ただし、二箇所、どうしても書き換えねばならないところがありますので、 その箇所にコメントをつけています。その二箇所は 環境に応じて書き換えてください。

ここで、各ファイルの位置関係は、以下のようになっています。

---mk_manifest.py

---bib.csv

---0040_01---tiffファイル群

---0040_02---tiffファイル群

---0040_03---tiffファイル群

---0040_04---tiffファイル群

この状態で、以下のようにコマンドを走らせます。

% python3 mk_manifest.py bib.csv

そうすると、各画像フォルダの中に manifest.json ファイルができていると思います。これで、できあがりです。 ディレクトリ構成も適正になっている場合には、manifestファイル中のこのマニフェストファイルのURIを 示す@idを使ってWebブラウザで表示させてみてください。人間の目では見づらいですが、こんな風になって いれば多分大丈夫です。あとは、MiradorやUniversal viewer、IIIF Curation viewer、TIFYなどに読み込ませれば 普通に表示できるはずです。

閑話休題

さて、これを作った経緯についても簡単に書いておきますと、IIIF Manifestの書き方がよくわからない、という 話をしばしばいただいていて、そこがなんとなくハードルになっているように感じたためです。すでに 江草氏による解説記事も用意されていますが、もう少し別の角度から情報提供してみてもいいのかもしれないと 思って、こういうものをちょっと作ってご紹介してみました。

もう少し言えば、内製でこれから勉強して取り組まなければならない人がいる、ということを風の噂で耳にしたということもあります。

この記事は、少しだけプログラミングができる人を対象としたものです。ただ、本当に少々でいいというのが、上記のものを 見ていただくとわかると思います。CSVファイルの内容を読み込んで、画像の大きさとファイル名も読み込んで、 それらをIIIF Manifest の形にあわせてlist とdictを組み合わせて構造化し、最後にjsonとして書き出しをする、という 形になっています。画像フォルダの階層がもっと深い場合は glob.glob() に読み込ませるワイルドカードを 少し変更してみる、などの手立てが必要です。とはいえ、もっとエレガントな書き方もできますので、改良版を作成&公開してくださる人がおられたら 大変ありがたいところです。

画像フォルダとmanifestファイルの位置関係については、色々なやり方があると思いますので、場合によっては、 それにあわせてmanifestファイルの保存場所も変更したりする必要があるかもしれません。そこら辺は適宜 修正する必要があると思いますが、どうしてもよくわからないけどなんとかしたい・しなければならない という人はご相談ください。

なお、元々はPHPで書いていたものを簡素化してPythonで書き直したものですので、書き方にPHPっぽい(Pythonに最適化されてない)ところがあるかもしれませんが、 その点はご容赦ください。