2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

OpenCVのmat画像をコンソール上に表示してみた!(AAとかではないですよ)

Posted at

TL;DR:mat画像をコンソール上に表示できることを確認しました。

  • TUI(Terminal User Interface)の表示手段として、libsixelが利用できることを確認した。
  • 比較的簡単な方法でコンソール上に画面表示できることを確認できた。

はじめに:ふと思ったのです、コンソールに画像出せたらちょっと幸せ、かもと。

  • OpenCVで画像確認するときにわざわざwindow表示とかメンドクサイ。
  • gtk3とかインストールするのもメンドクサイ。
  • だったら、matをそのままコンソール画面上に表示するにはどうしたらいいのか!?

コンソールに画像を出すにはどうするのか -> libsixelを使えばよい

コンソール上で画像を表示する方法としては、libsixelを用いるとよい、らしい。
それではこれで遊んでいく!なお、ライセンスはMITライセンス、と書かれている。

libsixel ですかね!

(え?出力結果をjpgで書き出してこのツール通せばいい、そんな野暮な話はやめてください、泣きます)

開発環境

OpenCV-next Ubuntu-2104 mlterm-3.9.0

ライブラリインストール

kmtr@ubuntu2104opencv:~/work$ sudo apt install libsixel*

この操作後に追加で 5,094 kB のディスク容量が消費されます。
取得:1 http://jp.archive.ubuntu.com/ubuntu hirsute/universe amd64 libsixel1 amd64 1.8.6-2 [94.6 kB]
取得:2 http://jp.archive.ubuntu.com/ubuntu hirsute/universe amd64 libsixel-bin amd64 1.8.6-2 [17.8 kB]
取得:3 http://jp.archive.ubuntu.com/ubuntu hirsute/universe amd64 libsixel-dev amd64 1.8.6-2 [125 kB]
取得:4 http://jp.archive.ubuntu.com/ubuntu hirsute/universe amd64 libsixel-examples all 1.8.6-2 [3,579 kB]

対応している端末のインストール(mlterm)

ubuntu 21.04で試したところ、mltermでは画面表示ができたので、これを使っていく!

表示可能?
Gnome端末
xterm(361)
mlterm(3.9.0)
kmtr@ubuntu2104opencv:~/work$ sudo apt install mlterm

以下のパッケージが新たにインストールされます:
  libssh2-1 mlterm mlterm-common mlterm-tools ncurses-term
アップグレード: 0 個、新規インストール: 5 個、削除: 0 個、保留: 0 個。
1,866 kB のアーカイブを取得する必要があります。
この操作後に追加で 13.4 MB のディスク容量が消費されます。
続行しますか? [Y/n] Y
取得:1 http://jp.archive.ubuntu.com/ubuntu hirsute/universe amd64 libssh2-1 amd64 1.9.0-2 [87.7 kB]
取得:2 http://jp.archive.ubuntu.com/ubuntu hirsute/main amd64 ncurses-term all 6.2+20201114-2build1 [249 kB]
取得:3 http://jp.archive.ubuntu.com/ubuntu hirsute/universe amd64 mlterm-common amd64 3.9.0-1 [1,242 kB]
取得:4 http://jp.archive.ubuntu.com/ubuntu hirsute/universe amd64 mlterm amd64 3.9.0-1 [228 kB]
取得:5 http://jp.archive.ubuntu.com/ubuntu hirsute/universe amd64 mlterm-tools amd64 3.9.0-1 [59.4 kB]
1,866 kB を 1秒 で取得しました (2,133 kB/s)

ということで、img2sixelコマンドを使って、jpeg画像をコンソール表示はできた!

image.png

確かにこれでもいいんですけどね……

system()関数使って、img2sixelで画像を表示させてもいいんですけどね、、、 やっぱりなんか残念な感じなので、プログラマらしく「不要な努力」かましたいです!

ライブラリのつなぎ方を確認する

どうやらこのあたりにファイルがインストールされたらしい。
お、pkg-configにも繋がっている、これは勝ちましたかね!!

kmtr@ubuntu2104opencv:~/work/sixel$ find /usr/ -name "libsixel*"
/usr/lib/x86_64-linux-gnu/pkgconfig/libsixel.pc
/usr/lib/x86_64-linux-gnu/libsixel.so
/usr/lib/x86_64-linux-gnu/libsixel.so.1
/usr/lib/x86_64-linux-gnu/libsixel.so.1.0.6
/usr/lib/x86_64-linux-gnu/libsixel.a
/usr/share/lintian/overrides/libsixel1
/usr/share/lintian/overrides/libsixel-dev
/usr/share/lintian/overrides/libsixel-bin
/usr/share/doc/libsixel-examples
/usr/share/doc/libsixel1
/usr/share/doc/libsixel-dev
/usr/share/doc/libsixel-bin
/usr/bin/libsixel-config
kmtr@ubuntu2104opencv:~/work/sixel$ pkg-config libsixel --cflags --libs
-I/usr/include/sixel -lsixel
kmtr@ubuntu2104opencv:~/work/sixel$ ls /usr/include/sixel/
sixel.h

