Java使用OpenCV进行答题卡识别

前言

安装OpenCV

Windows环境

https://github.com/opencv/opencv/releases

下载这个

image-20220621164553382

下面的exe也可以 这个exe是一个自解压文件

安装的时候选择目录的时候不用新建opencv,在解压的时候会自动创建opencv文件夹。

CentOS源码编译

下载到服务器上

image-20220623140454903

安装依赖

1
2
3
yum groupinstall -y "Development Tools"

sudo yum -y install epel-release cmake3 gtk2-devel libpng-devel jasper-devel openexr-devel libwebp-devel libjpeg-turbo-devel libtiff-devel tbb-devel eigen3-devel boost boost-thread boost-devel libv4l-devel

查看gcc版本

1
gcc -v

安装:

1
2
3
4
5
6
unzip opencv-4.6.0.zip
cd opencv-4.6.0 && mkdir build && cd build

cmake3 -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local/opencv4 \
-D OPENCV_GENERATE_PKGCONFIG=ON ..

然后输入下述命令进行编译:

1
make -j8

使用make -j4或者make -j8进行多线程编译,虽然速度快但是容易出问题。

使用make如果中间如果因为特殊原因断了,可以使用make命令再继续。

编译100%完成后最后输入下述命令进行安装:

1
make install

输入以下命令:

1
2
3
4
cd /etc/ld.so.conf.d/
sudo touch opencv.conf
sudo sh -c 'echo "/usr/local/opencv4/lib64" > opencv.conf'
sudo ldconfig

然后,复制.pc文件:

1
sudo cp -f /usr/local/opencv4/lib64/pkgconfig/opencv4.pc /usr/lib64/pkgconfig/opencv4.pc

注意,我安装的是4.6.0,生成的是opencv4.pc,不是opencv.pc
测试一下:

1
pkg-config --modversion opencv4

升级cmake

注意

本文中使用的cmake3就不用再升级cmake了。

cmake需要3.5.1以上的版本

1
2
3
4
5
6
7
8
sudo yum install build-essential libssl-dev
wget http://www.cmake.org/files/v3.16/cmake-3.16.6.tar.gz
tar xf cmake-3.16.6.tar.gz
sudo chmod -R 777 cmake-3.16.6
cd cmake-3.16.6
./bootstrap
make
make install

通过yum命令移除原先的CMake。

1
yum remove cmake -y

建立目标版本CMake的软连接。

1
ln -s /usr/local/bin/cmake /usr/bin

查看版本

1
cmake --version

升级libstdc

升级cmake的时候又报错

version `GLIBCXX_3.4.20‘ not found 解决方法

解决方法

1
2
3
4
5
6
7
8
9
10
su root
cd /usr/local/lib64
# 下载最新版本的libstdc.so_.6.0.26
sudo wget http://www.vuln.cn/wp-content/uploads/2019/08/libstdc.so_.6.0.26.zip
unzip libstdc.so_.6.0.26.zip
# 将下载的最新版本拷贝到 /usr/lib64
cp libstdc++.so.6.0.26 /usr/lib64
cd /usr/lib64
# 查看 /usr/lib64下libstdc++.so.6链接的版本
ls -l | grep libstdc++

可以看到

libstdc++.so.6 ->libstdc++.so.6.0.19

1
2
3
4
5
6
# 删除/usr/lib64原来的软连接libstdc++.so.6,删除之前先备份一份
sudo rm libstdc++.so.6
# 链接新的版本
sudo ln -s libstdc++.so.6.0.26 libstdc++.so.6
# 查看新版本,成功
strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX

CentOS中YUM安装

1
2
yum install opencv opencv-devel opencv-python -y
pkg-config --modversion opencv

卸载

1
yum remove opencv opencv-devel opencv-python -y

Ubuntu中APT安装

1
2
sudo apt update
sudo apt install -y libopencv-dev python3-opencv

获取版本

1
python3 -c "import cv2; print(cv2.__version__)"

卸载

1
sudo apt remove -y libopencv-dev python3-opencv

Ubuntu源码编译

https://github.com/opencv/opencv/releases

添加环境依赖

1
2
3
4
5
6
sudo apt install -y build-essential cmake git pkg-config libgtk-3-dev \
libavcodec-dev libavformat-dev libswscale-dev libv4l-dev \
libxvidcore-dev libx264-dev libjpeg-dev libpng-dev libtiff-dev \
gfortran openexr libatlas-base-dev python3-dev python3-numpy \
libtbb2 libtbb-dev libdc1394-22-dev libopenexr-dev \
libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev

cmake需要3.5.1以上的版本

查看cmake版本

1
cmake --version

安装:

1
2
3
4
5
6
7
unzip opencv-4.6.0.zip
cd opencv-4.6.0
mkdir build && cd build

cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local/opencv4 \
-D OPENCV_GENERATE_PKGCONFIG=ON ..

然后输入下述命令进行编译:

1
make -j12

使用make -j4或者make -j8进行多线程编译,虽然速度快但是容易出问题。

使用make如果中间如果因为特殊原因断了,可以使用make命令再继续。

编译100%完成后最后输入下述命令进行安装:

1
make install

输入以下命令:

1
2
3
4
5
cd /etc/ld.so.conf.d/
sudo touch opencv.conf
# 根据安装位置
sudo sh -c 'echo "/usr/local/opencv4/lib" > opencv.conf'
sudo ldconfig

然后,复制.pc文件:

1
sudo cp -f /usr/local/opencv4/lib/pkgconfig/opencv4.pc /usr/lib/pkgconfig/opencv4.pc

注意,我安装的是4.6.0,生成的是opencv4.pc,不是opencv.pc
测试一下:

1
pkg-config --modversion opencv4

加载依赖

项目中添加jar

jar的位置在安装目录下build\java的目录下。

下面两种方式任选其一即可。

推荐方式1,方式2在Linux上就失效了。

方式1

添加测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;

public class Test01 {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

public static void main(String[] args) {
System.out.println("Welcome to OpenCV " + Core.VERSION);
Mat m = new Mat(5, 10, CvType.CV_8UC1, new Scalar(0));
System.out.println("OpenCV Mat: " + m);
Mat mr1 = m.row(1);
mr1.setTo(new Scalar(1));
Mat mc5 = m.col(5);
mc5.setTo(new Scalar(5));
System.out.println("OpenCV Mat data:\n" + m.dump());
}
}

配置运行时参数。

通过菜单Run->Edit Configurations...打开Run/Debug Configurations对话框。

在对话框窗口右侧,找到VM options标签对应的文本框。

在文本框中填写参数

-Djava.library.path=D:\Tools\opencv\build\java\x64

看好自己电脑是64为就选x64,32位就选x86。

当然也可以直接把opencv_java455.dll放在Java的bin目录下,这样也就不用配置java.library.path

D:\Tools\Java\jdk1.8.0_102\bin

方式2

这种方式就不用再指定java.library.path了。

把jar和dll放到项目中。

image-20220621163543761

代码中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import java.net.URL;

public class Test01 {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
URL url = ClassLoader.getSystemResource("lib/opencv/opencv_java455.dll");
System.load(url.getPath());
}

public static void main(String[] args) {
System.out.println("Welcome to OpenCV " + Core.VERSION);
Mat m = new Mat(5, 10, CvType.CV_8UC1, new Scalar(0));
System.out.println("OpenCV Mat: " + m);
Mat mr1 = m.row(1);
mr1.setTo(new Scalar(1));
Mat mc5 = m.col(5);
mc5.setTo(new Scalar(5));
System.out.println("OpenCV Mat data:\n" + m.dump());
}
}

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import utils.OpenCVUtil;


public class Test01 {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

public static void main(String[] args) {
// 以灰度方式,读取图片
Mat img = Imgcodecs.imread("D:\\Pic\\0.png", Imgcodecs.IMREAD_GRAYSCALE);
Imgcodecs.imwrite("D:\\Pic\\1.png", img);

// 转成二值化图片
Mat img2 = new Mat();
Imgproc.threshold(img, img2, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
Imgcodecs.imwrite("D:\\Pic\\2.png", img2);

// 膨胀
Mat img3 = OpenCVUtil.eroding(img2);
Imgcodecs.imwrite("D:\\Pic\\3.png", img3);


}
}

如上就分别展示了

  1. 图片转灰度
  2. 灰度图片二值化
  3. 二值化图片黑色区域膨胀
  4. 图片的裁剪

灰度

1
2
3
// 以灰度方式,读取图片
Mat img = Imgcodecs.imread("D:\\Pic\\0.png", Imgcodecs.IMREAD_GRAYSCALE);
Imgcodecs.imwrite("D:\\Pic\\1.png", img);

二值化

1
2
3
4
// 转成二值化图片
Mat img2 = new Mat();
Imgproc.threshold(img, img2, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
Imgcodecs.imwrite("D:\\Pic\\2.png", img2);

膨胀

1
2
3
// 膨胀
Mat img3 = OpenCVUtil.eroding(img2);
Imgcodecs.imwrite("D:\\Pic\\3.png", img3);

高斯模糊

1
2
3
Mat img01 = new Mat();
Imgproc.GaussianBlur(img, img01, new Size(1, 1), 10, 10);
Imgcodecs.imwrite("D:\\Pic\\img01.png", img01);

剪裁

1
2
3
4
// 截取左上角四分之一区域
Rect rect = new Rect(0, 0, img2.cols() / 2, img2.rows() / 2);
Mat img4 = new Mat(img2, rect);
Imgcodecs.imwrite("D:\\Pic\\4.png", img4);

工具类

通用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package utils.opencv;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.util.*;

import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

public class OpenCVUtil {
public static BufferedImage covertMat2Buffer(Mat mat) throws IOException {
long time1 = new Date().getTime();
// Mat 转byte数组
BufferedImage originalB = toBufferedImage(mat);
long time3 = new Date().getTime();
System.out.println("保存读取方法2转=" + (time3 - time1));
return originalB;
// ImageIO.write(originalB, "jpg", new File("D:\\test\\testImge\\ws2.jpg"));
}

public static byte[] covertMat2Byte(Mat mat) throws IOException {
long time1 = new Date().getTime();
// Mat 转byte数组
byte[] return_buff = new byte[(int) (mat.total() * mat.channels())];
Mat mat1 = new Mat();
mat1.get(0, 0, return_buff);
long time3 = new Date().getTime();
System.out.println(mat.total() * mat.channels());
System.out.println("保存读取方法2转=" + (time3 - time1));
return return_buff;
}

public static byte[] covertMat2Byte1(Mat mat) throws IOException {
long time1 = new Date().getTime();
MatOfByte mob = new MatOfByte();

Imgcodecs.imencode(".jpg", mat, mob);

long time3 = new Date().getTime();
// System.out.println(mat.total() * mat.channels());
System.out.println("Mat转byte[] 耗时=" + (time3 - time1));
return mob.toArray();
}

public static BufferedImage toBufferedImage(Mat m) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if (m.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = m.channels() * m.cols() * m.rows();
byte[] b = new byte[bufferSize];
m.get(0, 0, b); // get all the pixels
BufferedImage image = new BufferedImage(m.cols(), m.rows(), type);
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(b, 0, targetPixels, 0, b.length);
return image;
}

/**
* 腐蚀膨胀是针对于白色区域来说的,腐蚀即腐蚀白色区域
* 腐蚀算法(黑色区域变大)
*
* @param source
* @return
*/
public static Mat eroding(Mat source) {
return eroding(source, 1);
}

public static Mat eroding(Mat source, double erosion_size) {
Mat resultMat = new Mat(source.rows(), source.cols(), source.type());
Mat element = Imgproc.getStructuringElement(
Imgproc.MORPH_RECT,
new Size(erosion_size + 1, erosion_size + 1)
);
Imgproc.erode(source, resultMat, element);
return resultMat;
}

/**
* 腐蚀膨胀是针对于白色区域来说的,膨胀是膨胀白色区域
* 膨胀算法(白色区域变大)
*
* @param source
* @return
*/
public static Mat dilation(Mat source) {
return dilation(source, 1);
}

/**
* 腐蚀膨胀是针对于白色区域来说的,膨胀是膨胀白色区域
*
* @param source
* @param dilation_size 膨胀因子2*x+1 里的x
* @return Mat
*/
public static Mat dilation(Mat source, double dilation_size) {
Mat resultMat = new Mat(source.rows(), source.cols(), source.type());
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2 * dilation_size + 1,
2 * dilation_size + 1));
Imgproc.dilate(source, resultMat, element);
return resultMat;
}
}

透视变换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package utils.opencv;

import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.utils.Converters;

import java.util.Arrays;
import java.util.List;

/**
* 透视变换工具类
* 因为我透视变换做的也不是很好,就仅提供一个大概的函数...
*/
public class WarpPerspectiveUtils {

/**
* 透视变换
*
* @param src
* @param points
* @return
*/
public static Mat warpPerspective(Mat src, Point[] points) {
// 点的顺序[左上 ,右上 ,右下 ,左下]
List<Point> listSrcs = Arrays.asList(
points[0],
points[1],
points[2],
points[3]
);
Mat srcPoints = Converters.vector_Point_to_Mat(listSrcs, CvType.CV_32F);

List<Point> listDsts = Arrays.asList(
new Point(0, 0),
new Point(src.width(), 0),
new Point(src.width(), src.height()),
new Point(0, src.height())
);


Mat dstPoints = Converters.vector_Point_to_Mat(listDsts, CvType.CV_32F);

Mat perspectiveMmat = Imgproc.getPerspectiveTransform(dstPoints, srcPoints);

Mat dst = new Mat();

Imgproc.warpPerspective(
src,
dst,
perspectiveMmat,
src.size(),
Imgproc.INTER_LINEAR + Imgproc.WARP_INVERSE_MAP,
1,
new Scalar(0)
);

return dst;
}
}

轮廓相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package utils.opencv;

import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.imgproc.Imgproc;

import java.util.Vector;

/**
* 轮廓工具类
*/
public class ContoursUtils {

/**
* 获取图片的四个顶点
*
* @param img
* @return
*/
public static Point[] getAllPoints(Mat img) {
Point[] potArr = new Point[4];
for (int i = 0; i < 4; i++) {
potArr[i] = new Point(-1, -1);
}

int[] spaceArr = new int[]{-1, -1, -1, -1};
int cols = img.cols();
int rows = img.rows();
int x1 = cols / 3;
int x2 = cols * 2 / 3;
int y1 = rows / 3;
int y2 = rows * 2 / 3;
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
if (x > x1 && x < x2 && y > y1 && y < y2) {
continue;
}
double[] darr = img.get(y, x);
if (darr != null && darr.length >= 1 && darr[0] == 0) {
if (spaceArr[0] == -1) {
potArr[0].x = x;
potArr[0].y = y;
potArr[1].x = x;
potArr[1].y = y;
potArr[2].x = x;
potArr[2].y = y;
potArr[3].x = x;
potArr[3].y = y;
spaceArr[0] = getSpace(0, 0, x, y);
spaceArr[1] = getSpace(cols, 0, x, y);
spaceArr[2] = getSpace(cols, rows, x, y);
spaceArr[3] = getSpace(0, rows, x, y);
} else {
int s0 = getSpace(0, 0, x, y);
int s1 = getSpace(cols, 0, x, y);
int s2 = getSpace(cols, rows, x, y);
int s3 = getSpace(0, rows, x, y);
if (s0 < spaceArr[0]) {
spaceArr[0] = s0;
potArr[0].x = x;
potArr[0].y = y;
}
if (s1 < spaceArr[1]) {
spaceArr[1] = s1;
potArr[1].x = x;
potArr[1].y = y;
}
if (s2 < spaceArr[2]) {
spaceArr[2] = s2;
potArr[2].x = x;
potArr[2].y = y;
}
if (s3 < spaceArr[3]) {
spaceArr[3] = s3;
potArr[3].x = x;
potArr[3].y = y;
}
}

}
}
}
return potArr;
}

/**
* 轮廓识别,使用最外轮廓发抽取轮廓RETR_EXTERNAL,轮廓识别方法为CHAIN_APPROX_SIMPLE
*
* @param source 传入进来的图片Mat对象
* @return 返回轮廓结果集
*/
public static Vector<MatOfPoint> findContours(Mat source) {
Mat rs = new Mat();
/**
* 定义轮廓抽取模式
*RETR_EXTERNAL:只检索最外面的轮廓;
*RETR_LIST:检索所有的轮廓,并将其放入list中;
*RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
*RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次。
*/
/**
* 定义轮廓识别方法
* 边缘近似方法(除了RETR_RUNS使用内置的近似,其他模式均使用此设定的近似算法)。可取值如下:
*CV_CHAIN_CODE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
*CHAIN_APPROX_NONE:将所有的连码点,转换成点。
*CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
*CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用the flavors of Teh-Chin chain近似算法的一种。
*LINK_RUNS:通过连接水平段的1,使用完全不同的边缘提取算法。使用CV_RETR_LIST检索模式能使用此方法。
*/
Vector<MatOfPoint> contours = new Vector<MatOfPoint>();
Imgproc.findContours(
source,
contours,
rs,
Imgproc.RETR_LIST,
Imgproc.CHAIN_APPROX_SIMPLE
);
return contours;
}

/**
* 计算两点之间的距离
*
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
private static int getSpace(int x1, int y1, int x2, int y2) {
int xspace = Math.abs(x1 - x2);
int yspace = Math.abs(y1 - y2);
return (int) Math.sqrt(Math.pow(xspace, 2) + Math.pow(yspace, 2));
}
}

处理全流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import utils.opencv.ContoursUtils;
import utils.opencv.OpenCVUtil;
import utils.opencv.WarpPerspectiveUtils;

import java.util.Vector;


public class Test01 {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

public static void main(String[] args) {
String basepath = "D:\\Project\\Java\\opencv-demo01\\pic\\";

// 以灰度方式,读取图片
Mat img = Imgcodecs.imread(basepath + "0.jpg", Imgcodecs.IMREAD_GRAYSCALE);
Imgcodecs.imwrite(basepath + "1.png", img);

// 转成二值化图片
Mat img2 = new Mat();
Imgproc.threshold(img, img2, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
Imgcodecs.imwrite(basepath + "2.png", img2);

// 透视变形
Mat img3 = WarpPerspectiveUtils.warpPerspective(img2, ContoursUtils.getAllPoints(img2));
Imgcodecs.imwrite(basepath + "3.png", img3);

// 膨胀
Mat img4 = OpenCVUtil.eroding(img3);
Imgcodecs.imwrite(basepath + "4.png", img4);

// 截取选择题区域
Rect rect = new Rect(68, 834, 1536, 220);
Mat img5 = new Mat(img4, rect);
Imgcodecs.imwrite(basepath + "5.png", img5);

// 获取边界
Vector<MatOfPoint> rectVec = ContoursUtils.findContours(img5);
Vector<MatOfPoint> rectVec2 = new Vector<>();
for (MatOfPoint matOfPoint : rectVec) {
Rect rect2 = Imgproc.boundingRect(matOfPoint);
if (rect2.width > 36 && rect2.height > 20 && rect2.width < 50 && rect2.height < 40) {
rectVec2.add(matOfPoint);
}
}
Mat img6 = new Mat(img5.rows(), img5.cols(), CvType.CV_8UC3, new Scalar(255, 255, 255));
Imgproc.drawContours(img6, rectVec2, -1, new Scalar(0, 0, 255), 1);
Imgcodecs.imwrite(basepath + "6.png", img6);
}
}

绘图

画线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String basePath = "D:\\Project\\Java\\opencv-demo01\\pic\\";

Scalar bgColor = new Scalar(255, 255, 255, 0); // B G R 0 白色
Scalar color = new Scalar(0, 0, 255, 0); // B G R 0 红色
Mat img01 = new Mat(new Size(400, 500), CvType.CV_8UC3, bgColor);
Imgproc.line(
img01,
new Point(11, 22),
new Point(220, 330),
color,
1,
Imgproc.LINE_AA
);
Imgcodecs.imwrite(basePath + "line.png", img01);

说明

参数 解释
InputOutPutArray img 在 img 图像上绘制
Point pt1 端点1
Point pt2 端点2
Scalar& color 颜色
int thickness 线条厚度
lineType 线条边缘类型(LINE_4(边缘像素采用4连通,即上下左右),LINE_8(边缘像素采用8连通,即上下左右还有四个对角),LINE_AA(边缘像素采用高斯滤波,抗锯齿))

画椭圆

1
2
3
4
5
6
7
String basePath = "D:\\Project\\Java\\opencv-demo01\\pic\\";

Scalar bck = new Scalar(255, 255, 255, 0); // B G R 0 白色
Scalar scalar = new Scalar(0, 0, 255, 0); // B G R 0 红色
Mat img01 = new Mat(new Size(400, 500), CvType.CV_8UC3, bck);
Imgproc.ellipse(img01, new Point(256, 256), new Size(100, 50), 0, 0, 360, scalar);
Imgcodecs.imwrite(basePath + "ellipse.png", img01);

矩形

1
2
3
4
5
6
7
String basePath = "D:\\Project\\Java\\opencv-demo01\\pic\\";

Scalar bck = new Scalar(255, 255, 255, 0); // B G R 0 白色
Scalar scalar = new Scalar(0, 0, 255, 0); // B G R 0 红色
Mat img01 = new Mat(new Size(400, 500), CvType.CV_8UC3, bck);
Imgproc.rectangle(img01, new Point(100, 20), new Point(310, 148), scalar);
Imgcodecs.imwrite(basePath + "rectangle.png", img01);

圆圈

1
2
3
4
5
6
7
String basePath = "D:\\Project\\Java\\opencv-demo01\\pic\\";

Scalar bck = new Scalar(255, 255, 255, 0); // B G R 0 白色
Scalar scalar = new Scalar(0, 0, 255, 0); // B G R 0 红色
Mat img01 = new Mat(new Size(400, 500), CvType.CV_8UC3, bck);
Imgproc.circle(img01, new Point(100, 100), 63, scalar);
Imgcodecs.imwrite(basePath + "circle.png", img01);

文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
String basePath = "D:\\Project\\Java\\opencv-demo01\\pic\\";

Scalar bck = new Scalar(255, 255, 255, 0); // B G R 0 白色
Scalar scalar = new Scalar(0, 0, 255, 0); // B G R 0 红色
Mat img01 = new Mat(new Size(400, 500), CvType.CV_8UC3, bck);
Imgproc.putText(
img01,
"Hello Word",
new Point(10, 100),
Imgproc.FONT_HERSHEY_SIMPLEX,
2,
scalar,
2,
Imgproc.LINE_AA,
false
);
Imgcodecs.imwrite(basePath + "text.png", img01);

要将文本放入图像中,需要指定以下内容。

  • 您要写入的文字数据
  • 您要放置它的位置坐标(即文字的左下角)。
  • 字体类型
  • 字体比例(指定字体大小)
  • 线条类型 为了获得更好的外观,建议使用LINE_AA