目标
本节内容我们将学习:
- 通过 line() 方法绘制一条线
- 通过ellipse() 方法绘制一个椭圆
- 通过rectangle() 方法绘制一个矩形
- 通过circle() 方法绘制一个圆形
- 通过fillPoly() 方法填充一个多边形
OpenCV理论
在本节教程中,我们将着重使用两个类cv::Point 和cv::Scalar 。
点
用x和y坐标代表二维图像中的一个点,可以用如下代码定义:
Point point = new Point();
point.x = 10;
point.y = 8;
或者
Point point1 = new Point(10, 8);
Scalar
- 代表一个四元素的向量。Scalar类型广泛应用在像素值的传输中。
- 在本篇教程中我们将会广泛的应用Scalar去表示一个BGR颜色值(3个参数)。我们并不需要去定义最后一个参数值,除非需要的话。
- 让我们看一下例子:
Scalar scalar = new Scalar(0, 0, 0);
代码
public class Main {
private static final int W = 400;
public static void main(String[] args) {
System.loadLibrary("libs/"+ Core.NATIVE_LIBRARY_NAME);
String atom_window = "Drawing 1:Atom";
String rook_window = "Drawing 2:rook";
Mat atom_image = Mat.zeros(W, W, CvType.CV_8UC3);
Mat rook_image = Mat.zeros(W, W, CvType.CV_8UC3);
MyEllipse(atom_image,90.0);
MyEllipse(atom_image,0.0);
MyEllipse(atom_image,45.0);
MyEllipse(atom_image,-45.0);
MyFilledCircle(atom_image,new Point(W/2,W/2));
MyPolygon(rook_image);
rectangle(rook_image,
new Point(0,7*W/8),
new Point(W,W),
new Scalar(0,255,255),
-1,
8,
0);
MyLine( rook_image, new Point( 0, 15*W/16 ), new Point( W, 15*W/16 ) );
MyLine( rook_image, new Point( W/4, 7*W/8 ), new Point( W/4, W ) );
MyLine( rook_image, new Point( W/2, 7*W/8 ), new Point( W/2, W ) );
MyLine( rook_image, new Point( 3*W/4, 7*W/8 ), new 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();
System.exit(0);
}
private static void MyEllipse(Mat img, double angle){
int thickness = 2;
int lineType = 8;
int shift = 0;
ellipse(img,
new Point(W/2,W/2),
new Size(W/4,W/16),
angle,
0.0,
360.0,
new Scalar(255,0,0),
thickness,
lineType,
shift);
}
public static void MyFilledCircle(Mat img,Point center){
int thickness = 2;
int lineType = 8;
int shift = 0;
circle(img,
center,
W/32,
new Scalar(0,0,255),
thickness,
lineType,
shift);
}
private static void MyPolygon(Mat img){
int lineType = 8;
int shift = 0;
Point[] rook_points = new Point[20];
rook_points[0] = new Point( W/4, 7*W/8 );
rook_points[1] = new Point( 3*W/4, 7*W/8 );
rook_points[2] = new Point( 3*W/4, 13*W/16 );
rook_points[3] = new Point( 11*W/16, 13*W/16 );
rook_points[4] = new Point( 19*W/32, 3*W/8 );
rook_points[5] = new Point( 3*W/4, 3*W/8 );
rook_points[6] = new Point( 3*W/4, W/8 );
rook_points[7] = new Point( 26*W/40, W/8 );
rook_points[8] = new Point( 26*W/40, W/4 );
rook_points[9] = new Point( 22*W/40, W/4 );
rook_points[10] = new Point( 22*W/40, W/8 );
rook_points[11] = new Point( 18*W/40, W/8 );
rook_points[12] = new Point( 18*W/40, W/4 );
rook_points[13] = new Point( 14*W/40, W/4 );
rook_points[14] = new Point( 14*W/40, W/8 );
rook_points[15] = new Point( W/4, W/8 );
rook_points[16] = new Point( W/4, 3*W/8 );
rook_points[17] = new Point( 13*W/32, 3*W/8 );
rook_points[18] = new Point( 5*W/16, 13*W/16 );
rook_points[19] = new Point( W/4, 13*W/16 );
MatOfPoint matPt = new MatOfPoint();
matPt.fromArray(rook_points);
ArrayList<MatOfPoint> ppt = new ArrayList<>();
ppt.add(matPt);
fillPoly(img,
ppt,
new Scalar(255,255,255),
lineType,
shift,
new Point(0,0));
}
private static void MyLine(Mat img,Point start, Point end){
int thickness = 2;
int lineType = 8;
int shift = 0;
line(img,
start,
end,
new Scalar(0,0,0),
thickness,
lineType,
shift);
}
}
解释
首先我们创建两个窗体和Mat对象用于显示保存我们需要绘制的两个图像:
String atom_window = "Drawing 1:Atom";
String rook_window = "Drawing 2:rook";
Mat atom_image = Mat.zeros(W, W, CvType.CV_8UC3);
Mat rook_image = Mat.zeros(W, W, CvType.CV_8UC3);
我们创建不同的方法去绘制不同的图形。在本例中绘制原子需要用到MyEllipse 和 MyFilledCircle:
MyEllipse(atom_image,90.0);
MyEllipse(atom_image,0.0);
MyEllipse(atom_image,45.0);
MyEllipse(atom_image,-45.0);
MyFilledCircle(atom_image,new Point(W/2,W/2));
绘制国际象棋需要用到MyLine, rectangle 和a MyPolygon:
MyPolygon(rook_image);
rectangle(rook_image,
new Point(0,7*W/8),
new Point(W,W),
new Scalar(0,255,255),
-1,
8,
0);
MyLine( rook_image, new Point( 0, 15*W/16 ), new Point( W, 15*W/16 ) );
MyLine( rook_image, new Point( W/4, 7*W/8 ), new Point( W/4, W ) );
MyLine( rook_image, new Point( W/2, 7*W/8 ), new Point( W/2, W ) );
MyLine( rook_image, new Point( 3*W/4, 7*W/8 ), new Point( 3*W/4, W ) );
接下来让我们看看每个方法内部是怎么实现的:
MyLine
private static void MyLine(Mat img,Point start, Point end){
int thickness = 2;
int lineType = 8;
int shift = 0;
line(img,
start,
end,
new Scalar(0,0,0),
thickness,
lineType,
shift);
}
- 如我们看到的,MyLine 仅仅只是简单的调用了line()方法,并传入如下参数:
- 线段所要绘制的图像
- 所要绘制线段的起点位置以及终点位置
- 线段的颜色,本例用
new Scalar(0,0,0),
绘制一条黑色的线段 - 线段的粗细thickness
- 以及线型lineType ,lineType=8指的是8联通线型
MyEllipse
private static void MyEllipse(Mat img, double angle){
int thickness = 2;
int lineType = 8;
int shift = 0;
ellipse(img,
new Point(W/2,W/2),
new Size(W/4,W/16),
angle,
0.0,
360.0,
new Scalar(255,0,0),
thickness,
lineType,
shift);
}
- 从上述代码中我们可以看出ellipse()方法绘制一个如下参数的椭圆:
- 椭圆的显示图像img
- 椭圆的中心位置new Point(W/2,W/2),以及椭圆所在的框的大小new Size(W/4,W/16)
- 椭圆的旋转角度angle
- 椭圆绘制的弧度范围0,360
- 绘制的线条的颜色 new Scalar(255,0,0)
- 粗细thickness
- 线型lineType=8
- shift参数,是将坐标的小数点向前移动几位。
MyFilledCircle
public static void MyFilledCircle(Mat img,Point center){
int thickness = 2;
int lineType = 8;
int shift = 0;
circle(img,
center,
W/32,
new Scalar(0,0,255),
thickness,
lineType,
shift);
}
- 类似于上述的ellipse方法,circle方法包含如下参数:
- 所要显示圆的图像img
- 绘制圆的中心坐标center
- 圆的半径W/32
- 绘制圆的颜色new Scalar(0,0,255),
- 粗细thickness
- 线型lineType=8
- shift参数,是将坐标的小数点向前移动几位。
MyPolygon
private static void MyPolygon(Mat img){
int lineType = 8;
int shift = 0;
Point[] rook_points = new Point[20];
rook_points[0] = new Point( W/4, 7*W/8 );
rook_points[1] = new Point( 3*W/4, 7*W/8 );
rook_points[2] = new Point( 3*W/4, 13*W/16 );
rook_points[3] = new Point( 11*W/16, 13*W/16 );
rook_points[4] = new Point( 19*W/32, 3*W/8 );
rook_points[5] = new Point( 3*W/4, 3*W/8 );
rook_points[6] = new Point( 3*W/4, W/8 );
rook_points[7] = new Point( 26*W/40, W/8 );
rook_points[8] = new Point( 26*W/40, W/4 );
rook_points[9] = new Point( 22*W/40, W/4 );
rook_points[10] = new Point( 22*W/40, W/8 );
rook_points[11] = new Point( 18*W/40, W/8 );
rook_points[12] = new Point( 18*W/40, W/4 );
rook_points[13] = new Point( 14*W/40, W/4 );
rook_points[14] = new Point( 14*W/40, W/8 );
rook_points[15] = new Point( W/4, W/8 );
rook_points[16] = new Point( W/4, 3*W/8 );
rook_points[17] = new Point( 13*W/32, 3*W/8 );
rook_points[18] = new Point( 5*W/16, 13*W/16 );
rook_points[19] = new Point( W/4, 13*W/16 );
MatOfPoint matPt = new MatOfPoint();
matPt.fromArray(rook_points);
ArrayList<MatOfPoint> ppt = new ArrayList<>();
ppt.add(matPt);
fillPoly(img,
ppt,
new Scalar(255,255,255),
lineType,
shift,
new Point(0,0));
}
- 我们使用fillPoly绘制一个多边形。
- 绘制的图像容器img
- 包含多边形顶点的Point集合ppt
- 多边形填充的颜色new Scalar(255,255,255),代表白色
rectangle
rectangle(rook_image,
new Point(0,7*W/8),
new Point(W,W),
new Scalar(0,255,255),
-1,
8,
0);
- 最后我们直接调用cv::rectangle方法绘制矩形:
- 矩形将绘制在rook_image中
- 两个对角线顶点坐标分别定义 new Point(0,7*W/8), new Point(W,W),
- 矩形的颜色new Scalar(0,255,255),
- 由于thickness 值=-1 因此矩形将被填充
结果
编译运行代码我们回得到如下图像: