BlankTar

about | blog | works | photo

海外旅行行ってきました。行ってきたのは良いのですが、カメラの日時を修正するのを忘れておりまして。
明らかに朝撮った写真なのにメタデータでは午後だったりするのがきもちわるい。というわけで、さくっと修正してみました。

修正にはexiv2というツールを使います。windows版もあるそうなので、linuxに限らず使えると思います。
また、この記事ではJPEG画像の例だけ書いていますが、DNG画像に対しても同じコマンドで使えました。他の形式のraw画像も多分平気。

とりあえず現状確認から。

$ exiv2 test.jpg

これで簡単な情報が出るのですが、簡単すぎて日時が載ってません。

詳細出力は以下のような感じで。

$ exiv2 -pt test.jpg

長いのでgrepするとこんな感じ。

$ exiv2 -pt test.jpg | grep DateTime
Exif.Image.DateTime                          Ascii      20  2015:09:14 15:00:29
Exif.Photo.DateTimeOriginal                  Ascii      20  2015:09:14 15:00:29
Exif.Photo.DateTimeDigitized                 Ascii      20  2015:09:14 15:00:29
Exif.Image.DateTimeOriginal                  Ascii      20  2015:09:14 15:00:29

私のカメラではこんなんなりました。物によっては別のタグが付いたり付かなかったりする、かも。
この画像の撮影時間を7時間巻き戻してみます。

時間を足したり引いたりするのに便利な-aオプションというのがあるので、それを使ってみます。

$ exiv2 -a -7 test.jpg
$ exiv2 -pt test.jpg | grep DateTime
Exif.Image.DateTime                          Ascii      20  2015:09:14 08:00:29
Exif.Photo.DateTimeOriginal                  Ascii      20  2015:09:14 08:00:29
Exif.Photo.DateTimeDigitized                 Ascii      20  2015:09:14 08:00:29
Exif.Image.DateTimeOriginal                  Ascii      20  2015:09:14 15:00:29

出来ました。…と思ったら、出来てない。
何故だか分らないのですが、Exif.Image.DateTimeOriginalは修正してくれないようです。
ちなみに、-a -6:30:10とかやって細かく修正することも出来ます。詳しくはman pageをどうぞ。

で、やってくれないんじゃ仕方がないので、DateTimeOriginalは手動で設定します。

$ exiv2 -M 'set Exif.Image.DateTimeOriginal "2015:09:14 08:00:29"' test.jpg
$ exiv2 -pt test.jpg | grep DateTime
Exif.Image.DateTime                          Ascii      20  2015:09:14 08:00:29
Exif.Photo.DateTimeOriginal                  Ascii      20  2015:09:14 08:00:29
Exif.Photo.DateTimeDigitized                 Ascii      20  2015:09:14 08:00:29
Exif.Image.DateTimeOriginal                  Ascii      20  2015:09:14 08:00:29

うまくいきました。やったね。
クォートがとても不思議な囲い方をしているので注意です。

このままだとかなり面倒なので、シェルスクリプトにしてみます。

for img in `ls *.jpg`; do
	exiv2 -a -7 $img
	exiv2 -M "set Exif.Image.DateTimeOriginal '`exiv2 -g Exif.Image.DateTime $img | sed -e 's/.*20 \+//'`'" $img
done

こんな感じで。

そうそう、実行する前には必ずバックアップを忘れずに。

参考:
exiv2でexifをいじる - 海と空と山
Exiv2 - Image metadata library and tools

しばらく前にOpenCVで輪郭検出をやりましたが、それに近いことがPillowで出来るらしいのでやってみました。
OpenCVと比べると簡単に出来ることは多いけれど、その分細かい調整は出来ない、そんな感じみたいです。

例によって、こちらのレナさんの画像に手を加えてゆきます。
恒例のレナさん

まずは最もシンプルな輪郭検出。

from PIL import Image, ImageFilter

img = Image.open('lena.jpg')
filtered = img.filter(ImageFilter.FIND_EDGES)
filtered.save('edges.jpg')

こんな感じ。
出力は以下のようになります。
黒地に白い線でレナさん

CONTOURを使うと、FIND_EDGESで得られる画像を白黒反転させたような画像を得られます。

img.filter(ImageFilter.CONTOUR).save('contour.jpg')

