Queueのどうでも良い話

thread.rbでQueue#.popがこのように定義されている。

 def pop(non_block=false)
    @mutex.synchronize{
      while true
        if @que.empty?
          raise ThreadError, "queue empty" if non_block
          @waiting.push Thread.current
          @mutex.sleep
        else
          return @que.shift
        end
      end
    }
  end

そこで、次のようなコードを実行すると

require 'thread'

queue = Queue.new

t = Thread.start do
  loop{ queue.pop }
end

2.times do
  nil until t.stop?
  t.wakeup
end

nil until t.stop?

queue.instance_eval{ p @waiting }
p queue.num_waiting

以下のようになる。

[#<Thread:0x00000001899698 sleep>, #<Thread:0x00000001899698 sleep>, #<Thread:0x00000001899698 sleep>]
3

本当にどうでも良い。

cvAvgを用いた標本分散フィルタ

指定したサイズのROIをずらしながら、ROI内の画素の標本分散を出力するフィルタを書いた。

#include <math.h>
#include "cv.h"
#include "highgui.h"

void VarianceFilter(IplImage *src, IplImage* dst, int w_size);

int main(int argc, char *argv[]){
  IplImage *img, *dst;

  img = cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE); /* 画像をグレースケールで読み込む */
  dst = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_8U, 1);

  VarianceFilter(img, dst, 3); /* 標本分散フィルタ */

  cvSaveImage("result.jpg", dst, 0); /* 結果画像の保存 */

  /* メモリの解放 */
  cvReleaseImage(&img);
  cvReleaseImage(&dst);

  return 0;
}

/*
  標本平均フィルタ
  引数:
    src: 入力画像
    dst: 出力先
    w_size: ウィンドウサイズ
 */
void VarianceFilter(IplImage *src, IplImage* dst, int w_size){
  int i, j, wi, wj;
  double avg, e;
  uchar f;

  for(i=0; i<src->height; i++){
    for(j=0; j<src->width; j++){
      cvSetImageROI(src, cvRect(j, i, w_size, w_size)); /* ROIをセット */
      avg = cvAvg(src, NULL).val[0]; /* ROI内の画素の標本平均 */

      /* 標本分散の計算 */
      e = 0.0;
      for(wi=i; wi<i+w_size; wi++){
        for(wj=j; wj<j+w_size; wj++){
          f = CV_IMAGE_ELEM(src, uchar, wi, wj);
          e += pow(avg-f, 2);
        }
      }
      e /= w_size * w_size;

      dst->imageData[dst->widthStep * i + j] = e; /* 出力 */
    }
  }

  cvResetImageROI(src); /* ROIの解除 */

  return;
}

入力画像

結果

cvCalcHistを用いたエントロピーフィルタ

最近OpenCVを始めて、ふとエントロピーフィルタを書いてみようと思い立った。
ここではROIをずらしながらcvCalcHistでヒストグラムを求めるエントロピーフィルタについて書く。

#include <math.h>
#include "cv.h"
#include "highgui.h"

void EntropyFilter(IplImage *src, IplImage* dst, int w_size);

int main(int argc, char *argv[]){
  IplImage *img, *dst;

  img = cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE); /* 画像をグレースケールで読み込む */
  dst = cvCreateImage(cvSize(img->width, img->height), IPL_DEPTH_8U, 1);

  EntropyFilter(img, dst, 3); /* エントロピーフィルタ */

  cvSaveImage("result.jpg", dst, 0); /* 結果画像の保存 */

  /* メモリの解放 */
  cvReleaseImage(&img);
  cvReleaseImage(&dst);

  return 0;
}

