鎮守府史跡探訪〜佐世保軍港クルーズ〜

ゴールデンウィーク中、知人の船に乗せてもらい佐世保港からハウステンボスの見える波止場まで行き、バーベキューをしてきました。

はりきって写真を撮ったんですが自分のスキルでは全然上手く撮れませんでした。でもやっぱり貼りつけておこうと思います。撮影スキルもがんばるぞい!

DSC04928

昼下がり、鯨瀬埠頭に泊めた船に乗船。対岸に見えているのは米軍施設です。休みの日はよく対岸から米兵やその家族の叫び声が聞こえてきます。佐世保の街中にはアメリカ人がたくさんいますが、日本人では考えられない音量で会話してます。声がでかいのがアメリカ人。

DSC04934

出港するとすぐに左舷側に鯨瀬ターミナルが見えてきます。ここから五島行きなどのフェリーが出ています。マンガ「トッキュー」の第1巻で乗用車が海に転落したのは、ちょうど写真に写っている岸壁です。

DSC04939

鯨瀬埠頭の反対側、右舷側を見ると米軍佐世保基地にくっついている立神2号桟橋に、護衛艦「しまかぜ」が停泊していました。米軍基地エリアの奥深くにある桟橋部分だけが海上自衛隊のものです。佐世保港の敷地っていうのはなかなか複雑で、民間の佐世保重工業(SSK、旧佐世保海軍工廠)、アメリカ海軍佐世保基地、海上自衛隊佐世保基地、さらに一般人にとって大切な朝市や貨客船ターミナルがある万津・新港町などなど、たくさんのエリアに分割されています。

101021ganpeki2

佐世保市議会議員のサイトに良い地図があったので拝借しました。地図は少し古い2010年時点のようで、埋め立てして出来た土地にさせぼ五番街を作った新港町がまだ海の底です。平瀬係船池にある点線・斜線部分も埋め立てられているようです。昨年も返還のニュースが流れてましたが、SSKと米軍が共同で利用している立神岸壁ではいろいろとあるようですね。

なお、水陸機動団で話題となった陸上自衛隊佐世保基地は相浦地区にあり、地図では向かって左手の方になります。

DSC04947

船は進み、さらに左舷側を見ると今度は海上自衛隊倉島岸壁が見えます。ここが毎回一般公開でお世話になっている岸壁です。長崎三菱造船所建造の護衛艦「あきづき」が係留されています。

DSC04964

その後には、右手から護衛艦「あさゆき」、「さわぎり」、「じんつう」が係留中です。赤煉瓦風の海自厚生センターがまぶしいですね。

DSC04982

海から見た倉島岸壁の位置はこんな感じです。写真に写っている海岸線の一番左に佐世保駅が小さく見えています。

DSC05006

DSC05012

DSC05029-2

倉島岸壁を過ぎると、今度は米軍弾薬補給所が見えてきます。さらに、補給所の先には、崎辺にある海上自衛隊佐世保教育隊・警備隊の基地が見えます。崎辺の岬には戦前、佐世保海軍航空隊の滑走路がありました。

2

5

埋立地の崎辺では地盤が弱く、滑走路に不向きという理由から佐世保海軍航空隊は大村基地に拠点を移したそうです。写真は佐世保地方隊のページから拝借しています。(崎辺に関する詳しい記事があります)

滑走路といえば平地が少ない佐世保では、今でも佐世保湾内に水上機用の滑走路エリアが設けられているようです。

sbm

湾内のほとんどの場所から外海が見えないくらい岬や入江が複雑な海岸線を構成している佐世保です。 その日の天候にもよるとは思いますが、実際に船に乗って湾内を航行しても驚くほど波がありません。 素人目に見ても着水しやすいんだろうなあ、と思いました。上の地図でいうと「D」と書かれたエリアです。

DSC05032

さて、その水上滑走路付近に一隻の艦が停泊していました。どう見ても商船ではなさそうです。

