JoyLau's Blog

JoyLau 的技术学习与思考

背景

上一篇文章记录了因为远程桌面连接把 Ubuntu 的 /home 弄坏了
好一番折腾。。。。
其实这个远程桌面我早就想重新配置了,今天我终于受不了它了,于是我觉得仔细研究一番找到适合我自己的方式来操作

以前的方式

之前我的远程配置是 xrdp + tightvncserver
然后我每次都是使用 Windows 上的 mstsc 来连接的
连接上后会出现 xrpd 的登录选项
每次我都选第一个 sesman-Xvnc 然后输入用户名密码即可

可这样的连接方式有个很不好的方面,就是这种方式是多用户的,想回家继续没干完的事情
连接上发现是一个新的桌面
都不知道做到什么地方了

这也就算了

最大的问题远程操作操作这就没响应了,鼠标的指针变成了 × 号,所有的东西都不能点,而且第二天到公司桌面卡死不动,只能重启桌面或重启系统,很多打开软件和工具都会还原

这是我最不能忍的地方

决定改变

我决定不使用这种方式来进行远程,远程 teamviewer 是比较合适的选择,但是工作由于连接的终端太多,被检测商用,每次连接都是只有 1 min 的操作时间
很尴尬…

最后决定使用轻量级的 vnc 服务来解决这个问题,并且搭配 xrdpany vnc 来使用 mstsc 远程连接

重新配置

x11vnc

  1. 卸载以前的 vnc 服务端
1
2
3
sudo apt remove tigervncserver
sudo apt remove tightvncserver
systemcrl auto remove
  1. 安装 x11vnc ,并进行配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sudo apt install x11vnc -y

sudo x11vnc -storepasswd /etc/x11vnc.pass # 配置访问密码并存储

vim /lib/systemd/system/x11vnc.service # 创建系统服务

# 服务配置
[Unit]
Description=Start x11vnc at startup.
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/x11vnc -auth guess -forever -loop -noxdamage -repeat -rfbauth /etc/x11vnc.pass -rfbport 5900 -shared
[Install]
WantedBy=multi-user.target

systemctl enable x11vnc.service
systemctl start x11vnc.service

  1. 解决复制粘贴的问题
    • sudo apt install autocutsel 安装 autocutsel
    • autocutsel -f 后台运行

问题及解决

下载 vnc-view 新建一个连接发现连不上…
尴尬。。。
检查 5900 端口,是开放的

1
2
3
joylau@joylau-work-192:~$ sudo netstat -tnlp | grep :5900
tcp 0 0 0.0.0.0:5900 0.0.0.0:* LISTEN 4022/vino-server
tcp6 0 0 :::5900 :::* LISTEN 4022/vino-server

但是使用的进程是 vino-server ,这是 Ubuntu 自带程序开启的服务
原来端口被占用了
关闭服务 : 找到桌面共享,关闭 允许其他人查看您的桌面
重启 x11vnc 服务
连接成功

最后

现在有 4 中方式使用

  1. 使用 vnc-view 使用是单用户的,类似 teamviewer 那样,2 边操作都能互相看见
  2. 使用 mstsc , 连接到 xrdp 后,再选中 any vnc 使用 vnc 协议连接,效果和第一种是一样的,只不过不需要客户端了
  3. 浏览器直接远程,这种最方便,下面有说明
  4. 以前的那种使用方式, 多用户的,估计我是不会再用了

补充

第三种多用户方式连接,没连接一次就生成一个新的桌面,这个很烦,想连接回上次的桌面,可修改配置 /etc/xrdp/xrdp.ini

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
[globals]

bitmap_cache=yes 位图缓存

bitmap_compression=yes 位图压缩

port=3389 xrdp监听的端口(重要)

crypt_level=low 加密程度(low为40位,high为128位,medium为双40位)

channel_code=1

max_bpp=24 XRDP最大连接数

[xrdp1]

name=sesman-Xvnc XRDP的连接模式

lib=libvnc.so

username=ask

password=ask

ip=127.0.0.1

port=-1

修改 port 为 固定端口号或者 ask-1
下次连接不修改即可

注:再记录下 sesman.ini 的配置

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
[Globals]

ListenAddress=127.0.0.1 监听ip地址(默认即可)

ListenPort=3350 监听端口(默认即可)

EnableUserWindowManager=1 1为开启,可让用户自定义自己的启动脚本

UserWindowManager=startwm.sh

DefaultWindowManager=startwm.sh

[Security]

AllowRootLogin=1 允许root登陆

MaxLoginRetry=4 最大重试次数

TerminalServerUsers=tSUSErs 允许连接的用户组(如果不存在则默认全部用户允许连接)

TerminalServerAdmins=tsadmins 允许连接的超级用户(如果不存在则默认全部用户允许连接)

[Sessions]

MaxSessions=10 每个用户最大会话数

KillDisconnected=0 是否立即关闭断开的连接(如果为1,则断开连接后会自动注销)

IdleTimeLimit=0 空闲会话时间限制(0为没有限制)

DisconnectedTimeLimit=0 断开连接的存活时间(0为没有限制)

[Logging]

LogFile=./sesman.log 登陆日志文件

LogLevel=DEBUG 登陆日志记录等级(级别分别为,core,error,warn,info,debug)

EnableSyslog=0 是否开启日志

SyslogLevel=DEBUG 系统日志记录等级

使用浏览器来远程桌面

像阿里云等云服务提供商一样直接在浏览器上进行远程操作

1
docker run  -e REMOTE_HOST=192.168.10.192 -e REMOTE_PORT=5900 -p 8081:8081 -d --restart always --name novnc dougw/novnc

打开浏览器 http://host:8081/vnc.html

秀啊!!!

背景

