SVG + opencvでベクター形式の図形を出力


#SVG形式
個人的にベクター形式の画像が必要になり，かといってepsをプログラムで出力するのは大変そうだ。と思っていたところ，以下のようなSVGと呼ばれるHTMLのように簡単に書けるベクターフォーマットがあることがわかりました。

https://www.w3schools.com/graphics/svg_polyline.asp

なんか簡単そうですね。
C++の簡易ラッパーは以下で見つけることができました。

https://qiita.com/tmichi/items/d85eb40c72b843cbef93

これにはPolyline関数はなかったのですが、簡単に作れそうだったので、上記を参考にopencvのPoint形式でSVG出力できるクラスを作ってみました。透過色も指定できるようです。とりあえず、僕はPolylineが使えれば十分だったので、それしかないのですが、ほかの描画関数も簡単に追加できると思います。

<a href="http://www.cs.kumamoto-u.ac.jp/~koutaki/2017/svg.h">svg.hのコード</a>

```c++:svg.h
#include <fstream>
#include <iostream>

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

class svg_drawer {

	std::ofstream fout;
	int width, height;

public:

	svg_drawer(char *filename, int w, int h) {

		fout.open(filename);
		width  = w;
		height = h;
		
		fout << "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width= \"" << width << "\" height=\"" << height << "\">" << std::endl;

	}
	~svg_drawer(void) {
		fout << "</svg>" << std::endl;
		return;
	}

	void drawPolyLine(const vector<Point2d> & pts, Scalar col, int stroke_width, Scalar fcol, int dash = 0, double opacity = 1.0) {

		char stroke_color[255];
		char fill_color[255];
		sprintf(stroke_color, "rgb(%d,%d,%d)", (int) col[2], (int) col[1], (int) col[0]);
		sprintf(fill_color,   "rgb(%d,%d,%d)", (int)fcol[2], (int)fcol[1], (int)fcol[0]);

		fout << "<polyline points=\"";

		int N = pts.size();
		for (int i = 0; i <= N; i++) {

			double vx = pts[i%N].x;
			double vy = pts[i%N].y;

			fout << vx << "," << vy << " ";
		}
		fout << "\"";
		if (dash > 0) fout << " stroke-dasharray=\"" << dash << "\"";
		fout << " stroke-width=\"" << stroke_width << "\" stroke=\"" << stroke_color << "\" fill=\"" << fill_color << "\" fill-opacity=\"" << opacity << "\"/>" << std::endl;

		return;
	}
	void drawLine(const double x0, const double y0, const double x1, const double y1, int stroke_width, Scalar col, int dash = 0, double opacity = 1.0) {

		char stroke_color[255];
		sprintf(stroke_color, "rgb(%d,%d,%d)", (int)col[2], (int)col[1], (int)col[0]);
		
		fout << "<line x1=\"" << x0 << "\" y1=\"" << y0 << "\" x2=\"" << x1 << "\" y2=\"" << y1 << "\"";
		if (dash > 0) fout << " stroke-dasharray=\"" << dash << "\"";
		fout << " stroke-width=\"" << stroke_width << "\" stroke=\"" << stroke_color << "\" stroke-opacity=\"" << opacity << "\"/>" << std::endl;

		return;
	}
};
```

##サンプル
上のクラスを使って適当な図形を描画します。svg.hを読み込むだけで使えます。

```c++:main.cpp
#include <stdio.h>
#include "svg.h"

int main(){
	
	svg_drawer sd("test.svg", 500,500);


	int div = 360;

	for (int n = 0; n < 50; n++) {
		vector<Point2d> pts;
		int   dx = rand() % 500;
		int   dy = rand() % 500;
		float dr = (rand() % 100) / 100.0;
		float dt = rand() % 30 + 3;
		for (int i = 0; i < div; i++) {
			float t = 2 * 3.14159 * i / float(div);
			float x, y, r;
			r = dr * (50 + sin(dt * t) * 20);
			x = dx + r * cos(t);
			y = dy + r * sin(t);
			pts.push_back(Point2d(x, y));
		}

		sd.drawPolyLine(pts, CV_RGB(rand() % 255, 0, 0), 1, CV_RGB(rand() % 255, rand() % 192, rand() % 32), 0, 0.5);
	}

	return 0;
}

```

実行するとtest.svgというファイルができます。ファイルをダブルクリックしてブラウザなどで開けます。ブラウザ上でCtrを押しながらマウスホイールすると拡大できます。ベクター形式なので拡大してもきれいです。イラストレータなどのペイントツールで開いて、epsに変換すればパワーポイントやlatexに図を張り付けることができます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/217237/e9dcf854-e1b0-9ae3-ddc8-590c9aa4f6d6.png" width=30%></img>適当に作った謎の図形。

<a href="http://www.cs.kumamoto-u.ac.jp/~koutaki/2017/test.svg">test.svgをブラウザで開く</a>