だいぶ書き方を省略しましたが、基本的にはなにも変わってません。
filterの引数が変わっただけですね。
出力は以下のような感じに。
輪郭を白地に黒い線で書いたレナさん

更に、輪郭強調も出来ます。
シンプルな方法は以下の二通り。

img.filter(ImageFilter.EDGE_ENHANCE).save('enhance.jpg')
img.filter(ImageFilter.EDGE_ENHANCE_MORE).save('enhance_more.jpg')

上が弱め、下が強め。そのまんまですね。
細かい調整はどうも出来ないっぽいです。
出力はこんな感じ。
弱い輪郭強調を掛けたレナさん 強い輪郭強調を掛けたレナさん
一枚目が弱い方、二枚目が強い方です。
不自然に輪郭が際立ってしまった感じ。そしてノイズを拾いまくり。

ある程度ナチュラルに強調してくれるSHAPENってやつがあります。

img.filter(ImageFIlter.SHAPEN).save('shapen.jpg')

なんというか、お察しの通りな書き方?
出力はこんな感じ。
シャープ化したレナさん
ちょっともの足りない感じがします。

SHAPENは自分でパラメータを決めることが出来るようで、以下のような書き方をします。

img.filter(ImageFilter.UnsharpMask(radius=10, percent=200, threshold=5)).save('unsharp.jpg')

画像編集ソフトで見掛けるアンシャープマスク、ってやつですね。
分かりやすくするためにパラメータは適当に強めのものを設定してあります。
このパラメータで実行すると出力は以下のような感じ。
アンシャープマスクで輪郭を強烈に強調したレナさん
かなり強烈です。強烈ですが、そこまで違和感は無いと思います。良い感じ。

他にもよく分からないフィルターが色々あって楽しいので、一度リファレンスをご覧になることをおすすめします。
もうちょいパラメータいじれたらもっと楽しいんでしょうけれど、手軽さの代償ですかねぇ。

参考:
How to use Pillow, a fork of PIL
ImageFilter Module — Pillow (PIL Fork) 2.6.1 documentation

8月1日から始めた自由研究、今日で終わりで御座います。いや長かったぜ。スクリプト走らせてるだけだったけれど。
初日中間の記事もよろしければどうぞ。

GitHubで公開されているLinuxカーネルを落してきて、それをひたすらコンパイルしまくって時間計測をしたり、行数やら関数呼び出しやらを数えてみました。
なお、今回行なった計測は非常にてきとーなものであり、その正確性を保証するものではありません。夏休みの自由研究クオリティです。

方法とか

実験に用いたPCはIntel Core i5-4690Sで4コアの3.2GHz、メモリを24G搭載しているものです。
すべての作業をtmpfs上で行なったので、多分ディスクIOの影響はほとんど受けていません。

コンパイルは概ね次のようなスクリプトで行ないました。

yes "" | make config
/usr/bin/time -a -o "$OUTDIR/$tag" -f '%E %U %S' make -j$PARALLEL
make clean distclean
git clean -df
git reset --hard

実際はエラー処理だのなんだのが入りますのでもっと長いです。スクリプト全体も公開していますのでご興味があれば。
yes "" | make configで設定している、というのが重要な点でしょうか。エンター連打と同義ですので、初期設定のような状態になっております。
そんな適当コンパイルなので、古いバージョンはほとんどまったくコンパイル出来ませんでした。コンパイル出来るようにするのも面倒なので、本稿ではコンパイル出来なかったバージョンは飛ばしております。

サイズとコンパイル時間

コンパイル時間に影響を与えそうなものとしてまっさきに思い付くのはやっぱりソースコードの行数ですね。
グラフにすると次のような感じです。
ソースコードの行数とコンパイル時間。大体どちらも右肩上がり。
黒いのがコンパイルに掛かるCPU時間、白いのが行数です。40万行ですって、凄まじい。
なんとなく一緒に増えていっているような気がしますが、v3.4あたりとv3.14あたりで逆行しているような感じがあります。コンパイル時間と完全に比例、というわけでも無いみたい。

では今度は、出力されるbzImageのサイズとコンパイル時間。
bzImageのサイズとコンパイル時間。大体同じに見えるが、ちょっとbzImageの方がゆるやか。
ほとんど重なっていますが、黒いのがコンパイル時間(CPU時間)で、白いのがbzImageのサイズです。
かなり良い感じに一致しているように見えますが、v3.3付近はなんだか一致していない感じです。微妙です。