在家里使用 vnc 协议远程连接公司的 Ubuntu 电脑
导致桌面卡死
期间还遇到了 搜狗输入法崩溃,提示我删除用户目录下的一个文件然后重启
鼠标可以动
界面上的任何东西都无法点击
没再操作
等第二天到公司解决

不重启解决 Ubuntu 桌面卡死

这样的情况遇到很多了
ctrl + alt + f1

1
2
3
4
5
6
7
ps -t tty7

PID TTY TIME CMD
1758 tty7 00:00:55 Xorg

kill -9 1758

之后桌面上的应用都会被关闭,回到登录界面

重启进入紧急模式

之后我想着电脑很久没关机了,想重启一下,顺便去倒杯水
回来之后发现系统正在进行磁盘检测并且之后进入了紧急模式

journalctl -xb 查看启动日志

一直往下翻

发现 /dev/sdb6 分区出现问题导致系统无法启动

使用 lsblk 查看分区挂载情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
joylau@joylau-work-192:~$ lsblk -f
NAME FSTYPE LABEL UUID MOUNTPOINT
loop1 squashfs /snap/core/6405
sdb
├─sdb2
├─sdb5 ext4 4ff695c6-b2ef-46d9-8501-c7e8ee61edda /
├─sdb1 ext4 98f0eb66-3d90-4bc5-a1f0-d3117de87809 /boot
└─sdb6 ext4 76ad5dc1-37b7-4624-830b-d923dac8ac48
loop4 squashfs /snap/redis-desktop-manager/191
loop2 squashfs /snap/core/6673
loop0 squashfs /snap/redis-desktop-manager/156
sdc
├─sdc2 ntfs 新加卷 AE5CA91F5CA8E2F7 /media/extra
└─sdc1 ntfs 新加卷 0CBC9840BC9825EC
sda
├─sda2 ntfs 5E68EE4D68EE240D
├─sda7 ntfs EAD67107D670D573
├─sda5 ntfs 9E14908214905ED9
├─sda3
├─sda1 ntfs 系统保留 A27AE98B7AE95C93
└─sda6 ntfs E2D84A6BD84A3E53 /media/extra_2
loop3 squashfs

解决问题

上面看到 sdb6 没有挂载点,实际上是有的,只不过现在出问题了没有挂载上
可以找 UUID 76ad5dc1-37b7-4624-830b-d923dac8ac48

查看 /etc/fstab

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point> <type> <options> <dump> <pass>
# / was on /dev/sdb5 during installation
UUID=4ff695c6-b2ef-46d9-8501-c7e8ee61edda / ext4 errors=remount-ro 0 1
# /boot was on /dev/sdb1 during installation
UUID=98f0eb66-3d90-4bc5-a1f0-d3117de87809 /boot ext4 defaults 0 2
# /home was on /dev/sdb6 during installation
UUID=76ad5dc1-37b7-4624-830b-d923dac8ac48 /home ext4 defaults 0 2
# swap was on /dev/sdc5 during installation
#UUID=a99b0d98-9282-4e52-8f49-74b9b1f2ed8e none swap sw 0 0

UUID=AE5CA91F5CA8E2F7 /media/extra ntfs defaults 0 0
UUID=E2D84A6BD84A3E53 /media/extra_2 ntfs defaults 0 0

查看到 76ad5dc1-37b7-4624-830b-d923dac8ac48 对应挂载的 /home 目录

后面的 pass 写的是 2 ,就是说开机进行磁盘检查,并且数值越小,越先检查

这里有个临时的解决方式就是将 /home 的 pass 改为 0 ,也就是开机不进行检查,该分区有问题并不代表分区不可用

改完后依然可以访问 /home 目录

磁盘修复

强迫症让我不能就这么将就
我觉得修复这个磁盘错误
使用命令修复这个错误如下

1
fsck -y /dev/sdb6

结果提示 分区已挂载,操作被终止

修改 fstab 将 sdb6 挂载的那行注释
重启
进入紧急模式
运行 fsck -y /dev/sdb6
这时会打印很多日志
重复执行,直到没有日志打印

这时在修改 fstab, 去掉注释,pass 改为 2
重启
解决

背景

Docker 启动报错: Error starting daemon: Error initializing network controller: list bridge addresses failed: no available network

错误详情