/*
  エントロピーフィルタ
  引数:
    src: 入力画像
    dst: 出力先
    w_size: ウィンドウサイズ
*/
void EntropyFilter(IplImage *src, IplImage* dst, int w_size){
  int i, j, k, hist_size = 256;
  double e, f, e_max;
  float range[] = { 0, 256 };
  float *ranges[] = { range };
  CvHistogram *hist;

  hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, 0, 1); /* ヒストグラムを生成する */

  f = 1.0 / (w_size*w_size);
  e_max = f * log(f) * w_size * w_size; /* エントロピーの最大値 */

  for(i=0; i<src->height; i++){
    for(j=0; j<src->width; j++){
      cvSetImageROI(src, cvRect(j, i, w_size, w_size)); /* ROIをセット */
      cvCalcHist(&src, hist, 0, NULL); /* ヒストグラムを計算する */
      /* エントロピーの計算 */
      e = 0.0;
      for(k=0; k<256; k++){
        if(f = cvQueryHistValue_1D(hist, k)){
          f /= w_size*w_size;
          e += f * log(f);
        }
      }
      dst->imageData[dst->widthStep * i + j] = e/e_max*255; /* 0〜255で正規化して格納 */
    }
  }

  cvResetImageROI(src); /* ROIの解除 */
  cvReleaseHist(&hist); /* ヒストグラムを解放する */

  return;
}

入力画像

結果

ウェザーニューズから受信した緊急地震速報をeew_parserに通して何かする

WNIから受信した緊急地震速報をeew_parserに通して色々するクラス
WNI_EEW.newにWNIのメールアドレスとパスワードとブロックを渡すと、WNIから緊急地震速報を受信した時にその緊急地震速報から生成したEEWParserオブジェクトをブロックに渡して実行してくれる。

require 'socket'
require 'eew_parser'
require 'digest/md5'
require 'open-uri'

class WNI_EEW
  DEBUG = true

  trap(:INT) do
    exit
  end

  def initialize(user_id, pass, &b)
    http = TCPSocket.open(get_server_addr, 80)
    http.print "GET /login HTTP/1.0\r\nX-WNI-Account: #{user_id}\r\nX-WNI-Password: #{Digest::MD5.hexdigest(pass)}\r\n\r\n"
    begin
      if WNI_HTTPHeader.new(http.readline("\n\n"))["X-WNI-Result"] == "OK"
        debug "[#{Time.now.strftime("%F %T")}] #{get_server_addr} との接続を確立しました"
      else
        abort "認証に失敗しました。"
      end
    rescue => ex
      abort "接続に失敗しました。#{ex.message}"
    end
    loop do
      case WNI_HTTPHeader.new(http.readline("\n\n"))["X-WNI-ID"]
      when "Keep-Alive"
        debug "[#{Time.now.strftime("%F %T")}] Keep-Alive"
      when "Data"
        http.readline("\n\x02\n\x02\n")
        yield EEWParser.new(http.readline("9999=").strip)
      end
    end
    http.close
  end

  def debug(str)
    puts str if DEBUG
  end

  def get_server_addr
    open('http://lst10s-sp.wni.co.jp/server_list.txt') do |list|
      list.read.lines.to_a.sample[0..-5]
    end
  end

  class WNI_HTTPHeader
    def initialize(str)
      @lines = str.lines.to_a
    end

    def [](key)
      @lines[1..-2].each do |line|
        field = line.split(":", 2)
        return field.last.strip if field.first == key
      end
    end
  end
end

WNI_EEW.new('WNIのメールアドレス', 'WNIのパスワード') do |eew|
  # ここで何かする
end

WNIのサーバはHTTPのヘッダフィールド名の大文字小文字を区別するので、net/httpは使えない。
仕方が無いのでTCPSocketを使って通信している。

高度利用者向け緊急地震速報の電文を扱う為のライブラリeew_parser

ウェザーニューズなどから受信した高度利用者向け緊急地震速報の電文を扱う為のRubyライブラリを書きました。
https://github.com/Glasssaga/eew_parser

require "eew_parser"

str = <<EOS # 緊急地震速報の電文
37 03 00 110415233453 C11
110415233416
ND20110415233435 NCN005 JD////////////// JN///
251 N370 E1408 010 66 6+ RK66324 RT01/// RC13///
EBI 251 S6+6- ////// 11 300 S5+5- ////// 11 250 S5+5- ////// 11
310 S0404 ////// 11 311 S0404 ////// 11 252 S0404 ////// 11
301 S0404 ////// 11 221 S0404 ////// 01 340 S0404 ////// 01
341 S0404 ////// 01 321 S0404 233455 00 331 S0404 233457 10
350 S0404 233501 00 360 S0404 233508 00 243 S0403 ////// 01
330 S0403 233454 00 222 S0403 233455 00
9999=
EOS

fc = EEWPaser.new(str)

