视觉组学习内容:Zhang-Suen骨架提取算法
前言
这是视觉组dalao给大家布置的学期末学习任务。
因为之前没有接触过linux,环境也没有配置好,对很多操作不够熟悉,做这个任务从头到尾大约花了两天合计15个小时的时间,中间还问过dalao两个小时左右的问题(此处给大佬比心),虽然和大佬说的三个小时相去甚远但是最后还是赶在ddl之前完成了任务。
总的来说收获很多,了解了ZhangSuen骨架提取算法的原理和算法实现,复习了面向对象编程的C++,还对linux系统(Ubuntu)有了进一步的了解,写第一篇博文记录一下大致过程和心得。
任务要求
使用ZhangSuen骨架提取算法实现简化数字轮廓
效果如图所示:
实现要求
- 使用类简化代码量,增强可读性;类的声明放在.h头文件中,函数实现单独放在.cpp文件中
- 不允许包含<opencv2/opencv.hpp>;把要用到的模块搞清楚,包含最少的头文件
- Linux下使用命令行或makefile编译程序,不允许使用pkg-config,不能有-lopencv_*这种写法,需要用到哪个库文件就明确调用哪个库文件
任务完成流程概要
1.在Windows平台的VS 2017上写好函数风格代码
这里参考了CSDN上和博客园的两篇文章,分别涉及原理和代码实现,链接附上
- 原理
两种图像骨架提取算法的研究(1)原理部分 博主:zhubaohua_bupt - 代码实现
【20160924】GOCVHelper 图像增强部分(3) 博主:jsxyhelu - 了解opencv各个模块的功能
要求2是不能使用opencv.hpp,那就要了解其他模块的功能。
参考Opencv3.2各个模块功能详细简介(包括与Opencv2.4的区别) 博主:朱铭德
2.使用类对原函数进行封装与调试
- 复习C++面向对象编程
类的成员函数定义、初始化函数等概念的复习 - 类的声明/定义/使用
类的封装风格参考过图像处理之Zhang Suen细化算法 博主:gloomyfish,虽然是java平台的opencv,但是可以启发灵感
代码应该是在Ubuntu的环境中直接写的,但是由于没有配置,所以先在Windows写好以后再到Ubuntu里面使用,代码见后。
3.配置Ubuntu环境
- 换源
换源是为了提高网络速度。这里弄了一阵子的时间,原因是因为很多教程都不是从零起步,而且有一定问题。很多网站上都是直接用vim(有可能没安装) 或者使用“打开”/"编辑"这种用语,对初入Ubuntu的小白很不友好。
正确操作是先按Ctrl+Alt+T打开终端,然后输入sudo gedit /etc/apt/sources.list
,在打开的文件窗口中将内容全部删除并复制上镜像源地址:参考Ubuntu 18.04换国内源 中科大源 阿里源 163源 清华源 博主:nudt_qxx,从中选一个复制粘贴(终端中的粘贴是Ctrl+Shift+V
),然后把结尾CSDN附带的注释删除再保存就可以了,不知道需不需要重启。 - 安装cmake(虽然没用上)
用于cmake和makefile(?) - 安装opencv环境
任务3要求在终端中编译(编译用g++,这个我是自己用sudo apt-get install g++
安装的)或者用makefile。不配置opencv的环境无法编译项目。
在终端中使用sudo apt-get install synapic
,再在系统自带的新立得安装包管理器里搜索opencv就可以下载了。出乎意料的方便。 - 安装编辑器及其他软件
4.在Terminal中使用g++完成编译
- 使用g++编译
大佬实力操作。
到这一步要准备编译。首先把我写好的两个cpp和一个.h文件放到同一个文件夹下,然后在右键菜单中点击“在终端打开”,输入g++ -o Zhenglin main.cpp class.cpp `pkg-config --libs --cflags opencv`
要求3是不允许使用pkg-config和-lopencv_*,那写g++ -o z class.cpp main.cpp -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs
也是正确的。注意,虽然头文件没有,但是终端还是要通过-l调用-lopencv_imgcodecs,否则会报错。
如果程序正确,编译成功了就会生成一个叫做Zhenglin的文件,此时再输入./Zhenglin
就可以执行这个文件了。可以看到输出结果。
注意:使用文件时要注意有没有输入的图片,需要在源文件中修改路径。
封装代码
头文件
#ifndef C_ZHANGSUEN_DIY_H
#define C_ZHANGSUEN_DIY_H
#include<opencv2/core.hpp> ///基本数据类型
#include<opencv2/imgproc/imgproc.hpp> ///图像存取&线性变换
#include<opencv2/highgui.hpp> ///输出接口&交互接口
using namespace std;
using namespace cv;
class Skeleton {
public:
void initialize();
void getDst(Mat src);
void DstToImg();
int getAP(int i, int j);
int getBP(int i, int j);
bool condition34IsOK_1(int i, int j);
bool condition34IsOK_2(int i, int j);
void erasePoint(int i, int j);
Mat Mat_return();
Skeleton();
private:
Mat dst;
Mat tmpImg;
int height;
int width;
};
//主函数调用函数 函数调用类 并在函数体中判断条件 最后利用函数体返回
Mat skeleton(Mat src);
#endif //C_ZHANGSUEN_DIY_H
主文件
//*recoverd by Zhenglin/on 2018/12/29
#include "ZhangSuen_DIY.h"/
Mat skeleton(Mat src);
int main(){
Mat src = imread("E:/Python/6.jpg");
Mat gray, binary, dst, skeleton_image;
//图像预处理
cvtColor(src, gray, CV_BGR2GRAY);
threshold(gray, binary, 200, 255, CV_THRESH_BINARY); ///二值化需要在灰度基础上进行
namedWindow("Binary", 0); ///只能对黑底白线进行提取,否则需要位反
imshow("Binary", binary);
//骨架提取
skeleton_image = skeleton(binary);<