関数とソースコードの行数

ソースコードの規模を表わす指標として、メモリ操作関数の呼び出し回数はなかなか使えるような気がします。というわけで数えてみました。
grepを使って正規表現で数えたので、正確な数が出ているかちょっと分かりません。まあ、大幅に違うということも無いのではないかと思います。
メモリ操作関数の呼び出し回数とソースコードの行数。free系がめっちゃ多い。
kfreeばっかり呼ばれてます。次いで多いのがfree。開放ばっかりです。その次の3位がやっとkmallocで確保なのですが、4位はvfreeです。開放の嵐です。
全体的に比例しているような印象ですが、v2.6.23あたりまでkmallocが減少しているのが気になりますね。freeの独壇場です。
ゆるやかに推移していて、ソースコードの規模がなんとなく分かりそうな感じです。

次は名高きprintfとその仲間たち、それとopenfopen。メジャーな関数たちです。
出力をやるなら入力も数えようかと思ったのですが、入出力関数は色々あって手に負えなくなりそうなのでやめておきました。
printf系とopen系の呼び出し回数とソースコードの行数。printf/sprintfがすごくあばれている。
なんだかprintfsprintfが暴れています。printfはコンパイル時間と良い感じに比例していそうなのですが、残念ながら重ねたらずれてました。
少し意外だったのは、linuxカーネルってfopenを殆んど使わないんですね。高級関数に興味は無いようです。

構造体の数なんかを数えると、プログラムの規模に直結しそうな気がします。数えてみました。
構造体の数とソースコードの行数。かなりうねりながら増えている。
printfを上回る大暴れです。なんだこれ。
v2.6.28からv2.6.32までの期間、v3.6からv3.16あたりまでが特に急激に増加しているような感じです。
ソースコードの規模とはあまり関係無く増えているような印象です。

プロセス数とコンパイル時間

最後に、複数のプロセスでコンパイルした場合のコンパイル時間を比べてみました。
なお、検証している間も裏で普通に遊んでいたり作業していたりしたので、他のプロセスもばりばり走っている状況下でのテストです。厳密に測ればもっとプロセス数を増やせるはず。
カーネルバージョン別のプロセス数とコンパイル時間。CPU時間は変わらず、現実時間では変わる。
上段がCPU時間、下段が現実時間です。
当然のことなのですが、プロセスを増やすとCPU時間はむしろ微増します。プロセスの管理に手間が掛かるのでしょうね。
これに対して、現実時間ではプロセスを1から2に増やしたらコンパイル時間が約半分になりました。凄い。
更に3つ以上になると大きな差が出なくなり、頭打ちな印象です。

見づらいので、今回計測した全バージョンのコンパイル時間の平均をグラフ化してみました。
プロセス数ごとのコンパイル時間。現実時間がなんというか、指数グラフ的。
CPU時間は1プロセスの時が一番短かく、プロセスの数を増やすと少し増えます。
現実時間はプロセス数1から3までは猛烈に時間短縮になっていますが、あとはあんまり変わらない。
CPUの数の2倍である8プロセスのときが最速という結果になりました。このあとは時間が伸びていく、のかな?

プロセス数1のときは各バージョンにつき30回ととにかく回しまくったのですが、2以上は各5回ずつしかやっていないので、誤差が結構出ているような気がします。
他のプロセスもがんがん走っている状況での検証ですし。

まとめ

Linuxカーネルはどんどん成長して膨れ上がっているようです。ただ、v3.0あたりから穏かになってきているので、そのうち増えなくなるときが来るのかもしれません。
完全な比例ではないとはいえやっぱりソースの規模とコンパイル時間は比例してますから、巨大化しすぎないことを祈るばかりですね。

関数の数を数えてみるのはとても面白いことですが、単純に数えるだけでは何の参考にもならなそうです。
変更の内容と照らし合せると色々分かってより面白いのでしょうが、流石に面倒なのでやめておきます。

結論としては、コンパイル時のプロセス数は増やしすぎても大した害はなさそう。ということでしょうか。
これは結構重要な事な気がします。CPUの数+1とか言いますが、CPUの数*2くらい設定しちゃってもきっと大丈夫です。効率は上がらずとも、コンパイル時間が大幅に伸びることは無さそうです。

あともう一つ。自由研究、楽しい。以上。

[ << ] [ 19 ] [ 21 ] [ 23 ] [ >> ]