アーカイブ

‘未分類’ カテゴリーのアーカイブ

26歳になったので

2010 年 8 月 18 日 shokai コメントはありません
カテゴリー: 未分類 タグ: , , ,

8月15日に26歳になったので、新しい自分を探すためにtwitterのプロフィールを自動的に更新するようにした

30ed9b71553d2f33922bc39e7368a008

838d669e96f189702c3f9844965ace8e

人間以外にもなれる

eac518c56870949065eeeed905d34ae4

e85cb1527f60394255fe556f41cc3e62

9c4c1cb1c6fadac1ff09f120b5585892

7d4fcd1cd71ba0e8693133439b4751eb

ソースコードは全部githubに置いた

ランダムに適当な紹介文を取ってくる

wikipedia.rb


# -*- coding: utf-8 -*-

require 'rubygems'

require 'open-uri'

require 'uri'

require 'nokogiri'

require 'kconv'

require 'net/http'

class Wikipedia

  def initialize(agent_name)

    @agent_name = agent_name

  end

  def random

    get('特別:おまかせ表示')

  end

 

  def get(name)

    doc = Nokogiri::HTML open(URI.encode("http://ja.wikipedia.org/wiki/#{name}"), 'User-Agent' => @agent_name).read.toutf8

   

    title = doc.xpath('//title').first.text

    name = doc.xpath('//h1').first.text

    descriptions = doc.xpath('//div[@id="bodyContent"]//p').map{|i|i.text}

    {

      :title => title,

      :name => name,

      :descriptions => descriptions

    }

  end

end

このスクリプトをcronで定期的に実行して更新してる

change-profile.rb


#!/usr/bin/env ruby

# -*- coding: utf-8 -*-

require 'rubygems'

require 'twitter'

require 'yaml'

require File.dirname(__FILE__)+'/lib/wikipedia'

$KCODE = 'u'

begin

  conf = YAML::load open(File.dirname(__FILE__) + '/config.yaml')

rescue

  STDERR.puts 'config.yaml load error'

  exit 1

end

tw = Twitter::Base.new(Twitter::HTTPAuth.new(conf['name'], conf['pass']))

w = Wikipedia.new('shokai')

desc = nil

5.times do

  data = w.random

 

  desc = data[:descriptions].first

  desc.gsub!(/\[\d+\]/, '')

  tmp = desc.split(/(と?は)/)

  left = tmp.shift

  while left =~ /([^)]+$/ do

    tmp.shift

    left = tmp.shift

  end

  desc = "#{conf['your_name']}#{tmp.join('')}".toutf8

  puts '-'*10

  print data[:name] + ' => '

  puts desc

  break if desc != conf['your_name']

end

exit if desc == nil or desc == conf['your_name']

tw.update_profile({'description' => desc})


ZeroMQでOpenCV cvOpticalFlowのデータを配信する

2010 年 8 月 11 日 shokai コメントはありません
カテゴリー: 未分類 タグ: , , , , , ,

1VQ9がZeroMQで遊んでたので、俺も橋本商会 » cvCalcOpticalFlowBMをZeroMQでpubしてみた。ZeroMQはなんか面倒な事を適当にやってくれるmessaging libraryで、色々な言語のバインディングが出ている。

ZeroMQのpubはセンサーのデータとかを垂れ流しにするのに都合がよさそう。
clientが何台いるかどうかを考えないで良いし、pub/subどちらが先に起動していても適当に接続処理をしてくれる。cookbookを見てるとmulticastやthread間通信にも使ってる。とりあえずセンサーデータ垂れ流しという用途に俺はよく使いそう。


ソースコードはgithubに置いた
他にも単純なカウントアップのpub/sub両方をC++/C/Rubyで書いた(6種)のと、twitterのstream APIをZMQ_PUBで中継するのを作ってみた(解説:zeromqインストール、twitter stream APIを中継 – 橋本詳解)。特にstream APIのHUB的存在は便利。

あと、mongrel2WebSocketやXMLSocketとZeroMQの接続をしてくれるようになるらしくて期待してる。



受信側
opticalflow_sub.rb

#!/usr/bin/env ruby
require 'rubygems'
require 'zmq'

ctx = ZMQ::Context.new
sock= ctx.socket(ZMQ::SUB)
sock.connect('tcp://127.0.0.1:5000')
sock.setsockopt(ZMQ::SUBSCRIBE, 'opticalflow')

loop do
puts sock.recv()
end