puts <<EOS
電文種別コード: #{fc.type}
発信官署: #{fc.from}
訓練等の識別符: #{fc.drill_type}
電文の発表時刻: #{fc.report_time}
電文がこの電文を含め何通あるか: #{fc.number_of_telegram}
コードが続くかどうか: #{fc.continue?}
地震発生時刻もしくは地震検知時刻: #{fc.earthquake_time}
地震識別番号: #{fc.id}
発表状況(訂正等)の指示: #{fc.status}
発表する高度利用者向け緊急地震速報の番号(地震単位での通番): #{fc.number}
震央地名コード: #{fc.epicenter}
震央の位置: #{fc.position}
震源の深さ(単位 km)(不明・未設定時,キャンセル時:///): #{fc.depth}
マグニチュード(不明・未設定時、キャンセル時:///): #{fc.magnitude}
最大予測震度(不明・未設定時、キャンセル時://): #{fc.seismic_intensity}
震央の確からしさ: #{fc.probability_of_position}
震源の深さの確からしさ: #{fc.probability_of_depth}
マグニチュードの確からしさ: #{fc.probability_of_magnitude}
震央の確からしさ(気象庁の部内システムでの利用): #{fc.probability_of_position_jma}
震源の深さの確からしさ(気象庁の部内システムでの利用): #{fc.probability_of_depth_jma}
震央位置の海陸判定: #{fc.land_or_sea}
警報を含む内容かどうか: #{fc.warning?}
最大予測震度の変化: #{fc.change}
最大予測震度の変化の理由: #{fc.reason_of_chaage}
EOS
fc.ebi.each do |local|
  puts "地域コード: #{local[:area_cord]} 最大予測震度: #{local[:intensity]} 予想到達時刻: #{local[:arrival_time]}"
  puts "警報を含むかどうか: #{local[:warning]} 既に到達しているかどうか: #{local[:arrival]}"
end

このようなコードを書くと、

電文種別コード: マグニチュード、最大予測震度及び主要動到達予測時刻の高度利用者向け緊急地震速報(グリッドサーチ法、EPOS自動処理手法)
発信官署: 東京
訓練等の識別符: 通常
電文の発表時刻: 2011-04-15 23:34:53 +0900
電文がこの電文を含め何通あるか: 1
コードが続くかどうか: true
地震発生時刻もしくは地震検知時刻: 2011-04-15 23:34:16 +0900
地震識別番号: 20110415233435
発表状況(訂正等)の指示: 通常発表
発表する高度利用者向け緊急地震速報の番号(地震単位での通番): 5
震央地名コード: 福島県浜通り
震央の位置: N37.0 E140.8
震源の深さ(単位 km)(不明・未設定時,キャンセル時:///): 10
マグニチュード(不明・未設定時、キャンセル時:///): 6.6
最大予測震度(不明・未設定時、キャンセル時://): 6強
震央の確からしさ: 防災科研システム(5点以上)[防災科学技術研究所データ]
震源の深さの確からしさ: 防災科研システム(5点以上)[防災科学技術研究所データ]
マグニチュードの確からしさ: 全点P相(最大5点)[気象庁データ]
震央の確からしさ(気象庁の部内システムでの利用): テリトリー法(2点)[気象庁データ]
震源の深さの確からしさ(気象庁の部内システムでの利用): グリッドサーチ法(5点)[気象庁データ]
震央位置の海陸判定: 陸域
警報を含む内容かどうか: true
最大予測震度の変化: 最大予測震度が1.0以上大きくなった
最大予測震度の変化の理由: M及び震源位置が変化したため
地域コード: 福島県浜通り 最大予測震度: 6弱から6強 予想到達時刻:
警報を含むかどうか: true 既に到達しているかどうか: true
地域コード: 茨城県北部 最大予測震度: 5弱から5強 予想到達時刻:
警報を含むかどうか: true 既に到達しているかどうか: true
地域コード: 福島県中通り 最大予測震度: 5弱から5強 予想到達時刻:
警報を含むかどうか: true 既に到達しているかどうか: true
地域コード: 栃木県北部 最大予測震度: 4 予想到達時刻:
警報を含むかどうか: true 既に到達しているかどうか: true
地域コード: 栃木県南部 最大予測震度: 4 予想到達時刻:
警報を含むかどうか: true 既に到達しているかどうか: true
地域コード: 福島県会津 最大予測震度: 4 予想到達時刻:
警報を含むかどうか: true 既に到達しているかどうか: true
地域コード: 茨城県南部 最大予測震度: 4 予想到達時刻:
警報を含むかどうか: true 既に到達しているかどうか: true
地域コード: 宮城県南部 最大予測震度: 4 予想到達時刻:
警報を含むかどうか: false 既に到達しているかどうか: true
地域コード: 千葉県北東部 最大予測震度: 4 予想到達時刻:
警報を含むかどうか: false 既に到達しているかどうか: true
地域コード: 千葉県北西部 最大予測震度: 4 予想到達時刻:
警報を含むかどうか: false 既に到達しているかどうか: true
地域コード: 群馬県南部 最大予測震度: 4 予想到達時刻: 2011-04-15 23:34:55 +0900
警報を含むかどうか: false 既に到達しているかどうか: false
地域コード: 埼玉県南部 最大予測震度: 4 予想到達時刻: 2011-04-15 23:34:57 +0900
警報を含むかどうか: true 既に到達しているかどうか: false
地域コード: 東京都23区 最大予測震度: 4 予想到達時刻: 2011-04-15 23:35:01 +0900
警報を含むかどうか: false 既に到達しているかどうか: false
地域コード: 神奈川県東部 最大予測震度: 4 予想到達時刻: 2011-04-15 23:35:08 +0900
警報を含むかどうか: false 既に到達しているかどうか: false
地域コード: 山形県置賜 最大予測震度: 3から4 予想到達時刻:
警報を含むかどうか: false 既に到達しているかどうか: true
地域コード: 埼玉県北部 最大予測震度: 3から4 予想到達時刻: 2011-04-15 23:34:54 +0900
警報を含むかどうか: false 既に到達しているかどうか: false
地域コード: 宮城県中部 最大予測震度: 3から4 予想到達時刻: 2011-04-15 23:34:55 +0900
警報を含むかどうか: false 既に到達しているかどうか: false

こんな風に解析してくれます。

gem install eew_parser

でインストールできます。
詳しくはドキュメントを参照して下さい。

配列のようなものを可読な状態で保存

最近はロイディという人工無脳を弄って遊んでいる。 
本家ロイディは、ログと単語の保存をバイナリな内部ファイルと、human readableな外部ファイルに分けて保存し、外部ファイルに変更があればそれを内部ファイルに反映するという少し面倒な事をしているので、これを改善しようと思い立った。
人間が読めてかつRubyのオブジェクトとしての復元も可能な形で保存が出来れば、内外の区別を廃して1つのファイルにまとめる事ができる。
必要としては、n番目の要素を取り出せ、末尾に要素を追加出来ればそれで良かった。
まず思いついたのがYAML::Storeだったが、何もかも1つのYAMLドキュメントに突っ込む仕様である為、トランザクションの出入りを行う度に巨大なYAMLドキュメントを読み書きする事になり、メモリの消費も激しく、遅い。
そこでYAMLドキュメントを細かく分け、必要な部分だけをロードするようにした。

#n番目の要素を取り出す
def [](n)
  n += @size if n < 0 #末尾からのインデックス
  File.open(@filename) do |f|
    f.each_line("\n---") do |s|
      return YAML.load(s) if f.lineno > n
    end
  end
end

新たな要素をpushしたい時は、追加したい要素をYAML.dumpしてファイルに追記するだけでよい。
複数のロイディが同じファイルを共有して動作する事は想定していないので、ファイルのロックもしないし、トランザクションという概念もないが、用途には十分であった。

慶應義塾大学の学生がDreamSparkで幸せになる方法

ac.jpなアドレスを持っていない学生は以下の方法でDreamSparkできる。

  1. https://www.dreamspark.com/ に適当なLiveアカウントでログインしておく。なければ作る。
  2. http://linno.jp/ でkeio.jpのメールアドレスを使って適当にアカウントを作る。
  3. http://linno.jp/campaign/dreamspark から「ダウンロード」と書いてあるボタンをクリック

 
これで認証され、無料にてダウンロードができる。
受験生の皆さんはac.jpなメールアドレスをくれるまともな大学を選んだ方が良いと思います。