查看错误日志: journalctl -xe | grep docker

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
[root@lenovo docker]# journalctl -xe | grep docker
-- Subject: Unit docker.socket has begun start-up
-- Unit docker.socket has begun starting up.
-- Subject: Unit docker.socket has finished start-up
-- Unit docker.socket has finished starting up.
-- Subject: Unit docker.service has begun start-up
-- Unit docker.service has begun starting up.
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.909025064+08:00" level=info msg="parsed scheme: \"unix\"" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.909923898+08:00" level=info msg="scheme \"unix\" not registered, fallback to default scheme" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.910865280+08:00" level=info msg="parsed scheme: \"unix\"" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.910909267+08:00" level=info msg="scheme \"unix\" not registered, fallback to default scheme" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.928785984+08:00" level=info msg="ccResolverWrapper: sending new addresses to cc: [{unix:///run/containerd/containerd.sock 0 <nil>}]" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.928902169+08:00" level=info msg="ClientConn switching balancer to \"pick_first\"" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.929039549+08:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0xc420606e00, CONNECTING" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.937533340+08:00" level=info msg="ccResolverWrapper: sending new addresses to cc: [{unix:///run/containerd/containerd.sock 0 <nil>}]" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.937601232+08:00" level=info msg="ClientConn switching balancer to \"pick_first\"" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.937707487+08:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0xc42015bf00, CONNECTING" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.950807950+08:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0xc42015bf00, READY" module=grpc
4月 08 16:42:09 lenovo dockerd[1742]: time="2019-04-08T16:42:09.952160247+08:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0xc420606e00, READY" module=grpc
4月 08 16:42:10 lenovo dockerd[1742]: time="2019-04-08T16:42:10.216864045+08:00" level=info msg="Graph migration to content-addressability took 0.00 seconds"
4月 08 16:42:10 lenovo dockerd[1742]: time="2019-04-08T16:42:10.218710988+08:00" level=info msg="Loading containers: start."
4月 08 16:42:10 lenovo dockerd[1742]: Error starting daemon: Error initializing network controller: list bridge addresses failed: no available network
4月 08 16:42:10 lenovo systemd[1]: docker.service: main process exited, code=exited, status=1/FAILURE
-- Subject: Unit docker.service has failed
-- Unit docker.service has failed.
4月 08 16:42:10 lenovo systemd[1]: Unit docker.service entered failed state.
4月 08 16:42:10 lenovo systemd[1]: docker.service failed.
4月 08 16:42:13 lenovo systemd[1]: docker.service holdoff time over, scheduling restart.
-- Subject: Unit docker.socket has begun shutting down
-- Unit docker.socket has begun shutting down.
-- Subject: Unit docker.socket has begun start-up
-- Unit docker.socket has begun starting up.
-- Subject: Unit docker.socket has finished start-up
-- Unit docker.socket has finished starting up.
-- Subject: Unit docker.service has begun start-up
-- Unit docker.service has begun starting up.

看到这样一句话: Error starting daemon: Error initializing network controller: list bridge addresses failed: no available network

查看本机网络: ip a

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@lenovo docker]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp7s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether b8:70:f4:24:61:a7 brd ff:ff:ff:ff:ff:ff
3: wlp8s0b1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether cc:af:78:25:31:51 brd ff:ff:ff:ff:ff:ff
inet 192.168.10.145/24 brd 192.168.10.255 scope global noprefixroute wlp8s0b1
valid_lft forever preferred_lft forever
inet6 fe80::8de1:5b7d:b7d7:2788/64 scope link noprefixroute
valid_lft forever preferred_lft forever
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100
link/none
inet 192.168.255.10 peer 192.168.255.9/32 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::e41d:195:f566:33e1/64 scope link flags 800
valid_lft forever preferred_lft forever

没有 docker0 的桥接网络

手动添加一个即可

解决

1
2
ip link add name docker0 type bridge
ip addr add dev docker0 172.17.0.1/16

再看一下,多了一个 docker0

1
2
3
4
5
5: docker0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether a6:7d:d7:94:ab:f3 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever

重启 docker 即可

背景

OpenCV 提供的 API 是直接根据路径读取图片的, 在实际生产环境中,可能大部分情况下都是直接读取网络图片

在内存就完成图片和 opencv 的 Mat 对象的转换

那么该如何读取 byte[] 的图片呢?

API

openCV 提供的 API

1
Mat src = Imgcodecs.imread("/static/img/17.png");

很简单的就转化为 Mat 对象

而 该方法后面还有一个参数, flags, 该参数可选项有:

  • IMREAD_UNCHANGED = -1,
  • IMREAD_GRAYSCALE = 0,
  • IMREAD_COLOR = 1,
  • IMREAD_ANYDEPTH = 2,
  • IMREAD_ANYCOLOR = 4,
  • IMREAD_LOAD_GDAL = 8,
  • IMREAD_REDUCED_GRAYSCALE_2 = 16,
  • IMREAD_REDUCED_COLOR_2 = 17,
  • IMREAD_REDUCED_GRAYSCALE_4 = 32,
  • IMREAD_REDUCED_COLOR_4 = 33,
  • IMREAD_REDUCED_GRAYSCALE_8 = 64,
  • IMREAD_REDUCED_COLOR_8 = 65,
  • IMREAD_IGNORE_ORIENTATION = 128;

IMREAD_UNCHANGED: 以图片原有的方式读入,不进行任何改变
IMREAD_GRAYSCALE: 以灰度图读取
IMREAD_COLOR: 以彩色图读取

过渡

为了支持 OpenCV 读取 byte[] 的图片,为此我查找了很多资料做了大量的实验,有很多失败报错了,也有读取成功的,下面我将一一列举出来….

读取失败

Converters 类

我留意到 opencv 提供的 api 里有一个 utils 包, 里面有个转换类 Converters, 可以将 Mat 和 一些 java 的基本数据类型进行互相转换,其中有这样 2 个方法: vector_uchar_to_Matvector_char_to_Mat
参数是 List<Byte>

1
2
3
4
5
6
    private static Mat testConvertChar2Mat(byte[] bytes){
@SuppressWarnings("unchecked")
List<Byte> bs = CollectionUtils.arrayToList(bytes);
return Converters.vector_uchar_to_Mat(bs);
// return Converters.vector_char_to_Mat(bs);
}

vector_uchar_to_Mat 指有符号

转换出来的图片是一个像素的竖直线,读取失败

new Mat

Mat 对象除了转化得到,还可以 new , 再利用 Mat 的 put 方法,来创建 Mat

1
2
3
4
5
private static Mat testNewMat(int height, int width, byte[] bytes) throws IOException {
Mat data = new Mat(height, width, CvType.CV_8UC3);
data.put(0, 0, bytes);
return data;
}

转换出来的图片也不对,一些花花绿绿的像素点

new BufferByte

Mat 对象还有个构造方法,最后一个参数是传入 BufferByte,这时只需要在上述步骤中再将 byte[] 转化为 BufferByte

1
2
3
4
private static Mat testNewBuffer(int height, int width, byte[] bytes){
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
return new Mat(height, width, CvType.CV_8UC3,byteBuffer);
}

抛出异常: CvException [org.opencv.core.CvException: cv::Exception: OpenCV(4.1.0-pre) /Users/joylau/opencv4/opencv/modules/core/include/opencv2/core/mat.inl.hpp:548: error: (-215:Assertion failed) total() == 0 || data != NULL in function ‘Mat’

读取成功

BufferedImage 转换

一次我在调试代码时 发现 HighGui.waitKey(); 的实现是将 Mat 对象转化为 BufferedImage 的逻辑,于是我明白了,OpenCV 里操作的 Mat 在显示的时候也需要转化为 BufferedImage
源码里有这样一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static Image 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;
}

此时,我逆向转化,将 byte[] 转 BufferedImage ,BufferedImage 再转 Mat 即可

1
2
3
4
5
6
7
8
9
10
11
private static byte[] getBufferedImageByte(byte[] bytes) throws IOException{
BufferedImage bImage = ImageIO.read(new ByteArrayInputStream(bytes));
return ((DataBufferByte) bImage.getRaster().getDataBuffer()).getData();
}

// 再将从 BufferedImage 得到的 byte[] 使用 new Mat 对象
private static Mat testNewMat(int height, int width, byte[] bytes) throws IOException {
Mat data = new Mat(height, width, CvType.CV_8UC3);
data.put(0, 0, bytes);
return data;
}

该方法成功读取显示了图片

于是又引发了我的思考: 为什么直接从文件读取的 byte[] 无法被转化,而 BufferedImage 中得到的 byte[] 却可以被转化

于是我将 BufferedImage 中得到的 byte[] 在使用,调用 Converters.vector_char_to_Mat 方法

可惜却失败了…..

imdecode

Imgcodecs 类中有一个编码的方法 Imgcodecs.imdecode(Mat buf, int flags)
Mat 还有个子类 MatOfByte

1
2
3
private static Mat testImdecode(byte[] bytes){
return Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.IMREAD_COLOR);
}

