OpenCV(C++)チュートリアル：描画基礎

公式チュートリアルの[Basic Drawing](https://docs.opencv.org/3.4/d3/d96/tutorial_basic_geometric_drawing.html)の要約。
筆者の環境はdebian9

##やること
* 線 `line()`
* 楕円 `ellipse()`
* 矩形 `rectangle()`
* 円 `circle()`
* 塗りつぶされたポリゴン `fillPoly()`

を使用して図形を描画する

###よく使うデータ構造
####**`Point`**
x, yで指定された２次元の点。
座標の設定は

```cpp
Point pt;
pt.x = 10;
pt.y = 8;
```

またはコンストラクタ

```cpp
Point pt =  Point(10, 8);
```

####**`Scalar`**

* 名前はScalarだが4要素のベクトルを表す。ピクセルの値を渡すのによく使う。`cv::Vec< _Tp, cn >`の派生クラス。継承関係は画像の通り
[![Scalar](https://docs.opencv.org/3.4/d1/da0/classcv_1_1Scalar__.png "Scalar")](https://docs.opencv.org/3.4/d1/da0/classcv_1_1Scalar__.html#details)

* BGRで色を指定する（３パラメータ）。必要ないなら４番目(アルファ)は設定不要
* `Scalar( a, b, c )` とすると、BGRカラーではBlue=a, Green=b, Red=cとして定義している。



##コード全体
原子モデル図と、ルーク（チェスの駒）図を描画する。

```cpp:draw.cpp
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#define w 400
using namespace cv;
//描画関数をラップした自作関数
void MyEllipse( Mat img, double angle );
void MyFilledCircle( Mat img, Point center );
void MyPolygon( Mat img );
void MyLine( Mat img, Point start, Point end );

int main( void ){
  //２枚描画するのでウィンドウ、画像配列とも２つ作る
  char atom_window[] = "Drawing 1: Atom";
  char rook_window[] = "Drawing 2: Rook";
  Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
  Mat rook_image = Mat::zeros( w, w, CV_8UC3 );
  //原子
  MyEllipse( atom_image, 90 );
  MyEllipse( atom_image, 0 );
  MyEllipse( atom_image, 45 );
  MyEllipse( atom_image, -45 );
  MyFilledCircle( atom_image, Point( w/2, w/2) );
  //ルーク
  MyPolygon( rook_image );
  rectangle( rook_image,
         Point( 0, 7*w/8 ),
         Point( w, w),
         Scalar( 0, 255, 255 ),
         FILLED,
         LINE_8 );
  MyLine( rook_image, Point( 0, 15*w/16 ), Point( w, 15*w/16 ) );
  MyLine( rook_image, Point( w/4, 7*w/8 ), Point( w/4, w ) );
  MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) );
  MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) );

  //表示
  imshow( atom_window, atom_image );
  moveWindow( atom_window, 0, 200 );
  imshow( rook_window, rook_image );
  moveWindow( rook_window, w, 200 );
  waitKey( 0 );
  return(0);
}

void MyEllipse( Mat img, double angle )
{
  int thickness = 2;
  int lineType = 8;
  //中心が( w/2, w/2 ),( w/4, w/16 )の矩形に囲まれる楕円
  //angle度だけ回転しており、弧は0~360度（途切れていない）
  ellipse( img,
       Point( w/2, w/2 ),
       Size( w/4, w/16 ),
       angle,
       0,
       360,
       Scalar( 255, 0, 0 ),
       thickness,
       lineType );
}
void MyFilledCircle( Mat img, Point center )
{
  //中心center, 半径w/32の円。色は赤(Scalar( 0, 0, 255 ))
  circle( img,
      center,
      w/32,
      Scalar( 0, 0, 255 ),
      FILLED,
      LINE_8 );
}
void MyPolygon( Mat img )
{
  int lineType = LINE_8;
  //頂点座標の定義
  Point rook_points[1][20];
  rook_points[0][0]  = Point(    w/4,   7*w/8 );
  rook_points[0][1]  = Point(  3*w/4,   7*w/8 );
  rook_points[0][2]  = Point(  3*w/4,  13*w/16 );
  rook_points[0][3]  = Point( 11*w/16, 13*w/16 );
  rook_points[0][4]  = Point( 19*w/32,  3*w/8 );
  rook_points[0][5]  = Point(  3*w/4,   3*w/8 );
  rook_points[0][6]  = Point(  3*w/4,     w/8 );
  rook_points[0][7]  = Point( 26*w/40,    w/8 );
  rook_points[0][8]  = Point( 26*w/40,    w/4 );
  rook_points[0][9]  = Point( 22*w/40,    w/4 );
  rook_points[0][10] = Point( 22*w/40,    w/8 );
  rook_points[0][11] = Point( 18*w/40,    w/8 );
  rook_points[0][12] = Point( 18*w/40,    w/4 );
  rook_points[0][13] = Point( 14*w/40,    w/4 );
  rook_points[0][14] = Point( 14*w/40,    w/8 );
  rook_points[0][15] = Point(    w/4,     w/8 );
  rook_points[0][16] = Point(    w/4,   3*w/8 );
  rook_points[0][17] = Point( 13*w/32,  3*w/8 );
  rook_points[0][18] = Point(  5*w/16, 13*w/16 );
  rook_points[0][19] = Point(    w/4,  13*w/16 );
  //描画するための頂点をpptに入れる
  const Point* ppt[1] = { rook_points[0] };
  int npt[] = { 20 };
  fillPoly( img,
        ppt,
        npt,
        1,
        Scalar( 255, 255, 255 ),
        lineType );
}

//start から end まで線を引く。imgに描画される
void MyLine( Mat img, Point start, Point end )
{
  int thickness = 2;
  int lineType = LINE_8; //8-connected(隣接するピクセルを、
                         //上下左右とななめ方向も含めた８ピクセルで定義する方法)
  line( img,
    start,
    end,
    Scalar( 0, 0, 0 ),
    thickness,
    lineType );
}

```



###コンパイル

```
g++ draw.cpp -I/usr/local/include/opencv2 -I/usr/local/include/opencv -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc
```

###結果

[![result](https://docs.opencv.org/3.4/Drawing_1_Tutorial_Result_0.png "result")](https://docs.opencv.org/3.4/Drawing_1_Tutorial_Result_0.png)
こんな感じになる