DSC05047

近づいて見ると、海上自衛隊の補給艦「おうみ」でした。旧ユニバーサル造船建造、2005年就役の比較的新しい艦です。全長220メートルで、さきほどの「あさゆき」は130メートル、旧海軍の重巡洋艦「高雄」でも203メートルなので艦船の中ではかなり大型のほうです。陸からだとこんなに間近に見れない艦をぐるりと見てまわることができるのもクルーズの醍醐味って感じですね!

また、造船システムの開発関係者として見ると「船型のあんなところにナックルがー」と困惑してしまいました。補給艦として容積を稼ぎたい一方で、軍艦として速力を上げるための船型なんでしょうか。ともかく仕事ってこわい。

さあ、どんどん南下していきます。

DSC05102

針尾島に近づくと針尾無線送信所の電波塔が見えました。旧海軍が造った大正時代から立っている軍用電波塔です。周りには森と畑しかないのでスケール感がよく分からなくなりますがなかなか大きいです。明治の産業遺産を世界遺産に申請して盛り上がってますが、佐世保も教会群だけじゃなくてこういう巨大遺構をもっと観光資源にすればいいのに…と思ってみたり。

新海誠監督の「雲のむこう、約束の場所」に出てくる「ユニオンの塔」のようです。近くで見ると威圧感というか、一種の不気味ささえ感じられる塔です。

DSC05122

針尾島の南端には西海橋があります。前述の「トッキュー」では遊覧船が沈没していた、渦潮の名所でもあります。この日は通った時間帯のせいもあってか、全然渦潮がありませんでした。水色の方が新西海橋、赤い方が旧西海橋です。

DSC05143

旧の方は建造当時、アーチ式鉄橋としては日本一の長さ(世界では3位)だったらしいです。そのせいか、昭和31年公開の怪獣映画の怪獣「ラドン」によって見事に破壊されてます。

DSC05176

西海橋を過ぎ、大村湾に入ると左舷側にハウステンボスが見えてきます。オランダの都市をモチーフにしたテーマパークですが、横から見ると山の起伏が激しくて、海抜0メートル地帯で有名なオランダの沿岸部とは似ても似つかない地形に…。横から見ると小規模に見えますが、敷地面積は東京ディズニーランドよりも大きく、ディズニーシーを合わせたのと同じくらいの面積はあるそうです。山の起伏が激しくて海岸付近まで山がせまっているのは佐世保の特徴で、軍港になった理由の一つでもあります。山が海岸まで来ているということは遠浅ではないので、比較的大型の艦船も入港しやすい地形だと言えるのです。

ちなみに、ハウステンボスがある土地は終戦直後の復員事業の際、検疫所があった場所です。

DSC05196

ハウステンボスの対岸にある小さな波止場に上陸し、バーベキュー開始。

DSC05203

ワンピースに登場するサウザントサニー号のクルーズ船がいったりきたりしていました。後から知った話ですが、2015年5月6日で終了し、愛知県にある「ラグーナテンボス」に移されたとのこと。撮影日は5月2日です。ちなみに、ラグーナテンボスの存在もさっき知りました。

DSC05329

DSC05345

DSC05383

まわりに光源が何もない地域なので、花火はより一層綺麗に見えました。

DSC05412

帰りに撮影した補給艦「おうみ」です。来た時とは艦首の方向が違っていました。写真はとも側から見たおうみの船尾部です。潮の流れに向かって動いているようです。ライトアップしてて綺麗ですね。

DSC05451

最後に倉島岸壁です。楽しかったな〜。

鎮守府史跡探訪〜堺木峠減圧井〜

先日、豚トロを食べようと雪平鍋に油をひいて加熱している途中、誤って鍋をひっくり返してしまいました。 結果、足に全治2週間の火傷を負いました。情けない・・・。

火傷した日からちょうど2週間が過ぎた日、通院のため自転車に乗っていると堺木付近に「堺木峠減圧井(げんあつせい)」というものを見つけました。

