1

26歳になったので

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})



0

JRubyでglitch iconを作る(2)

プラグイン機構を採用し、ランダムにいろいろ作れるようにしてみた。
普通は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したい。

0

mbedにEthernetを接続しtwitterにpostする

@1VQ9の助力を得てmbedがethernetにつながった。1VQ9はパルストランスも通さず、分解したLANケーブルをmbedに直接差し込んで動かしてたけど、俺はmbed Ethernet Testingに従ってパルストランス入りのEthernet jackを使った。



ethernetにつないだところ
mbed + ethernet
mbedとethernet jackは
  • mbed p1 = Ethernet RD+
  • p2 = RD-
  • p7 = TD-
  • p8 = TD+
という風に接続した。こっちでは別のつなぎ方をしているが、この配置だと動かなかったのでjack毎に違うのかもしれない。よくわからない。


配線はこっちの方が見やすいかも
mbed + ethernet


裏のピン配置がブレッドボードに刺せるピッチ幅ではなかったので、秋月で100円で売ってる両面ガラススルーホール基盤の切れ端とピンヘッダを駆使してピン配置を変更した。
ethernet jack for breadboard
ethernet jack for breadboard


mbedにはHTTPClientというDNSやDHCPやHTTPを適当に解決してくれるモジュールがある。
ほぼmbed EMAC/HTTPClientのまま動いた。
HTTPClientを使うために、
http://mbed.org/projects/cookbook/svn/EMAC/lwip/trunk
をprojectにimportする必要があった。mbedのエディタのGUIからimportできた。

tweet_test | mbed
#include "mbed.h"
#include "HTTPClient.h"
using namespace std;

DigitalOut led(LED1);

HTTPClient http; // use DHCP

/* // use static IP
HTTPClient http("mbed", // hostname.
                IPv4(192,168,1,39), // IPv4 address
                IPv4(255,255,255,0), // netmask
                IPv4(192,168,1,1), // default gateway
                IPv4(192,168,1,1)); // dns server
/**/

const char msg[] = "status=mbed test";

int main(void) {
    http.auth("username", "password");
    http.post("http://twitter.com/statuses/update.xml", msg);
  
    while(1) {
        led = !led;
        wait(0.2);
    }
}
twitter APIにbasic認証を通ってhttp-postし、成功するとLED点滅のループに入る。

twitter APIは同じtweetを連続で受け取らないようになっているので、このプログラムだと一度tweetした後はmsgを別の文字列に変えないと再確認できない。後ろに乱数とか時刻とかをつける必要がある。

0

最近作ったYahooPipes

Pipes: 秋月電子 新製品FullFeed
秋月のRSSが無くなったので、新製品ページから取り出した。


Pipes: ストロベリー・リナックス FullFeed
strawberry-linux.comのnews feedがタイトルのみ配信だったので

どちらも商品イメージとデータシートへのリンクと価格を入れてある。

千石は元気の良いblogで新製品を教えてくれる。spark funと液晶工房もblogがある。スイッチサイエンスもフィード作ろうかと思ったけど新製品ページが半年ぐらい更新されてなかったからやめた。マルツもblogあるけど勉強会情報しかなくて、新製品情報はwebで一覧できない。電子工作系はいまだに新製品情報をネットで効率的に収集する手段が乏しいな。


Pipes: twitter list timeline
twitterのlist機能にrssが無いので。list出た時に作ったんだけど、twitterのHTMLが変わっていつのまにか動かなくなってたのを修理した。
ユーザ名とlist名を入れるとRSSを出す。他人の作ったlistをいろいろ購読してみてる。


pipesってソースのHTMLが変わっていつのまにかエラーになってるんだけど、RSSリーダに登録してるだけだと配信されなくなるだけで、「動かなくなった」事に気づけないんだよなあ
なんとかならないものか。1件も出力できなかったら一番上にエラー文を入れるようにすればいいのか?

0

opencv-haartrainingの進行状況をtwitterに流すbot

OpenCVをソースからビルドするとhaarlike分類器(顔認識などに使われているやつ)の学習ツールが手に入るんだけど、たくさんのマシンでたくさん学習させているとそれぞれの進行状況をチェックするのが面倒になってくる。

でも、入力した画像ファイルが壊れていると学習が強制終了してしまったり、データがばらつきすぎてて収束しなくてあきらめて終了されたりするので、プロセスが死んでいたらパラメータを直してすぐやり直しをさせたい。学習中は予断を許さない状況が続く。

なので、進行状況を監視してtwitterアカウントshokai_logにpostするbotを作った。
5分間隔でopencv-haartrainingの作業ディレクトリとプロセスが生きているかをチェックする。
学習stageが進む毎に適当に通知し、プロセスが強制終了していた場合は激しくreplyしてくれる。これで安心して寝れる。OpenCV1.0/2.0両方対応。

プロセスが落ちていると教えてくれたり、段階が進む毎にtwitterに投稿したりする。
一見何言ってるのかわかりにくいpostもあるが、「ドドドド」だったらstage4が終わったという意味。
tweet-haartraining.rb
辞書はコード内にある。


