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年前の記事