最近の Android 端末だと RAW 撮影に対応していることがある。あまり対応アプリケーションがないのだが Lightroom Mobile を使うと DNG (Adobe が策定した RAW 画像形式) 撮影して、そのまま現像もすることができる。

DNG 撮影すれば、たとえスマフォ撮影でも後処理はしやすくなる。通常ユースでは全くいらないかもしれないが突発的に時間ができて手元にスマフォしかないみたいな場合でも救えるケースがでてくるかもしれない。

スマフォでも一昔前のコンデジぐらいの画質はあることが多いので、RAW で撮影すればそこそこ見れる写真になることが期待できる。

端末の DNG 対応

端末が DNG 対応しているかどうか調べられないか? と思ったら、Lightroom Mobile だとちゃんと DNG 対応しているかどうかを表示するところがあった。まぁもちろん実際の撮影設定で DNG に設定できるなら対応ということなんだけど。

DNG 対応してない場合は以下の通り

Android DNG

API Level 21 (5.0 Lolipop) から ImageFormat.RAW_SENSOR や DngCreator というのが入っていたらしい。

現実的には CameraDevice が RAW をサポートしているかどうかで実際に出力できるか変わりそうだが、いまいち市場での対応状況はわからない。スペックに RAW 撮影対応かどうか書いてあるのを見たことがない。

手元の ZenFone 3 だと Lightroom Mobile で DNG が生成できることは確認できた。ただ、 ZenFone 3 はそもそもカメラの性能がいまいちなのでそれほどテンションあがるわけではない。

  1. トップ
  2. tech
  3. Android 版 Lightroom Mobile の DNG 撮影

やりたいのは 1文字だけの改行の拒否 - ウェブログ - Hail2u.net のようなことの延長です。長めの見出しがブラウザによって改行されると、どうもバランスが悪くなったり可読性が微妙になることが多い。これをなんとかする。

仕様

  • 元の高さから変わらないこと
    • 非同期にやってもガタガタしないこと
  • 各行ができるだけバランスをとること
    • 文字数 (正確には幅) をあわせる

元の高さから変わらないことというのは行数を変えないということです。これを制約にしてページ全体の高さに影響を与えないようにすることで、非同期的に行数を調整しても閲覧に支障がないようにという意図があります。

「各行のバランス」はできるだけ行ごとの幅を揃えるという意味でいっています。基本的に幅が揃うことはないので、あくまでできるだけです。また、揃わない場合には一番下の行が一番長くなるようにします。これはデザイン上、重心が下になるほうが安定して見えるからです。

実装

  • 改行しても良い単位ごとに文を分割する
  • 各セグメントの文字幅を計算し、各行に埋めていく

デモ

また、このサイトにも適用済みです。

文の分割

いわゆる形態素解析での単語単位の「わかち書き」だと分割されすぎてしまいます。基本的に文節単位で改行するのが適切ではないかと思うので、文節単位のわかち書き機みたいなのが欲しくなります。

そこで TinySegmenterMaker を使ってみました。TinySegmenter を任意の学習データから生成できる優れものです。TinySegmenter 自身は言語非依存のアルゴリズムのため、一般的な形態素解析の分割位置とは違う分割でも学習さえさせれば動いてくれそうです。

適当なコーパスを用意できなかったため、とりあえず自分で書いた日記 (これ) の過去ログを全て MeCab で解析し、副詞などを結合する処理を加えたあとにスペース区切りで出力し、TinySegmenterMaker の入力としました。学習データ的に汎用性は落ちますが、そもそも自分のサイト用なのでまぁいい気もします。

そんなに元データは多くありませんが結構時間がかかりました。

なお分割時の MeCab 辞書に mecab-ipadic-neologd も入れてます。不必要な分割が減ることを期待しています。

各行に埋める

1行に収まっている場合は処理しません。

場合によって全く改行位置を調整できないケースも生じます。つまり各行に文字がいっぱいっぱいに詰まっている場合、調整できません。この場合も諦めてブラウザにまかせます。ただ、最後のセグメントには改行禁止ゼロスペース文字を入れることで1文字だけ残るというのは可能な限り避けます。

文字幅の計算には canvas の measureText を使っています。カーニングやリガチャなどが適切に反映されない可能性がありますが、現時点だとこれ以上良い方法がない気がします。

Webフォントを使う場合、ロードされていることを実行前に保証する必要があります。document.fonts.ready がちゃんと使えればいいんですが、Safari の挙動がおかしいので使えませんでした。

そこで以下のようにしてWebフォントのロードを判定しています。

function webfontReady (font, opts) {
	if (!opts) opts = {};
	return new Promise(function (resolve, reject) {
		var canvas = document.createElement('canvas');
		var ctx = canvas.getContext('2d');
		var TEST_TEXT = "test.@01N日本語";
		var TEST_SIZE = "100px";


		var timeout = Date.now() + (opts.timeout || 3000);
		(function me () {
			ctx.font = TEST_SIZE + " '" + font + "', sans-serif";
			var w1 = ctx.measureText(TEST_TEXT).width;
			ctx.font = TEST_SIZE + " '" + font + "', serif";
			var w2 = ctx.measureText(TEST_TEXT).width;
			ctx.font = TEST_SIZE + " '" + font + "', monospace";
			var w3 = ctx.measureText(TEST_TEXT).width;
			console.log(w1, w2, w3);
			if (w1 === w2 && w1 === w3) {
				resolve();
			} else {
				if (Date.now() < timeout) {
					setTimeout(me, 100);
				} else {
					reject('timeout');
				}
			}
		})();
	});
}

もっと良くできるか?

本当は各形態素境界ごとにスコアリングして、読みやすい順に改行を加えるみたいなことができればいいんですが、僕の技術力だとむずかしそうです。クライアント幅によるという性質上、処理はクライアントサイドでやる必要がありますが、そうすると実行ファイルサイズも問題になってきます。

そもそも学習データとして「適切な改行位置」を与えるのが難しい問題があります。Cabocha を使えばもうちょっとマシになるでしょうか?

「下のほうを長くする」という方針がいまいちだと思います。意味的に区切れるところを優先して区切るのが正しいと思われます。

備考: TinySegmenterMaker のマルチスレッドバージョン

なんかどうも boost_system も必要でした。以下のようにコンパイルしました。

g++ -I/usr/local/include -L/usr/local/lib -DMULTITHREAD -lboost_thread-mt -lboost_system -O3 -o train train.cpp

あと train に引数を与えないとマルチスレッドで処理されませんでした。8スレッドでやるなら -m 8 を加えます。

./extract < /tmp/corpus.txt > features.txt
./train -t 0.001 -n 10000 -m 8 features.txt model
./maker javascript < model

レポジトリ

https://github.com/cho45/midashi-kaigyo あまり整理されてません。

追記: budou

ブコメ で教えてもらいましたが、Google がまさに同じことをやってました

Google の NL APIを使っているみたいです。

追記

文字を変形させるという発想がなかったのですが、編集系の識者のかたから長体かけつつ字送り詰めて押し込んだりしますという意見をいただきました。また、これを実現する方法として CSS transform を使えばいいのではないかという意見もいただきました。

  1. トップ
  2. tech
  3. 見出しの改行位置を適正化する試み