DSC02296-2

減圧井とは送水管の破裂を防ぐため、水圧の調整を行う施設らしいです。鎮守府があった市街地から大野方面に行く際、堺木付近はもっとも高さがある場所に当たります。山間部から集めた水をここで一旦減圧してから、下方にある鎮守府まで送水していたみたいですね。以前から目にしていたものの、明治時代の旧跡であることは知りませんでした。

DSC02295-2

説明の板には、堺木峠減圧井の歴史が書いてあります。

旧海軍佐世保鎮守府は海軍専用水道の水源拡充のため、当時の北松浦郡皆瀬村(現在の十文野町)にあった湧水の溜池を改良することを計画し、岡本貯水池を明治32年3月に起工、同33年5月に竣工しました。時を前後して、同貯水池の原水を浄化するための施設(旧矢岳浄水場)を明治31年10月に着工し両方を8インチ(20cm強)鉄管で結びました。 しかし、両方の高低差が146メートルにもあり水圧が高くなりすぎることから、調整を行う減圧井を「堺木」と「野中」に設けました。 この建物は、赤煉瓦造りで屋根は日本瓦葺、和洋折衷の当時としては大変モダンな水道施設で、旧矢岳浄水場が竣工した明治34年頃完成したものと思われます。 現在は使用されていませんが、当時を偲ぶ貴重な施設です。 平成5年7月 佐世保市水道局

なお、堺木峠減圧井は通りに面しているため、Google ストリートビューでも見ることができます。

ImageMagickを使ったハミング距離による画像の同一性チェック

画像認識に用いられるライブラリといえば OpenCV が一般的ですが、毎回 face detect のデモを動かしてみて安心してしまい、結果的にいつも本腰を入れて使ってみることがありませんでした。(やりたいなぁとは思いつつ、特に目的がない…)

今回、知人と OpenCV の話題が出たのをきっかけに ruby-opencv を使ってみようとしたけど挫折。OpenCV 本体のソースコードを clone してきて 3 時間近くもビルドを走らせて、環境作りにえらく時間がかかっちゃいました。もう少し根気があればいいんですが…。

さて、高機能な画像認識は必要がないような例、例えば、かなりゆるめの画像の同一性チェックなどでは、OpenCV のような本格的なライブラリを用意しなくても既存の環境でなんとかしたくなってしまいます。おっと、挫折したからって逃げではないですよ!ほんとですよ!

ということで、みんな大好き ImageMagick の convert コマンドに丸投げする形で、Ruby で書いていきたいと思います。 こちらの記事によると、縮小した画像を様々な視点からハッシュ化して、その値を比較することで類似度を算出しています。

今回は単純に明度同士の比較用いて、類似度を計算したいと思います。

手順

  1. ImageMagick を使って、比較対象の画像をそれぞれ 16×16 ピクセルの小さな画像に変換。言うなれば、モザイクのように色の平均値のパッチの集合となる。
  2. それぞれを2値化して、XBM で保存。XBM で保存するのは、Ruby 側から大掛かりな画像ライブラリを介すことなく、簡単に各々のピクセルの色情報を取得するため。
  3. 各々の XBM に含まれるピクセルデータをビット列に変換。
  4. 2つのビット列を比較し、類似性スコアを算出。

こういうのを「ハミング距離」を用いた判定と言うらしいです。

スクリプト

入力画像

試しに、次の2つの画像を入力として与えます。256×256 と 512×512 の大きさの違うレナさんです。

lenna256

lenna512

2値化した XBM として出力されるのは、それぞれ次のとおり。

lenna1

lenna2

似ていますが、入力のソースが若干違うために2値化結果にも差異が出ています。

この画像をビット列に変換し、スコア値を印字すると 0.9375 と表示されました。