送信側。これを適当なパソンコにUSBカメラ刺して動かしておけば、別のマシンから動きが取れる!!
opticalflow_pub.cpp
// http://opencv.jp/sample/optical_flow.html
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
#include <ctype.h>
#include <stdio.h>
#include <iostream>
#include <boost/format.hpp>
#include <zmq.hpp>

using namespace std;
using namespace boost;

void detect_flow(IplImage *img, IplImage *img_p, IplImage *dst);
zmq::context_t ctx(1);
zmq::socket_t sock(ctx, ZMQ_PUB);

int main(int argc, char* argv[]) {
IplImage *img = NULL;
CvCapture *capture = NULL;
capture = cvCreateCameraCapture(0);
//capture = cvCaptureFromAVI("test.mov");
if(capture == NULL){
cerr << "capture device not found!!" << endl;
return -1;
}

sock.bind("tcp://127.0.0.1:5000");

CvSize size = cvSize(320, 240);
IplImage *img_resized = cvCreateImage(size, IPL_DEPTH_8U, 3);
IplImage *img_gray = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage *img_gray_p = cvCreateImage(size, IPL_DEPTH_8U, 1);
IplImage *img_dst = cvCreateImage(size, IPL_DEPTH_8U, 3);

char winNameCapture[] = "Capture";
cvNamedWindow(winNameCapture, CV_WINDOW_AUTOSIZE);

while (1) {
img = cvQueryFrame(capture);
cvResize(img, img_resized);
cvCvtColor(img_resized, img_gray, CV_BGR2GRAY);
cvCopy(img_resized, img_dst);
detect_flow(img_gray, img_gray_p, img_dst);
cvShowImage(winNameCapture, img_dst);
cvCopy(img_gray, img_gray_p);
if (cvWaitKey(10) == 'q') break;
}

cvReleaseCapture(&capture);
cvDestroyWindow(winNameCapture);

return 0;
}

void detect_flow(IplImage *src_img1, IplImage *src_img2, IplImage *dst_img){
int i, j, dx, dy, rows, cols;
int block_size = 24;
int shift_size = 10;
CvMat *velx, *vely;
CvSize block = cvSize(block_size, block_size);
CvSize shift = cvSize(shift_size, shift_size);
CvSize max_range = cvSize(50, 50);

rows = int(ceil (double (src_img1->height) / block_size));
cols = int(ceil (double (src_img1->width) / block_size));
velx = cvCreateMat(rows, cols, CV_32FC1);
vely = cvCreateMat(rows, cols, CV_32FC1);
cvSetZero(velx);
cvSetZero(vely);

cvCalcOpticalFlowBM(src_img1, src_img2, block, shift, max_range, 0, velx, vely);
string result_str = string("");
for (i = 0; i < velx->width; i++) {
for (j = 0; j < vely->height; j++) {
dx = (int)cvGetReal2D(velx, j, i);
dy = (int)cvGetReal2D(vely, j, i);
cvLine(dst_img, cvPoint(i * block_size, j * block_size),
cvPoint(i * block_size + dx, j * block_size + dy), CV_RGB(255, 0, 0), 1, CV_AA, 0);
if(dx != 0 || dy != 0){
result_str += str(format("[%d,%d,%d,%d]") % (i*block_size) % (j*block_size) % dx % dy);
}
}
}
if(result_str.size() > 0){
result_str = str(format("opticalflow %s") % result_str);
cout << result_str << endl;
zmq::message_t msg(result_str.size()+1); // ZeroMQ
memcpy(msg.data(), result_str.c_str(), result_str.size()+1);
sock.send(msg);
}
}


g++ -O opticalflow_pub.cpp -o opticalflow_pub.bin -I/opt/local/include/opencv -lcv -lcvaux -lcxcore -lhighgui  -I/usr/local/include /usr/local/lib/libzmq.a


これで動いた座標とその方向 [x,y,dx,dy] が連続で送られてくる。
opticalflow [48,216,4,-29][72,216,0,-29][96,216,0,-29][264,216,-9,-29]
opticalflow [48,216,4,-29][96,216,0,-29][120,216,0,-29][264,216,-9,-29]
opticalflow [48,216,4,-29][96,168,0,10][96,192,-10,-20][96,216,0,-29][120,192,0,10][120,216,0,-29][144,216,0,-29][168,216,0,-29][192,48,-10,0][192,216,0,-29][216,216,0,-29][264,216,-9,-29]
opticalflow [96,168,0,10][96,192,-10,-10][96,216,0,-29][120,168,0,10][120,192,0,10][120,216,0,-29][144,216,0,-29][168,48,0,10][168,96,0,10][168,216,0,-29][192,72,0,40][192,96,0,-30][192,216,0,-29][264,216,-9,-29]
opticalflow [48,216,4,-29][96,168,0,10][96,216,0,-29][120,168,0,10][120,192,0,10][120,216,0,-29][144,216,0,-29][168,48,10,0][168,96,0,10][168,216,0,-29][192,96,0,-30][192,216,0,-29][264,216,-9,-29]

