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

ソースコードは全部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
enddef 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
}
endend
このスクリプトを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
endtw = 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のデータを配信する
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的存在は便利。
あと、mongrel2がWebSocketや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使ってみる
mongo単体で使ってみててだいたい分かったので、mongoidというmapperを使ってみる。
mongoidの良いのは
- default値を入れておきたい場合も簡単に書ける。created_atとか。
- _idでdocumentを取り出すとき、素のmongoだとcollection.find_one(BSON::ObjectID(id))とかしないとならないけどmongoidだと_idに文字列でID入れればいい
そもそもこういうのmongoの機能にあるかもしれないけど。
■ドキュメント
- mongodbインストール – 橋本詳解 Macにインストールした
- Rubyからmongo使う – 橋本詳解
- Ruby Tutorial – MongoDB
- MongoRuby-1.0.7 mongoドライバのドキュメント
- ハンズオンで分かる MongoDB チュートリアル – ζ*’ワ’)ζ<ちれすですの! mongoのコンソールから使う
- Mongoid Documentation: Documents mongoid公式ドキュメント
- Rails3 対応 MongoDB ORM、Mongoid 詳解―ドキュメント – ζ*’ワ’)ζ<ちれすですの! 公式ドキュメント翻訳中。すげー。
■インストール
sudo gem install mongoid1.9.1を使う。–pre付けるとRails3対応の2.x系統が入る。
■modelを作る
適当にperson class作って、Mongoid::Documentにする
person.rb
require 'rubygems'string以外は型指定する。型はArray, BigDecimal, Boolean, Date, DateTime, Float, Integer, String, Symbol, Timeがある。
class Person
include Mongoid::Document
field :fullname # 指定無しでtype=>stringになる
field :username
field :age, :type => Integer
field :created_at, :type => DateTime, :default => Time.now
end
→ Mongoid Documentation: Documents
defaultで現在時刻を入れるようにした。
■mongodbへ接続
Mongoid.configureのブロック内で接続する。
conf.masterに普通のmongoで接続してdbを指定した時の返り値(Mongo::DBオブジェクト)を与えれば、mongoidで使える。
require 'rubygems'
require 'mongoid'
require File.dirname(__FILE__)+'/person'
Mongoid.configure do |conf|
conf.master = Mongo::Connection.new('localhost', 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っぽい。
% mongo2回保存したから複数保存されてた
MongoDB shell version: 1.4.4
url: test
connecting to: test
type "help" for help
> show dbs
admin
chirpstream_shokai
local
mongoid-test
people
povietest
test
testdb
> use mongoid-test
switched to db mongoid-test
> show collections
people
system.indexes
> db.people.find()
{ "_id" : "4c61463c2f7306e9fe000001", "created_at" : "Tue Aug 10 2010 21:29:48 GMT+0900 (JST)", "fullname" : "sho hashimoto", "username" : "shokai", "age" : 25 }
{ "_id" : "4c614d652f73060653000001", "created_at" : "Tue Aug 10 2010 22:00:21 GMT+0900 (JST)", "fullname" : "sho hashimoto", "username" : "shokai", "age" : 25 }
■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)
プラグイン機構を採用し、ランダムにいろいろ作れるようにしてみた。
普通はjpegとかのバイナリを直接いじるみたいだけどよく分からないのでJRubyでjavax.imageioを使ってやっている。
ランダムに96個作ってみて8×12に敷き詰めてみたのがこれ。
で、定期的にランダムに作ってtwitterのアイコンとしてアップロードしてる。

敷き詰めるのはImageMagickと一緒にインストールされるmontageコマンドでできる montageコマンド – 橋本詳解
ソースコードは全てgithubに置いた。
■仕組み
単機能モジュールをランダムに連結する事である程度ランダムな画像を生成できる。
javax.imageio.BufferedImageのインスタンスを受け取り、少し加工して返すというJRubyのmoduleをプラグインとし、それらをGlitchというclassがランダムに呼び出す。
今のところプラグインは23種類ある。単体だとそれほど派手にはならない。

■プラグイン
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を作る
こういうのを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*'))
}
}






最近のコメント