面白い傾向として、最初に小さな画像にサンプリングする際の解像度を上げれば、より精度の高い類似性検出ができるのかと思いがちですが、実際にはその逆のパターンが多く見受けられました。

出力されるスコア値は 0.0 から 1.0 の値を取り、1.0 に近くなればなるほど類似度が高くなります。また、0.0 に近くなればなるほど、比較対象の明度が元画像に対して反転している可能性が高くなります。

この例では明度を2値化しているので、例えばほとんどのピクセルの明度が閾値以下(または、以上)になるような画像同士の比較には向いていません。また、部分マッチやトランスフォーメーション(回転・移動)に対する類似性を検出もできません。高度な検出を可能にするならば、やはり OpenCV を使うべきですが、明度だけではなく彩度やレベル・トーンカーブなど色々な側面を数列化して比較するとコストをかけずに検出精度を上げることも可能だと思われます。

HTML5+WebGLシーンがブラウザで簡単に作れる「Goo」を試してみた

siren で船型を作成し、Maya で艤装を作成して、Blender に持っていって Unity 向けに出力した FBX 形式の駆逐艦「島風」のモデルを試しに読み込ませてみると、難なく使うことができました。

上の画面内でマウスをドラッグすることにより、視点を変更することができます。また、Android の Firefox や Chrome でも、こちらの URL に直にアクセスすることにより、3D 表示&フリックによる視点回転とズームができました。

簡単な操船や海戦ゲームくらい作れそうですね!

ツイキャス録画スクリプト

リアルタイム動画配信サービス「ツイキャス」の録画をしてくれるシェルスクリプトを書いてみました。…と言っても、ffmpeg の入力ソースに食わせるだけですので、スクリプトというよりは例によって自分の覚え書きのようなものになっています。


使い方は、実行権限を付与して

./twitcast.sh ツイキャスアカウント名

という具合です。配信中であれば ffmpeg による録画が開始されます。

なお、パスワード付き配信は試していませんが未対応だと思います。

sinatra, mongoidで簡易ユーザセッション管理

DSC01813

先日の呑み会で Web フレームワークの話が出て、「サーバ挙動からまとめて書けたら便利だよね」という話の流れから久々に sinatra で遊んでみました。

下記、参考にしたページです。

sinatra は簡単に目的の Web アプリケーションが書けるというのが最大のメリットですが、かならずしも「初心者にも簡単」というわけではありません。 それぞれ、何のために何をやっているのかを十分に理解しておかないと変なところで詰まづくと思われます。

かくいう自分も、仕事でも趣味でも Web アプリケーションを書く機会に恵まれず、Rails すらまともに使ったことがない程度の知識。 実は数年前に nginx で Ruby を使ってちょっとしたツールを書こうと思った際に sinatra で一通り動くものを作ったことがありました。数年経つとほとんど忘れていて怖いです。

そんな自分のための覚書です。

セッション管理する基本的な構造

ディレクトリ構成

よく使う構成はテンプレート化してどこかに置いておけばいいんですが、やるたびに忘れているので毎回ゼロから構築してしまいます。かえって手間をかけてしまう…。

    app/
      +- assets/       … CSS や JavaScript のコードが格納されているディレクトリ。
      +- models/
      |    +- user.rb  … MongoDB に格納してもらうオブジェクトを定義してる User クラス
      +- spec/
      +- vender/
      |    +- bundle/  … 利用する gem 置き場
      +- views/
      +- Gemfile       … 利用する gem 定義
      +- app.rb        … Web アプリ本体
      +- config.ru     … Web アプリの定義ファイル
      +- mongoid.yml   … Web アプリが利用する MongoDB の定義ファイル

カレントディレクトリを app ディレクトリにしておいて、次のコマンドを使えばいろいろとできます。

    bundle init             # … このディレクトリを bundle アプリとして設定する
                            # bundle とは依存 gem 一式をローカルに配置して用いる仕組み

    bundle exec shotgun     # … サーバを起動
                            # shotgun はサーバを停止しなくても、スクリプトファイルの
                            # 更新時に自動リロードしてくれる便利な gem

    bundle install --path vender/bundle
                            # … Web アプリに必要な gem ファイルを vender/bundle にインストール
                            # Gemfile に明示的に書いたものをインストールしてくれる