mongoid使ってみる

2010 年 8 月 10 日 shokai コメントはありません
カテゴリー: 未分類 タグ: , , ,

mongo単体で使ってみててだいたい分かったので、mongoidというmapperを使ってみる。

mongoidの良いのは

  • default値を入れておきたい場合も簡単に書ける。created_atとか。
  • _idでdocumentを取り出すとき、素のmongoだとcollection.find_one(BSON::ObjectID(id))とかしないとならないけどmongoidだと_idに文字列でID入れればいい
とかがぱっと使ってみて思った。。
そもそもこういうのmongoの機能にあるかもしれないけど。


■ドキュメント


■インストール
sudo gem install mongoid
1.9.1を使う。–pre付けるとRails3対応の2.x系統が入る。


■modelを作る
適当にperson class作って、Mongoid::Documentにする

person.rb
require 'rubygems'

class Person
include Mongoid::Document
field :fullname # 指定無しでtype=>stringになる
field :username
field :age, :type => Integer
field :created_at, :type => DateTime, :default => Time.now
end
string以外は型指定する。型はArray, BigDecimal, Boolean, Date, DateTime, Float, Integer, String, Symbol, Timeがある。
Mongoid Documentation: Documents

defaultで現在時刻を入れるようにした。


■mongodbへ接続
Mongoid.configureのブロック内で接続する。
conf.masterに普通のmongoで接続してdbを指定した時の返り値(Mongo::DBオブジェクト)を与えれば、mongoidで使える。
require&nbsp;'rubygems'
require&nbsp;'mongoid'
require&nbsp;File.dirname(__FILE__)+'/person'

Mongoid.configure&nbsp;do&nbsp;|conf|
&nbsp;&nbsp;conf.master&nbsp;=&nbsp;Mongo::Connection.new('localhost',&nbsp;27017).db('mongoid-test')
end



■modelの操作
新しいpersonオブジェクト作って保存
person = Person.new(:fullname => 'sho hashimoto',
:username => 'shokai',
:age => 25)

puts person.fullname
puts person.age

person.save


保存されてるか、mongoのコンソールで確かめる
personで保存したら、自動的に複数形のpeopleになってた。ActiveRecordっぽい。
% mongo
MongoDB&nbsp;shell&nbsp;version:&nbsp;1.4.4
url:&nbsp;test
connecting&nbsp;to:&nbsp;test
type&nbsp;"help"&nbsp;for&nbsp;help
>&nbsp;show&nbsp;dbs
admin
chirpstream_shokai
local
mongoid-test
people
povietest
test
testdb
>&nbsp;use&nbsp;mongoid-test
switched&nbsp;to&nbsp;db&nbsp;mongoid-test
>&nbsp;show&nbsp;collections
people
system.indexes
>&nbsp;db.people.find()
{&nbsp;"_id"&nbsp;:&nbsp;"4c61463c2f7306e9fe000001",&nbsp;"created_at"&nbsp;:&nbsp;"Tue&nbsp;Aug&nbsp;10&nbsp;2010&nbsp;21:29:48&nbsp;GMT+0900&nbsp;(JST)",&nbsp;"fullname"&nbsp;:&nbsp;"sho&nbsp;hashimoto",&nbsp;"username"&nbsp;:&nbsp;"shokai",&nbsp;"age"&nbsp;:&nbsp;25&nbsp;}
{&nbsp;"_id"&nbsp;:&nbsp;"4c614d652f73060653000001",&nbsp;"created_at"&nbsp;:&nbsp;"Tue&nbsp;Aug&nbsp;10&nbsp;2010&nbsp;22:00:21&nbsp;GMT+0900&nbsp;(JST)",&nbsp;"fullname"&nbsp;:&nbsp;"sho&nbsp;hashimoto",&nbsp;"username"&nbsp;:&nbsp;"shokai",&nbsp;"age"&nbsp;:&nbsp;25&nbsp;}
2回保存したから複数保存されてた


