読者です 読者をやめる 読者になる 読者になる

non vorrei lavorare

ブログ名の通りです。javascript three.js mruby rust OCaml golang julialang blender

mrubyでWebカメラを黒い画面にそのまま表示できるようにした

おはようございます。切り替えの遅かった長男が最近ではすっかり、お気に入りのYoutubeを見ていても、お風呂の号令でさっと、お風呂に来るようになりました。しかし、次男の悪さが今度は目立ってしまってます。まぁ、長男もそうだったから、まぁ、いいかぁ。っとこれだから次男は。。。kjunichiです。

背景

長編まとめ・Sixel Graphics復活への動き(1) - Togetterまとめ

libsixelの使い方

// gcc `pkg-config libsixel --cflags --libs` main.c
// ./a.out > test.sixel

#include <stdio.h>
#include <stdlib.h>
#include <sixel.h>

        static int
sixel_write(char *data, int size, void *priv)
{
        return fwrite(data, 1, size, (FILE *)priv);
}

        static SIXELSTATUS
output_sixel(unsigned char *pixbuf, int width, int height,
                int ncolors, int pixelformat)
{
        sixel_output_t *context;
        sixel_dither_t *dither;
        SIXELSTATUS status;

        context = sixel_output_create(sixel_write, stdout);
        dither = sixel_dither_create(ncolors);
        status = sixel_dither_initialize(dither, pixbuf,
                        width, height,
                        pixelformat,
                        SIXEL_LARGE_AUTO,
                        SIXEL_REP_AUTO,
                        SIXEL_QUALITY_AUTO);

        if (SIXEL_FAILED(status))
                return status;

        status = sixel_encode(pixbuf, width, height,
                        pixelformat, dither, context);
        if (SIXEL_FAILED(status))
                return status;

        sixel_output_unref(context);
        sixel_dither_unref(dither);

        return status;
}

int main(int argc,char **argv) {
        SIXELSTATUS status;
        int WIDTH = 128;
        int HEIGHT =  128;

        unsigned char *pixbuf;
        pixbuf = malloc(sizeof(char)*WIDTH*HEIGHT*3);

        for(int h=0; h < HEIGHT; h++) {
        for(int w=0; w<WIDTH;w++) {
                int index = 3*(h*WIDTH+w);
                int v = (w*h)%255;
                pixbuf[index] = 0;
                pixbuf[index+1]=v;
                pixbuf[index+2]=0;
        }
        }
        status =        output_sixel(pixbuf, WIDTH,HEIGHT,256,SIXEL_PIXELFORMAT_RGB888);
        if (SIXEL_FAILED(status)) {
                fprintf(stderr, "%s\n%s\n",
                                sixel_helper_format_error(status),
                                sixel_helper_get_additional_message());
                return status;
        }
        printf("test\n");
}

画像サイズと、RGBの順にunsigned charが並んだ配列を渡せば、良きに計らってくれることが分かった。

mruby-webcamの対応

JPEG出力一択だったところをWebcam#setFmtメソッドを新設して、 “jpg”,“ppm"等のファイル拡張子による指定を可能として、 libsixelで処理し易い、ppm形式での出力を実装した。

実装と言っても、

imencode(".jpg", mat, buff, param);

と固定だった拡張子の指定を以下の様に可変にしただけ。

imencode(type.c_str(), mat, buff, param); 

mruby-webcamでカメラの映像をppm形式で出力する

cam = Webcam.new
cam.setFmt "ppm"
cam.capture {|img|
  # img : PPMのP6形式で渡される。
  puts img.length
}
cam.start

mruby-sixelの作成

mrbgemの概要

libsixelにRubyのGemを参考に

encoder = SixelEncoder.new
encoder.encode_from_ppm ppm_image_buf

動かす

必要な物

OSX,Linuxではmltermを用意すれば、試せる。WindowsでもRLoginから試せる。

github.com

スペースキーを押すと、端末に画像が表示される。

cam = Webcam.new
encoder = SixelEncoder.new

cam.setFmt("ppm")
cam.capture {|img|
  puts "\ec"
   encoder.encode_from_ppm img
 }
cam.start

f:id:kjw_junichi:20170225080802p:plain

成果物

github.com

github.com

macOSでやるには

まとめ

前々から、sixel表示を手元の環境で試したと思いながら数年経ってしまったが、 2017年にようやく実現でき、mruby-sixelなるmrbgemも作成することが出来た。

関連記事

9年前の記事

6年前の記事

3年前の記事

アメッシュでECONNRESETを食らっててslackbotが死んでた件

おはようございます。次男の保護者会に行けずに、個別に面談する予定でしたが、電車の人身事故で保育園のお迎え時間もぎりぎりになり面談はなくなり、資料をもらうだけになりました。まぁ次男はうまいことやってることが予想されるのであんまり気にしてません。kjunichiです。

背景

気づいたら、botkitで作ってHerokuにあげていたslackbotが死んでました

調査

このbotは「雨どう」と尋ねると、東京アメッシュの画像を張り付ける機能があり、 ここでエラーが起き、node.jsが死んでました。

原因

エラーの原因は、アメッシュからJPEGファイルを取得する際なぜか、通信途中でECONNRESETがかかり、 JPEGファイルの一部が欠落した状態でした。

2017-02-09T22:20:00.602972+00:00 app[web.1]: http://tokyo-ame.jwa.or.jp/mesh/000/201702100715.gif
2017-02-09T22:20:01.290589+00:00 app[web.1]: { Error: read ECONNRESET
2017-02-09T22:20:01.290599+00:00 app[web.1]:     at exports._errnoException (util.js:1026:11)
2017-02-09T22:20:01.290600+00:00 app[web.1]:     at TCP.onread (net.js:569:26) code: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read' }
2017-02-09T22:20:03.239987+00:00 app[web.1]: Premature end of JPEG file

対策

Node.jsでの定番のハンドリングできなかった例外をキャッチして続行することで、bot自体は 死なないようにできました。

process.on('uncaughtException', function(err) {
            console.log(err);
});

ECONNRESETへの対応

これが謎。 公式のhttp.getの解説だと

getの外でerrorをとらえてはいるが、このやり方だと、今回設定したuncaughtExceptionまでいってしまった。

また、連続して画像を取得するのが悪いのかと思い、各レイヤーごとにウェイトを入れてアクセスしてみたが、 これもダメだった。

関連記事

Tensorflowのclassify_image.pyにElectronでGUIを被せてみた

パソコン・インターネット

こんにちは、最近次男は保育園の帰り道、自分で走って帰るといって聞かず、自宅までの走って帰る日々です。長男も同じような時期にそういうことがあったので、そういうもんなんだと諦めて、次男の走りに付き合ってます。kjunichiです。

GPUを使えるTensorflowをWin,Macに入れたものの

tensorflowを入れたものの、動かし方が、実はよくわからず、Python APIのみの pipで入れても、実はサンプルコードがsite-lib配下にインストールされてることすら 最近まで知りませんでしたw。

classify_image.pyにElectronでGUIをつけてみた。

Linux,Windowsで画像ファイルをドロップすると、classify_image.pyで画像分類できるように してみた。

これで、子供たちと、簡単に画像ファイルを選んで、人工知能にどんな写真かを調べてもらうことが できました。

f:id:kjw_junichi:20170205134019p:plain

pipでいれたTensorflowのサンプルコードのありか

import site
print(site.getsitepackages())

これで、pipでインストールされるインストール先のディレクトリ一覧が取得できる。 環境によって、複数のパスが返されるので、この中からtensorflowディレクトリを探す。

画像ファイルをドロップして読み込む

大昔、HTML5の修業であれこれ作った

jsdo.it

から持ってきた。

iphoneの写真だとEXIFを考慮する必要ありだった

これまた大昔、

jsdo.it

を作ったが、この知識が役に立った。

Exifでカメラの回転情報を保持しているので、 これ考慮してclassify_image.pyに渡さないと分類結果に影響が出た。

もっとも、オリジナルのJPEGファイルをそのまま、classify_image.pyに渡せばこの辺り、Tensorflow側で 処理してくれたのかもしれないが。。

成果物

github.com

Windowsの場合はConadaのPython3を優先して動かすようにしてます。 その他のプラットフォームではパスに登録されてるPython3を優先して、そこにtensorflowがあれば、 それを使う感じにしてます。

関連記事