Gemfile の例

使うものを使うだけ定義。ローカルの sinatra を使おうとしたら、バグがあったので最新リビジョンのやつを取ってきています。

    # A sample Gemfile
    source "https://rubygems.org"

    #gem "sass"
    gem "haml"
    gem "bcrypt"
    #gem "coffee-script"
    gem "mongoid"
    gem "shotgun"

    gem 'sinatra', git: 'git@github.com:sinatra/sinatra.git',
      ref: '5f6168bfc92280892e819df524d4508cf9032f6d'
      # for https://github.com/sinatra/sinatra/issues/961

    group :test do
      gem 'rspec'
    end

config.ru の設定

    root = ::File.dirname(__FILE__)
    require ::File.join(root, 'app')
    run Server

MongoDB の設定

MongoDB をインストール後、使う DB やホスト名を指定します。

    development:
      sessions:
        default:
          database: sinatora_test
          hosts:
            - localhost:27017
    production:
      sessions:
        default:
          uri: <%= ENV['MONGOHQ_URL'] %>

HAML テンプレート

views/login.haml

ログイン前に表示されるページのテンプレート。

    %fieldset
      %legend ログイン
      %form{:action => '/session', :method => 'post'}

        %label{:for => "name"} 名前:
        %input{:name => "name", :type => "text", :value => ""}

        %label{:for => "password"} パスワード:
        %input{:name => "password", :type => "password", :value => ""}

        %input{:type => "submit", :value => "ログイン"}

    %fieldset
      %legend 新規登録
      %form{:action => '/regist', :method => 'post'}

        %label{:for => "name"} 名前:
        %input{:name => "name", :type => "text", :value => ""}

        %label{:for => "password"} パスワード:
        %input{:name => "password", :type => "password", :value => ""}

        %input{:type => "submit", :value => "新規登録"}

views/dashboard.haml

ログイン後に表示されるページのテンプレート。

    %h1 Dashboard
    %table.table
      %tbody
        %tr
          %td Name
          %td= @user.name
        %tr
          %td Password(hashed)
          %td= @user.password_hash
    %a(href="/logout") ログアウト

models/user.rb

User クラス

    require 'mongoid'
    require 'bcrypt'
    Mongoid.load!('./mongoid.yml')

    class User
      include Mongoid::Document

      field :name
      field :password_hash
      field :password_salt

      attr_readonly :password_hash, :password_salt

      validates :name, presence: true
      validates :name, uniqueness: true
      validates :password_hash, confirmation: true
      validates :password_hash, presence: true
      validates :password_salt, presence: true

      # パスワードを暗号化するメソッド
      def encrypt_password(password)
        if password.present?
          self.password_salt = BCrypt::Engine.generate_salt
          self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
        end
      end

      # ユーザーが存在するか
      def self.has_user(name)
        self.where(name: name).first != nil
      end

      # ユーザー認証
      def self.authenticate(name, password)
        user = self.where(name: name).first
        if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
          user
        else
          nil
        end
      end

    end

app.rb

メインスクリプト

    require 'sinatra/base'
    require 'haml'

    require_relative 'models/user'

    class Server < Sinatra::Base

      enable :sessions
      set :session_secret, "My session secret"

      get '/login' do
        session[:user_id] ||= nil
        if session[:user_id]
          redirect '/'
        end
        if params['failed'] == "1"
          @msg = "ログインに失敗しました。"
        elsif params['failed'] == "2"
          @msg = "登録に失敗しました。"
        end
        haml :login
      end

      get '/logout' do
        session[:user_id] = nil
        redirect '/login'
      end

      post '/session' do
        if session[:user_id]
          redirect "/"
        end
        user = User.authenticate(params[:name], params[:password])
        if user
          session[:user_id] = user._id
          redirect '/'
        else
          redirect "/login?failed=1"
        end
      end

      post '/regist' do
        if User.has_user(params[:name])
          redirect "/login?failed=2"
        else
          user = User.new(name: params[:name])
          user.encrypt_password(params[:password])
          if user.save!
            session[:user_id] = user._id
            redirect "/"
          else
            redirect "/login?failed=2"
          end
        end
      end

      get '/' do
        @user = User.where(_id: session[:user_id]).first
        if @user
          haml :dashboard
        else
          redirect '/login'
        end
      end

    end