该方法可成功转化

而且比上一个方法的优势是:

  1. byte[] 不需要再通过 BufferedImage 转化
  2. 不需要初始化 Mat 的长和宽

为此还可以逆向得出 Mat 转换成 byte[] 的方法

1
2
3
4
5
6
7
8
9
10
11
/**
* Mat转换成byte数组
*
* @param matrix 要转换的Mat
* @param fileExtension 格式为 ".jpg", ".png", etc
*/
public static byte[] mat2Byte(Mat matrix, String fileExtension) {
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(fileExtension, matrix, mob);
return mob.toArray();
}

最后

以下是全部测试代码

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
/**
* Created by liuf on 2019-04-01.
* cn.joylau.code
* liuf@ahtsoft.com
*/
@Slf4j
public class Byte2Mat {

public static void main(String[] args) throws Exception {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

// Mat mat = testImdecode(getImageByte());

// Mat mat = testConvertChar2Mat(getBufferedImageByte(getImageByte()));


// Mat mat = testNewBuffer(480,480,getImageByte());

// Mat mat = testNewMat(480,480,getImageByte());

Mat mat = testNewMat(480,480,getBufferedImageByte(getImageByte()));

log.info("{},{}",mat.rows(),mat.cols());
HighGui.imshow("byte2mat",mat);
HighGui.waitKey();
HighGui.destroyAllWindows();
}

private static byte[] getImageByte() throws IOException{
Resource resource = new FileSystemResource("/Users/joylau/work/anhui-project/traffic-service-layer/src/main/resources/static/img/1.jpg");
return IOUtils.toByteArray(resource.getInputStream());
}

private static byte[] getBufferedImageByte(byte[] bytes) throws IOException{
BufferedImage bImage = ImageIO.read(new ByteArrayInputStream(bytes));
return ((DataBufferByte) bImage.getRaster().getDataBuffer()).getData();
}


private static Mat testNewMat(int height, int width, byte[] bytes) throws IOException {
Mat data = new Mat(height, width, CvType.CV_8UC3);
data.put(0, 0, bytes);
return data;
}

private static Mat testNewBuffer(int height, int width, byte[] bytes){
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
return new Mat(height, width, CvType.CV_8UC3,byteBuffer);
}


private static Mat testConvertChar2Mat(byte[] bytes){
@SuppressWarnings("unchecked")
List<Byte> bs = CollectionUtils.arrayToList(bytes);
return Converters.vector_uchar_to_Mat(bs);
// return Converters.vector_char_to_Mat(bs);
}

private static Mat testImdecode(byte[] bytes){
return Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.IMREAD_COLOR);
}





/**
* Mat转换成byte数组
*
* @param matrix 要转换的Mat
* @param fileExtension 格式为 ".jpg", ".png", etc
*/
public static byte[] mat2Byte(Mat matrix, String fileExtension) {
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(fileExtension, matrix, mob);
return mob.toArray();
}
}

一些概念

数字图像

数字图像指的是现在的图像都是以二维数字表示,每个像素的灰度值均由一个数字表示,范围为0-255(2^8)

二值图像

图像中每个像素的灰度值仅可取0或1,即不是取黑,就是取白,二值图像可理解为黑白图像

灰度图像

图像中每个像素可以由0-255的灰度值表示,具体表现为从全黑到全白中间有255个介于中间的灰色值可以取

彩色图像

每幅图像是由三幅灰度图像组合而成,依次表示红绿蓝三通道的灰度值,即我们熟知的RGB,此时彩色图像要视为三维的 [height,width, 3]

CvType

通道

OpenCV 中,图像可以分别为1,2,3,4 通道

  • 通道为灰度图;
  • 通道的图像是RGB555和RGB565。2通道图在程序处理中会用到,如傅里叶变换,可能会用到,一个通道为实数,一个通道为虚数,主要是编程方便。RGB555是16位的,2个字节,5+6+5,第一字节的前5位是R,后三位+第二字节是G,第二字节后5位是B,可见对原图像进行压缩了
  • 通道为彩色图(RGB);
  • 通道为 RGBA ,是RGB加上一个A通道,也叫alpha通道,表示透明度,PNG图像是一种典型的4通道图像。alpha通道可以赋值0到1,或者0到255,表示透明到不透明

常使用的是1,3,4通道; 2通道不常见

组合规则