■find
探す。Mongoid Documentation: Queryingにqueryの書き方が載ってる。
適当にユーザ名shokaiの最初の一件を取得して、表示する
person = Person.first(:conditions => {:username => 'shokai'})
puts person._id
puts person.username
puts person.created_at

他にも、全件とか色々な書き方ができる。
person = Person.find(:first, :conditions => {:username => 'shokai'})
person = Person.all(:conditions => {:username => 'shokai'}).first
person = Person.first(:conditions => {:_id => '4c61463c2f7306e9fe000001'})
allで検索したら結果が1件しか無くても、collectionで返ってくる。eachで回せる。



■modelに書いてない値を入れてみる
modelにない、person.placeを入れてみる
person = Person.new(:fullname => 'sho hashimoto',
:username => 'shokai',
:age => 25,
:place => 'fujisawa')

puts person.fullname
puts person.age
puts person.place

person.save
普通に入ってた。このへんは複数人でやるときは何か考えないとならないな。
でもクロールしてきた値とかを適当にどんどん入れてしまうのにはすごくいい。twitterのchirp streamとか。


数値はlt,gtで大なり小なり条件指定できるらしい。
あとmongodbは空間型があるはずだけどそれは使えないのかな?filedの型になかったけど。

JRubyでglitch iconを作る(2)

2010 年 8 月 6 日 shokai コメントはありません

プラグイン機構を採用し、ランダムにいろいろ作れるようにしてみた。
普通はjpegとかのバイナリを直接いじるみたいだけどよく分からないのでJRubyでjavax.imageioを使ってやっている。

ランダムに96個作ってみて8×12に敷き詰めてみたのがこれ。
で、定期的にランダムに作ってtwitterのアイコンとしてアップロードしてる
montage96
敷き詰めるのはImageMagickと一緒にインストールされるmontageコマンドでできる montageコマンド – 橋本詳解

ソースコードは全てgithubに置いた


■仕組み
単機能モジュールをランダムに連結する事である程度ランダムな画像を生成できる。
javax.imageio.BufferedImageのインスタンスを受け取り、少し加工して返すというJRubyのmoduleをプラグインとし、それらをGlitchというclassがランダムに呼び出す。


今のところプラグインは23種類ある。単体だとそれほど派手にはならない。
glitchicon montage



■プラグイン
http://github.com/shokai/glitchicon/tree/master/plugins/にある。
pluginが動くルールはこれ。

  • pluginsディレクトリの中に置いておく
  • ファイル名末尾が.rbである
  • JRubyのmoduleである
  • ファイル名の先頭1文字を大文字にしたmodule名である
  • BufferedImageを受け取って、同じサイズのBufferedImageを返す glitch(BufferedImage) というstatic methodを持つ
  • 受け取ったBufferedImageに対しては破壊的に処理しても、そうでなくてもいい

プラグインはBufferedImageの処理に集中できるように、他の事はglitch.rbがやる。


どのプラグインも簡単にできている。30行ぐらい。

drumroll_verticalプラグインの場合(画像では一番左、下から2番目)
#!/usr/bin/env jruby
require 'java'
import 'java.lang.System'
import 'javax.imageio.ImageIO'
import 'java.awt.image.BufferedImage'


module Drumroll_vertical

  def Drumroll_vertical.glitch(img)
    img_result = BufferedImage.new(img.width, img.height, img.type)
    roll = 0
    for x in 0...img.width do
      roll = rand(img.height) if rand > 0.95
      roll = 0 if rand > 0.95
      for y in 0...img.height do
        pix = img.get_rgb(x, y)
        y2 = y+roll
        y2 -= img.height if y2 > img.height-1
        img_result.set_rgb(x, y2, pix)
      end
    end
    return img_result
  end

end
ランダムに縦に区切って、ランダムにスロットのドラムみたいにずらす。


色を反転させるcolor_reverseプラグイン
#!/usr/bin/env jruby
require 'java'
import 'java.lang.System'
import 'javax.imageio.ImageIO'
import 'java.awt.image.BufferedImage'


module Color_reverse

  def Color_reverse.glitch(img)
    for y in 0...img.height do
      for x in 0...img.width do
        pix = img.get_rgb(x, y)
        r = pix >> 16 & 0xFF
        g = pix >> 8 & 0xFF
        b = pix & 0xFF
        r = 256-r
        g = 256-g
        b = 256-b
        pix = ((r << 16)&0xFF0000 | (g << 8)&0xFF00 | b)
        img.set_rgb(x,y, pix)
      end
    end
    return img
  end

end


量子化した後に輪郭抽出するquantize_contour
#!/usr/bin/env jruby
require 'java'
import 'java.lang.System'
import 'javax.imageio.ImageIO'
import 'java.awt.image.BufferedImage'


module Quantize_contour

  def Quantize_contour.glitch(img)

    for y in 0...img.height do
      for x in 0...img.width do
        pix = img.get_rgb(x, y)
        r = pix >> 16 & 0xFF
        g = pix >> 8 & 0xFF
        b = pix & 0xFF
        gray = (r+g+b)/3
        quant = gray & 0xC0
        pix = ((quant << 16)&0xFF0000 | (quant << 8)&0xFF00 | quant)
        img.set_rgb(x, y, pix)
      end
    end

    img_result = BufferedImage.new(img.width, img.height, img.type)
    for y in 1...img.height-1 do
      for x in 1...img.width-1 do
        pix = img.get_rgb(x, y)
        around = (img.get_rgb(x-1,y)+img.get_rgb(x+1,y)+img.get_rgb(x,y-1)+img.get_rgb(x,y+1))/4
        if around < pix
          pix = 0
        else
          pix = 0xFFFFFF
        end
        img_result.set_rgb(x, y, pix)
      end
    end
    return img_result
  end

end


そんなかんじ。画像をいじるけど、元の画像を知っている人なら元画像を思い浮かべられる程度にglitchしたい。

JRubyでglitch iconを作る

2010 年 8 月 2 日 shokai コメントはありません
カテゴリー: 未分類 タグ: , , , ,

こういうのをtwitter iconにしたかった


Twitter のアイコンの目を光らせるライフハック – 地獄の猫日記を参考にアニメgifを作った。末尾も3C2Cにした。

でもちょうど今日からtwitterがGIFアニメアイコンのチェックを厳しくしたらしく、最初の1フレームだけの静止アイコンになってしまう。
既にアップされている他の人のアイコンを見ると、3C00002Cとかになってるのもあったけど色々ためしたけどやっぱりアップロードすると静止画像になる。


画像を生成するスクリプト
jrubyでjavax.imageioを使うと簡単。
最後のあたりのコメントアウトしているconvertかffmpegどちらでも、アニメーションgifを作れる。
glitchicon.rb

#!/usr/bin/env jruby
require 'java'
import 'java.lang.System'
import 'javax.imageio.ImageIO'
import 'java.awt.image.BufferedImage'

if ARGV.size < 2
  STDERR.puts 'require : input file and output dir'
  STDERR.puts 'jruby glitchicon.rb /path/to/img.jpg /path/to/out/'
  exit 1
end

img_in = ImageIO.read(java.io.File.new(ARGV.shift))
out_dir = ARGV.shift
out_dir += '/' unless out_dir =~ /\/$/

puts "#{img_in.width}x#{img_in.height}"

for i in 1..10
  img = BufferedImage.new(img_in.width, img_in.height, img_in.type);
  img.graphics.drawImage(img_in, 0, 0, nil)
  shifts = [0,0,0]
  for y in 0...img.height do
    if rand > 0.85
      shifts = shifts.map{|j|
        j = rand(255)-128
      }
    end
    for x in 0...img.width do
      pix = img.get_rgb(x, y)
      r = pix >> 16 & 0xFF
      g = pix >> 8 & 0xFF
      b = pix & 0xFF
      r += shifts[0]
      g += shifts[1]
      b += shifts[2]
      r = 255 if r > 255
      g = 255 if g > 255
      b = 255 if b > 255
      r = 0 if r < 0
      g = 0 if g < 0
      b = 0 if b < 0
      pix = ((r << 16)&0xFF0000 | (g << 8)&0xFF00 | b)
      "#{r},#{g},#{b}"
      img.set_rgb(x,y, pix)
    end
  end
  out_name = "#{out_dir}#{i}.bmp"
  ImageIO.write(img, 'bmp', java.io.File.new(out_name))
  puts out_name
end

puts `convert #{out_dir}*.bmp #{out_dir}out.gif`
#puts `ffmpeg -y -i #{out_dir}%d.bmp -s 100x100 -pix_fmt rgb24 -loop_output 0 -sameq #{out_dir}out.gif`

open("#{out_dir}out.gif"){|gif|
  bytes = gif.read.unpack('H*')
  bytes = [bytes.first.gsub!(/3b$/,'3c2c')]
  open("#{out_dir}glitchicon.gif",'w+'){|out|
    out.write(bytes.pack('H*'))
  }
}

track feed