sinatra や MongoDB が悪いわけではなく、自分の頭が悪いだけなんですが、ひっぱってくるものが多すぎて普段使わないと思い出すだけで頭が痛くなります…(´・ω・`) 「sinatra」「bundle」「Rack」「haml」「slim」「erb」「mongodb」…普段全然使ってないものをひっぱってこようとすると、バックエンドまでちゃんと知らないとまともに使えないのはどの環境でも同じことですが…。 普段から使い始めて、自由自在に使えるようになったらこういう手順も楽チンなんだろうなあと憧れるばかりです。

とりあえず、仕事や趣味のプロジェクトでも比較的絡めやすそうな MongoDB の取り扱いから慣れていこうと考えてます。

グラフィックLCD風ディスプレイシミュレータ

去年の11月、フレームバッファに直接値を書き込んでプログラムからお絵描きがするような、手軽なプログラミングをやってみたい人向けに LCD 風ディスプレイシミュレータを書きました。

ある通常ファイルを /dev/fb0 のようなデバイスファイルに見立て、ファイルを開いてシークして値を書き込むことによって、疑似画面上にドットを打つことができます。

ダウンロード

Gist に Base64 エンコードした display.zip を置いておきました。Linux や Mac OS の場合は、base64 コマンドに -d オプションをつけることによって、元のバイナリにデコードできます。Windows の場合は、Lhaplus などのアーカイバでバイナリに戻せます。

使い方

display.exe はコマンドラインからオプション付きで実行します。

display 横ドット数 縦ドット数 1ドットあたりの大きさ 中間ファイルパス

次のように実行します。

display 64 24 5 fb0.bin

上の例では、解像度 64×24 ドットのグラフィック LCD ディスプレイとしてシミュレートし、1ドットあたりの表示ピクセル数は5、ファイルに fb0.bin を用いて起動しています。

仕組み

プログラム側から、

  1. ファイルを開く
  2. ビット列を書き込む
  3. ファイルを閉じる

の一連の作業を行います。アニメーションさせる場合は、1 から 3 を sleep() を入れて繰りかえします。

サンプルプログラム

次のサンプルでは、ファイル fb0.bin を中間ファイルとして display.exe から監視させています。

このサンプルを実行しながら display.exe で表示を行うと、次のような絵が表示されて、波がアニメーションします。

anime

ufwを使ったファイアウォール設定

ufw コマンドは、複雑な設定を要する iptables を直接操作することなく、簡単な操作だけでファイアウォールの設定を行うことができる便利なコマンドです。 以下、簡単な操作のはずなのに、設定するたびに忘れる私のダメな脳味噌のための備忘録です。

  • ポートを許可する、制限する

    ufw allow 1234
    ufw allow 1234/TCP
    ufw denny 1234
    
  • ファイアウォールを有効、無効にする

    ufw enable
    ufw disable
    
  • 現在の設定状況を調べる

    ufw status
    

※ ちなみに、設定方法から設定例まで man ページに丁寧に書いてあります。

祖父の写真

今月いよいよ30歳になることもあり、身の周りを綺麗にしておこうと、年が明けてから週末はもっぱら家の掃除をしています。引っ越してからずっと開けていなかった段ボールを開いてみると懐しいものがたくさん出てきました。 (断捨離的側面から考えると、そういう段ボールは開封せずにそのまま捨てた方がいいのかもしれませんが…)

特に中学から高校にかけて、自分のパソコンのバックアップやデータ移動のために焼いた CD-R やフロッピーディスクには、その当時やっていたこと・興味を持っていたことがたくさん詰まっています。

その中に、ちょうど1年前の今日他界した祖父の写真データが3枚だけありました。 大正9年生まれの祖父は、特攻で有名な鹿児島県の知覧飛行場の目と鼻の先にある川辺中学校を卒業後、江田島の海軍兵学校に第70期生として入学しました。

写真は岩国海軍航空隊で航空術教練中の祖父(主翼上中央)です。江田島からだと岩国よりも呉海軍航空隊の方が近いですが、予科練生やその他実習生の教練はもっぱら岩国の方だったようです。飛行機はあんまり詳しくないのですが、写っている機体はどうも九九式艦上爆撃機のようです。生前の祖父から聞いた話では「赤トンボ(九三式中間練習機)に乗って飛んだ」という話ですので、写真撮影用に主力の艦爆と一緒に撮影したのかもしれません。

次の写真は、航空母艦「隼鷹」の第一士官次室記念撮影です。前列左から5番目が祖父で、部下の皆さんと昭和20年の正月に撮ったようです。隼鷹の飛行甲板上で撮影したと思われる写真です。祖父は海軍兵学校卒業後、潜水母艦「長鯨」で洋上任務についた後、「隼鷹」の後部機銃座の指揮(ケップガン)をやっていました。

昭和19年の暮れ、戦艦「武蔵」の生存者200名を乗せた隼鷹は、戦艦「榛名」、駆逐艦「」とともに台湾から日本へ帰投する途中、12月9日の夜に米潜水艦の攻撃を受けて船体と機関部を損傷しています。祖父から「浸水によるすさまじい傾斜だったが、なんとか佐世保まで持ち堪えた」と聞いています。時系列に関しては、Wikipedia に詳しい記事が載っています。

なお、後列左から2番目に上を見上げている人がいますが、祖父曰く「正月で酒を飲んだ後の撮影だったから、酔っぱらってふざけているんだろう」とのことでした。

最後の写真は、駆逐艦「」の准士官以上記念撮影です。これは終戦直後の昭和20年〜21年ごろ撮られた写真で、祖父は前列左から2番目です。復員輸送に従事していた際に「楠」の副長をしていました。その向かって右隣が艦長、さらにその右の顎髭を蓄えた方は軍医だと聞いています。なお、最終的な階級は大尉だったようです。

MD5ハッシュ値をURLセーフな文字列に変換

大量に保存した画像ファイルなどの重複潰しも兼ねて、ファイル名を MD5 ハッシュ値にリネームして保存しています。

dyama/hashmv · GitHub

まったく同一のファイルであれば、ハッシュ値が同一になりますので、ファイル名が同一になります。上のスクリプト「hashmv」ではリネーム時に同一ファイルを上書きしますので、結果、重複ファイルを除去することができます。

実行イメージ

    $ hashmv foobar.jpg
    foobar.jpg -> ./d41d8cd98f00b204e9800998ecf8427e.jpg    

hashmv では、32 文字のハッシュ値のそのままファイル名に変換しているので、ちょっと長いなと思いました。md5sum のオプションを見ても、もうちょっとコンパクトな出力を得るオプションが見当たらなかったので、車輪の再発明上等でシェルスクリプトを書いてみました。

    #!bash
    function compress_hash()
    {
      echo $2 | sed \
        -e 's/aaa*/A/g' \
        -e 's/abb*/B/g' \
        -e 's/acc*/C/g' \
        -e 's/add*/D/g' \
        -e 's/aee*/E/g' \
        -e 's/aff*/F/g' \
        -e 's/baa*/G/g' -e 's/ecc*/g/g' \
        -e 's/bbb*/H/g' -e 's/edd*/h/g' \
        -e 's/bcc*/I/g' -e 's/eee*/i/g' \
        -e 's/bdd*/J/g' -e 's/eff*/j/g' \
        -e 's/bee*/K/g' -e 's/faa*/k/g' \
        -e 's/bff*/L/g' -e 's/fbb*/l/g' \
        -e 's/caa*/N/g' -e 's/fcc*/n/g' \
        -e 's/cbb*/M/g' -e 's/fdd*/m/g' \
        -e 's/ccc*/O/g' -e 's/fee*/o/g' \
        -e 's/cdd*/P/g' -e 's/fff*/p/g' \
        -e 's/cee*/Q/g' -e 's/0[0-9][0-9]*/q/g' \
        -e 's/cff*/R/g' -e 's/1[0-9][0-9]*/r/g' \
        -e 's/daa*/S/g' -e 's/2[0-9][0-9]*/s/g' \
        -e 's/dbb*/T/g' -e 's/3[0-9][0-9]*/t/g' \
        -e 's/dcc*/U/g' -e 's/4[0-9][0-9]*/u/g' \
        -e 's/ddd*/V/g' -e 's/5[0-9][0-9]*/v/g' \
        -e 's/dee*/W/g' -e 's/6[0-9][0-9]*/w/g' \
        -e 's/dff*/X/g' -e 's/7[0-9][0-9]*/x/g' \
        -e 's/eaa*/Y/g' -e 's/8[0-9][0-9]*/y/g' \
        -e 's/ebb*/Z/g' -e 's/9[0-9][0-9]*/z/g' \
        | cut -c -$1
    }

    for hashstr in $@
    do
      compress_hash 6 $hashstr
    done

元のハッシュ値は [a-f0-9] の文字集合ですが、パターンマッチングによって [a-fA-Z0-9] の文字集合に置き替え、桁数を落としています。見たとおり、ある一定のパターンの繰り返しを単一文字に置き換えているので、上のスクリプトでは元ハッシュ値に対する可逆性はありません。さらに、破壊的に圧縮したハッシュ値をcut で任意の桁数に切り落としています。

次に、評価用のコードを示します。

    while :
    do
      hash=$(date +%s | md5sum | cut -f 1 -d ' ')
      compress_hash 6 $hash
      sleep 1
    done

1秒おきに、UNIX タイム文字列を変換して印字する評価コードです。実行すると

    dFc7wa
    vqA5cx
    rc1a1d
    ucufyV
    1d2b8c
    qm9Le4
    6sqc5c
    atavIr
    7bqc5r
    3auf7b
    ....

のように、6 桁の圧縮されたハッシュ値が出てきます。

短縮 URL などの用途に代表されるように、URL セーフな文字列で桁数を抑えつつもある程度のパターン数は稼ぎたい場合に有効かもしれません。なお、元のハッシュ値は基数が a-z0-f の 16 ですが、これは a-zA-Z0-9 の 62 となる為、6 桁でも約 568 億パターンも稼ぐことが可能です。

    # 62^10 = 839299365868340224 (約84京パターン)
    # 62^9  =  13537086546263552 (約1.3京パターン)
    # 62^8  =    218340105584896 (約218兆パターン)
    # 62^7  =      3521614606208 (約3.5兆パターン)
    # 62^6  =        56800235584 (約568億パターン)
    # 62^5  =          916132832 (約9億パターン)
    # 62^4  =           14776336 (約1477万パターン)

6 桁なら長くなく、覚えられない長さではないし、1/568億の確率で衝突するくらいの強さは持ってますので、ファイル管理には十分だと思いました。

なお、a-zA-Z0-9 の文字集合に加えて記号 /+= を許可する場合は、上のようなスクリプトではなくハッシュ値をそのまま base64 エンコードした方が手っ取り早いです。