CV_[bite](U|S|F)C[channels]

bite : 比特数,位数。 有 8bite,16bite,32bite,64bite,对应在 Mat 中,每个像素的所占的空间大小,8位即 CV_8

U|S|F :
- U : unsigned int , 无符号整形
- S : signed int , 有符号整形
- F : float , 单精度浮点型,float类型本身即有符号

这里的有符号、无符号是针对图像二进制编码来讲的。我在写的过程中大多数情况下都是使用的无符号,即 CV_8U ,CV_16U,当有计算时可能会介入有符号(存在负数),没学过 C++,对底层也一知半解,望高手解答。

C (channels):图像的通道数

比如: CV_8UC3 即 8位无符号的3通道(RGB 彩色)图像

参数说明

8U
- 说明:无符号的8位图
- 值:CV_8UC1,CV_8UC2,CV_8UC3,CV_8UC4
- 通道取值范围:0~255

8S
- 说明:有符号的8位图
- 值:CV_8SC1,CV_8SC2,CV_8SC3,CV_8SC4
- 通道取值范围:-128~127

16U
- 说明:无符号的16位图
- 值:CV_16UC1,CV_16UC2,CV_16UC3,CV_16UC4
- 通道取值范围:0~65535

16S
- 说明:有符号的16位图
- 值:CV_16SC1,CV_16SC2,CV_16SC3,CV_16SC4
- 通道取值范围:-32768~32767

32S
- 说明:无符号的32位图
- 值:CV_32SC1,CV_32SC2,CV_32SC3,CV_32SC4
- 通道取值范围:2147483648~2147483647

32F
- 说明:浮点型32位图
- 值:CV_32FC1,CV_32FC2,CV_32FC3,CV_32FC4
- 通道取值范围:1.18*(10(-38次方))~3.40*(10(38次方))

64F
- 说明:浮点型64位图
-值:CV_64FC1,CV_64FC2,CV_64FC3,CV_64FC4
- 通道取值范围:2.23*(10(-308次方))~1.79*(10(308次方))

1U
- 说明:1位
- 值:IPL_DEPTH_1U
- 通道取值范围:0~1

色彩空间

常见的色彩空间

  • RGB
  • HSV
  • HIS
  • YCRCB
  • YUV

HSV

HSV分别是色调(Hue),饱和度(Saturation)和亮度(Value)

H调整颜色;S越大,图像色彩越丰富,颜色越鲜艳;V越大,图像越亮

HSV颜色取值范围

  1. H:0— 180 : 之所以不是 360,是因为 8 位图 最大是 255,360 已经超出范围,以 180 为限定

  2. S: 0— 255

  3. V: 0— 255

记住下面这张图, 可使用这张图中的范围来查找某种颜色

HSV

转换方法

Imgproc.cvtColor(src,det,Imgproc.COLOR_BGR2HSV);

问题

启动 docker 容器时挂载容器以前存在的数据文件时出现了 Permission denied 的错误

解决

  1. 首先以为是挂载的文件夹有读写数据的权限问题 chmod -R 777 xxxx , 没有解决,依然报错
  2. 再分析是文件目录的所属者的问题: chown -R gname:uname xxxx , 没有解决,依然报错
  3. 这时我们进入容器之后 使用 ll 查看挂载的目录的所属者,发现组名和户名跟宿主机的组名和用户名不一致
  4. 原因在于,操作系统判断用户组和用户其实并不是根据名称来的,而是根据名称对应的 id 来的
  5. 查看用户组和用户名对象的 id, 可查看 /etc/passwd
  6. 此时,我们需要将宿主机的用户组用户的 ID 和 容器内挂在目录所需的用户组和用户的 ID 对应起来,写一直即可
  7. 举个例子
  8. redis 镜像产生的数据文件在 /var/lib/redis 中,并且该目录的用户组和用户都为 redis, 此时我们查看容器的 redis:redis 的 id , 假如是 102:103
  9. 此时我们宿主机挂载目录是 /opt/docker/redis/data ,我们改变这个目录的所属者 chown -R 102:103 /opt/docker/redis/data
  10. 不要管 102:103 在宿主机系统中有没有该用户组和用户
  11. 再次进入容器就可以看到 /var/lib/redis 目录的所属者是正确的了

mysql 和 mariaDB 的问题