サンプルコードを見ながら解析

初期化コードはここからですねー

    sixel_output_t *output = NULL;
    sixel_dither_t *dither = NULL;

    status = sixel_output_new(&output, sixel_write, stdout, NULL);
    if (SIXEL_FAILED(status))
        goto end;

    dither = sixel_dither_get(SIXEL_BUILTIN_G8);
    sixel_dither_set_pixelformat(dither, SIXEL_PIXELFORMAT_G8);
    status = sixel_encode(c.buf, c.width, c.height, 0, dither, output);
    if (SIXEL_FAILED(status))
        goto end;

画面表示はここから。

                            status = sixel_encode(c.buf, c.width, c.height, 0, dither, output);
                            if (SIXEL_FAILED(status)) {
                                goto end;
                            }

動作結果

image.png

サンプルコード

先にライセンスの話を…

当該サンプルコードの一部はlibsixelのサンプルコードをベースとしております。こちらは、下記の通りCC0となっております、

I declared main.c is in Public Domain (CC0 - "No Rights Reserved"). This example is offered AS-IS, without any warranty.

実装

こんな感じになりました。 本当は、std::cout << src << std::endl; で画像表示までやりたかったのですが、 めんどくさ  複雑になりすぎるので、とりあえずこれで完成とします!!

Makefile
a.out : a.cpp
	g++ a.cpp `pkg-config opencv5 libsixel --libs --cflags `

a.cpp
# include <iostream>
# include <opencv2/opencv.hpp>
# include <sixel.h>

struct canvas {
	int width;
	int height;
	int skip;
	unsigned char *buf;
};

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

static void show_to_sixel(cv::Mat src)
{
	struct canvas c;
	SIXELSTATUS status = SIXEL_FALSE;	
	sixel_output_t *output = NULL;
	sixel_dither_t *dither = NULL;

	// Copy BGR 3ch
	c.width  = src.cols;
	c.height = src.rows;
	c.skip   = c.width * 3;
	c.buf = new unsigned char[ c.height * c.skip ];
	{
		for ( int iy = 0; iy < c.height; iy++) {
			unsigned char *p = src.ptr<unsigned char>(iy);
			memcpy(
				c.buf + iy * c.skip,
				&(p[0]),
				c.skip );
		}
	}

	// Create output 
	status = sixel_output_new(&output, sixel_write, stdout, NULL);
	if (SIXEL_FAILED(status)){
		perror("sixel_output_new()");
		goto end3;
	}

	// Create dither with VT340 COLOR
	dither = sixel_dither_get(SIXEL_BUILTIN_VT340_COLOR);
	sixel_dither_set_pixelformat(dither, SIXEL_PIXELFORMAT_BGR888);

	// Output Image
	status = sixel_encode(c.buf, c.width, c.height, 0, dither, output);
	if (SIXEL_FAILED(status)){
		perror("sixel_encode()");
		goto end;
	}
end:
	sixel_dither_destroy(dither);
	sixel_output_destroy(output);
end3:
	delete []c.buf;
}

int main(int argc, char **argv)
{
	if ( argc == 2 ) {
		cv::Mat test = cv::imread(argv[1], 3 );

		std::cout <<" << Original Image >> " << std::endl;
		show_to_sixel(test);

		cv::blur ( test, test, cv::Size(10,10), cv::Point(-1, -1) );

		// If you want to update in the same position, do call again.
		// If you want to show next position, cout<<endl;
		std::cout <<" << Filtered Image >> " << std::endl;
		show_to_sixel(test);
        }else{
		std::cout << argv[0] << " filename " << std::endl;
	}

	return 0;
}

もし、もっと効果的にやるならば…

  • ditherとかoutputの使いまわしとかも考えたほうがいいかなあ…
  • dithered imageの扱いってどうしたらいいのかなあ、など等。

あと気を付ける事、かなあ?

OpenCVのバッファーは必ずしも連続しているとは限らないので、この実装みたいに「1行ずつコピー」をするのが無難だったはず。ちがってたらごめんねー。連続している時は丸ごとコピーっていう実装でもよかったかもしれないけど、まあ細かいことはキニシナイ!

まとめ

  • TUI(Terminal User Interface)の表示手段として、libsixelが利用できることを確認した。
  • 比較的簡単な方法でコンソール上に画面表示できることを確認できた。

以上になります!ありがとうございました。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?