第2引数にopencv-haartrainingの-dataオプションで渡した「結果の書き出し先ディレクトリ名」を指定する。第3引数は無しでもいいが、twitter投稿の末尾にメモを付けられる。複数のマシンで実行していてどれの進行状況かわからなくなる時は、マシンの名前を入れておけばいい。
ruby tweet-haartraining.rb /Users/sho/path/to/training/dir/ "Macbook黒"

tweet-haartraining.rb
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rubygems'
require 'twitter'

# setting
USER = 'your-account'
PASS = 'your-password'
INTERVAL = 300 # sleep sec
YOU = 'shokai' # 時々replyしてくる nilでreplyなし
NOPOST = false # debug用

def post(message)
  return if !message
  message = "@#{YOU} #{message}" if rand(3)<1 if !(message =~ /@#{YOU}/) && rand(2)<1
  puts message + "\t" + Time.now.to_s
  return if NOPOST
  httpAuth = Twitter::HTTPAuth.new(USER, PASS)
  tw = Twitter::Base.new(httpAuth)
  tw.update(message)
end

if ARGV.size < 1
  puts '結果が出力されるディレクトリへのパスが必要です。メモも付けられます(オプション)'
  puts 'e.g. ruby tweet-haartraining.rb /path/to/haar/training/dir/ "研究室の学習用パソコン"'
  exit 1
end

puts path = ARGV.shift
memo = ARGV.shift || ""
dir_path = path
if path =~ /\/$/
  dir_path = path
  xml_path = path[0...path.size-1]+'.xml'
else
  dir_path = path+'/'
  xml_path = path+'.xml'
end


if stage_p = Dir.glob(dir_path+'*').delete_if{|i| File::ftype(i) != 'directory'}.map{|i| i.split(/\//).last.to_i}.max
  post "ステージ#{stage_p}から開始" + " " + memo
else
  messages = ["開始。",
              "はじめ",
              "起床",
              "おきた",
              "start",
              "スタートしました",
              "hello world",
              "hello work",
              "はじめますわっ",
              "スタンバイレディ セタップ"]
  post messages[rand(messages.size)] + " " + memo
end


while true do
  sleep INTERVAL
  stage = Dir.glob(dir_path+'*').delete_if{|i| File::ftype(i) != 'directory'}.map{|i| i.split(/\//).last.to_i}.max
  if File.exists? xml_path
    messages = ["全行程完了(ステージ#{stage})。お疲れ様でした。",
                "全部オワタ(#{stage})",
                "修了しました",
                "寝る。#{stage}時に起きる。",
                "終わったので、#{stage}時に帰ります",
                "全段階完了しました。データを回収し、電源を落としてください(#{xml_path.split(/\//).last})",
                "全ステージ完了しました(#{xml_path.split(/\//).last})",
                "ⓢⓤⓨⓐⓡⓘ"]
    post "@#{YOU} " + messages[rand(messages.size)] + " " + memo
    exit 0
  end
  if nil == `ps aux | grep opencv-haartraining`.split(/[\r\n]/).delete_if{|m|m=~/grep opencv-haartraining/}.first
    messages = ["#{stage}段階目まで来たけど異常終了したかも",
                "落ちてる",
                "ERROR! haartraining is not working. please restart \(^o^)/",
                "異常終了",
                "異常です",
                "動いてないっぽい・・・",
                "死んだかも",
                "だめっぽい・・",
                "おい、異常終了してるぞ",
                "冒 険 の 書 (#{stage}) は 消 え ま し た",
                "おお、死んでしまうとは情けない",
                "\(^o^)/"*stage,
                "ピッコロの気が消えた",
                "なん・・だと・・",
                "#{stage}面でピチュった"]
    post "@#{YOU} " + messages[rand(messages.size)] + " " + memo
    sleep INTERVAL*2
    next
  end
  next if stage == stage_p or stage == nil
  stage_p = stage
  messages = ["#{stage}段階目まで進みました",
              "バリバリです(stage#{stage})",
              "ばっちりですわっ",
              "------ここまで読んだ(#{stage})------",
              "がんばってます(#{stage})",
              "stage #{stage}",
              "ステージ#{stage}なう",
              "now finished stage#{stage}.",
              "よし!ステージ#{stage}まで終わった!!!",
              "うわ"+"あ"*stage,
              "ド"*stage,
              "ゴ"*stage,
              "ゴ"+"ー"*stage,
              "もりもり",
              "ふむふむなるほど"+"・"*stage,
              "頭が"*stage+"おかしくなりそうだ",
              "もういや",
              "無理"*stage,
              "ズザ"+"ー"*stage,
              "帰りたい",
              "まだ#{stage}段階目だ",
              "もうstage#{stage}まで終わった。超はやい",
              "もうstage#{stage}まで終わった",
              "stage#{stage}まで終わった",
              "stage#{stage}まで終わったし",
              "ククク・・遂に#{stage}界までまで昇ってきたか・・・",
              "ⓢⓤⓨⓐ"*stage]
  
  post messages[rand(messages.size)] + " " + memo
end