这样的情况也发生在 mysql 和 mariaDB 上
按照上述的方法似乎没有奏效,确切的说奏效一半
因为 /var/lib/mysql 目录中文件夹可以看到,文件却没有权限看到
类似这样

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
190321 06:02:13 mysqld_safe Logging to '/var/lib/mysql/d240623581db.err'.
190321 06:02:13 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
chown: /var/lib/mysql/60689c28e4a1.err: Permission denied
chown: /var/lib/mysql/60689c28e4a1.pid: Permission denied
chown: /var/lib/mysql/aria_log.00000001: Permission denied
chown: /var/lib/mysql/aria_log_control: Permission denied
chown: /var/lib/mysql/ib_buffer_pool: Permission denied
chown: /var/lib/mysql/ibdata1: Permission denied
chown: /var/lib/mysql/ib_logfile0: Permission denied
chown: /var/lib/mysql/ib_logfile1: Permission denied
chown: /var/lib/mysql/ibtmp1: Permission denied
chown: /var/lib/mysql/multi-master.info: Permission denied
chown: /var/lib/mysql/mysql: Permission denied
chown: /var/lib/mysql/mysql-bin.000001: Permission denied
chown: /var/lib/mysql/mysql-bin.000002: Permission denied
chown: /var/lib/mysql/mysql-bin.000003: Permission denied
chown: /var/lib/mysql/mysql-bin.000004: Permission denied
chown: /var/lib/mysql/mysql-bin.000005: Permission denied
chown: /var/lib/mysql/mysql-bin.000006: Permission denied
chown: /var/lib/mysql/mysql-bin.000007: Permission denied
chown: /var/lib/mysql/mysql-bin.000008: Permission denied
chown: /var/lib/mysql/mysql-bin.000009: Permission denied
chown: /var/lib/mysql/mysql-bin.index: Permission denied
chown: /var/lib/mysql/owncloud: Permission denied
chown: /var/lib/mysql/performance_schema: Permission denied
chown: /var/lib/mysql: Permission denied
chown: /var/lib/mysql: Permission denied
190321 06:02:14 mysqld_safe Logging to '/var/lib/mysql/d240623581db.err'.
190321 06:02:14 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
chown: /var/lib/mysql/60689c28e4a1.err: Permission denied
chown: /var/lib/mysql/60689c28e4a1.pid: Permission denied
chown: /var/lib/mysql/aria_log.00000001: Permission denied
chown: /var/lib/mysql/aria_log_control: Permission denied
chown: /var/lib/mysql/ib_buffer_pool: Permission denied
chown: /var/lib/mysql/ibdata1: Permission denied
chown: /var/lib/mysql/ib_logfile0: Permission denied
chown: /var/lib/mysql/ib_logfile1: Permission denied
chown: /var/lib/mysql/ibtmp1: Permission denied
chown: /var/lib/mysql/multi-master.info: Permission denied
chown: /var/lib/mysql/mysql: Permission denied
chown: /var/lib/mysql/mysql-bin.000001: Permission denied
chown: /var/lib/mysql/mysql-bin.000002: Permission denied
chown: /var/lib/mysql/mysql-bin.000003: Permission denied
chown: /var/lib/mysql/mysql-bin.000004: Permission denied
chown: /var/lib/mysql/mysql-bin.000005: Permission denied
chown: /var/lib/mysql/mysql-bin.000006: Permission denied
chown: /var/lib/mysql/mysql-bin.000007: Permission denied
chown: /var/lib/mysql/mysql-bin.000008: Permission denied
chown: /var/lib/mysql/mysql-bin.000009: Permission denied
chown: /var/lib/mysql/mysql-bin.index: Permission denied
chown: /var/lib/mysql/owncloud: Permission denied
chown: /var/lib/mysql/performance_schema: Permission denied
chown: /var/lib/mysql: Permission denied
chown: /var/lib/mysql: Permission denied

原因分析是:
SELinux 造成的
有以下 4 中解决方法:

  1. setenforce 0 : 临时关闭
  2. vi /etc/selinux/config : 将 SELINUX=enforcing 改为 SELINUX=disabled ,重启
  3. 在docker run 中加入 --privileged=true 给容器加上特定权限
  4. 修改 SELinux 规则 chcon -t mysqld_db_t -R /opt/docker/mysql/data

配置前

配置前

@ 导包的类无法点击跳转,也不识别

配置

在项目根目录添加配置文件 webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
/**
* 不是真实的 webpack 配置,仅为兼容 webstorm 和 intellij idea 代码跳转
*/

module.exports = {
resolve: {
alias: {
'@': require('path').resolve(__dirname, 'src'), // eslint-disable-line
},
},
};

然后,在 idea 的 preference -> language & frameworks -> javascript -> webpack 路径到更目录下的webpack.config.js

完成

为什么没有 Windows 下的编译安装

因为官网已经提供的编译好的 exe 包,双击运行就会解压到特定的目录了,除此之外官网还提供了 ios 版和 安卓版
这里着重记录下 CentOS , Ubuntu 和 Mac OS 下的安装,因为官网没有提供编译好的包

条件

  1. GCC 4.4.x or later
  2. CMake 2.8.7 or higher
  3. Git
  4. GTK+2.x or higher, including headers (libgtk2.0-dev)
  5. pkg-config
  6. Python 2.6 or later and Numpy 1.5 or later with developer packages (python-dev, python-numpy)
  7. ffmpeg or libav development packages: libavcodec-dev, libavformat-dev, libswscale-dev
  8. [optional] libtbb2 libtbb-dev
  9. [optional] libdc1394 2.x
  10. [optional] libjpeg-dev, libpng-dev, libtiff-dev, libjasper-dev, libdc1394-22-dev
  11. [optional] CUDA Toolkit 6.5 or higher

步骤

  1. 安装常用的开发编译工具包, Centos 的命令为: yum groupinstall “Development Tools”, Ubuntu 的命令为: apt-get install build-essential
  2. 安装 cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
  3. mkdir opencv4; cd opencv4
  4. git clone https://github.com/opencv/opencv.git
  5. git clone https://github.com/opencv/opencv_contrib.git
  6. cd opencv
  7. mkdir build
  8. cd build
  9. cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local .. (如果不工作的话,删除 -D的空格,cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local ..)
  10. make -j7 # runs 7 jobs in parallel 使用7个并行任务来编译
  11. 生成文档 cd ~/opencv/build/doc/; make -j7 doxygen
  12. make install

编译好的包

  1. centos7 版: http://cloud.joylau.cn:1194/s/kUoNelmj1SX810K 或者 https://pan.baidu.com/s/1qaZ-TbF0xP0DxaEJKbdt-A 提取码: jkir
  2. Ubuntu 16.04 版: http://cloud.joylau.cn:1194/s/TsNRKwxJhM0v0HE 或者 https://pan.baidu.com/s/1ha6nATLrSt5WPL1iQlmWSg 提取码: gduu
  3. java 调用所需 opencv-410.jar 包: //s3.joylau.cn:9000/blog/opencv-410.jar

Mac OS 上

  1. AppStore 上安装 XCode, 安装完成打开 XCode , 同意 license
  2. 安装 HomeBrew
  3. 安装必要依赖: Python 3, CMake and Qt 5
1
2
3
brew install python3
brew install cmake
brew install qt5
  1. 安装环境
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
mkdir ~/opencv4
git clone https://github.com/opencv/opencv.git
git clone https://github.com/opencv/opencv_contrib.git

# 变量定义
cwd=$(pwd)
cvVersion="master"
QT5PATH=/usr/local/Cellar/qt/5.12.2

rm -rf opencv/build
rm -rf opencv_contrib/build

# Create directory for installation
mkdir -p installation/OpenCV-"$cvVersion"

sudo -H pip3 install -U pip numpy
# Install virtual environment
sudo -H python3 -m pip install virtualenv virtualenvwrapper
VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3
echo "VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3" >> ~/.bash_profile
echo "# Virtual Environment Wrapper" >> ~/.bash_profile
echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bash_profile
cd $cwd
source /usr/local/bin/virtualenvwrapper.sh

############ For Python 3 ############
# create virtual environment 由于 mac OS 本身使用的是 Python 2.7 , 而一些本身的应用依赖于 Python 2 ,为了不影响原来的环境,这里创建一个 Python3 的虚拟环境来进行编译
mkvirtualenv OpenCV-"$cvVersion"-py3 -p python3
workon OpenCV-"$cvVersion"-py3

# now install python libraries within this virtual environment
pip install cmake numpy scipy matplotlib scikit-image scikit-learn ipython dlib

# quit virtual environment
deactivate
######################################

cd opencv
mkdir build
cd build

cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=$cwd/installation/OpenCV-"$cvVersion" \
-D INSTALL_C_EXAMPLES=ON \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D WITH_TBB=ON \
-D WITH_V4L=ON \
-D OPENCV_SKIP_PYTHON_LOADER=ON \
-D CMAKE_PREFIX_PATH=$QT5PATH \
-D CMAKE_MODULE_PATH="$QT5PATH"/lib/cmake \
-D OPENCV_PYTHON3_INSTALL_PATH=~/.virtualenvs/OpenCV-"$cvVersion"-py3/lib/python3.7/site-packages \
-D WITH_QT=ON \
-D WITH_OPENGL=ON \
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \
-D BUILD_EXAMPLES=ON ..

make -j$(sysctl -n hw.physicalcpu)
make install

  1. cmake 后输出如下:
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
--   OpenCV modules:
-- To be built: aruco bgsegm bioinspired calib3d ccalib core cvv datasets dnn dnn_objdetect dpm face features2d flann freetype fuzzy gapi hfs highgui img_hash imgcodecs imgproc java java_bindings_generator line_descriptor ml objdetect optflow phase_unwrapping photo plot python2 python3 python_bindings_generator quality reg rgbd saliency shape stereo stitching structured_light superres surface_matching text tracking ts video videoio videostab xfeatures2d ximgproc xobjdetect xphoto
-- Disabled: world
-- Disabled by dependency: -
-- Unavailable: cnn_3dobj cudaarithm cudabgsegm cudacodec cudafeatures2d cudafilters cudaimgproc cudalegacy cudaobjdetect cudaoptflow cudastereo cudawarping cudev hdf js matlab ovis sfm viz
-- Applications: tests perf_tests examples apps
-- Documentation: NO
-- Non-free algorithms: NO
--
-- GUI:
-- QT: YES (ver 5.12.2)
-- QT OpenGL support: YES (Qt5::OpenGL 5.12.2)
-- Cocoa: YES
-- OpenGL support: YES (/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/OpenGL.framework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/OpenGL.framework)
-- VTK support: NO
--
-- Media I/O:
-- ZLib: build (ver 1.2.11)
-- JPEG: build-libjpeg-turbo (ver 2.0.2-62)
-- WEBP: build (ver encoder: 0x020e)
-- PNG: build (ver 1.6.36)
-- TIFF: build (ver 42 - 4.0.10)
-- JPEG 2000: build (ver 1.900.1)
-- OpenEXR: build (ver 1.7.1)
-- HDR: YES
-- SUNRASTER: YES
-- PXM: YES
-- PFM: YES
--
-- Video I/O:
-- DC1394: NO
-- FFMPEG: YES
-- avcodec: YES (58.35.100)
-- avformat: YES (58.20.100)
-- avutil: YES (56.22.100)
-- swscale: YES (5.3.100)
-- avresample: YES (4.0.0)
-- GStreamer: NO
-- AVFoundation: YES
-- v4l/v4l2: NO
--
-- Parallel framework: GCD
--
-- Trace: YES (with Intel ITT)
--
-- Other third-party libraries:
-- Intel IPP: 2019.0.0 Gold [2019.0.0]
-- at: /Users/joylau/opencv4/opencv/build/3rdparty/ippicv/ippicv_mac/icv
-- Intel IPP IW: sources (2019.0.0)
-- at: /Users/joylau/opencv4/opencv/build/3rdparty/ippicv/ippicv_mac/iw
-- Lapack: YES (/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Accelerate.framework)
-- Eigen: NO
-- Custom HAL: NO
-- Protobuf: build (3.5.1)
--
-- OpenCL: YES (no extra features)
-- Include path: NO
-- Link libraries: -framework OpenCL
--
-- Python 2:
-- Interpreter: /usr/bin/python2.7 (ver 2.7.10)
-- Libraries: /usr/lib/libpython2.7.dylib (ver 2.7.10)
-- numpy: /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy/core/include (ver 1.8.0rc1)
-- install path: lib/python2.7/site-packages
--
-- Python 3:
-- Interpreter: /usr/local/bin/python3 (ver 3.7.2)
-- Libraries: /usr/local/Frameworks/Python.framework/Versions/3.7/lib/libpython3.7m.dylib (ver 3.7.2)
-- numpy: /usr/local/lib/python3.7/site-packages/numpy/core/include (ver 1.16.2)
-- install path: /Users/joylau/.virtualenvs/OpenCV-master-py3/lib/python3.7/site-packages
--
-- Python (for build): /usr/bin/python2.7
--
-- Java:
-- ant: /Users/joylau/dev/apache-ant-1.10.5/bin/ant (ver 1.10.5)
-- JNI: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/JavaVM.framework/Headers /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/JavaVM.framework/Headers /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/JavaVM.framework/Headers
-- Java wrappers: YES
-- Java tests: YES
--
-- Install to: /Users/joylau/opencv4/installation/OpenCV-master
-- -----------------------------------------------------------------
--
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/joylau/opencv4/opencv/build
  1. 编译好的安装包: http://cloud.joylau.cn:1194/s/6GMLl09ZAYNAUMU 或者: https://pan.baidu.com/s/1YBxUD_vB1zKOcxHeAtn6Xw 提取码: twsq

遇到的问题

CentOS 上 CMake 版本太低的解决方法

  1. yum 上安装的版本太低,先卸载掉版本低的,yum remove cmake

  2. cd /opt
    tar zxvf cmake-3.10.2-Linux-x86_64.tar.gz

  3. vim /etc/profile
    export CMAKE_HOME=/opt/cmake-3.10.2-Linux-x86_64
    export PATH=$PATH:$CMAKE_HOME/bin

  4. source /etc/profile

没有生成 opencv-410.jar

1
2
3
4
5
6
Java:                          
-- ant: /bin/ant (ver 1.9.4)
-- JNI: /usr/lib/jvm/java-1.8.0-openjdk/include /usr/lib/jvm/java-1.8.0-openjdk/include/linux /usr/lib/jvm/java-1.8.0-openjdk/include
-- Java wrappers: YES
-- Java tests: NO

需要 ant 环境,安装后即可, java 即可进行调用

IDEA 及 Spring Boot 项目中的使用

  1. 下载 opencv-410.jar 包,引入到项目中
1
2
3
4
5
6
7
8
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

compile fileTree(dir:'libs',include:['*.jar'])
}
  1. 配置动态库路径, vm options: -Djava.library.path=/home/joylau/opencv4/opencv/build/lib

vm options

mac os 下路径为: -Djava.library.path=/Users/joylau/opencv4/installation/OpenCV-master/share/java/opencv4

  1. 加载动态库
1
2
3
4
5
6
7
8
9
@SpringBootApplication
public class OpencvTestApplication {

public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
System.out.println(Core.VERSION);
SpringApplication.run(OpencvTestApplication.class, args);
}
}
  1. 脸部识别 demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static void testFace() {
// 1 读取OpenCV自带的人脸识别特征XML文件
CascadeClassifier facebook = new CascadeClassifier("/home/joylau/opencv4/opencv/data/haarcascades/haarcascade_frontalface_alt.xml");
// 2 读取测试图片
Mat image = Imgcodecs.imread("/home/joylau/图片/image-test-4.jpg");
// 3 特征匹配
MatOfRect face = new MatOfRect();
facebook.detectMultiScale(image, face);
// 4 匹配 Rect 矩阵 数组
Rect[] rects = face.toArray();
System.out.println("匹配到 " + rects.length + " 个人脸");
// 5 为每张识别到的人脸画一个框
for (int i = 0; i < rects.length; i++) {
Imgproc.rectangle(image,new Point(rects[i].x, rects[i].y), new Point(rects[i].x + rects[i].width, rects[i].y + rects[i].height), new Scalar(0, 0, 255));
Imgproc.putText(image,"face-" + i, new Point(rects[i].x, rects[i].y),Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(0, 255, 0),1,Imgproc.LINE_AA,false);
}
// 6 展示图片
HighGui.imshow("人脸-匹配", image);
HighGui.waitKey(0);
}

test_face

注: 图片来自微博

  1. 边缘检测 demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static void testContours() {
//1 获取原图
Mat src = Imgcodecs.imread("/home/joylau/图片/image-test.jpg");
//2 图片灰度化
Mat gary = new Mat();
Imgproc.cvtColor(src, gary, Imgproc.COLOR_RGB2GRAY);
//3 图像边缘处理
Mat edges = new Mat();
Imgproc.Canny(gary, edges, 200, 500, 3, false);
//4 发现轮廓
List<MatOfPoint> list = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(edges, list, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
//5 绘制轮廓
for (int i = 0, len = list.size(); i < len; i++) {
Imgproc.drawContours(src, list, i, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA);
}
HighGui.imshow("边缘检测", src);
HighGui.waitKey(0);
}

test_source
test_contours

  1. 实时人脸识别
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
/**
* OpenCV-4.0.0 实时人脸识别
*
*/
public static void videoFace() {
VideoCapture capture=new VideoCapture(0);
Mat image=new Mat();
int index=0;
if (capture.isOpened()) {
do {
capture.read(image);
HighGui.imshow("实时人脸识别", getFace(image));
index = HighGui.waitKey(1);
} while (index != 27);
}
}

/**
* OpenCV-4.0.0 人脸识别
* @param image 待处理Mat图片(视频中的某一帧)
* @return 处理后的图片
*/
public static Mat getFace(Mat image) {
// 1 读取OpenCV自带的人脸识别特征XML文件
CascadeClassifier facebook=new CascadeClassifier("/Users/joylau/opencv4/opencv/data/haarcascades/haarcascade_frontalface_alt.xml");
// 2 特征匹配类
MatOfRect face = new MatOfRect();
// 3 特征匹配
facebook.detectMultiScale(image, face);
Rect[] rects=face.toArray();
log.info("匹配到 "+rects.length+" 个人脸");
// 4 为每张识别到的人脸画一个圈
for (Rect rect : rects) {
Imgproc.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0));
Imgproc.putText(image, "Human", new Point(rect.x, rect.y), Imgproc.FONT_HERSHEY_SIMPLEX, 2.0, new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, false);
//Mat dst=image.clone();
//Imgproc.resize(image, image, new Size(300,300));
}
return image;
}
0%